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 java.util.concurrent.locks.*; 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.*; import java.text.*; import java.nio.charset.Charset; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.TimeZone; import javax.swing.undo.UndoManager; import static x30_pkg.x30_util.DynamicObject; import java.awt.datatransfer.StringSelection; import java.nio.file.Path; import javax.swing.border.*; import javax.swing.event.AncestorListener; import javax.swing.event.AncestorEvent; import javax.swing.Timer; import javax.imageio.metadata.*; import javax.imageio.stream.*; import java.awt.geom.*; class main { static String passwordSalt() { File f = programSecretFile("password-salt"); if (fileExists(f)) return loadTextFile(f); File global = javaxSecretDir("password-salt"); if (fileExists(global)) return loadTextFile(global); return loadTextFileOrCreateWithRandomID(f); } static class PostReferenceable extends Concept {} // Note: If you change/add fields here, make sure to edit // GazelleBEA.findOrCreateUserForLogin static class User extends PostReferenceable { static final String _fieldOrder = "name passwordMD5 contact isMaster botToken lastSeen"; String name; SecretValue passwordMD5; String contact; // e.g. mail address boolean isMaster = false; SecretValue botToken = aSecretGlobalID/*UnlessLoading*/(); long lastSeen; String name() { return name; } public String toString() { return nempty(name) ? "User " + name : super.toString(); } } static class UserCreatedObject extends PostReferenceable { static final String _fieldOrder = "creator botInfo"; Ref creator = new Ref(); String botInfo; // info on how this was made. set to anything non-empty if made by bot //S nameInRepo; // unique by user, for URL } static class UserPost extends UserCreatedObject { String type, title, text; boolean isPublic = true, hidden; boolean creating = false; // new post, still editing long xmodified; // modification timestamp that also includes replies long bumped; // modification timestamp that includes "bumps" (re-posted identical bot reply) static List fieldsToSaveOnDelete = ll("id", "text", "title", "type", "creatorID", "postRefs", "postRefTags", "hidden", "isPublic", "created", "_modified", "xmodified", "botInfo", "creating"); // TODO: migrate to this eventually (but need to change CRUD too) //int flags; //static int F_PUBLIC = 1, F_HIDDEN = 2; String text() { return text; } RefL postRefs = new RefL(); List postRefTags = new ArrayList(); static String _fieldOrder = "title text type postRefs isPublic"; public String toString() { String content = nempty(title) ? shorten(title) : escapeNewLines(shorten(text)); //S _type = isReply() ? "reply" : "post"; //if (hidden) _type += " (hidden)"; //ret firstToUpper(_type) + " by " + author() + ": " + content; return content + " [by " + author() + "]"; } boolean isReply() { return nempty(postRefs); } boolean isBotPost() { return nempty(botInfo); } boolean isMasterMade() { return creator.has() && creator.get().isMaster; } String author() { String author = userName(creator.get()); if (isBotPost()) author += "'s bot " + quote(botInfo); return author; } void change() { super.change(); ((DynGazelleRocks) botMod()).distributePostChanges(this); } List> postRefsWithTags() { return zipTwoListsToPairs_lengthOfFirst(postRefs, postRefTags); } List postRefsWithTag(String tag) { return pairsAWhereB(postRefsWithTags(), t -> eqic_unnull(t, tag)); } UserPost primaryRefPost() { return optCast(UserPost.class, first(postRefsWithTag(""))); } void _setModified(long modified) { super._setModified(modified); setXModified(modified); } void bump() { bumped = now(); _change_withoutUpdatingModifiedField(); for (UserPost p : syncInstancesOf(UserPost.class, postRefs)) p.setXModified(); } long modifiedOrBumped() { return max(_modified, bumped); } // Notify of changes in replies void setXModified() { setXModified(now()); } void setXModified(long modified) { boolean changed = modified > xmodified; xmodified = modified; _change_withoutUpdatingModifiedField(); if (changed) ((DynGazelleRocks) botMod()).distributePostChanges(this); } void _backRefsModified() { super._backRefsModified(); setXModified(now()); } // -1 if no user post found in hierarchy int distanceToUserPost() { int n = 0; UserPost post = this; while (post != null && n < 100) { if (!post.isBotPost()) return n; post = post.primaryRefPost(); } return -1; } boolean isJavaXCode() { return eqicOrSwicPlusSpace(type, "JavaX Code"); } } // end of UserPost /*concept BotHandle > UserCreatedObject { S globalID = aGlobalIDUnlessLoading(); S comment; SecretValue token; }*/ abstract static class DynGazelleRocks extends DynNewBot2 { transient boolean inlineSearch = false; // show search form in nav links static boolean _switchableField_webPushEnabled = true; boolean webPushEnabled = true; static boolean _switchableField_showPostStats = true; boolean showPostStats = false; static boolean _switchableField_showSuggestorBot = true; boolean showSuggestorBot = true; static boolean _switchableField_showMetaBotOnEveryPage = true; boolean showMetaBotOnEveryPage = true; static boolean _switchableField_delayAfterSuggestion = true; int delayAfterSuggestion = 1000; static boolean _switchableField_defaultBotPost = true; int defaultBotPost = 238410; static boolean _switchableField_teamPostID = true; long teamPostID; static boolean _switchableField_favIconID = true; long favIconID; // NB: it's an UploadedFile id in this DB static boolean _switchableField_defaultFavIconSnippet = true; String defaultFavIconSnippet = gazelleFavIconSnippet(); static boolean _switchableField_gazelleBotURL = true; String gazelleBotURL = null; //"https://gazellebot.botcompany.de"; static boolean _switchableField_salterService = true; String salterService; transient double systemLoad; transient long processSize; void init() { dm_require("#1017856/SystemLoad"); dm_vmBus_onMessage("systemLoad", new VF1() { public void get(Double load) { try { if (setField("systemLoad" , load)) distributeDivChanges("serverLoadRightHemi"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (setField(systemLoad := load))\r\n distributeDivChanges(\"serverLoadRi..."; }}); dm_vmBus_onMessage("processSize", new VF1() { public void get(Long processSize) { try { if (setField("processSize", processSize)) distributeDivChanges("memRightHemi"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (setField(+processSize))\r\n distributeDivChanges(\"memRightHemi\");"; }}); botName = heading = adminName = "gazelle.rocks"; templateID = "#1030086"; cssID = /*#1030233*/"237267"; // now a post ID enableUsers = true; useWebSockets = true; showRegisterLink = true; showTalkToBotLink = false; alwaysRedirectToHttps = true; redirectOnLogout = true; showFullErrors = true; inlineSearch = true; lockWhileDoingBotActions = false; // fix the _gazelle_text bug? showMailSenderInfo = false; if (empty(salterService)) passwordSalt(); // make early } void makeIndices() { super.makeIndices(); indexConceptFieldCI(User.class, "name"); indexConceptFieldDesc(UserPost.class, "xmodified"); indexConceptFieldDesc(UserPost.class, "_modified"); indexConceptFieldCI(UserPost.class, "botInfo"); } void start() { init(); super.start(); db_mainConcepts().modifyOnCreate = true; printConceptIndices(); onConceptsChange(new Runnable() { int postCount = countConcepts(UserPost.class); public void run() { AutoCloseable __11 = enter(); try { int count = countConcepts(UserPost.class); if (count != postCount) { postCount = count; distributeDivChanges("postCount"); } } finally { _close(__11); }} }); internalUser(); // legacy clean-up /*for (UserPost post) { for i to 5: cset(post, "postRefTags_" + i, null); }*/ } // web socket stuff void requestServed() { distributeDivChanges("webRequestsRightHemi"); } class WebSocketInfo extends Meta implements AutoCloseable { boolean _isTransient() { return true; } String uri; Map params; Req req; WeakReference webSocket; Set> messageHandlers = syncLinkedHashSet(); Set> binMessageHandlers = syncLinkedHashSet(); Set closeHandlers = syncLinkedHashSet(); // Any helper can store things here like jqueryLoaded := true Map misc = syncMap(); WebSocketInfo(Object webSocket) { this.webSocket = weakRef(webSocket); setFieldToIVF1Proxy(webSocket, "onMessage" , msg -> { AutoCloseable __12 = enter(); try { try { Object opCode = call(msg, "getOpCode"); byte opCodeValue = (byte) (call(opCode, "getValue")); boolean isBinary = opCodeValue == 2; // isso! if (isBinary) { byte[] data = (byte[]) (rcall("getBinaryPayload", msg)); pcallFAll(binMessageHandlers, data); } else { String data = rcall_string("getTextPayload", msg); pcallFAll(messageHandlers, data); } } catch (Throwable __e) { _handleException(__e); } } finally { _close(__12); }}); } final String subUri() { return subURI(); } String subURI() { return req.subURI(); } String uri() { return uri; } Map params() { return params; } String get(String param) { return mapGet(params, param); } void setParams(Map params) { this.params = params; req.params = params; } Object dbRepresentation; Set> liveDivs = new HashSet(); // id/content info void eval(String jsCode) { eval(jsCode, null); } void eval(String jsCode, Object... __) { jsCode = jsDollarVars(jsCode, __); if (empty(jsCode)) return; dm_call(webSocket.get(), "send", jsonEncode(litmap("eval" , jsCode))); } void send(String jsonData) { if (empty(jsonData)) return; dm_call(webSocket.get(), "send", jsonData); } AutoCloseable onStringMessage(IVF1 onMsg) { return tempAdd(messageHandlers, onMsg); } AutoCloseable onBinaryMessage(IVF1 onMsg) { return tempAdd(binMessageHandlers, onMsg); } AutoCloseable onClose(Runnable r) { return tempAdd(closeHandlers, r); } void callCloseHandlers() { pcallFAll(closeHandlers); } public void close() { cleanUp(webSocket.get()); } } transient Map webSockets = syncWeakHashMap(); void cleanMeUp_webSockets() { closeAllKeysAndClear((Map) webSockets); } // funny: useWebSockets exists in DynNewBot2, // but only in this subclass do we define handleWebSocket void handleWebSocket(Object ws) { set(ws, "onClose" , new Runnable() { public void run() { try { var info = webSockets.get(ws); if (info != null) { try { onWebSocketClose(info); } catch (Throwable __e) { _handleException(__e); }} webSockets.remove(ws); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "var info = webSockets.get(ws);\r\n if (info != null) pcall { onWebSocketCl..."; }}); set(ws, "onOpen" , new Runnable() { public void run() { try { AutoCloseable __13 = enter(); try { String uri = (String) (rcall("getUri", ws)); Map params = (Map) (rcall("getParms", ws)); print("WebSocket opened! uri: " + uri + ", params: " + params); WebSocketInfo info = new WebSocketInfo(ws); IWebRequest webRequest = proxy(IWebRequest.class, call(ws, "webRequest")); info.req = webRequestToReq(webRequest); String cookie = cookieFromWebRequest(webRequest); AuthedDialogID auth = authObject(cookie); fillReqAuthFromCookie(info.req, cookie, auth); AutoCloseable __14 = tempSetTL(currentReq, info.req); try { // why not info.uri = uri; info.params = params; webSockets.put(ws, info); try { onNewWebSocket(info); } catch (Throwable __e) { _handleException(__e); } long objectID = toLong(params.get("objectID")); long modified = toLong(params.get("modified")); if (objectID != 0) { UserPost c = getConceptOpt(UserPost.class, objectID); print("Modification: " + c.xmodified + " / " + c._modified + " / " + modified); if (c != null && c.xmodified > modified) reloadBody(ws); } } finally { _close(__14); }} finally { _close(__13); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); \r\n String uri = (String) (rcall getUri(ws));\r\n Map..."; }}); setFieldToIVF1Proxy(ws, "onMessage" , msg -> { AutoCloseable __15 = enter(); try { try { WebSocketInfo info = webSockets.get(ws); if (info == null) return; String data = rcall_string("getTextPayload", msg); Map map = jsonDecodeMap(data); Object div = map.get("liveDiv"); if (div instanceof String) { String contentDesc = (String) div; syncAdd(info.liveDivs, pair(((String) div), (Object) contentDesc)); reloadDiv(ws, ((String) div), calcDivContent(contentDesc)); } } catch (Throwable __e) { _handleException(__e); }} finally { _close(__15); }}); } String serveRegisterForm(Map params) { String user = trim(params.get("user")); String pw = trim(params.get("f_pw")); String pw2 = trim(params.get("pw2")); String redirect = params.get("redirect"); String contact = trim(params.get("contact")); redirect = dropParamFromURL(redirect, "logout"); // don't log us out right again if (empty(redirect)) redirect = baseLink + "/"; List msgs = new ArrayList(); if (nempty(user) || nempty(pw)) { Lock __5 = dbLock(); lock(__5); try { if (empty(user)) msgs.add("Please enter a user name"); else if (l(user) < 4) msgs.add("Minimum user name length: 4 characters"); else if (l(user) > 30) msgs.add("Maximum user name length: 30 characters"); else if (hasConceptIC(User.class, "name" , user)) msgs.add("This user exists, please choose a different name"); if (regexpContainsIC("[^a-z0-9\\-\\.]", user)) msgs.add("Bad characters in user name (please use only a-z, 0-9, - and .)"); if (empty(pw)) msgs.add("Please enter a password. Don't use one of your regular (important) passwords! Just make one up and let the browser save it."); else if (l(pw) < 6) msgs.add("Minimum password length: 6 characters"); if (neq(pw, pw2)) msgs.add("Passwords don't match"); if (empty(msgs)) { User userObj = cnew(User.class, "name" , user, "passwordMD5" , new SecretValue(hashPW(pw)), "contact", contact); vmBus_send("userCreated", userObj); return hrefresh(5.0, redirect) + "User " + user + " created! Redirecting..."; } } finally { unlock(__5); } } return hhtml(hhead(htitle("Register new user") + hsansserif() + hmobilefix() + hresponstable()) + hbody(hfullcenter( h3_htmlEncode(adminName + " | Register new user") + hpostform( hhidden("redirect", redirect) + tag("table", (empty(msgs) ? "" : tr(td() + td() + td(htmlEncode2_nlToBr(lines_rtrim(msgs))))) + tr(td_valignTop("Choose a user name:") + td_valignTop(hinputfield("user", user) + "
" + "(Minimum length 4. Characters allowed: a-z, 0-9, - and .)")) + tr(td_valignTop("Choose a password:") + td_valignTop(hpassword("f_pw" , pw) + "
" + "(Minimum length 6 characters)")) + tr(td_valignTop("Repeat the password please:") + td(hpassword("pw2", pw2))) + tr(td_valignTop("Way to contact you (e.g. e-mail) - optional:") + td(hinputfield("contact", contact))) + tr(td() + td(hsubmit("Register"))), "class" , "responstable"), "action" , baseLink + "/register") ))); } boolean calcMasterAuthed(Req req) { return super.calcMasterAuthed(req) || req.auth != null && req.auth.user.has() && req.auth.user.get().isMaster; } Object serveOtherPage(Req req) { String uri = req.uri; Matches m = new Matches(); if (eq(uri, "/register")) return serveRegisterForm(req.params); if (eq(uri, "/becomeMaster") && req.auth != null && req.auth.user != null) { String pw = trim(req.params.get("masterPW")); if (eq(pw, realPW())) { cset(req.auth.user, "isMaster" , true); return "You are master, " + req.auth.user + "!"; } if (nempty(pw)) return "Bad master PW"; return hhtml(hhead(htitle("Become master") + hsansserif() + hmobilefix() + hresponstable()) + hbody(hfullcenter( h3_htmlEncode(adminName + " | Become master") + hpostform( tag("table", tr(td_valignTop("You are:") + td_valignTop(htmlEncode2(req.auth.user.get().name))) + tr(td_valignTop("Enter the master password:") + td_valignTop(hpassword("masterPW" , pw))) + tr(td() + td(hsubmit("Become master user"))), "class" , "responstable"), "action" , baseLink + "/becomeMaster") ))); } if (swic(uri, "/text/", m)) { long id = parseLong(m.rest()); UserPost post = getConceptOpt(UserPost.class, id); if (post == null) return serve404("Post with ID " + id + " not found"); if (!post.isPublic) return serve404("Post is not public"); String ct = req.params.get("ct"); Object response = nempty(ct) ? serveWithContentType(post.text(), ct) : serveText(post.text()); if (eq(ct, "text/javascript")) addHeader("Service-Worker-Allowed", "/", response); return response; } if (swic(uri, "/postRefs/", m)) { long id = parseLong(m.rest()); UserPost post = getConceptOpt(UserPost.class, id); if (post == null) return serve404("Post with ID " + id + " not found"); if (!post.isPublic) return serve404("Post is not public"); return serveJSON(lmap(__40 -> conceptID(__40), post.postRefs)); } if (swic(uri, "/postRefsWithTags/", m)) { long id = parseLong(m.rest()); UserPost post = getConceptOpt(UserPost.class, id); if (post == null) return serve404("Post with ID " + id + " not found"); if (!post.isPublic) return serve404("Post is not public"); return serveJSON(map(post.postRefsWithTags(), p -> litorderedmap("id" , conceptID(p.a), "as" , p.b))); } // Serve a post String uri2 = dropSlashPrefix(uri); if (isInteger(uri2)) { long id = parseLong(uri2); { Object __9= serveIntegerLink(req, id); if (__9 != null) return __9; } } { Object __10= serveOtherPage2(req); if (__10 != null) return __10; } return super.serveOtherPage(req); } static boolean _switchableField_bot_maxPollSeconds = true; double bot_maxPollSeconds = 600; static boolean _switchableField_bot_pollInterval = true; int bot_pollInterval = 500; Class defaultCRUDClass() { return Sequence.class; } Object serveOtherPage2(Req req) { Matches m = new Matches(); String uri = dropTrailingSlashIfNemptyAfterwards(req.uri); if (eq(uri, "/admin")) return hrefresh(baseLink + "/crud/" + shortClassName(defaultCRUDClass())); if (eq(uri, "/latestPost")) { UserPost post = highestConceptByField(UserPost.class, "created"); if (post == null) return serve404("No posts in database"); return servePost(post, req); } if (eq(uri, "/latestModifiedPost")) { UserPost post = highestConceptByField(UserPost.class, "_modified"); if (post == null) return serve404("No posts in database"); return servePost(post, req); } if (eq(uri, "/rootPosts")) { Collection posts = sortedByConceptIDDesc(filter(list(UserPost.class), post -> empty(post.postRefs))); framer().navBeforeTitle = true; framer().title = "Root Posts"; framer().add(ul(lmap(__41 -> postToHTMLWithDate(__41), posts))); return framer().render(); } if (eq(uri, "/allPosts")) { Collection posts = sortedByConceptIDDesc(list(UserPost.class)); framer().navBeforeTitle = true; framer().title = "All Posts"; framer().add(ul(lmap(__42 -> postToHTMLWithDate(__42), posts))); return framer().render(); } if (eq(uri, "/latestPosts")) { Collection posts = takeFirst(50, sortedByConceptIDDesc(list(UserPost.class))); framer().navBeforeTitle = true; framer().title = "Latest Posts & Replies"; framer().add(ul(lmap(__43 -> postToHTMLWithDate(__43), posts))); return framer().render(); } if (eq(uri, "/latestModifiedPosts")) { Collection posts = takeFirst(50, sortedByFieldDesc("_modified", list(UserPost.class))); framer().navBeforeTitle = true; framer().title = "Latest Modified Posts & Replies"; framer().add(ul(lmap(__44 -> postToHTMLWithDate(__44), posts))); return framer().render(); } if (eq(uri, "/mainPosts")) { Collection posts = takeFirst(50, sortedByConceptID(conceptsWhereCI(UserPost.class, "type" , "Main"))); framer().navBeforeTitle = true; framer().title = "Main Posts"; framer().add(ul(lmap(__45 -> postToHTMLWithDate(__45), posts))); return framer().render(); } if (swic(uri, "/html/", m) && isInteger(m.rest())) { long id = parseLong(m.rest()); return serveHTMLPost(id); } if (swic(uri, "/htmlEmbedded/", m) && isInteger(m.rest())) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); HTMLFramer1 framer = framer(); framer.title = "[" + post.id + "] " + or2(post.title, shorten(post.text)); framer.add(hcomment("Post begins here") + "\n" + post.text + "\n" + hcomment("Post ends here")); return framer.render(); } if (swic(uri, "/css/", m) && isInteger(m.rest())) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); return serveWithContentType(post.text, "text/css"); } if (eq(uri, "/search")) { framer().navBeforeTitle = true; String q = trim(req.params.get("q")); framer().title = joinNemptiesWithColon("Search", q); framer().add(hcomment("cookie: " + req.webRequest.cookie())); framer().add(hform(hinputfield("q", q, "autofocus" , true) + " " + hsubmit("Search"))); framer().add(p(small("Search help: If you search for multiple words, they can appear in any order in the text. An underscore matches any character. Plus means space. Post titles, texts and types are searched."))); if (nempty(q)) { ScoredSearcher < UserPost > searcher = new ScoredSearcher<>(q); long id = parseLongOpt_pcall(q); for (UserPost post : list(UserPost.class)) searcher.add(post, (post.id == id ? 4 : 0) + searcher.score(post.title)*3 + searcher.score(post.text)*2 + searcher.score(post.type) + searcher.score(joinWithSpace(post.postRefTags))*0.5); List posts = searcher.get(); framer().add(p(b(addPlusToCount(searcher.maxResults, l(posts), nPosts(posts)) + " found for " + htmlEncode2(q)))); framer().add(ul(lmap(__46 -> postToHTMLWithDate(__46), posts))); } return framer().render(); } // serve replies for JSTree AJAX call if (eq(uri, "/jstree/replies")) { UserPost post = getConcept(UserPost.class, parseLong(req.params.get("post"))); if (post == null) return serveJSON(ll()); Collection refs = referencingPosts(post); /*[ { "id" : "demo_root_1", "text" : "Root 1", "children" : true, "type" : "root" }, { "id" : "demo_root_2", "text" : "Root 2", "type" : "root" } ]*/ return serveJSON(map(refs, p -> litorderedmap( "id" , p.id, "text" , postToHTMLWithDate(post), "children" , nempty(referencingPosts(p)) ? true : null, "type" , "root" // ? ))); } if (startsWith(uri, "/touchPost/", m)) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); if (!canEditPost(post)) return "You are not authorized to do this"; touchConcept(post); return hscript("setTimeout('history.go(-1)', 1000);") + "Post " + post.id + " touched"; } if (startsWith(uri, "/hidePost/", m)) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); if (!canEditPost(post)) return "You are not authorized to do this"; cset(post, "hidden" , true); return hrefresh(postLink(post)); } if (startsWith(uri, "/unhidePost/", m)) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); if (!canEditPost(post)) return "You are not authorized to do this"; cset(post, "hidden" , false); return hrefresh(postLink(post)); } if (startsWith(uri, "/markSafe/", m)) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); UserPost codePost = optCast(UserPost.class, first(post.postRefs)); if (codePost == null) return "Code post not found"; if (!canEditPost(codePost)) return "You are not authorized to do this"; // create "Mark safe" post uniqCI(UserPost.class, "creator" , currentUser(), "text" , "Mark safe", "postRefs" , ll(post)); sleepSeconds(6); // allow bot to react // touch code post touchConcept(codePost); return hrefresh(postLink(codePost)); } if (eq(uri, "/mirrorAllConversations")) { if (!req.masterAuthed) return serveAuthForm(rawLink(uri)); for (Conversation c : list(Conversation.class)) rstUpdateMirrorPosts.add(c); return "OK"; } if (startsWith(uri, "/mirrorConversation/", m)) { if (!req.masterAuthed) return serveAuthForm(rawLink(uri)); long id = parseLong(m.rest()); Conversation conv = getConcept(Conversation.class, id); if (conv == null) return "Conversation not found"; conv.updateMirrorPost(); return "Mirror post updated (" + htmlEncode2(str(conv.mirrorPost.get())) + ")"; } if (eqic(uri, "/favicon.ico")) return serveFavIcon(); // start of /bot commands if (startsWith(uri, "/bot/", m)) { req.subURI = m.rest(); String json = req.params.get("json"); Map data = new HashMap(); if (nempty(json)) data = jsonDecodeMap(json); data.putAll(withoutKey("json", req.params)); User user; String userName = (String) (data.get("_user")); if (nempty(userName)) { String botToken = (String) (data.get("_botToken")); if (botToken == null) return serveJSON("error" , "Need _botToken"); user = conceptWhereIC(User.class, "name" , userName); if (user == null) return serveJSON("error" , "User not found"); if (!eq(botToken, getVar(user.botToken))) return serveJSON("error" , "Wrong bot token"); } else user = user(req); String function = beforeSlashOrAll(req.subURI); req.subURI = substring(req.subURI, l(function)+1); req.webRequest.noSpam(); // might want to change this { Object __1= servePossiblyUserlessBotFunction(req, function, data, user); if (__1 != null) return __1; } if (user == null) return serveJSON("error" , "Need _user"); if (eq(function, "authTest")) return serveJSON("status" , "You are authorized as " + user.name); if (eq(function, "postCount")) return serveJSON("result" , countConcepts(UserPost.class)); if (eq(function, "listPosts")) { List fields = unnull(stringToStringListOpt(__47 -> tok_identifiersInOrder(__47), data.get("fields"))); if (!fields.contains("id")) fields.add(0, "id"); long changedAfter = toLong(data.get("changedAfter")); long repliesTo = toLong(data.get("repliesTo")); double pollFor = min(bot_maxPollSeconds, toLong(data.get("pollFor"))); // how long to poll (seconds) long startTime = sysNow(); // We're super-anal about catching all changes. This will probably never trigger if (changedAfter > 0 && changedAfter == now()) sleep(1); Collection posts; while (true) { if (repliesTo != 0) { posts = referencingPosts(getConcept(UserPost.class, repliesTo)); if (changedAfter != 0) posts = objectsWhereFieldGreaterThan(posts, "_modified" , changedAfter); } else { posts = changedAfter == 0 ? list(UserPost.class) : conceptsWithFieldGreaterThan_sorted(UserPost.class, "_modified" , changedAfter); } // return when there are results, no polling or poll expired if (nempty(posts) || pollFor == 0 || elapsedSeconds_sysNow(startTime) >= pollFor) return serveJSON_breakAtLevels(2, "result" , map(posts, post -> mapToValues(fields, field -> getPostFieldForBot(post, field)) )); // sleep and try again sleep(bot_pollInterval); } } if (eq(function, "createPost")) { Either e = createPostArgs(user, data); if (e.isB()) return serveJSON("error" , e.b()); Pair post = uniq2(UserPost.class, e.a()); if (!post.b) post.a.bump(); // bump if exact same post exists return serveJSON("status" , post.b ? "Post created" : "Post existed already, bumped", "postID" , post.a.id); } if (eq(function, "editPost")) { long postID = toLong(data.get("postID")); UserPost post = getConcept(UserPost.class, postID); if (post == null) return serveJSON("error" , "Post " + postID + " not found"); Either e = createPostArgs(user, data); if (e.isB()) return serveJSON("error" , e.b()); int changes = cset(post, e.a()); // TODO: bump if no changes? return serveJSON(changes > 0 ? "Post updated" : "Post updated, no changes", "postID", postID); } if (eq(function, "deletePosts")) { List ids = allToLong(tok_integersInOrder((String) data.get("ids"))); List results = new ArrayList(); List errors = new ArrayList(); for (long id : unnullForIteration(ids)) { UserPost post = getConceptOpt(UserPost.class, id); if (post == null) errors.add("Post " + id + " not found"); else { if (!user.isMaster && neq(post.creator.get(), user)) errors.add("Can't delete post " + id + " from other user"); else { deletePost(post); results.add("Post " + id + " deleted"); } } } return serveJSON(litorderedmap("results", results, "errors", errors)); } return serveBotFunction(req, function, data, user); } if (teamPostID != 0 && eq(uri, "/team")) return serveHTMLPost(teamPostID); if (startsWith(uri, "/history/", m)) { long id = parseLong(m.rest()); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); return servePostHistory(post); } if (startsWith(uri, "/htmlBot/", m)) { long id = parseLong(beforeSlashOrAll(m.rest())); UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post not found"); return doPost(htmlBotURLOnBotServer(id), req.params()); } if (eq(uri, "/deletedPosts")) { CloseableIterableIterator lines = linesFromFile(deletedPostsFile()); try { List posts = new ArrayList(); while (lines.hasNext()) { String line = lines.next(); if (isProperlyQuoted(line)) { Map map = safeUnstructureMap(unquote(line)); posts.add(onlyKeys(map, "id", "title", "type")); } } // TODO: show more info, allow restoring posts return serveText("Deleted posts:\n" + lines(lmap(__48 -> struct(__48), posts))); } finally { _close(lines); }} if (eq(uri, "/formToPost")) { User user = currentUser(); if (user == null) serve500("Please log in first"); Map params = cloneMap(req.params); String type = or2(getAndRemove(params, "_type"), "Form Input"); String title = getAndRemove(params, "_title"); List postRefs = map(id -> getConceptOpt(UserPost.class, parseLong(id)), tok_integersInOrder(getAndRemove(params, "_postRefs"))); List postRefTags = lines(getAndRemove(params, "_postRefTags")); String text = sortLinesAlphaNumIC(mapToLines(params, (k, v) -> urlencode(k) + "=" + urlencode(v))); Pair p = uniq2(UserPost.class, "creator" , user, "text", text, "type", type, "title", title, "postRefs", postRefs, "postRefTags", postRefTags); return (p.b ? "Post " + p.a.id + " created" : "Post " + p.a.id + " exists") + hrefresh(2.0, "/" + p.a.id); } if (eq(uri, "/webPushSubscribe")) { Map data = jsonDecodeMap(mapGet(req.webRequest.files(), "postData")); printVars_str("webPushSubscribe", "data", data); cnew(WebPushSubscription.class, "data", data, "clientIP" , req.webRequest.clientIP()); return serveJSON(litmap("message" , "success")); } if (eq(uri, "/changePassword")) { if (!req.masterAuthed) return serveAuthForm(rawLink(uri)); String name = assertNempty(req.get("user")); String newPW = assertNempty(req.get("newPW")); User user = conceptWhereCI(User.class, "name", name); if (user == null) return "User not found"; cset(user, "passwordMD5" , new SecretValue(hashPW(newPW))); return "PW updated"; } if (eq(uri, "/webPushNotify")) { if (!req.masterAuthed) return serveAuthForm(rawLink(uri)); String msg = or2(req.params.get("msg"), "Hello user. It is " + localTimeWithSeconds() + " on the server"); WebPushSubscription sub = getConcept(WebPushSubscription.class, toLong(req.params.get("webPushSubID"))); if (sub == null) return serve404("webPushSubID not found"); String mod = dm_require("#1030463/WebPushKeyManager"); dm_call(mod, "sendNotification", sub.data, msg); return "Push message sent"; } if (eq(uri, "/hashPW") && req.masterAuthed) return hashPW(req.params.get("pw")); return null; } // end of serveOtherPage2 Object servePostHistory(UserPost post) { return serveText(unquoteAllLines(loadTextFile(postHistoryFile(post)))); } // helper for bot functions. return params or error Either createPostArgs(User user, Map data) { String text = (String) (data.get("text")); String type = (String) (data.get("type")); String title = (String) (data.get("title")); String botInfo = or2((String) data.get("botInfo"), "Made by bot"); List postRefTags = unnull(lines((String) data.get("refTags"))); List postRefs = new ArrayList(); Object _refs = data.get("refs"); if (_refs instanceof String) for (String s : tok_integersInOrder((String) _refs)) { UserPost ref = getConcept(UserPost.class, parseLong(s)); if (ref == null) return eitherB("Post " + s + " not found"); postRefs.add(ref); } if (empty(text) && empty(title)) return eitherB("Need either a text or a title"); boolean isPublic = eqOneOf(data.get("isPublic"), null, true, "1", "t", "true"); return eitherA(litparams("creator" , user, "text", text, "type", type, "title", title, "isPublic", isPublic, "botInfo", botInfo, "postRefs", postRefs, "postRefTags", postRefTags)); } Object serveBotFunction(Req req, String function, Map data, User user) { return serveJSON("error" , "You are logged in correctly but function is unknown: " + function); } Object servePossiblyUserlessBotFunction(Req req, String function, Map data, User user) { return null; } Object servePost(UserPost post, Req req) { if (!post.isPublic) return serve404("Post is not public"); HTMLFramer1 framer = framer(); framer.add(hjs("webSocketQuery = " + jsQuote("?objectID=" + post.id + "&modified=" + post.xmodified) + ";\n" + "if (ws != null) ws.url = ws.url.split(\"?\")[0] + webSocketQuery;")); framer.title = "[" + post.id + "] " + or2(post.title, shorten(post.text)); framer.add(p("By " + htmlEncode2(post.author()) + ". " + renderConceptDate(post))); if (nempty(post.type)) framer.add(p("Post type: "+ htmlEncode2(post.type))); List actions = new ArrayList(); // show render link if (eqic(post.type, "HTML")) actions.add(targetBlank("/html/" + post.id, "Show as HTML page")); if (eqic(post.type, "HTML (embedded)")) actions.add(targetBlank("/htmlEmbedded/" + post.id, "Show as HTML page")); if (eqicOneOf(post.type, "JavaX Code (HTML Bot)", "JavaX Code (Live Home Page)")) actions.add(targetBlank(htmlBotURL(post.id), "Show as HTML page")); if (eqic(post.type, "Conversation HTML")) actions.add(targetBlank("/demo?cookie=htmlDemo&_autoOpenBot=1&chatContentsPost=" + post.id, "Show in chat box")); // show edit link if (canEditPost(post)) { actions.add(ahref(conceptEditLink(post), "Edit post")); actions.add(ahref(touchLink(post), "Touch post")); actions.add(post.hidden ? ahref(unhideLink(post), "Unhide post") : ahref(hideLink(post), "Hide post")); } // show reply link etc. actions.add(ahref(replyLink(post), "Reply")); actions.add(ahref(conceptDuplicateLink(post), "Duplicate")); actions.add(ahref(conceptEditLink(post, "onlyFields" , "title"), "Rename")); if (postHasHistory(post)) actions.add(ahref("/history/" + post.id, "History")); actions.add(ahref(baseLink + "/text/" + post.id, "Raw Text")); // show "Talk to this bot" if (post.isJavaXCode()) { actions.add(targetBlank(simulateBotLink(post), "Talk to this bot")); actions.add(ahref(gazelleBotURL + "/transpilation/" + post.id, "Show Java transpilation")); } framer.add(p_vbar(actions)); // show post refs List postRefs = cloneList(post.postRefs); if (nempty(postRefs)) { framer.add(p("In reference to:")); List> l = post.postRefsWithTags(); framer.add(ul(mapPairsToList(l, (ref, as) -> htmlEncode2(as) + " " + objectToHTML(ref)))); } String text = post.text; String html = null; if (post.isJavaXCode()) { try { JavaXHyperlinker hl = new JavaXHyperlinker(); hl.targetBlank = true; html = hl.codeToHTML(text); } catch (Throwable __e) { _handleException(__e); }} if (html == null) html = htmlEncodeWithLinking(post.text); if (eq(req.params.get("showLineFeeds"), "1")) html = html_showLineFeedsForPRE(html); // render the actual text framer.add(div(sourceCodeToHTML_noEncode(html), "style" , hstyle_sourceCodeLikeInRepo())); // show referencing posts Collection refs = referencingPosts(post); refs = reversed(refs); // latest on top Pair, List> p = filterAntiFilter(refs, post2 -> !post2.hidden); if (nempty(p.a)) { framer.add(p("Referenced by posts (latest first):")); UserPost latestCodeSafetyPost = highestBy(p2 -> p2.modifiedOrBumped(), filter(p.a, p2 -> eqic(p2.botInfo, "Code Safety Checker"))); framer.add(ul(map(p.a, p2 -> { String html2 = htmlEncode2(squareBracketIfNempty(getPostRefTag(p2, post))) + " " + objectToHTML(p2); if (p2 == latestCodeSafetyPost && cic(p2.text, "Unknown identifiers:")) html2 += " " + hbuttonLink("/markSafe/" + p2.id, htmlEncode2("Mark safe")); return html2; }))); } if (nempty(p.b)) { framer.add(p("Referenced by hidden posts:")); framer.add(ul(map(p.b, p2 -> htmlEncode2(squareBracketIfNempty(getPostRefTag(p2, post))) + " " + objectToHTML(p2)))); } if (showPostStats) { String stats = nLines(countLines(post.text)) + ", distance to user post: " + post.distanceToUserPost(); framer.add(p_alignRight(small(span_title(stats, "Post stats")))); } return framer.render(); } String getPostRefTag(UserPost a, UserPost b) { return get(a.postRefTags, indexOf(a.postRefs, b)); } // turn "post 123" into a link to post 123 String htmlEncodeWithLinking(String text) { String html = htmlEncode2(text); return regexpReplace(html, gazelle_postMentionRegexp(), matcher -> { UserPost post = getConceptOpt(UserPost.class, parseLong(matcher.group(1))); return post == null ? matcher.group() : ahref(postLink(post), matcher.group()); }); } List referencingPosts(UserPost post) { return sortedByCalculatedField(p -> p.modifiedOrBumped(), instancesOf(UserPost.class, allBackRefs(post))); } List referencingPostsWithTag(UserPost post, String tag) { return filter(referencingPosts(post), p -> eqic_unnull(getPostRefTag(p, post), tag)); } String serveAuthForm(String redirect) { return hAddToHead(super.serveAuthForm(redirect), hInitWebSocket()); } String authFormMoreContent() { return navDiv(); } String navDiv() { return div_vbar(navLinks(), "style" , "margin-bottom: 0.5em"); } // handle user log-in (create/update AuthedDialogID concept) Object handleAuth(Req req, String cookie) { String name = trim(req.params.get("user")), pw = trim(req.params.get("pw")); if (nempty(name) && nempty(pw) && nempty(cookie)) { Domain authDomain; User user = findOrCreateUserForLogin(name, pw); if (user == null) return errorMsg("User " + htmlEncode2(name) + " not found"); if (!eq(getVar(user.passwordMD5), hashPW(pw))) return errorMsg("Bad password, please try again"); // auth cset(uniq(AuthedDialogID.class, "cookie", cookie), "restrictedToDomain" , null, "master" , user.isMaster, "user", user); String redirect = req.params.get("redirect"); if (nempty(redirect)) return hrefresh(redirect); } return null; } // e.g. grab from central database // does not have to check the PW User findOrCreateUserForLogin(String name, String pw) { return conceptWhereCI(User.class, "name", name); } Object serve2(Req req) { /*S userMode = req.params.get("userMode"); if (nempty(userMode) && req.auth != null) req.auth.userMode = eq(userMode, "1");*/ if (req.auth != null) { cset(req.auth, "userMode" , false); if (req.auth.user.get() != null) cset(req.auth.user.get(), "lastSeen" , now()); } return super.serve2(req); } String hashPW(String pw) { if (nempty(salterService)) return assertMD5(postPage(salterService, "pw", pw)); else return md5(pw + passwordSalt()); } Req currentReq() { return currentReq.get(); } final boolean isMasterAuthed() { return inMasterMode(); } final boolean masterAuthed() { return inMasterMode(); } boolean inMasterMode() { return inMasterMode(currentReq()); } final boolean isMasterAuthed(Req req) { return inMasterMode(req); } final boolean masterAuthed(Req req) { return inMasterMode(req); } boolean inMasterMode(Req req) { return req.masterAuthed && !(req.auth != null && req.auth.userMode); } Map filtersForClass(Class c, Req req) { if (c == UserPost.class) { if (req.auth != null && !inMasterMode(req)) return litmap("creator" , req.auth.user.get()); } return super.filtersForClass(c, req); } String loggedInUserDesc_html(Req req) { // no auth if (req.auth == null) return ahref(loginLink(), "not logged in"); // old school nameless master auth if (req.auth.user.get() == null) return req.masterAuthed ? "master user" : "anonymous user " + req.auth.id; // actual Gazelle user return "user " + req.auth.user.get().name; } String loginLink() { return baseLink + "/"; } HCRUD_Concepts crudData(Class c, Req req) { HCRUD_Concepts cc = super.crudData(c, req); if (c == UserPost.class) { cc.useDynamicComboBoxes = true; cc.lsMagic = true; cc.itemName = () -> "Post"; cc.dropEmptyListValues = false; //cc.verbose = true; if (eq(req.params.get("noace"), "1")) cc.addRenderer("text", new HCRUD_Data.TextArea(80, 20)); else cc.addRenderer("text", new HCRUD_Data.AceEditor(80, 20)); cc.addRenderer("postRefTags", new HCRUD_Data.FlexibleLengthList(new HCRUD_Data.TextField(20))); cc.addRenderer("type", new HCRUD_Data.ComboBox(true, itemPlus("", userPostTypesByPopularity()))); cc.fieldHelp( "type" , "Type of post (any format, optional)", "title" , "Title for this post (any format, optional)", "text" , "Text contents of this post (any format)", "nameInRepo" , "Name in repository (not really used yet)", "botInfo" , "Info on which bot made this post (if any)"); cc.massageItemMapForList = (item, map) -> { applyFunctionToValue(__49 -> shorten(__49), map, "text", "title", "type", "nameInRepo"); List refs = ((UserPost) item).postRefs; map.put("postRefs", new HTML(joinWithBR(lmap(__50 -> objectToHTML(__50), refs)))); }; cc.getObject = id -> { Map map = cc.getObject_base(id); List refs = (List) (map.get("postRefs")); List tags = (List) (map.get("postRefTags")); print("counts:" + l(refs) + "/" + l(tags)); map.put("postRefTags", padList(tags, l(refs), "")); return map; }; cc.emptyObject = () -> { Map item = cc.emptyObject_base(); item.put("creator" , req.auth.user.get()); // set default creator to current user return item; }; cc.getObjectForDuplication = id -> { Map item = cc.getObjectForDuplication_base(id); item.put("creator" , req.auth.user.get()); // set default creator to current user // get post and refs UserPost post = getConcept(UserPost.class, toLong(id)); List postRefs = cloneList((List) item.get("postRefs")); List tags = cloneList((List) item.get("postRefTags")); // drop old "created from" references for (int i = 0; i < l(tags); i++) if (eqic(tags.get(i), "created from")) { remove(postRefs, i); tags.remove(i--); } // add "created from" reference int idx = l(postRefs); postRefs.add(post); listSet(tags, idx, "created from"); // store refs item.put("postRefs", postRefs); item.put("postRefTags", tags); print("tags", tags); return item; }; cc.fieldsToHideInCreationForm = litset("hidden", "creating"); cc.lockDB = true; cc.afterUpdate = (post, oldValues) -> { Map newValues = cgetAll_cloneLists(post, keys(oldValues)); for (Map.Entry __4 : _entrySet( newValues)) { String field = __4.getKey(); Object newVal = __4.getValue(); Object oldVal = oldValues.get(field); if (eq(oldVal, newVal)) { printVars_str("field", field, "oldVal", oldVal); continue; } IF1 f = o -> o instanceof Concept ? ((Concept) o).id : null; Object oldValForLog = defaultMetaTransformer().transform(f, oldVal); Object newValForLog = defaultMetaTransformer().transform(f, newVal); Map logEntry = litorderedmap("date" , now(), "field", field, "oldVal" , oldValForLog, "newVal" , newValForLog); printStruct("logEntry", logEntry); logStructure(postHistoryFile(((UserPost) post)), logEntry); } }; cc.actuallyDeleteConcept = post -> deletePost(((UserPost) post)); } return cc; } HCRUD makeCRUD(Class c, Req req) { HCRUD crud = super.makeCRUD(c, req); if (c == UserPost.class) { crud.refreshAfterCommand = (params, msgs) -> { UserPost post = getConcept(UserPost.class, toLong(crud.objectIDToHighlight)); return post != null ? hrefresh(postLink(post)) : crud.refreshAfterCommand_base(params, msgs); }; crud.renderCmds = map -> { UserPost post = getConcept(UserPost.class, toLong(crud.itemID(map))); return joinNemptiesWithVBar(crud.renderCmds_base(map), ahref(postLink(post), "Show post"), ahref(touchLink(post), "Touch", "title" , "Mark post as modified so it is looked at by bots again")); }; crud.uneditableFields = litset("xmodified", "creating", "bumped"); if (showSuggestorBot) crud.moreSelectizeOptions = name -> ", onChange: function() { console.log(\"selectize onChange\"); sugTrigger(); }"; crud.showQuickSaveButton = true; crud.massageFormMatrix = (map, matrix) -> { /*if (map.containsKey(crud.idField())) // editing, not creating matrix.add(0, ll("", hbuttonOnClick_noSubmit("Save & keep editing", [[ $.ajax({ type: 'POST', url: $('#crudForm').attr('action'), data: $('#crudForm').serialize(), success: function(response) { successNotification("Saved"); }, }).error(function() { errorNotification("Couldn't save"); }); ]])));*/ // mergeTables businesss broken somehow /*int idx1 = indexOfPred(matrix, row -> cic(first(row), "Post Refs")); int idx2 = indexOfPred(matrix, row -> cic(first(row), "Post Ref Tags")); if (idx1 < 0 || idx2 < 0) ret; // reduced form (e.g. rename) LS row1 = matrix.get(idx1), row2 = matrix.get(idx2); row1.set(1, hcrud_mergeTables(row1.get(1), row2.get(1), "as")); matrix.remove(idx2);*/ // TODO: handle changes in AceEditor if (showSuggestorBot) { List entries = ((HCRUD_Concepts) crud.data).comboBoxItems( conceptsWhereIC(UserPost.class, "type" , "JavaX Code (Post Edit Suggestor)")); String js = hscript(replaceVars("\r\n const suggestorSelector = $(\"[name=suggestorID]\");\r\n var sugLoading = false, sugTriggerAgain = false;\r\n\r\n function sugTrigger() {\r\n //console.log(\"sugTrigger\");\r\n if (sugLoading) { sugTriggerAgain = true; return; }\r\n\r\n const sugText = suggestorSelector.text();\r\n //console.log(\"sugText=\" + sugText);\r\n const sugMatch = sugText.match(/\\d+/);\r\n if (!sugMatch) {\r\n //$(\"#suggestorResult\").html(\"\");\r\n $(\"#suggestorResult\").hide();\r\n return;\r\n }\r\n const suggestorID = sugMatch[0];\r\n\r\n // get form data as JSON\r\n \r\n var data = {};\r\n $(suggestorSelector).closest(\"form\").serializeArray().map(function(x){data[x.name] = x.value;});\r\n const json = JSON.stringify(data);\r\n console.log(\"JSON: \" + json);\r\n \r\n const url = \"" + gazelleBotURL + "/chatBotReply/\" + suggestorID;\r\n console.log(\"Loading \" + url);\r\n sugLoading = true;\r\n $.post(url, {q : json},\r\n function(result) {\r\n console.log(\"Suggestor result: \" + result);\r\n const answer = !result ? \"\" : JSON.parse(result).answer;\r\n if (answer) {\r\n $(\"#suggestorResult .sectionContents\").html(answer);\r\n $(\"#suggestorResult\").show();\r\n } else\r\n $(\"#suggestorResult\").hide();\r\n //$(\"#suggestorResult\").html(answer ? \"Suggestor says: \" + answer : \"\");\r\n }\r\n ).always(function() {\r\n console.log(\"sug loading done\");\r\n setTimeout(function() {\r\n sugLoading = false;\r\n if (sugTriggerAgain) { sugTriggerAgain = false; sugTrigger(); }\r\n }, delayAfterSuggestion);\r\n });\r\n }\r\n\r\n $(document).ready(function() {\r\n $(\"input[type=text], input[type=hidden], textarea, select\").on('input propertychange change', sugTrigger);\r\n sugTrigger();\r\n });\r\n suggestorSelector.change(sugTrigger);\r\n ", "delayAfterSuggestion", delayAfterSuggestion)); long defaultSuggestor = parseFirstLong(userDefaults(req.auth.user.get()).get("Default Suggestor Bot")); String selectedSuggestor = firstWhereFirstLongIs(entries, defaultSuggestor); matrix.add(0, ll("Suggestor Bot", crud.addHelpText( "Choose a bot to assist you in editing this post [feature in development]", p_alignRight(crud.renderComboBox("suggestorID", selectedSuggestor, entries, false))) //+ hdiv("", id := "suggestorResult") + htitledSectionWithDiv("Suggestion", "", "id" , "suggestorResult", "style" , "display: none", "innerDivStyle" , "max-height: 150px; overflow: auto") + js)); } }; crud.flexibleLengthListLeeway = 3; } if (c == User.class) { crud.allowCreate = false; //crud.uneditableFields = litset("passwordMD5"); } return crud; } String authFormHeading() { return h3_htmlEncode("Welcome to the Gazelle AI System") + p(hsnippetimg_scaleToWidth(200, "#1101482", 425, 257, "title" , "Gazelle")); } void makeFramer(Req req) { super.makeFramer(req); /*if (req.masterAuthed) req.framer.add(p_vbar( ahrefIf(req.auth.userMode, appendQueryToURL(baseLink + req.uri, userMode := 0), "Master mode", title := "View pages as master"), ahrefIf(!req.auth.userMode, appendQueryToURL(baseLink + req.uri, userMode := 1), "User mode", title := "View pages as user")));*/ req.framer.renderTitle = () -> h1(ahref(baseLink + "/", himgsnippet("#1102967", "style" , "height: 1em; vertical-align: bottom", "title" , "Gazelle.rocks Logo")) + " " + req.framer.encodedTitle()); req.framer.add(() -> navDiv()); // calculate on completeFrame if (showMetaBotOnEveryPage && !eq(req.params.get("_reloading"), "1")) req.framer.willRender.add(new Runnable() { public void run() { try { req.framer.add(hscript("var botConfig = \"codePost=" + defaultBotPost + "\"; ") + hjssrc(baseLink + "/script")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "req.framer.add(hscript(\"var botConfig = \\\"codePost=\" + defaultBotPost + \"\\\"; ..."; }}); } HTMLFramer1 framer() { return framer(currentReq.get()); } HTMLFramer1 framer(Req req) { if (req.framer == null) makeFramer(req); return req.framer; } // link to show post String postLink(UserPost post) { return objectLink(post); } String postLink(long postID) { return objectLink(getConcept(UserPost.class, postID)); } String touchLink(UserPost post) { return baseLink + "/touchPost/" + post.id; } String hideLink(UserPost post) { return baseLink + "/hidePost/" + post.id; } String unhideLink(UserPost post) { return baseLink + "/unhidePost/" + post.id; } String objectLink(Concept c) { return baseLink + "/" + c.id; } String objectToHTML(Concept c) { return c == null ? "-" : ahref(objectLink(c), htmlEncode2(str(c))); } String postToHTMLWithDate(UserPost post) { return post == null ? "" : objectToHTML(post) + " " + span(htmlEncode2( renderHowLongAgoPlusModified(post.created, post._modified)), "style" , "color: #888"); } boolean canEditPost(UserPost post) { User user = currentUser(); return user != null && (user.isMaster || user == post.creator.get()); } User currentUser() { Req req = currentReq.get(); return req != null && req.auth != null ? req.auth.user.get() : null; } String replyLink(UserPost post) { return appendQueryToURL(crudLink(UserPost.class), "cmd" , "new", "fList_postRefs_0" , post == null ? null : post.id); } String newPostLink() { return replyLink(null); } List navLinks(Object... __) { return gazelle_navLinks(baseLink, inlineSearch && !eq(currentReq.get().uri, "/search") ? "" : null, paramsPlus(__, "withTeam" , teamPostID != 0)); } List botCmdClasses() { return ll(); } List crudClasses(Req req) { List l = super.crudClasses(req); l = listMinusSet(l, hiddenCrudClasses()); l.add(UserPost.class); l.add(UploadedImage.class); if (req.masterAuthed) { l.add(User.class); // currently not putting in CRUD list // (but can be viewed with /crud/WebPushSubscription) //if (webPushEnabled) l.add(WebPushSubscription); } return l; } @Override Collection cruddableClasses(Req req) { return addAllAndReturnCollection(super.cruddableClasses(req), WebPushSubscription.class); } Set hiddenCrudClasses() { return litset(Lead.class, ConversationFeedback.class, Domain.class, UserKeyword.class, UploadedSound.class, Settings.class); } Object getPostFieldForBot(UserPost post, String field) { if (eq(field, "creatorID")) return post.creator.get().id; if (eq(field, "creatorName")) return post.creator.get().name; if (eq(field, "creatorIsMaster")) return post.creator.get().isMaster; if (eq(field, "postRefs")) return lmap(__51 -> conceptID(__51), post.postRefs); return cget(post, field); } void deletePost(UserPost post) { if (post == null) return; print("Deleting post " + post); Lock __6 = dbLock(); lock(__6); try { long filePos = l(deletedPostsFile()); logStructure(deletedPostsFile(), mapToValues(UserPost.fieldsToSaveOnDelete, field -> getPostFieldForBot(post, field))); for (Pair p : post.postRefsWithTags()) if (p.a instanceof UserPost) logStructure(deletedRepliesFile((UserPost) p.a), litorderedmap("tag" , p.b, "id" , post.id, "filePos", filePos, "type" , post.type)); cdelete(post); } finally { unlock(__6); } } File deletedPostsFile() { return programFile("deleted-posts.log"); } Object serveHTMLPost(long id) { UserPost post = getConcept(UserPost.class, id); if (post == null) return serve404("Post " + id + " not found"); return post.text; } transient ReliableSingleThread_Multi < UserPost > rstDistributePostChanges = new ReliableSingleThread_Multi<>(1000, __18 -> distributePostChanges_impl(__18)); void distributePostChanges(UserPost post) { rstDistributePostChanges.add(post); } // This is also called when replies change void distributePostChanges_impl(UserPost post) { AutoCloseable __16 = enter(); try { //print("distributePostChanges_impl " + post); String uri = "/" + post.id; for (Pair p : syncMapToPairs(webSockets)) { if (eq(p.b.uri, uri)) reloadBody(p.a); } if (eqic(post.type, "Line Labels")) { UserPost parent = post.primaryRefPost(); print("Line labels post " + post.id + ", parent=" + parent); updateLineLabels(parent); } } finally { _close(__16); }} void updateLineLabels(UserPost post) { try { if (post == null || !eqic(post.type, "Detailed Conversation Mirror")) return; print("Looking at conversation mirror " + post.id + " - " + post.title); long convID = toLong(regexpFirstGroupIC("Conversation (\\d+) with details", post.title)); Conversation conv = getConcept(Conversation.class, convID); if (conv == null) return; print("updateLineLabels " + convID + " / " + post.id); MultiMap, String> labels = new MultiMap(); for (UserPost p : referencingPostsWithTag(post, "")) if (eqic(p.type, "Line Labels")) { try { Map map = safeUnstructMapAllowingClasses(p.text(), Pair.class); print("Got labels map: " + map); putAll(labels, map); } catch (Throwable __e) { _handleException(__e); }} boolean change = false; for (Msg msg : cloneList(conv.msgs)) { List lbls = uniquifyAndSortAlphaNum(allToUpper(labels.get(pair(msg.time, msg.text)))); if (nempty(lbls)) print("Got labels: " + lbls); if (!eq(msg.labels, lbls)) { msg.labels = lbls; change = true; } } if (change) conv.incReloadCounter(); } catch (Throwable __e) { _handleException(__e); }} void reloadBody(Object ws) { print("Reloading body through WebSocket"); String jsCode = " {\r\n const loc = new URL(document.location);\r\n const params = new URLSearchParams(loc.search);\r\n params.set('_reloading', '1');\r\n loc.search = params;\r\n console.log(\"Getting: \" + loc);\r\n $.get(loc, function(html) {\r\n var bodyHtml = /([\\s\\S]*)<\\/body>/.exec(html)[1];\r\n if (bodyHtml) {\r\n //$(\"body\").html(bodyHtml);\r\n $('body > *:not(.chatbot)').remove();\r\n $(\"body\").prepend(bodyHtml);\r\n }\r\n });\r\n } "; dm_call(ws, "send", jsonEncode(litmap("eval" , jsCode))); } transient ReliableSingleThread_Multi < String > rstDistributeDivChanges = new ReliableSingleThread_Multi<>(1000, __19 -> distributeDivChanges_impl(__19), new F0() { public AutoCloseable get() { try { return enter(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret enter();"; }}); void distributeDivChanges(String contentDesc) { rstDistributeDivChanges.add(contentDesc); } void distributeDivChanges_impl(String contentDesc) { AutoCloseable __17 = enter(); try { //print("distributeDivChanges_impl " + contentDesc); String content = null; for (Pair p : syncMapToPairs(webSockets)) { for (String div : asForPairsWithB(p.b.liveDivs, contentDesc)) { if (content == null) content = calcDivContent(contentDesc); if (content == null) { print("No content for " + contentDesc); return; } reloadDiv(p.a, div, content); } } } finally { _close(__17); }} void reloadDiv(Object ws, String div, String content) { //print("Reloading div " + div + " through WebSocket"); String jsCode = replaceDollarVars( " $(\"#\" + $div).html($content);", "div" , jsQuote(div), "content" , jsQuote(content)); dm_call(ws, "send", jsonEncode(litmap("eval" , jsCode))); } String calcDivContent(String contentDesc) { if (eq(contentDesc, "postCount")) return nPosts(countConcepts(UserPost.class)); if (eq(contentDesc, "webRequestsRightHemi")) return n2(requestsServed); if (eq(contentDesc, "serverLoadRightHemi")) return formatDoubleX(systemLoad, 1); if (eq(contentDesc, "memRightHemi")) return str_toM(processSize); return null; } int countUserPosts() { return countConcepts(UserPost.class, "botInfo" , null) + countConcepts(UserPost.class, "botInfo" , ""); } int countBotPosts() { return countConcepts(UserPost.class)-countUserPosts(); } Collection allUserPosts() { return filter(list(UserPost.class), p -> !p.isBotPost()); } List userPostTypesByPopularity() { return listToTopTenCI(map(allUserPosts(), p -> p.type)); } void startMainScript(Conversation conv) { UserPost post = getConceptOpt(UserPost.class, parseLong(mapGet(conv.botConfig, "codePost"))); if (post != null) { CustomBotStep step = uniq(CustomBotStep.class, "codePostID" , post.id); if (executeStep(step, conv)) nextStep(conv); } else super.startMainScript(conv); } String simulateBotLink(UserPost post) { return appendQueryToURL(baseLink + "/demo", "_botConfig" , makePostData("codePost" , post.id), "_newConvCookie" , 1, "_autoOpenBot" , 1); } @Override boolean isRequestFromBot(Req req) { //print("isRequestFromBot: " + req.uri); return startsWith(req.uri, "/bot/"); } User internalUser() { return uniqCI(User.class, "name" , "internal"); } transient ReliableSingleThread_Multi < Conversation > rstUpdateMirrorPosts = new ReliableSingleThread_Multi<>(100, c -> c.updateMirrorPost()); File postHistoryFile(UserPost post) { return programFile("Post Histories/" + post.id + ".log"); } boolean postHasHistory(UserPost post) { return fileNempty(postHistoryFile(post)); } File deletedRepliesFile(UserPost post) { return programFile("Deleted Replies/" + post.id + ".log"); } String makeClassNavItem(Class c, Req req) { String html = super.makeClassNavItem(c, req); if (c == UserPost.class) { int count1 = countUserPosts(), count2 = countBotPosts(); if (count1 != 0 || count2 != 0) html += " " + roundBracket(n2(count1) + " from users, " + n2(count2) + " from bots"); } return html; } @Override String modifyTemplateBeforeDelivery(String html, Req req) { // for design testing String contentsPostID = req.params.get("chatContentsPost"); if (nempty(contentsPostID)) { html = html.replace("#N#", "99999"); html = html.replace("chatBot_interval = 1000", "chatBot_interval = 3600*1000"); html = html.replace("#INCREMENTALURL#", baseLink + "/text/" + contentsPostID + "?"); } return html; } String headingForReq(Req req) { String codePost = decodeHQuery(req.params.get("_botConfig")).get("codePost"); if (nempty(codePost)) { UserPost post = getConcept(UserPost.class, parseLong(codePost)); return post == null ? null : post.title; } return null; } UserPost homePagePost() { return firstThat(p -> p.isMasterMade(), conceptsWhereIC(UserPost.class, "type" , "JavaX Code (Live Home Page)")); } Object serveHomePage() { try { UserPost post = homePagePost(); if (post != null) return loadPage(htmlBotURLOnBotServer(post.id)); } catch (Throwable __e) { _handleException(__e); } return null; } String callHtmlBot(long id, Map params) { return id == 0 ? "" : doPost(htmlBotURLOnBotServer(id), params); } String callHtmlBot_dropMadeByComment(long id, Map params) { return regexpReplace_direct(callHtmlBot(id, params), "^\n", ""); } String htmlBotURL(long postID) { return baseLink + "/htmlBot/" + postID; } String htmlBotURLOnBotServer(long postID) { return gazelleBotURL + "/htmlBot/" + postID; } UserPost userDefaultsPost(User user) { return conceptWhereIC(UserPost.class, "creator" , user, "type" , "My Defaults"); } Map userDefaults(User user) { UserPost post = userDefaultsPost(user); return post == null ? emptyMap() : parseColonPropertyCIMap(post.text); } double favIconCacheHours() { return 24; } Object favIconHeaders(Object response) { call(response, "addHeader", "Cache-Control", "public, max-age=" + iround(hoursToSeconds(favIconCacheHours()))); return response; } Object serveFavIcon() { if (favIconID != 0) { UploadedFile f = getConcept(UploadedFile.class, favIconID); if (f != null) return favIconHeaders(subBot_serveFile(f.theFile(), faviconMimeType())); } return favIconHeaders(subBot_serveFile(loadLibrary(defaultFavIconSnippet), faviconMimeType())); } String cssURL() { return gazelle_textURL(parseLong(cssID)); } String getText(long postID) { UserPost post = getConcept(UserPost.class, postID); return post == null ? null : post.text(); } @Override void addThingsAboveCRUDTable(Req req, Class c, List aboveTable) { if (c == User.class) aboveTable.add(p(ahref(baseLink + "/register", "Register new user"))); } User user(Req req) { if (req == null) return null; AuthedDialogID auth = req.auth; return auth == null ? null : auth.user(); } Object serveIntegerLink(Req req, long id) { UserPost post = getConceptOpt(UserPost.class, id); if (post == null) return null; //ret serve404("Post with ID " + id + " not found"); return servePost(post, req); } void onNewWebSocket(WebSocketInfo ws) {} void onWebSocketClose(WebSocketInfo ws) { ws.callCloseHandlers(); } String cookieFromWebRequest(IWebRequest req) { if (req == null) return null; { String __8 = req.cookie(); if (!empty(__8)) return __8; } return afterLastEquals(req.headers().get("cookie")); } void makeMaster() { var users = listMinus(list(User.class), internalUser()); if (empty(users)) { infoMessage("Please first register a user in the web interface!"); return; } ShowComboBoxForm cb = new ShowComboBoxForm(); cb.desc = "Select user to upgrade"; cb.itemDesc = "User to make master"; cb.items = map(users, user -> user.id + " " + user.name); cb.action = user -> { startThread(new Runnable() { public void run() { try { enter(); long userID = parseFirstLong(user); User userObj = getConcept(User.class, userID); printVars("user", user, "userID", userID, "userObj", userObj); makeMaster(userObj); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "enter();\r\n long userID = parseFirstLong(user);\r\n User userObj = get..."; }}); }; cb.show(); } void makeMaster(User user) { if (user == null) return; if (!swingConfirm("Turn " + user + " into a master user?")) return; cset(user, "isMaster" , true); infoMessage(user + " is now my master!!"); } Object[] popDownButtonEntries() { return objectArrayPlus(super.popDownButtonEntries(), "Make master user..." , runnableThread(new Runnable() { public void run() { try { AutoCloseable __21 = enter(); try { makeMaster(); } finally { _close(__21); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); makeMaster();"; }}) ); } } // end of module // Talk to a bot that is implemented in a Gazelle post static class CustomBotStep extends BotStep implements IInputHandler { long codePostID; public String toString() { return "CustomBotStep " + codePostID; } boolean run(Conversation conv) { String answer; try { Map result = (Map) (postJSONPage(replyURL(), "initial" , 1, "cookie" , conv.cookie)); answer = (String) result.get("answer"); } catch (Throwable e) { _handleException(e); answer = "Error: " + e.getMessage(); } conv.add(new Msg(false, answer)); cset(conv, "inputHandler" , this); return false; } String replyURL() { return ((DynGazelleRocks) botMod()).gazelleBotURL + "/chatBotReply/" + codePostID; } public boolean handleInput(String s, Conversation conv) { String answer; try { Map result = (Map) (postJSONPage(replyURL(), "q" , s, "cookie" , conv.cookie)); answer = (String) result.get("answer"); } catch (Throwable e) { _handleException(e); answer = "Error: " + e.getMessage(); } conv.add(new Msg(false, answer)); return true; // acknowledge that we handled the input } } // end of CustomBotStep static String userName(User user) { return user != null ? user.name : "?"; } static class WebPushSubscription extends Concept { String clientIP; Map data; } static File programSecretFile(String progID, String fileName) { return secretProgramFile(progID, fileName); } static File programSecretFile(String fileName) { return secretProgramFile(fileName); } static boolean fileExists(String path) { return path != null && new File(path).exists(); } static boolean fileExists(File f) { return f != null && f.exists(); } static String loadTextFile(String fileName) { return loadTextFile(fileName, null); } static String loadTextFile(File f, String defaultContents) { return loadTextFile(f, defaultContents, "UTF-8"); } static String loadTextFile(File f, String defaultContents, String encoding) { try { checkFileNotTooBigToRead(f); if (f == null || !f.exists()) return defaultContents; FileInputStream fileInputStream = new FileInputStream(f); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, encoding); return loadTextFile(inputStreamReader); } catch (Exception __e) { throw rethrow(__e); } } public static String loadTextFile(File fileName) { return loadTextFile(fileName, null); } static String loadTextFile(String fileName, String defaultContents) { return fileName == null ? defaultContents : loadTextFile(newFile(fileName), defaultContents); } 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 str(builder); } static File javaxSecretDir_dir; // can be set to work on different base dir static File javaxSecretDir() { return javaxSecretDir_dir != null ? javaxSecretDir_dir : new File(userHome(), "JavaX-Secret"); } static File javaxSecretDir(String sub) { return newFile(javaxSecretDir(), sub); } static String loadTextFileOrCreateWithRandomID(File f) { String id = trim(loadTextFile(f)); if (empty(id)) saveTextFile(f, id = aGlobalID()); return id; } static SecretValue aSecretGlobalID() { return secretValueOrNull(aGlobalID()); } static boolean nempty(Collection c) { return !empty(c); } static boolean nempty(CharSequence s) { return !empty(s); } static boolean nempty(Object[] o) { return !empty(o); } static boolean nempty(byte[] o) { return !empty(o); } static boolean nempty(int[] o) { return !empty(o); } static boolean nempty(Map m) { return !empty(m); } static boolean nempty(Iterator i) { return i != null && i.hasNext(); } static boolean nempty(Object o) { return !empty(o); } static boolean nempty(IntRange r) { return !empty(r); } static boolean nempty(Rect r) { return r != null && r.w != 0 && r.h != 0; } static boolean syncNempty(Collection l) { return !syncEmpty(l); } // returns number of changes static int cset(Concept c, Object... values) { try { if (c == null) return 0; warnIfOddCount(values = expandParams(c.getClass(), values)); int changes = 0; for (int i = 0; i+1 < l(values); i += 2) if (_csetField(c, (String) values[i], values[i+1])) ++changes; return changes; } catch (Exception __e) { throw rethrow(__e); } } static int cset(Iterable l, Object... values) { int changes = 0; for (Concept c : unnullForIteration(l)) changes += cset(c, values); return changes; } static int cset(Concept.Ref c, Object... values) { return cset(getVar(c), values); } static Concept cnew(String name, Object... values) { return cnew(db_mainConcepts(), name, values); } static Concept cnew(Concepts concepts, String name, Object... values) { Class cc = findClass(name); concepts_unlisted.set(true); Concept c; try { c = cc != null ? nuObject(cc) : new Concept(name); } finally { concepts_unlisted.set(null); } csetAll(c, values); concepts.register(c); return c; } static A cnew(Class cc, Object... values) { return cnew(db_mainConcepts(), cc, values); } static A cnew(Concepts concepts, Class cc, Object... values) { concepts_unlisted.set(true); A c; try { c = nuObject(cc); } finally { concepts_unlisted.set(null); } csetAll(c, values); concepts.register(c); return c; } static String lines_rtrim(Collection lines) { return rtrim_fromLines(lines); } static String indentedStructure(Object o) { return indentStructureString(struct(o)); } static List cdelete(Class c, Object... params) { return deleteConcepts(c, params); } static void cdelete(Concept c) { deleteConcept(c); } static void cdelete(Collection c) { for (A a : cloneList(c)) cdelete(a); } static List ll(A... a) { ArrayList l = new ArrayList(a.length); if (a != null) for (A x : a) l.add(x); return l; } static int shorten_default = 100; static String shorten(CharSequence s) { return shorten(s, shorten_default); } static String shorten(CharSequence s, int max) { return shorten(s, max, "..."); } static String shorten(CharSequence s, int max, String shortener) { if (s == null) return ""; if (max < 0) return str(s); return s.length() <= max ? str(s) : subCharSequence(s, 0, min(s.length(), max-l(shortener))) + shortener; } static String shorten(int max, CharSequence s) { return shorten(s, max); } static String escapeNewLines(String s) { return s == null ? null : fixNewLines(s).replace("\n", " | "); } static String quote(Object o) { if (o == null) return "null"; return quote(str(o)); } static String quote(String s) { if (s == null) return "null"; StringBuilder out = new StringBuilder((int) (l(s)*1.5+2)); quote_impl(s, out); return out.toString(); } static void quote_impl(String s, StringBuilder out) { out.append('"'); int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') out.append('\\').append(c); else if (c == '\r') out.append("\\r"); else if (c == '\n') out.append("\\n"); else if (c == '\t') out.append("\\t"); else if (c == '\0') out.append("\\0"); else out.append(c); } out.append('"'); } static List> zipTwoListsToPairs_lengthOfFirst(List l1, List l2) { int n = l(l1); List> out = emptyList(n); for (int i = 0; i < n; i++) addPair(out, l1.get(i), get(l2, i)); return out; } static List pairsAWhereB(Iterable> c, IF1 pred) { return pairsA(filter(c, p -> pred.get(p.b))); } static boolean eqic_unnull(String a, String b) { return eqic(unnull(a), unnull(b)); } static A optCast(Class c, Object o) { return isInstance(c, o) ? (A) o : null; } static Object first(Object list) { return first((Iterable) list); } static A first(List list) { return empty(list) ? null : list.get(0); } static A first(A[] bla) { return bla == null || bla.length == 0 ? null : bla[0]; } static A first(IterableIterator i) { return first((Iterator) i); } static A first(Iterator i) { return i == null || !i.hasNext() ? null : i.next(); } static A first(Iterable i) { if (i == null) return null; Iterator it = i.iterator(); return it.hasNext() ? it.next() : null; } static Character first(String s) { return empty(s) ? null : s.charAt(0); } static Character first(CharSequence s) { return empty(s) ? null : s.charAt(0); } static A first(Pair p) { return p == null ? null : p.a; } static A first(T3 t) { return t == null ? null : t.a; } static Byte first(byte[] l) { return empty(l) ? null : l[0]; } static A first(A[] l, IF1 pred) { return firstThat(l, pred); } static A first(Iterable l, IF1 pred) { return firstThat(l, pred); } static A first(IF1 pred, Iterable l) { return firstThat(pred, l); } static A first(AppendableChain a) { return a == null ? null : a.element; } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } static List syncInstancesOf(Iterable i, Class c) { if (i == null) return new ArrayList(); synchronized(i) { return collectInstances(i, c); } } static List syncInstancesOf(Class c, Iterable i) { return syncInstancesOf(i, c); } static int max(int a, int b) { return Math.max(a, b); } static int max(int a, int b, int c) { return max(max(a, b), c); } 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 float max(float a, float b) { return Math.max(a, b); } static double max(double a, double b) { return Math.max(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 float max(float[] c) { if (c.length == 0) return Float.MAX_VALUE; float 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 short max(short[] c) { short x = -0x8000; for (short d : c) if (d > x) x = d; return x; } static int max(int[] c) { int x = Integer.MIN_VALUE; for (int d : c) if (d > x) x = d; return x; } static boolean eqicOrSwicPlusSpace(String a, String b) { return swic(a, b) && (l(a) == l(b) || a.charAt(l(b)) == ' '); } static String gazelleFavIconSnippet() { return "#1400504"; } // makes dynamic & static modules static String dm_require(String moduleLibID) { return dm_makeModule(moduleLibID); } static AutoCloseable dm_vmBus_onMessage(final String msg, final VF1 onMessage) { return dm_ownResource(vmBus_onMessage(msg, onMessage)); } static AutoCloseable dm_vmBus_onMessage(final String msg, final IVF1 onMessage) { return dm_ownResource(vmBus_onMessage(msg, ivf1ToVF1(onMessage))); } static AutoCloseable dm_vmBus_onMessage(final String msg, final IVF2 onMessage) { return dm_ownResource(vmBus_onMessage(msg, ivf2ToVF2(onMessage))); } static AutoCloseable dm_vmBus_onMessage(String msg, final VF2 onMessage) { return dm_ownResource(vmBus_onMessage(msg, onMessage)); } static AutoCloseable dm_vmBus_onMessage(String msg, Runnable onMessage) { return dm_ownResource(vmBus_onMessage(msg, onMessage)); } static double systemLoad() { double load = osMXBean().getSystemLoadAverage(); if (load < 0) return systemCPULoad(); // fallback for Windows return load; } static RuntimeException rethrow(Throwable t) { if (t instanceof Error) _handleError((Error) t); throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } static RuntimeException rethrow(String msg, Throwable t) { throw new RuntimeException(msg, t); } static boolean empty(Collection c) { return c == null || c.isEmpty(); } static boolean empty(Iterable c) { return c == null || !c.iterator().hasNext(); } static boolean empty(CharSequence s) { return s == null || s.length() == 0; } 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); if (o instanceof byte[]) return empty((byte[]) o); if (o == null) return true; throw fail("unknown type for 'empty': " + getType(o)); } static boolean empty(Iterator i) { return i == null || !i.hasNext(); } static boolean empty(double[] a) { return a == null || a.length == 0; } static boolean empty(float[] a) { return a == null || a.length == 0; } static boolean empty(int[] a) { return a == null || a.length == 0; } static boolean empty(long[] a) { return a == null || a.length == 0; } static boolean empty(byte[] a) { return a == null || a.length == 0; } static boolean empty(short[] a) { return a == null || a.length == 0; } static boolean empty(MultiSet ms) { return ms == null || ms.isEmpty(); } static boolean empty(MultiMap mm) { return mm == null || mm.isEmpty(); } static boolean empty(File f) { return getFileSize(f) == 0; } static boolean empty(IntRange r) { return r == null || r.empty(); } static boolean empty(Rect r) { return !(r != null && r.w != 0 && r.h != 0); } static ConceptFieldIndexCI indexConceptFieldCI(Class c, String field) { return indexConceptFieldCI(db_mainConcepts(), c, field); } static ConceptFieldIndexCI indexConceptFieldCI(Concepts concepts, Class c, String field) { ConceptFieldIndexCI idx = getConceptFieldCIIndex(concepts, c, field); return idx != null ? idx : new ConceptFieldIndexCI(concepts, c, field); } static void indexConceptFieldDesc(Class c, String field) { indexConceptFieldDesc(db_mainConcepts(), c, field); } // if there is a non-ordered index, it keeps that one static void indexConceptFieldDesc(Concepts concepts, Class c, String field) { if (!isConceptFieldIndexed(concepts, c, field)) new ConceptFieldIndexDesc(concepts, c, field); } static volatile Concepts mainConcepts; // Where we create new concepts static Concepts db_mainConcepts() { if (mainConcepts == null) mainConcepts = newConceptsWithClassFinder(getDBProgramID()); return mainConcepts; } static void cleanMeUp_concepts() { if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp(); // mainConcepts = null; // TODO } static void printConceptIndices() { printConceptIndices(db_mainConcepts()); } static void printConceptIndices(Concepts cc) { if (cc == null) return; Set> classes = joinSets(keys(cc.fieldIndices), keys(cc.ciFieldIndices)); print(n2(classes, "indexed class", "indexed classes") + "."); for (Class c : classes) { print("Indices on " + c + ":"); for (Map.Entry __1 : _entrySet( cc.fieldIndices.get(c))) { String field = __1.getKey(); IFieldIndex idx = __1.getValue(); print(" " + field + ": " + idx); } for (Map.Entry __0 : _entrySet( cc.ciFieldIndices.get(c))) { String field = __0.getKey(); IFieldIndex idx = __0.getValue(); print(" " + field + " [case-insensitive]: " + idx); } } } static void onConceptsChange(Runnable r) { onConceptsChange(db_mainConcepts(), r); } static void onConceptsChange(Concepts cc, Runnable r) { cc.addConceptIndex(simpleConceptIndex(r)); cc.onAllChanged.add(r); } static int countConcepts(Concepts concepts, Class c, Object... params) { return concepts.countConcepts(c, params); } static int countConcepts(Class c, Object... params) { return db_mainConcepts().countConcepts(c, params); } static int countConcepts() { return db_mainConcepts().countConcepts(); } static int countConcepts(String className) { return db_mainConcepts().countConcepts(className); } static int countConcepts(Concepts concepts, String className) { return concepts.countConcepts(className); } static int countConcepts(Concepts concepts) { return concepts.countConcepts(); } static void _close(AutoCloseable c) { if (c != null) try { c.close(); } catch (Throwable e) { // Some classes stupidly throw an exception on double-closing if (c instanceof javax.imageio.stream.ImageOutputStream) return; else throw rethrow(e); } } static Set syncLinkedHashSet() { return synchroLinkedHashSet(); } static List syncMap(Object f, Map map) { return syncMap(map, f); } // map: func(key, value) -> list element static List syncMap(Map map, Object f) { return map(cloneLinkedHashMap(map), f); // TODO: use a temporary list instead } static Map syncMap() { return synchroHashMap(); } static Map syncMap(Map map) { return synchronizedMap(map); } static WeakReference weakRef(A a) { return newWeakReference(a); } static void setFieldToIVF1Proxy(Object o, String field, IVF1 target) { setFieldToSingleMethodProxy(o, field, target, "get"); } static Object call(Object o) { return callF(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) { //ret call_cached(o, method, args); return call_withVarargs(o, method, args); } static Object rcall(String method, Object o, Object... args) { return call_withVarargs(o, method, args); } static void pcallFAll(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) pcallF(f, args); } static void pcallFAll(Iterator it, Object... args) { while (it.hasNext()) pcallF(it.next(), args); } static String rcall_string(String method, Object o, Object... args) { return (String) rcall(method, o, args); } static volatile PersistableThrowable _handleException_lastException; static List _handleException_onException = synchroList(ll((IVF1) (__1 -> printStackTrace2(__1)))); static boolean _handleException_showThreadCancellations = false; static void _handleException(Throwable e) { _handleException_lastException = persistableThrowable(e); Throwable e2 = innerException(e); if (e2.getClass() == RuntimeException.class && eq(e2.getMessage(), "Thread cancelled.") || e2 instanceof InterruptedException) { if (_handleException_showThreadCancellations) System.out.println(getStackTrace_noRecord(e2)); return; } for (Object f : cloneList(_handleException_onException)) try { callF(f, e); } catch (Throwable e3) { try { printStackTrace2(e3); // not using pcall here - it could lead to endless loops } catch (Throwable e4) { System.out.println(getStackTrace(e3)); System.out.println(getStackTrace(e4)); } } } static B mapGet(Map map, A a) { return map == null || a == null ? null : map.get(a); } static B mapGet(A a, Map map) { return map == null || a == null ? null : map.get(a); } // callableFunctions also includes method names static Object eval(String text, Collection callableFunctions) { return new Eval(callableFunctions).eval(text); } static Object eval(String text) { return new Eval().eval(text); } static Set eval_standardsafefunctions = lithashset("bigint", "compareTo"); static class Eval { // safety switches Collection callableFunctions; boolean allowReadingStaticFields = false; HashMap variables = new HashMap(); List tok; Object _null = new Object(); Eval(Collection callableFunctions) { this.callableFunctions = callableFunctions;} Eval() { callableFunctions = eval_standardsafefunctions; } void setVar(String id, Object value) { variables.put(id, or(value, _null)); } Object eval(String text) { tok = javaTok(text); // XX: better get from parser List e = jparse(text, "exp"); return evalExp(e); } Object evalExp(List e) { // e == [1, 9, "call", " ( )", [1, 3, "identifier"], [5, 7, "quoted"]] String cl = getString(e, 2); if (eq(cl, "quoted")) return unquote(get(tok, (int) get(e, 0))); if (eq(cl, "identifier")) { String id = get(tok, (int) get(e, 0)); Object val = variables.get(id); if (val != null) return val == _null ? null : val; if (allowReadingStaticFields) return get(getMainClass(), id); else throw fail("Unknown variable: " + id); } if (eq(cl, "call")) { return evalCall(e, getMainClass()); } if (eq(cl, "methodcall")) { List e_exp = (List) (get(e, 4)); // submatch 1 List e_call = (List) (get(e, 5)); // submatch 2 Object obj = evalExp(e_exp); return evalCall(e_call, obj); } throw fail("woot exp: " + structure(e)); } Object evalCall(List e, Object obj) { List e_name = (List) (get(e, 4)); // submatch 1 List e_args = (List) (get(e, 5)); // submatch 2 String fname = get(tok, (int) get(e_name, 0)); List args = evalArgs(e_args); return callMethod(obj, fname, args); } List evalArgs(List e) { return litlist(evalExp(e)); // todo... } Object callMethod(Object obj, String fname, List args) { boolean mayCall = mayCallFunction(fname); if (!hasMethodNamed(obj, fname)) throw fail("Method not defined in " + getClassName(obj) + (mayCall ? "" : ", also not callable") + ": " + fname); if (!mayCallFunction(fname)) throw fail("Not allowed to call method: " + fname); return call(obj, fname, toObjectArray(args)); } boolean mayCallFunction(String fname) { return callableFunctions.contains(fname); } } static String jsDollarVars(String s, Object... __) { return replaceDollarVars_js(s, __); } static Object dm_call(Object moduleOrID, String method, Object... args) { Object mod = dm_getModule(moduleOrID); if (mod == null) return null; AutoCloseable __1 = dm_enter(mod); try { return call_withVarargs(mod, method, args); } finally { _close(__1); }} static String jsonEncode(Object o) { StringBuilder buf = new StringBuilder(); jsonEncode(o, buf); return str(buf); } static void jsonEncode(Object o, StringBuilder buf) { if (o == null) buf.append("null"); else if (o instanceof String) buf.append(quote((String) o)); else if (o instanceof Number || o instanceof Boolean) buf.append(o); else if (o instanceof Map) { Map map = (Map) o; buf.append("{"); boolean first = true; for (Object key : keys(map)) { if (first) first = false; else buf.append(","); buf.append(quote((String) key)); buf.append(":"); jsonEncode(map.get(key), buf); } buf.append("}"); } else if (o instanceof Collection) { Collection l = (Collection) o; buf.append("["); boolean first = true; for (Object element : l) { if (first) first = false; else buf.append(","); jsonEncode(element, buf); } buf.append("]"); } else throw fail("Unknown object for JSON encoding: " + className(o)); } static HashMap litmap(Object... x) { HashMap map = new HashMap(); litmap_impl(map, x); return map; } static void litmap_impl(Map map, Object... x) { if (x != null) for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) map.put(x[i], x[i+1]); } static AutoCloseable tempAdd(final Collection l, final A a) { if (l == null || l.contains(a)) return null; l.add(a); return new AutoCloseable() { public String toString() { return "l.remove(a);"; } public void close() throws Exception { l.remove(a); }}; } static boolean cleanUp_interruptThreads = false; // experimental static void cleanUp(Object c) { if (c == null) return; if (c instanceof AutoCloseable) { close_pcall((AutoCloseable) c); return; } if (c instanceof java.util.Timer) { ((java.util.Timer) c).cancel(); return; } if (c instanceof Collection) { cleanUp((Collection) c); return; } if (c instanceof Map) { for (Object o : keys((Map) c)) cleanUp(o); for (Object o : values((Map) c)) cleanUp(o); ((Map) c).clear(); return; } //if (!(c instanceof Class)) ret; try { // revoke license preCleanUp(c); // unpause setOpt(c, "ping_pauseAll", false); // call custom cleanMeUp() and cleanMeUp_*() functions innerCleanUp(c); // Java spec says finalize should only be called by GC, // but we care to differ. // Edit: Not anymore (illegal access warnings) /*if (isTrue(vmMap_get('callFinalize))) pcallOpt(c, "finalize");*/ // remove all virtual bots (hope this works) List androids = (List) getOpt(c, "record_list"); for (Object android : unnull(androids)) pcallOpt(android, "dispose"); // heck we'll dispose anything // sub-cleanup List classes = (List) (getOpt(c, "hotwire_classes")); if (classes != null) for (WeakReference cc : classes) { try { cleanUp(cc.get()); } catch (Throwable __e) { _handleException(__e); }} // interrupt all threads (experimental, they might be doing cleanup?) if (cleanUp_interruptThreads) { List threads = registeredThreads(c); if (nempty(threads)) { print("cleanUp: Interrupting " + n2(threads, "thread") + ": " + joinWithComma(allToString(threads))); interruptThreads(threads); } } } catch (Throwable __e) { _handleException(__e); } setOpt(c, "cleaningUp_flag" , false); if (c instanceof Class && ((Class) c).getName().equals("main")) retireClassLoader(((Class) c).getClassLoader()); } static void cleanUp(Collection l) { if (l == null) return; for (Object c : l) cleanUp(c); l.clear(); } static Map syncWeakHashMap() { return newWeakHashMap(); } static void closeAllKeysAndClear(Map map) { closeAllAndClear(keys(map)); } static A set(A o, String field, Object value) { if (o == null) return null; if (o instanceof Class) set((Class) o, field, value); else try { Field f = set_findField(o.getClass(), field); makeAccessible(f); smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } return o; } static void set(Class c, String field, Object value) { if (c == null) return; try { Field f = set_findStaticField(c, field); makeAccessible(f); 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() & java.lang.reflect.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 void set(BitSet bs, int idx) { { if (bs != null) bs.set(idx); } } static volatile StringBuffer local_log = new StringBuffer(); // not redirected static volatile Appendable 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 boolean print_silent = false; // total mute if set static Object print_byThread_lock = new Object(); static volatile ThreadLocal print_byThread; // special handling by thread - prefers F1 static volatile Object print_allThreads; static volatile Object print_preprocess; static void print() { print(""); } static A print(String s, A o) { print(combinePrintParameters(s, o)); return o; } // slightly overblown signature to return original object... static A print(A o) { ping_okInCleanUp(); if (print_silent) return o; String s = o + "\n"; print_noNewLine(s); return o; } static void print_noNewLine(String s) { try { Object f = getThreadLocal(print_byThread_dontCreate()); if (f == null) f = print_allThreads; if (f != null) // We do need the general callF machinery here as print_byThread is sometimes shared between modules if (isFalse( f instanceof F1 ? ((F1) f).get(s) : callF(f, s))) return; } catch (Throwable e) { System.out.println(getStackTrace(e)); } print_raw(s); } static void print_raw(String s) { if (print_preprocess != null) s = (String) callF(print_preprocess, s); s = fixNewLines(s); Appendable loc = local_log; Appendable 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); vmBus_send("printed", mc(), s); } static void print_autoRotate() { } static A proxy(Class intrface, final Object target) { if (target == null) return null; if (isInstance(intrface, target)) return (A) target; return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, new proxy_InvocationHandler(target)); } static A proxy(Object target, Class intrface) { return proxy(intrface, target); } static AutoCloseable tempSetTL(ThreadLocal tl, A a) { return tempSetThreadLocal(tl, a); } static AutoCloseable tempSetTL(x30_pkg.x30_util.BetterThreadLocal tl, A a) { return tempSetThreadLocalIfNecessary(tl, a); } static long toLong(Object o) { if (o instanceof Number) return ((Number) o).longValue(); if (o instanceof String) return parseLong((String) o); return 0; } static A getConceptOpt(Class cc, long id) { return getConceptOpt(db_mainConcepts(), cc, id); } static A getConceptOpt(Concepts concepts, Class cc, long id) { return concepts == null ? null : optCast(cc, concepts.getConcept(id)); } static Map jsonDecodeMap(String s) { Object o = jsonDecode(s); if (o instanceof List && empty((List) o)) return new HashMap(); if (o instanceof Map) return (Map) o; else throw fail("Not a JSON map: " + s); } static boolean syncAdd(Collection c, A b) { if (c == null) return false; synchronized(collectionMutex(c)) { return c.add(b); } } static void syncAdd(List l, int idx, A b) { if (l != null) synchronized(collectionMutex(l)) { l.add(idx, b); } } static Pair pair(A a, B b) { return new Pair(a, b); } static Pair pair(A a) { return new Pair(a, a); } 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(); } static String dropParamFromURL(String url, String key) { Map params = paramsFromURL(url); if (!containsKey(params, key)) return url; params.remove(key); return addAnchorToURL( appendParamsToURL(urlWithoutQueryAndAnchor(url), params), anchorFromURL(url)); } static Lock dbLock() { return db_mainConcepts().lock; } static Lock dbLock(Concepts cc) { return cc == null ? null : cc.lock; } static Lock dbLock(Concept c) { return dbLock(c == null ? null : c._concepts); } static void lock(Lock lock) { try { ping(); if (lock == null) return; try { vmBus_send("locking", lock, "thread" , currentThread()); lock.lockInterruptibly(); vmBus_send("locked", lock, "thread" , currentThread()); } catch (InterruptedException e) { Object reason = vm_threadInterruptionReasonsMap().get(currentThread()); print("Locking interrupted! Reason: " + strOr(reason, "Unknown")); printStackTrace(e); rethrow(e); } // NO call to ping here! Make sure lock is always released. } catch (Exception __e) { throw rethrow(__e); } } static void lock(Lock lock, String msg) { print("Locking: " + msg); lock(lock); } static void lock(Lock lock, String msg, long timeout) { print("Locking: " + msg); lockOrFail(lock, timeout); } static ReentrantLock lock() { return fairLock(); } static int l(Object[] a) { return a == null ? 0 : a.length; } static int l(boolean[] a) { return a == null ? 0 : a.length; } static int l(byte[] a) { return a == null ? 0 : a.length; } static int l(short[] a) { return a == null ? 0 : a.length; } static int l(long[] 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(double[] 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(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible static int l(Map m) { return m == null ? 0 : m.size(); } static int l(CharSequence s) { return s == null ? 0 : s.length(); } static long l(File f) { return f == null ? 0 : f.length(); } static int l(Object o) { return o == null ? 0 : o instanceof String ? l((String) o) : o instanceof Map ? l((Map) o) : o instanceof Collection ? l((Collection) o) : o instanceof Object[] ? l((Object[]) o) : o instanceof boolean[] ? l((boolean[]) o) : o instanceof byte[] ? l((byte[]) o) : o instanceof char[] ? l((char[]) o) : o instanceof short[] ? l((short[]) o) : o instanceof int[] ? l((int[]) o) : o instanceof float[] ? l((float[]) o) : o instanceof double[] ? l((double[]) o) : o instanceof long[] ? l((long[]) o) : (Integer) call(o, "size"); } static int l(MultiSet ms) { return ms == null ? 0 : ms.size(); } static int l(IntRange r) { return r == null ? 0 : r.length(); } static long l(LongRange r) { return r == null ? 0 : r.length(); } static int l(AppendableChain a) { return a == null ? 0 : a.size; } static boolean hasConceptIC(Class c, Object... params) { return hasConceptWhereIC(c, params); } static boolean hasConceptIC(Concepts cc, Class c, Object... params) { return hasConceptWhereIC(cc, c, params); } static boolean regexpContainsIC(String pat, String s) { return regexpFindsIC(pat, s); } static boolean neq(Object a, Object b) { return !eq(a, b); } static void vmBus_send(String msg, Object... args) { Object arg = vmBus_wrapArgs(args); pcallFAll_minimalExceptionHandling(vm_busListeners_live(), msg, arg); pcallFAll_minimalExceptionHandling(vm_busListenersByMessage_live().get(msg), msg, arg); } static void vmBus_send(String msg) { vmBus_send(msg, (Object) null); } static String hrefresh(String target) { return hrefresh(0, target); } static String hrefresh(double seconds) { return hrefresh(seconds, ""); } static String hrefresh(double seconds, String target) { return tag("meta", "", "http-equiv", "refresh", "content", iceil(seconds) + (nempty(target) ? "; url=" + target : "")); } static void unlock(Lock lock, String msg) { if (lock == null) return; lock.unlock(); vmBus_send("unlocked", lock, "thread" , currentThread()); print("Unlocked: " + msg); // print afterwards to make sure the lock is always unlocked } static void unlock(Lock lock) { if (lock == null) return; lock.unlock(); vmBus_send("unlocked", lock, "thread" , currentThread()); } static String hhtml(Object contents) { return containerTag("html", contents); } static String hhead(Object contents) { return tag("head", contents); } static String htitle(String title) { return hfulltag("title", htmlencode_noQuotes(title)); } static String hsansserif() { return hcss("body { font-family: Sans-Serif; }"); } static String hmobilefix() { return ""; } static String hmobilefix(String html) { return hAddToHead(html, hmobilefix()); } static String hresponstable() { return hcss_responstable(); } static String hbody(Object contents, Object... params) { return tag("body", contents, params); } static String hfullcenter(Object contents, Object... __) { return tag("table", tr(td(contents, "align" , "center")), paramsPlus(__, "width" , "100%", "height" , "100%")); } static String h3_htmlEncode(Object contents, Object... params) { return h3(htmlEncode2(str(contents)), params); } static String hpostform(Object contents, Object... params) { return tag("form", contents, concatArrays(new Object[] {"method", "POST"}, params)); } static String hhidden(String name, Object value, Object... params) { return tag("input", "", concatArrays(new Object[] {"type", "hidden", "name", name, "value", value}, params)); } static String hhidden(Map map, String... keys) { return hiddenFields(map, keys); } static String tag(String tag) { return htag(tag); } static String tag(String tag, Object contents, Object... params) { return htag(tag, str(contents), params); } static String tag(String tag, StringBuilder contents, Object... params) { return htag(tag, contents, params); } static String tag(String tag, StringBuffer contents, Object... params) { return htag(tag, contents, params); } static String tr(Object contents) { return tag("tr", contents); } static String td(Object contents, Object... params) { return hfulltag("td", contents, params); } static String td() { return td(""); } static String htmlEncode2_nlToBr(String s) { return htmlEncode_nlToBr(s); } static String td_valignTop(Object contents, Object... params) { return tdTop(contents, params); } static String hinputfield(String name, Object... params) { return htextinput(name, params); } // first element of params can be the value static String hpassword(String name, Object... params) { return hpasswordfield(name, params); } static String hpassword(String name) { return hpasswordfield(name); } static String hsubmit(String text, Object... params) { return tag("input", "", concatArrays(new Object[] {"type", "submit", "value", text}, params)); } static String hsubmit() { return hsubmit("Submit"); } static boolean eq(Object a, Object b) { return a == b || a != null && b != null && a.equals(b); } // a little kludge for stuff like eq(symbol, "$X") static boolean eq(Symbol a, String b) { return eq(str(a), b); } static String htmlEncode2(String s) { return htmlencode_noQuotes(s); } static String htmlEncode2(Object o) { return htmlEncode2(strOrEmpty(o)); } static boolean swic(String a, String b) { return startsWithIgnoreCase(a, b); } static boolean swic(String a, String b, Matches m) { if (!swic(a, b)) return false; m.m = new String[] {substring(a, l(b))}; return true; } static long parseLong(String s) { if (empty(s)) return 0; return Long.parseLong(dropSuffix("L", s)); } static long parseLong(Object s) { return Long.parseLong((String) s); } static Object serve404() { return subBot_serve404(); } static Object serve404(String msg) { return subBot_serve404(msg); } static Object serveWithContentType(String text, String contentType) { return serveByteArray(toUtf8(text), contentType); } // TODO: at least convert to UTF-8 on the fly static Object serveText(Object s) { return call(getMainBot(), "serveByteArray", toUtf8(str(s)), "text/plain; charset=utf8"); } static Object addHeader(String header, String value, Object response) { call(response, "addHeader", header, value); return response; } static Object serveJSON(Object... data) { return serveJSON_shallowLineBreaks(jsonPrepareData(data)); } static List lmap(IF1 f, Iterable l) { return lambdaMap(f, l); } static List lmap(IF1 f, A[] l) { return lambdaMap(f, l); } static long conceptID(Concept c) { return c == null ? 0 : c.id; } static long conceptID(Concept.Ref ref) { return conceptID(cDeref(ref)); } static List map(Iterable l, Object f) { return map(f, l); } static List map(Object f, Iterable l) { List x = emptyList(l); if (l != null) for (Object o : l) { ping(); x.add(callF(f, o)); } return x; } static List map(Iterable l, F1 f) { return map(f, l); } static List map(F1 f, Iterable l) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(callF(f, o)); } return x; } static List map(IF1 f, Iterable l) { return map(l, f); } static List map(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map(IF1 f, A[] l) { return map(l, f); } static List map(A[] l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map(Object f, Object[] l) { return map(f, asList(l)); } static List map(Object[] l, Object f) { return map(f, l); } static List map(Object f, Map map) { return map(map, f); } // map: func(key, value) -> list element static List map(Map map, Object f) { List x = new ArrayList(); if (map != null) for (Object _e : map.entrySet()) { ping(); Map.Entry e = (Map.Entry) _e; x.add(callF(f, e.getKey(), e.getValue())); } return x; } static List map(Map map, IF2 f) { return map(map, (Object) f); } // new magic alias for mapLL - does it conflict? static List map(IF1 f, A data1, A... moreData) { List x = emptyList(l(moreData)+1); x.add(f.get(data1)); if (moreData != null) for (A o : moreData) { ping(); x.add(f.get(o)); } return x; } static LinkedHashMap litorderedmap(Object... x) { LinkedHashMap map = new LinkedHashMap(); litmap_impl(map, x); return map; } static String dropSlashPrefix(String s) { return dropLeadingSlash(s); } static boolean isInteger(String s) { int n = l(s); if (n == 0) return false; int i = 0; if (s.charAt(0) == '-') if (++i >= n) return false; while (i < n) { char c = s.charAt(i); if (c < '0' || c > '9') return false; ++i; } return true; } static String dropTrailingSlashIfNemptyAfterwards(String s) { return l(s) > 1 ? dropSuffix("/", s) : s; } static String shortClassName(Object o) { if (o == null) return null; Class c = o instanceof Class ? (Class) o : o.getClass(); String name = c.getName(); return shortenClassName(name); } static A highestConceptByField(Class c, String field) { return highestConceptByField(db_mainConcepts(), c, field); } static A highestConceptByField(Concepts concepts, Class c, String field) { // indexed IFieldIndex index = concepts.getFieldIndex(c, field); if (index instanceof ConceptFieldIndexDesc) { Map.Entry e = ((NavigableMap) ((ConceptFieldIndexDesc) index).valueToObject.data).firstEntry(); return e == null ? null : (A) first((Collection) e.getValue()); } return highestByField(field, concepts.list(c)); } static List sortedByConceptIDDesc(Collection c) { return sortedByCalculatedFieldDesc(__52 -> conceptID(__52), c); } static List filter(Iterable c, Object pred) { if (pred instanceof F1) return filter(c, (F1) pred); List x = new ArrayList(); if (c != null) for (Object o : c) if (isTrue(callF(pred, o))) x.add(o); return x; } static List filter(Object pred, Iterable c) { return filter(c, pred); } static List filter(Iterable c, F1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static List filter(F1 pred, Iterable c) { return filter(c, pred); } //ifclass IF1 static List filter(Iterable c, IF1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static List filter(B[] c, IF1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static List filter(IF1 pred, Iterable c) { return filter(c, pred); } //endif static List list(Class type) { return db_mainConcepts().list(type); } static List list(Concepts concepts, Class type) { return concepts.list(type); } static List list(String type) { return db_mainConcepts().list(type); } static List list(Concepts concepts, String type) { return concepts.list(type); } static String ul(String... list) { return ul(asList(list)); } static String ul(Collection list, Object... params) { StringBuilder buf = new StringBuilder(); int i = indexOf(params, null); // null separates params for ul from params for li if (i == -1) i = l(params); for (Object s : withoutNulls(list)) buf.append(tag("li", s, subArray(params, i+1))).append("\n"); return containerTag("ul", buf, subArray(params, 0, i)) + "\n"; } static List takeFirst(List l, int n) { return l(l) <= n ? l : newSubListOrSame(l, 0, n); } static List takeFirst(int n, List l) { return takeFirst(l, n); } static String takeFirst(int n, String s) { return substring(s, 0, n); } static String takeFirst(String s, int n) { return substring(s, 0, n); } static CharSequence takeFirst(int n, CharSequence s) { return subCharSequence(s, 0, n); } static List takeFirst(int n, Iterator it) { if (it == null) return null; List l = new ArrayList(); for (int _repeat_0 = 0; _repeat_0 < n; _repeat_0++) { if (it.hasNext()) l.add(it.next()); else break; } return l; } static List takeFirst(int n, Iterable i) { if (i == null) return null; return i == null ? null : takeFirst(n, i.iterator()); } static List takeFirst(int n, IterableIterator i) { return takeFirst(n, (Iterator) i); } static int[] takeFirst(int n, int[] a) { return takeFirstOfIntArray(n, a); } static List sortedByFieldDesc(Collection c, String field) { List l = new ArrayList(c); sort(l, descFieldComparator(field)); return l; } static List sortedByFieldDesc(String field, Collection c) { return sortedByFieldDesc(c, field); } static List sortedByConceptID(Collection c) { return sortedByCalculatedField(__53 -> conceptID(__53), c); } static Collection conceptsWhereCI(Class c, Object... params) { return findConceptsWhereCI(c, params); } static Collection conceptsWhereCI(String c, Object... params) { return findConceptsWhereCI(c, params); } static Collection conceptsWhereCI(Concepts concepts, Class c, Object... params) { return findConceptsWhereCI(concepts, c, params); } static List conceptsWhereCI(Concepts concepts, String c, Object... params) { return findConceptsWhereCI(concepts, c, params); } static Concept getConcept(long id) { return db_mainConcepts().getConcept(id); } static Concept getConcept(Concepts concepts, long id) { return concepts.getConcept(id); } static A getConcept(Class cc, long id) { return getConcept(db_mainConcepts(), cc, id); } static A getConcept(Concepts concepts, Class cc, long id) { Concept c = concepts.getConcept(id); if (c == null) return null; if (!isInstance(cc, c)) throw fail("Can't convert concept: " + getClassName(c) + " -> " + getClassName(cc) + " (" + id + ")"); return (A) c; } static String or2(String a, String b) { return nempty(a) ? a : b; } static String or2(String a, String b, String c) { return or2(or2(a, b), c); } static String hcomment(String text) { return hcommentSafe(text); } static String joinNemptiesWithColon(String... strings) { return joinNempties(": ", strings); } static String joinNemptiesWithColon(Collection strings) { return joinNempties(": ", strings); } static String hform(Object contents, Object... params) { return htag("form", contents, params); } static String p(Object contents, Object... params) { return hfulltag("p", contents, params) + "\n"; } static String p() { return p(""); } static String small(Object contents, Object... params) { return tag("small", contents, params); } static long parseLongOpt_pcall(String s) { try { return isInteger(s) ? parseLong(s) : 0; } catch (Throwable __e) { print(exceptionToStringShort(__e)); } return 0; } static String joinWithSpace(Iterable c) { return join(" ", c); } static String joinWithSpace(String... c) { return join(" ", c); } static String b(Object contents, Object... params) { return tag("b", contents, params); } static String addPlusToCount(long max, long n, String s) { if (n != max) return s; return insertString(s, smartIndexOfSpaceEtc(s), "+"); } static String nPosts(long n) { return n2(n, "post"); } static String nPosts(Collection l) { return nPosts(l(l)); } static String nPosts(Map map) { return nPosts(l(map)); } static boolean startsWith(String a, String b) { return a != null && a.startsWith(unnull(b)); } static boolean startsWith(String a, char c) { return nemptyString(a) && a.charAt(0) == c; } static boolean startsWith(String a, String b, Matches m) { if (!startsWith(a, b)) return false; if (m != null) m.m = new String[] {substring(a, strL(b))}; return true; } static boolean startsWith(List a, List b) { if (a == null || listL(b) > listL(a)) return false; for (int i = 0; i < listL(b); i++) if (neq(a.get(i), b.get(i))) return false; return true; } static void touchConcept(Concept c) { if (c != null) c.change(); } static String hscript(String script) { return hjavascript(script); } static A uniqCI(Class c, Object... params) { return uniqueConcept(db_mainConcepts(), c, params); } static A uniqCI(Concepts cc, Class c, Object... params) { AutoCloseable __1 = tempDBLock(cc); try { params = expandParams(c, params); A x = findConceptWhereCI(cc, c, params); if (x == null) { x = unlisted(c); csetAll(x, params); cc.register(x); } return x; } finally { _close(__1); }} static void sleepSeconds(double s) { if (s > 0) sleep(round(s*1000)); } static String rawLink(String pageName) { return "/" + parseSnippetID(getProgramID()) + "/raw" + addPrefix("/", pageName); } static String rawLink() { return "/" + parseSnippetID(getProgramID()) + "/raw"; } static String rawLink(String pageName, String contents) { return ahref(rawLink(pageName), contents); } static String str(Object o) { return o == null ? "null" : o.toString(); } static String str(char[] c) { return new String(c); } static boolean eqic(String a, String b) { if ((a == null) != (b == null)) return false; if (a == null) return true; return a.equalsIgnoreCase(b); } static boolean eqic(Symbol a, Symbol b) { return eq(a, b); } static boolean eqic(Symbol a, String b) { return eqic(asString(a), b); } static boolean eqic(char a, char b) { if (a == b) return true; char u1 = Character.toUpperCase(a); char u2 = Character.toUpperCase(b); if (u1 == u2) return true; return Character.toLowerCase(u1) == Character.toLowerCase(u2); } static Object serveFavIcon(String icoSnippet) { return serveFavIcon(loadLibrary(icoSnippet)); } static Object serveFavIcon(File icoFile) { return favIconHeaders(subBot_serveFile(icoFile, faviconMimeType())); } static Map withoutKey(Map map, A key) { return mapWithoutKey(map, key); } static Map withoutKey(A key, Map map) { return withoutKey(map, key); } static A conceptWhereIC(Class c, Object... params) { return findConceptWhereCI(c, params); } static Concept conceptWhereIC(String c, Object... params) { return findConceptWhereCI(db_mainConcepts(), c, params); } static A conceptWhereIC(Concepts concepts, Class c, Object... params) { return findConceptWhereCI(concepts, c, params); } static Concept conceptWhereIC(Concepts concepts, String c, Object... params) { return findConceptWhereCI(concepts, c, params); } static A getVar(IF0 v) { return v == null ? null : v.get(); } static String beforeSlashOrAll(String s) { return beforeSlash_orAll(s); } static String substring(String s, int x) { return substring(s, x, strL(s)); } static String substring(String s, int x, int y) { if (s == null) return null; if (x < 0) x = 0; int n = s.length(); if (y < x) y = x; if (y > n) y = n; if (x >= y) return ""; return s.substring(x, y); } static String substring(String s, IntRange r) { return r == null ? null : substring(s, r.start, r.end); } // convenience method for quickly dropping a prefix static String substring(String s, CharSequence l) { return substring(s, lCharSequence(l)); } static String unnull(String s) { return s == null ? "" : s; } static Collection unnull(Collection l) { return l == null ? emptyList() : l; } static List unnull(List l) { return l == null ? emptyList() : l; } static int[] unnull(int[] l) { return l == null ? emptyIntArray() : l; } static char[] unnull(char[] l) { return l == null ? emptyCharArray() : l; } static double[] unnull(double[] l) { return l == null ? emptyDoubleArray() : l; } static Map unnull(Map l) { return l == null ? emptyMap() : l; } static Iterable unnull(Iterable i) { return i == null ? emptyList() : i; } static A[] unnull(A[] a) { return a == null ? (A[]) emptyObjectArray() : a; } static BitSet unnull(BitSet b) { return b == null ? new BitSet() : b; } static Pt unnull(Pt p) { return p == null ? new Pt() : p; } //ifclass Symbol static Symbol unnull(Symbol s) { return s == null ? emptySymbol() : s; } //endif static Pair unnull(Pair p) { return p != null ? p : new Pair(null, null); } static int unnull(Integer i) { return i == null ? 0 : i; } static long unnull(Long l) { return l == null ? 0L : l; } static double unnull(Double l) { return l == null ? 0.0 : l; } static List stringToStringListOpt(IF1> f, Object o) { if (o instanceof String) return f.get((String) o); return (List) o; } static List tok_identifiersInOrder(String s) { return filter(__54 -> isIdentifier(__54), javaTokC(s)); } static List tok_identifiersInOrder(List tok) { return filter(__55 -> isIdentifier(__55), codeTokens(tok)); } static int min(int a, int b) { return Math.min(a, b); } static long min(long a, long b) { return Math.min(a, b); } static float min(float a, float b) { return Math.min(a, b); } static float min(float a, float b, float c) { return min(min(a, b), c); } static double min(double a, double 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 float min(float[] c) { float x = Float.MAX_VALUE; for (float 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 short min(short[] c) { short x = 0x7FFF; for (short d : c) if (d < x) x = d; return x; } static int min(int[] c) { int x = Integer.MAX_VALUE; for (int d : c) if (d < x) x = d; return x; } static long sysNow() { ping(); return System.nanoTime()/1000000; } static volatile boolean sleep_noSleep = false; static void sleep(long ms) { ping(); if (ms < 0) return; // allow spin locks if (isAWTThread() && ms > 100) throw fail("Should not sleep on AWT thread"); try { Thread.sleep(ms); } catch (Exception e) { throw new RuntimeException(e); } } static void sleep() { try { if (sleep_noSleep) throw fail("nosleep"); print("Sleeping."); sleepQuietly(); } catch (Exception __e) { throw rethrow(__e); } } static List objectsWhereFieldGreaterThan(Collection c, String field, Object value) { List l = new ArrayList(); for (A x : unnull(c)) if (cmp(getOpt(x, field), value) > 0) l.add(x); return l; } static boolean conceptsWithFieldGreaterThan_sorted_verbose = false; static Collection conceptsWithFieldGreaterThan_sorted(Class c, String field, Object value) { return conceptsWithFieldGreaterThan_sorted(db_mainConcepts(), c, field, value); } static Collection conceptsWithFieldGreaterThan_sorted(Concepts concepts, Class c, String field, Object value) { // indexed IFieldIndex index = concepts.getFieldIndex(c, field); if (index instanceof ConceptFieldIndexDesc) return reverseInPlace(((ConceptFieldIndexDesc) index).objectsWithValueGreaterThan(value)); // filter manually if (conceptsWithFieldGreaterThan_sorted_verbose) print("conceptsWithFieldGreaterThan: table scan of " + c + " for field " + field); return sortByFieldInPlace(field, objectsWhereFieldGreaterThan(concepts.list(c), field, value)); } static double elapsedSeconds_sysNow(long time) { return elapsedSeconds(time); } static Object serveJSON_breakAtLevels(int levels, Object... data) { return serveText(jsonEncode_breakAtLevels(levels, jsonPrepareData(data))); } static Map mapToValues(Iterable l, Object f) { return mapKeyAndFunction(l, f); } static Map mapToValues(Object f, Iterable l) { return mapKeyAndFunction(f, l); } static Map mapToValues(Iterable l, IF1 f) { return mapKeyAndFunction(f, l); } static Map mapToValues(IF1 f, Iterable l) { return mapKeyAndFunction(f, l); } static Map mapToValues(Map map, IF2 f) { return mapKeyAndFunction(map, f); } static Pair uniq2(Class c, Object... params) { return uniq2(db_mainConcepts(), c, params); } static Pair uniq2(Concepts cc, Class c, Object... params) { AutoCloseable __1 = tempDBLock(cc); try { params = expandParams(c, params); A x = findConceptWhere(cc, c, params); if (x == null) { x = unlisted(c); csetAll(x, params); cc.register(x); return pair(x, true); } return pair(x, false); } finally { _close(__1); }} static List allToLong(Collection l) { return lmap(__56 -> toLong(__56), l); } static List tok_integersInOrder(String s) { return filter(__57 -> isInteger(__57), javaTokC(s)); } static String unnullForIteration(String s) { return s == null ? "" : s; } static Collection unnullForIteration(Collection l) { return l == null ? immutableEmptyList() : l; } static List unnullForIteration(List l) { return l == null ? immutableEmptyList() : l; } static int[] unnullForIteration(int[] l) { return l == null ? emptyIntArray() : l; } static char[] unnullForIteration(char[] l) { return l == null ? emptyCharArray() : l; } static double[] unnullForIteration(double[] l) { return l == null ? emptyDoubleArray() : l; } static short[] unnullForIteration(short[] l) { return l == null ? emptyShortArray() : l; } static Map unnullForIteration(Map l) { return l == null ? immutableEmptyMap() : l; } static Iterable unnullForIteration(Iterable i) { return i == null ? immutableEmptyList() : i; } static A[] unnullForIteration(A[] a) { return a == null ? (A[]) emptyObjectArray() : a; } static BitSet unnullForIteration(BitSet b) { return b == null ? new BitSet() : b; } static Pt unnullForIteration(Pt p) { return p == null ? new Pt() : p; } //ifclass Symbol static Symbol unnullForIteration(Symbol s) { return s == null ? emptySymbol() : s; } //endif static Pair unnullForIteration(Pair p) { return p != null ? p : new Pair(null, null); } static long unnullForIteration(Long l) { return l == null ? 0L : l; } static ThreadLocal doPost_silently = new ThreadLocal(); static ThreadLocal doPost_timeout = new ThreadLocal(); static ThreadLocal> doPost_extraHeaders = new ThreadLocal(); static String doPost(String url, Map urlParameters) { return doPost(urlParameters, url); } static String doPost(Map urlParameters, String url) { return doPost(makePostData(urlParameters), url); } static String doPost(String urlParameters, String url) { try { URL _url = new URL(url); ping(); return doPost(urlParameters, _url.openConnection(), _url); } catch (Exception __e) { throw rethrow(__e); } } static String doPost(String urlParameters, URLConnection conn, URL url) { try { boolean silently = isTrue(optParam(doPost_silently)); Long timeout = optParam(doPost_timeout); Map extraHeaders = optPar(doPost_extraHeaders); setHeaders(conn); for (String key : keys(extraHeaders)) { conn.setRequestProperty(key, extraHeaders.get(key)); } int l = lUtf8(urlParameters); if (!silently) print("Sending POST request: " + hideCredentials(url) + " (" + l + " bytes)"); // connect and do POST if (timeout != null) setURLConnectionTimeouts(conn, timeout); ((HttpURLConnection) conn).setRequestMethod("POST"); conn.setDoOutput(true); conn.setRequestProperty("Content-Length", str(l)); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); writer.write(urlParameters); writer.flush(); String contents = loadPage_utf8(conn, url, false); writer.close(); return contents; } catch (Exception __e) { throw rethrow(__e); } } static CloseableIterableIterator linesFromFile(File f) { return linesFromFile(f, null); } static CloseableIterableIterator linesFromFile(File f, IResourceHolder resourceHolder) { try { if (!f.exists()) return emptyCloseableIterableIterator(); if (ewic(f.getName(), ".gz")) return linesFromReader(utf8bufferedReader(newGZIPInputStream(f)), resourceHolder); return linesFromReader(utf8bufferedReader(f), resourceHolder); } catch (Exception __e) { throw rethrow(__e); } } static CloseableIterableIterator linesFromFile(String path) { return linesFromFile(path, null); } static CloseableIterableIterator linesFromFile(String path, IResourceHolder resourceHolder) { return linesFromFile(newFile(path), resourceHolder); } static boolean isProperlyQuoted(String s) { return s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"") && (!s.endsWith("\\\"") || s.endsWith("\\\\\"")) && !containsNewLine(s); } static Map safeUnstructureMap(String s) { return (Map) safeUnstructure(s); } static Map safeUnstructureMap(File f) { return (Map) safeUnstructure(f); } static String unquote(String s) { if (s == null) return null; if (startsWith(s, '[')) { 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.length() > 1) { char c = s.charAt(0); if (c == '\"' || c == '\'') { int l = endsWith(s, c) ? s.length()-1 : s.length(); StringBuilder sb = new StringBuilder(l-1); for (int i = 1; i < l; i++) { char ch = s.charAt(i); if (ch == '\\') { char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; } } sb.append((char) Integer.parseInt(code, 8)); continue; } switch (nextChar) { case '\"': ch = '\"'; break; 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; // Hex Unicode: u???? case 'u': if (i >= l - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.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(); } } return s; // not quoted - return original } // TODO: work the other way around when set is small static Map onlyKeys(Map map, Collection keys) { Set keySet = asSet(keys); return filterKeys(map, a -> contains(keySet, a)); } static Map onlyKeys(Map map, A... keys) { return onlyKeys(map, litset(keys)); } static String lines(Iterable lines) { return fromLines(lines); } static String lines(Object[] lines) { return fromLines(asList(lines)); } static List lines(String s) { return toLines(s); } // convenience map call static String lines(Iterable l, IF1 f) { return mapToLines(l, f); } static String struct(Object o) { return structure(o); } static String struct(Object o, structure_Data data) { return structure(o, data); } static Object serve500() { return subBot_serve500(); } static Object serve500(String msg) { return subBot_serve500(msg); } static Map cloneMap(Map map) { if (map == null) return new HashMap(); // assume mutex is equal to map synchronized(map) { return map instanceof TreeMap ? new TreeMap((TreeMap) map) // copies comparator : map instanceof LinkedHashMap ? new LinkedHashMap(map) : new HashMap(map); } } static List cloneMap(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : cloneList(l)) x.add(f.get(o)); return x; } static B getAndRemove(Map map, A a) { if (!containsKey(map, a)) return null; B b = map.get(a); map.remove(a); return b; } static B getAndRemove(A a, Map map) { return getAndRemove(map, a); } static String sortLinesAlphaNumIC(String s) { return lines_possiblyRTrim(s, sortedAlphanumIC(lines(s))); } static List mapToLines(Map map) { List l = new ArrayList(); for (Object key : keys(map)) l.add(str(key) + " = " + str(map.get(key))); return l; } static String mapToLines(Map map, Object f) { return lines(map(map, f)); } static String mapToLines(Object f, Map map) { return lines(map(map, f)); } static String mapToLines(Object f, Iterable l) { return lines(map(f, l)); } static String mapToLines(Iterable l, IF1 f) { return mapToLines((Object) f, l); } static String mapToLines(IF1 f, Iterable l) { return mapToLines((Object) f, l); } static String mapToLines(Map map, IF2 f) { return lines(map(map, f)); } static String mapToLines(IF1 f, A data1, A... moreData) { return lines(map(f, data1, moreData)); } static String urlencode(String x) { try { return URLEncoder.encode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } // Use like this: printVars_str(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(+x); static void printVars_str(Object... params) { print(renderVars_str(params)); } static A assertNempty(A a) { return assertNempty("empty", a); } static A assertNempty(String msg, A a) { if (empty(a)) throw fail(msg + ": " + a); return a; } static A conceptWhereCI(Class c, Object... params) { return findConceptWhereCI(c, params); } static Concept conceptWhereCI(String c, Object... params) { return findConceptWhereCI(db_mainConcepts(), c, params); } static A conceptWhereCI(Concepts concepts, Class c, Object... params) { return findConceptWhereCI(concepts, c, params); } static Concept conceptWhereCI(Concepts concepts, String c, Object... params) { return findConceptWhereCI(concepts, c, params); } static String localTimeWithSeconds(long time) { return simpleDateFormat_local("HH:mm:ss").format(time); } static String localTimeWithSeconds() { return localTimeWithSeconds(now()); } static String unquoteAllLines(String s) { return linesPreservingTrailingNewLine(s, unquoteAll(lines(s))); } static Either eitherB(B b) { return new Either(2, b); } static boolean eqOneOf(Object o, Object... l) { for (Object x : l) if (eq(o, x)) return true; return false; } static Either eitherA(A a) { return new Either(1, a); } static Object[] litparams(Object... l) { int n = 0, n1 = l(l); for (int i = 0; i < n1; i += 2) if (l[i] != null && l[i+1] != null) ++n; if (n == 0) return null; Object[] params = new Object[n*2]; int j = 0; for (int i = 0; i < n1; i += 2) { Object key = l[i], value = l[i+1]; if (key != null && value != null) { params[j++] = key; params[j++] = value; } } return params; } static String hjs(String script) { return hjs(script, (Object[]) null); } static String hjs(String script, Object... __) { return hjavascript(script, __); } static String hjs(JS script) { return hjs(script, (Object[]) null); } static String hjs(JS script, Object... __) { return hjavascript(script == null ? null : script.get(), __); } static String jsQuote(String s) { return javascriptQuote(s); } static String renderConceptDate(Concept cc) { if (cc == null) return ""; String c = formatLocalDateWithSeconds(cc.created); String m = formatLocalDateWithSeconds(cc._modified); String s = "Created " + c; if (neq(c, m)) s += ", modified " + m; return s; } static String targetBlank(String link, Object contents, Object... params) { return hrefBlank(link, contents, params); } static boolean eqicOneOf(String s, String... l) { for (String x : l) if (eqic(s, x)) return true; return false; } static String ahref(String link, Object contents, Object... params) { return link == null ? str(contents) : href(link, contents, params); } static String p_vbar(String... items) { return p_vbar(asList(items)); } static String p_vbar(Collection items, Object... __) { return pUnlessEmpty(joinNemptiesWithVBar(items), __); } static ArrayList cloneList(Iterable l) { return l instanceof Collection ? cloneList((Collection) l) : asList(l); } static ArrayList cloneList(Collection l) { if (l == null) return new ArrayList(); synchronized(collectionMutex(l)) { return new ArrayList(l); } } static List mapPairsToList(Iterable> l, F2 f) { List x = emptyList(l); if (l != null) for (Pair p : l) x.add(callF(f, p.a, p.b)); return x; } static List mapPairsToList(Iterable> l, IF2 f) { List x = emptyList(l); if (l != null) for (Pair p : l) x.add(f.get(p.a, p.b)); return x; } static List mapPairsToList(Object f, Iterable> l) { List x = emptyList(l); if (l != null) for (Pair p : l) x.add(callF(f, p.a, p.b)); return x; } static String html_showLineFeedsForPRE(String html) { return replace(html, "\n", unicode_newLineArrow() + "\n"); } static String div(Object contents, Object... params) { return hfulltag("div", contents, params); } static String div() { return div(""); } static BigInteger div(BigInteger a, BigInteger b) { return a.divide(b); } static BigInteger div(BigInteger a, int b) { return a.divide(bigint(b)); } static double div(double a, double b) { return a/b; } static String sourceCodeToHTML_noEncode(String html) { return "
" + html + "
"; } static String hstyle_sourceCodeLikeInRepo() { return "padding-left: 1em; padding-top: 0.5em; padding-bottom: 0.5em; background-color: #EEEEEE; border-style: solid; border-color: #666666; border-width: 1px 0px 1px 0px;"; } static
List reversed(Iterable l) { return reversedList(l); } static List reversed(A[] l) { return reversedList(asList(l)); } static String reversed(String s) { return reversedString(s); } static Pair, List> filterAntiFilter(Iterable c, Object pred) { List yes = new ArrayList(); List no = new ArrayList(); if (c != null) for (A o : c) (isTrue(callF(pred, o)) ? yes : no).add(o); return pair(yes, no); } static Pair, List> filterAntiFilter(Iterable c, IF1 pred) { return filterAntiFilter(c, (Object) pred); } static Pair, List> filterAntiFilter(IF1 pred, Iterable c) { return filterAntiFilter(c, pred); } static Pair, List> filterAntiFilter(Object pred, Iterable c) { return filterAntiFilter(c, pred); } static A highestBy(Iterable l, Object f) { return highestByFunction(l, f); } static A highestBy(Object f, Iterable l) { return highestByFunction(f, l); } static A highestBy(Iterable l, IF1 f) { return highestByFunction(f, l); } static A highestBy(IF1 f, A[] l) { return highestBy(f, asList(l)); } static A highestBy(IF1 f, Iterable l) { return highestByFunction(f, l); } static String squareBracketIfNempty(String s) { return empty(s) ? "" : squareBracket(s); } static boolean cic(Collection l, String s) { return containsIgnoreCase(l, s); } static boolean cic(Collection l, Symbol s) { return contains(l, s); } static boolean cic(String[] l, String s) { return containsIgnoreCase(l, s); } static boolean cic(String s, char c) { return containsIgnoreCase(s, c); } static boolean cic(String a, String b) { return containsIgnoreCase(a, b); } static String hbuttonLink(String url, String text, Object... params) { return hbuttonOnClick(text, "location.href=" + jsQuote(url) + ";", params); } static String nLines(long n) { return n2(n, "line"); } static String nLines(Collection l) { return nLines(l(l)); } static String nLines(String s) { return nLines(countLines(s)); } static int countLines(String s) { return l(toLines(s)); // yeah could be optimized :-) } static String p_alignRight(String s, Object... params) { return p(s, params_stylePlus(params, "text-align: right")); } static String span_title(String title, Object contents) { return spanTitle(title, contents); } // get purpose 1: access a list/array/map (safer version of x.get(y)) static A get(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } // seems to conflict with other signatures /*static B get(Map map, A key) { ret map != null ? map.get(key) : null; }*/ static A get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } // default to false static boolean get(boolean[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : false; } // get purpose 2: access a field by reflection or a map static Object get(Object o, String field) { try { if (o == null) return null; if (o instanceof Class) return get((Class) o, field); if (o instanceof Map) return ((Map) o).get(field); Field f = getOpt_findField(o.getClass(), field); if (f != null) { makeAccessible(f); return f.get(o); } if (o instanceof DynamicObject) return getOptDynOnly(((DynamicObject) o), field); } catch (Exception e) { throw asRuntimeException(e); } throw new RuntimeException("Field '" + field + "' not found in " + o.getClass().getName()); } static Object get_raw(String field, Object o) { return get_raw(o, field); } static Object get_raw(Object o, String field) { try { if (o == null) return null; Field f = get_findField(o.getClass(), field); makeAccessible(f); return f.get(o); } catch (Exception __e) { throw rethrow(__e); } } static Object get(Class c, String field) { try { Field f = get_findStaticField(c, field); makeAccessible(f); 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() & java.lang.reflect.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 Object get(String field, Object o) { return get(o, field); } static boolean get(BitSet bs, int idx) { return bs != null && bs.get(idx); } static int indexOf(List l, A a, int startIndex) { if (l == null) return -1; int n = l(l); for (int i = startIndex; i < n; i++) if (eq(l.get(i), a)) return i; return -1; } static int indexOf(List l, int startIndex, A a) { return indexOf(l, a, startIndex); } static int indexOf(List l, A a) { if (l == null) return -1; return l.indexOf(a); } static int indexOf(String a, String b) { return a == null || b == null ? -1 : a.indexOf(b); } static int indexOf(String a, String b, int i) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, char b) { return a == null ? -1 : a.indexOf(b); } static int indexOf(String a, int i, char b) { return indexOf(a, b, i); } static int indexOf(String a, char b, int i) { return a == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, int i, String b) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(A[] x, A a) { int n = l(x); for (int i = 0; i < n; i++) if (eq(x[i], a)) return i; return -1; } static String regexpReplace(String s, String pat, Object f) { return regexReplace(s, pat, f); } static String regexpReplace(String s, String pat, IF1 f) { return regexReplace(s, pat, f); } static String gazelle_postMentionRegexp() { return regexpCaseInsensitivePrefix() + "\\bpost\\s+(\\d+)"; } // f: A -> Comparable static List sortedByCalculatedField(Iterable c, Object f) { return sortByCalculatedField(c, f); } static List sortedByCalculatedField(Object f, Iterable c) { return sortedByCalculatedField(c, f); } static List sortedByCalculatedField(IF1 f, Iterable c) { return sortedByCalculatedField(c, f); } static List sortedByCalculatedField(Iterable c, IF1 f) { List l = cloneList(c); sort(l, new Comparator() { public int compare(A a, A b) { return stdcompare(f.get(a), f.get(b)); } }); return l; } static List instancesOf(Iterable i, Class c) { return collectInstances(i, c); } static List instancesOf(Class c, Iterable i) { return collectInstances(c, i); } static List allBackRefs(Concept c) { IdentityHashMap l = new IdentityHashMap(); if (c != null && c.backRefs != null) for (Concept.Ref r : cloneList(c.backRefs)) l.put(r.concept(), true); return keysList(l); } // if block exists: put tag after // if not: add tag in front of the whole HTML static String hAddToHead(String html, String tag) { List tok = htmlTok(html); List head = first(findContainerTag(tok, "head")); if (head == null) return tag + html; head.set(2, addLineBreak(tag) + head.get(2)); return join(tok); } static String hInitWebSocket(Object... __) { String wsVar = optPar("wsVar", __, "ws"); String onOpen = (String) (optPar("onOpen", __)); String onMessage = (String) (optPar("onMessage", __)); String readyMsg = optPar("readyMsg", __, "WebSocket ready!"); return hreconnectingWebSockets() + hscript("\r\n var wsReady = false;\r\n var wsInitialMsgs = [];\r\n var wsVerbose = false;\r\n var " + wsVar + " = new ReconnectingWebSocket(((window.location.protocol === \"https:\") ? \"wss://\" : \"ws://\") + window.location.host + \"/\");\r\n \r\n " + wsVar + ".onopen = function(event) {\r\n wsReady = true;\r\n console.log(" + jsQuote(readyMsg) + ");\r\n wsInitialMsgs.forEach(function(msg) {\r\n if (wsVerbose) console.log(\"Sending initial msg: \" + msg);\r\n ws.send(msg);\r\n });\r\n " + jsDropTrailingComments(onOpen) + " };\r\n \r\n " + wsVar + ".onmessage = function(event) {\r\n " + unnull(onMessage) + "\r\n };\r\n \r\n // add an initial message to send when websocket opens\r\n // also send it now if ws is open already\r\n function wsInitialMsg(msg) {\r\n wsInitialMsgs.push(msg);\r\n if (wsReady) {\r\n if (wsVerbose) console.log(\"Sending initial msg: \" + msg);\r\n ws.send(msg);\r\n }\r\n }\r\n "); } static String div_vbar(String... items) { return div_vbar(asList(items)); } static String div_vbar(Collection items, Object... params) { return divUnlessEmpty(joinNemptiesWithVBar(items), params); } static A uniq(Class c, Object... params) { return uniqueConcept(c, params); } static A uniq(Concepts cc, Class c, Object... params) { return uniqueConcept(cc, c, params); } static String assertMD5(String s) { return assertPossibleMD5(s); } static String postPage(String url, Object... params) { return doPost(litmap(params), url); } static String md5(String text) { try { if (text == null) return "-"; return bytesToHex(md5_impl(toUtf8(text))); // maybe different than the way PHP does it... } catch (Exception __e) { throw rethrow(__e); } } static String md5(byte[] data) { if (data == null) return "-"; return bytesToHex(md5_impl(data)); } static byte[] md5_impl(byte[] data) { try { return MessageDigest.getInstance("MD5").digest(data); } catch (Exception __e) { throw rethrow(__e); } } static String md5(File file) { return md5OfFile(file); } static List itemPlus(A a, Collection l) { return itemPlusList(a, l); } static Map applyFunctionToValue(IF1 f, A key, Map map) { return applyFunctionToMapValue(key, f, map); } static Map applyFunctionToValue(IF1 f, Map map, A... keys) { for (A key : unnullForIteration(keys)) applyFunctionToValue(f, key, map); return map; } static String joinWithBR(Iterable l) { return join("
\n", l); } static String joinWithBR(String... l) { return joinWithBR(asList(l)); } static
List padList(List l, int w) { return padList(l, w, null); } static List padList(List l, int w, A a) { if (l(l) >= w) return l; List x = cloneList(l); while (l(x) < w) x.add(a); return x; } static void remove(List l, int i) { if (l != null && i >= 0 && i < l(l)) l.remove(i); } static void remove(Collection l, A a) { if (l != null) l.remove(a); } static B remove(Map map, Object a) { return map == null ? null : map.remove(a); } static void listSet(List l, int i, A a, A emptyElement) { if (i < 0) return; while (i >= l(l)) l.add(emptyElement); l.set(i, a); } static void listSet(List l, int i, A a) { listSet(l, i, a, null); } static HashSet litset(A... items) { return lithashset(items); } static Map cgetAll_cloneLists(Concept c, Collection fields) { return mapToValues(fields, f -> cloneIfList(cget(c, f))); } static Set keys(Map map) { return map == null ? new HashSet() : map.keySet(); } // convenience shortcut for keys_gen static Set keys(Object map) { return keys((Map) map); } static Set keys(MultiSet ms) { return ms.keySet(); } static Set keys(MultiMap mm) { return mm.keySet(); } static Set keys(MultiSetMap mm) { return mm.keySet(); } static Set> _entrySet(Map map) { return map == null ? Collections.EMPTY_SET : map.entrySet(); } // MetaTransformer that understands Transformable and List static MetaTransformer defaultMetaTransformer() { return metaTransformer_transformableAndList(); } static A printStruct(String prefix, A a) { printStructure(prefix, a); return a; } static A printStruct(A a) { printStructure(a); return a; } static void logStructure(File logFile, Object o) { logQuoted(logFile, structure(o)); } // quick version - log to file in program directory static void logStructure(String fileName, Object o) { logStructure(getProgramFile(fileName), o); } static void logStructure(String progID, String fileName, Object o) { logStructure(getProgramFile(progID, fileName), o); } static String joinNemptiesWithVBar(String... strings) { return joinNempties(" | ", strings); } static String joinNemptiesWithVBar(Collection strings) { return joinNempties(" | ", strings); } static Collection conceptsWhereIC(Class c, Object... params) { return findConceptsWhereCI(c, params); } static List conceptsWhereIC(String c, Object... params) { return findConceptsWhereCI(c, params); } static Collection conceptsWhereIC(Concepts concepts, Class c, Object... params) { return findConceptsWhereCI(concepts, c, params); } static List conceptsWhereIC(Concepts concepts, String c, Object... params) { return findConceptsWhereCI(concepts, c, params); } static String replaceVars(String s, Map map) { return empty(map) ? s : join(replaceVars(javaTok(s), map)); } static List replaceVars(List tok, Map map) { if (empty(map)) return tok; tok = cloneList(tok); for (Map.Entry __0 : _entrySet( map)) { String var = __0.getKey(); Object val = __0.getValue(); jreplace(tok, var, str(val)); } return tok; } static String replaceVars(String s, Object... params) { return replaceVars(s, paramsToMap(params)); } static long parseFirstLong(String s) { return parseLong(jextract("", s)); } static String firstWhereFirstLongIs(Collection l, long i) { return firstThat(l, s -> parseFirstLong(s) == i); } // design by Kriti Shankar Senapati // title is in HTML static String htitledSectionWithDiv(String title, Object contents, Object... __) { String backgroundColor = "#fff"; // TODO: grab from params String labelStyle = "width: auto; float: top; position: relative; top: -.6em; left:15px; background-color: " + backgroundColor; String innerDivStyle = stringPar("innerDivStyle", __); String innerDivClass = stringPar("innerDivClass", __); String innerDivID = (String) (optPar("innerDivID", __)); __ = paramsMinus(__, "innerDivStyle", "innerDivID", "innerDivClass"); return div( htag("label", b(" " + title + " "), "style" , labelStyle) + div(contents, "style" , joinNemptiesWithSemicolon(innerDivStyle, "padding-left: 15px"), "class" , joinNemptiesWithSpace("sectionContents", innerDivClass), "id" , innerDivID), params_stylePlus("border: 2px solid", __)); } static String hsnippetimg_scaleToWidth(int width, String imageID, int originalWidth, int originalHeight, Object... params) { return hsnippetimg(imageID, paramsPlus(params, "width", width, "height" , calcHeight(originalWidth, originalHeight, width))); } static String h1(String s, Object... params) { return hfulltag("h1", s, params); } static String himgsnippet(String imageID, Object... params) { return hsnippetimg(imageID, params); } static String hjssrc(String src) { return hjavascript_src(src); } static String span(Object contents, Object... params) { return hfulltag("span", contents, params); } static String span() { return span(""); } static String renderHowLongAgoPlusModified(long timestamp, long timestamp2, Object... __) { String spacer = optPar("spacer", __, ", modified "); if (timestamp2 == 0) timestamp2 = timestamp; String s = renderHowLongAgo(timestamp); String s2 = renderHowLongAgo(timestamp2); return eq(s, s2) ? s : s + ", modified " + s2; } static String appendQueryToURL(String url, Map params) { if (url == null) return null; String data = makePostData(params); if (empty(data)) return url; int idx = smartIndexOf(url, '#'); String url2 = takeFirst(url, idx); return url2 + (url2.contains("?") ? "&" : "?") + data + substring(url, idx); } static String appendQueryToURL(String url, Object... data) { return appendQueryToURL(url, paramsToMap(data)); } static List gazelle_navLinks(String baseLink, String searchQuery, Object... __) { boolean withTeam = optPar("withTeam", __, true); boolean withStats = optPar("withStats", __, true); boolean flat = boolPar("flat", __); List more = ll(ahref(baseLink + "/deletedPosts", "Deleted Posts")); return llNonNulls(flattenStringArray2( ahref(baseLink + "/crud/UserPost?cmd=new", "New Post"), searchQuery != null ? hform(hinputfield("q" , searchQuery, "style" , "width: 75px") + " " + hsubmit("Search"), "style" , "display: inline", "action" , baseLink + "/search") : ahref(baseLink + "/search", "Search"), ahref(baseLink + "/rootPosts", "Root Posts"), ahref(baseLink + "/allPosts", "All Posts"), ahref(baseLink + "/latestPosts", "Latest Posts"), ahref(baseLink + "/latestModifiedPosts", "Latest Changes"), ahref(baseLink + "/mainPosts", "Main Posts"), !withTeam ? null : ahref(baseLink + "/team", "Team"), flat ? more : hPopDownButton(flattenStringArray2( !withStats ? null : ahref(baseLink + "/stats", "Stats"), more )))); } static Object[] paramsPlus(Object[] a1, Object... a2) { if (a2 == null) return a1; if (a1 == null) return a2; if (l(a1) == 1 && a1[0] instanceof Map) return new Object[] { mapPlus((Map) a1[0], a2) }; assertEvenLength(a1); assertEvenLength(a2); Map map = paramsToOrderedMap(a1); int n = l(a2); for (int i = 0; i < n; i += 2) { Object key = a2[i]; if (key != null) map.put(key, a2[i+1]); } return mapToParams(map); } static Object[] paramsPlus(Map a1, Object... a2) { return paramsPlus(new Object[] {a1}, a2); } static List listMinusSet(Iterable l, Collection stuff) { if (l == null) return null; if (empty(stuff)) return asList(l); Set set = asSet(stuff); List l2 = new ArrayList(); for (A a : l) if (!set.contains(a)) l2.add(a); return l2; } static List listMinusSet(Iterable l, Collection stuff, Collection stuff2) { return listMinusSet(listMinusSet(l, stuff), stuff2); } static > B addAllAndReturnCollection(B c, Collection b) { if (c != null && b != null) c.addAll(b); return c; } static > B addAllAndReturnCollection(B c, A... b) { addAll(c, b); return c; } static Object cget(Object c, String field) { c = derefRef(c); Object o = getOpt(c, field); return derefRef(o); } static Object cget(String field, Object c) { return cget(c, field); } static File programFile(String name) { return prepareProgramFile(name); } static File programFile(String progID, String name) { return prepareProgramFile(progID, name); } static List> syncMapToPairs(Map map) { if (map == null) return new ArrayList(); synchronized(collectionMutex(map)) { return mapToPairs(map); } } static String regexpFirstGroupIC(String pat, String s) { Matcher m = regexpIC(pat, s); if (m.find()) return m.group(1); else return null; } static Map safeUnstructMapAllowingClasses(String s, Class... allowedClasses) { return (Map) safeUnstructureAllowingClasses(s, allowedClasses); } static Map putAll(Map a, Map b) { if (a != null && b != null) a.putAll(b); return a; } static MultiMap putAll(MultiMap a, Map b) { if (a != null) a.putAll((Map) b); return a; } static Map putAll(Map a, Object... b) { if (a != null) litmap_impl(a, b); return a; } static List uniquifyAndSortAlphaNum(Collection l) { return sortedAlphaNum(uniquify(l)); } static List allToUpper(Collection l) { List x = new ArrayList(l(l)); if (l != null) for (String s : l) x.add(upper(s)); return x; } static List asForPairsWithB(B b, Iterable> l) { List out = new ArrayList(); for (Pair p : unnullForIteration(l)) if (eq(pairB(p), b)) out.add(pairA(p)); return out; } static List asForPairsWithB(Iterable> l, B b) { return asForPairsWithB(b, l); } static String replaceDollarVars(String s, Object... params) { if (empty(params)) return s; Map vars = mapKeys(__58 -> dropDollarPrefix(__58), (Map) litcimap(params)); return replaceDollarVars_dyn(s, var -> strOrNull(vars.get(var))); } static String replaceDollarVars(String s, IF1 f) { return replaceDollarVars_dyn(s, f); } static String n2(long l) { return formatWithThousands(l); } static String n2(AtomicLong l) { return n2(l.get()); } static String n2(Collection l) { return n2(l(l)); } static String n2(Map map) { return n2(l(map)); } static String n2(double l, String singular) { return empty(singular) ? str(l) : n2(l, singular, singular + "s"); } static String n2(double l, String singular, String plural) { if (fraction(l) == 0) return n2((long) l, singular, plural); else return l + " " + plural; } static String n2(long l, String singular, String plural) { return n_fancy2(l, singular, plural); } static String n2(long l, String singular) { return empty(singular) ? n2(l) : n_fancy2(l, singular, singular + "s"); } static String n2(Collection l, String singular) { return n2(l(l), singular); } static String n2(Collection l, String singular, String plural) { return n_fancy2(l, singular, plural); } static String n2(Map m, String singular, String plural) { return n_fancy2(m, singular, plural); } static String n2(Map m, String singular) { return n2(l(m), singular); } static String n2(long[] a, String singular) { return n2(l(a), singular); } static String n2(Object[] a, String singular) { return n2(l(a), singular); } static String n2(Object[] a, String singular, String plural) { return n_fancy2(a, singular, plural); } static String n2(MultiSet ms, String singular, String plural) { return n_fancy2(ms, singular, plural); } static String formatDoubleX(double d, int digits) { return formatDoubleFull(d, digits); } static String str_toM(long l) { return n2(toM(l)) + " MB"; } static List listToTopTenCI(Collection l) { return asCIMultiSet(l).highestFirst(); } static String makePostData(Map map) { StringBuilder buf = new StringBuilder(); for (Map.Entry e : castMapToMapO(map).entrySet()) { String key = (String) (e.getKey()); Object val = e.getValue(); if (val != null) { String value = str(val); if (nempty(buf)) buf.append("&"); buf.append(urlencode(key)).append("=").append(urlencode(/*escapeMultichars*/(value))); } } return str(buf); } static String makePostData(Object... params) { StringBuilder buf = new StringBuilder(); int n = l(params); for (int i = 0; i+1 < n; i += 2) { String key = (String) (params[i]); Object val = params[i+1]; if (val != null) { String value = str(val); if (nempty(buf)) buf.append("&"); buf.append(urlencode(key)).append("=").append(urlencode(/*escapeMultichars*/(value))); } } return str(buf); } static boolean fileNempty(File f) { return fileNotEmpty(f); } static String roundBracket(String s) { return "(" + s + ")"; } static String roundBracket(Object s) { return roundBracket(str(s)); } static Map decodeHQuery(String query) { Map map = new HashMap(); for (String s : splitAtAmpersand(query)) { int i = s.indexOf('='); if (i >= 0) map.put(urldecode(s.substring(0, i)), urldecode(s.substring(i+1))); } return map; } static A firstThat(Iterable l, IF1 pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static A firstThat(A[] l, IF1 pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static A firstThat(IF1 pred, Iterable l) { return firstThat(l, pred); } static A firstThat(IF1 pred, A[] l) { return firstThat(l, pred); } static int loadPage_defaultTimeout = 60000; static ThreadLocal loadPage_charset = new ThreadLocal(); static boolean loadPage_allowGzip = true, loadPage_debug; static boolean loadPage_anonymous = false; // don't send computer ID static int loadPage_verboseness = 100000; static int loadPage_retries = 1; //60; // seconds static ThreadLocal loadPage_silent = new ThreadLocal(); static volatile int loadPage_forcedTimeout; // ms static ThreadLocal loadPage_forcedTimeout_byThread = new ThreadLocal(); // ms static ThreadLocal>> loadPage_responseHeaders = new ThreadLocal(); static ThreadLocal> loadPage_extraHeaders = new ThreadLocal(); static ThreadLocal loadPage_sizeLimit = new ThreadLocal(); public static String loadPageSilently(String url) { try { return loadPageSilently(new URL(loadPage_preprocess(url))); } catch (Exception __e) { throw rethrow(__e); } } public static String loadPageSilently(URL url) { try { if (!networkAllowanceTest(str(url))) throw fail("Not allowed: " + url); IOException e = null; for (int tries = 0; tries < loadPage_retries; tries++) try { URLConnection con = loadPage_openConnection(url); return loadPage(con, url); } catch (IOException _e) { e = _e; if (loadPage_debug) print(exceptionToStringShort(e)); if (tries < loadPage_retries-1) sleepSeconds(1); } throw e; } catch (Exception __e) { throw rethrow(__e); } } static String loadPage_preprocess(String url) { if (url.startsWith("tb/")) // don't think we use this anymore url = tb_mainServer() + "/" + url; if (url.indexOf("://") < 0) url = "http://" + url; return url; } static String loadPage(String url) { try { url = loadPage_preprocess(url); if (!isTrue(loadPage_silent.get())) printWithTime("Loading: " + hideCredentials(url)); return loadPageSilently(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } static String loadPage(URL url) { return loadPage(url.toExternalForm()); } static String loadPage(URLConnection con, URL url) throws IOException { return loadPage(con, url, true); } static String loadPage(URLConnection con, URL url, boolean addHeaders) throws IOException { Map extraHeaders = getAndClearThreadLocal(loadPage_extraHeaders); Long limit = optPar(loadPage_sizeLimit); if (addHeaders) try { if (!loadPage_anonymous) setHeaders(con); if (loadPage_allowGzip) con.setRequestProperty("Accept-Encoding", "gzip"); con.setRequestProperty("X-No-Cookies", "1"); for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key)); } catch (Throwable e) {} // fails if within doPost vm_generalSubMap("URLConnection per thread").put(currentThread(), con); loadPage_responseHeaders.set(con.getHeaderFields()); InputStream in = null; try { in = urlConnection_getInputStream(con); //vm_generalSubMap("InputStream per thread").put(currentThread(), in); if (loadPage_debug) print("Put stream in map: " + currentThread()); String contentType = con.getContentType(); if (contentType == null) { //printStruct("Headers: ", con.getHeaderFields()); throw new IOException("Page could not be read: " + hideCredentials(url)); } //print("Content-Type: " + contentType); String charset = loadPage_charset == null ? null : loadPage_charset.get(); if (charset == null) charset = loadPage_guessCharset(contentType); if ("gzip".equals(con.getContentEncoding())) { if (loadPage_debug) print("loadPage: Using gzip."); in = newGZIPInputStream(in); } Reader r; try { r = new InputStreamReader(in, unquote(charset)); } catch (UnsupportedEncodingException e) { print(toHex(utf8(charset))); throw e; } boolean silent = isTrue(loadPage_silent.get()); StringBuilder buf = new StringBuilder(); int n = 0; while (limit == null || n < limit) { ping(); int ch = r.read(); if (ch < 0) break; buf.append((char) ch); ++n; if (!silent && (n % loadPage_verboseness) == 0) print(" " + n + " chars read"); } return buf.toString(); } finally { if (loadPage_debug) print("loadPage done"); //vm_generalSubMap("InputStream per thread").remove(currentThread()); vm_generalSubMap("URLConnection per thread").remove(currentThread()); if (in != null) in.close(); } } static String loadPage_guessCharset(String contentType) { Matcher m = regexpMatcher("text/[a-z]+;\\s*charset=([^\\s]+)\\s*", contentType); String match = m.matches() ? m.group(1) : null; if (loadPage_debug) print("loadPage: contentType=" + contentType + ", match: " + match); /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */ //return or(match, "ISO-8859-1"); return or(match, "UTF-8"); } static URLConnection loadPage_openConnection(URL url) { URLConnection con = openConnection(url); int timeout = toInt(loadPage_forcedTimeout_byThread.get()); if (timeout == 0) timeout = loadPage_forcedTimeout; if (timeout != 0) setURLConnectionTimeouts(con, loadPage_forcedTimeout); else setURLConnectionDefaultTimeouts(con, loadPage_defaultTimeout); return con; } static String regexpReplace_direct(String s, String pat, String replacement) { Matcher m = regexp(pat, s); return regexpReplace_direct(m, replacement); } static String regexpReplace_direct(Matcher m, String replacement) { StringBuffer buf = new StringBuffer(); while (m.find()) m.appendReplacement(buf, replacement); m.appendTail(buf); return str(buf); } static Map emptyMap() { return new HashMap(); } static Map parseColonPropertyCIMap(String text) { return parseColonProperties(text, ciMap()); } static int iround(double d) { return (int) Math.round(d); } static int iround(Number n) { return iround(toDouble(n)); } static double hoursToSeconds(double hours) { return hours*60*60; } static double favIconCacheHours() { return 24; } static Object favIconHeaders(Object response) { call(response, "addHeader", "Cache-Control", "public, max-age=" + iround(hoursToSeconds(favIconCacheHours()))); return response; } static Object subBot_serveFile(File file) { return call(getMainBot(), "serveFile", file); } static Object subBot_serveFile(File file, String mimeType) { return call(getMainBot(), "serveFile", file, mimeType); } static String faviconMimeType() { return "image/x-icon"; } static File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } static String gazelle_textURL(long postID) { return gazelle_server() + "text/" + postID; } static String afterLastEquals(String s) { return s == null ? null : substring(s, s.lastIndexOf('=')+1); } static List listMinus(Collection l, Object... stuff) { if (l == null) return null; List l2 = similarEmptyList(l); Set set = lithashset(stuff); for (Object o : l) if (!set.contains(o)) l2.add(o); return l2; } static boolean infoMessage_alwaysOnTop = true; static double infoMessage_defaultTime = 5.0; // automatically switches to AWT thread for you static JWindow infoMessage(String text) { return infoMessage(text, infoMessage_defaultTime); } static JWindow infoMessage(final String text, final double seconds) { printHidingCredentials(text); return infoMessage_noprint(text, seconds); } static JWindow infoMessage_noprint(String text) { return infoMessage_noprint(text, infoMessage_defaultTime); } static JWindow infoMessage_noprint(final String _text, final double seconds) { final String text = hideCredentials(_text); if (empty(text)) return null; logQuotedWithDate(infoBoxesLogFile(), text); if (isHeadless()) return null; return (JWindow) swingAndWait(new F0() { public Object get() { try { final JWindow window = showWindow(infoMessage_makePanel(text)); window.setSize(300, 150); moveToTopRightCorner(window); if (infoMessage_alwaysOnTop) window.setAlwaysOnTop(true); if (vmBus_noObjections("shouldShowInfoBox", window, text)) window.setVisible(true); if (seconds != 0) disposeWindowAfter(iround(seconds*1000), window); return window; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final JWindow window = showWindow(infoMessage_makePanel(text));\r\n window.s..."; }}); } static JWindow infoMessage(Throwable e) { //showConsole(); printStackTrace(e); return infoMessage(exceptionToStringShort(e)); } static Thread startThread(Object runnable) { return startThread(defaultThreadName(), runnable); } static Thread startThread(String name, Runnable runnable) { runnable = wrapAsActivity(runnable); return startThread(newThread(runnable, name)); } static Thread startThread(String name, Object runnable) { runnable = wrapAsActivity(runnable); return startThread(newThread(toRunnable(runnable), name)); } static Thread startThread(Thread t) { _registerThread(t); t.start(); return t; } // Use like this: printVars(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(, +x); static void printVars(Object... params) { printVars_str(params); } static boolean swingConfirm(Component owner, String msg) { return confirmOKCancel(owner, msg); } static boolean swingConfirm(String msg) { return confirmOKCancel(msg); } // one array plus more elements static Object[] objectArrayPlus(Object[] a1, Object... a2) { return arrayPlus(a1, a2); } static Runnable runnableThread(final Runnable r) { return new Runnable() { public void run() { try { startThread(r) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "startThread(r)"; }}; } static Object postJSONPage(String url, Object... params) { return jsonDecode(postPage(url, params)); } static AutoCloseable tempInterceptPrintIfNotIntercepted(F1 f) { return print_byThread().get() == null ? tempInterceptPrint(f) : null; } static File secretProgramFile(String progID, String fileName) { return new File(getSecretProgramDir(progID), fileName); } static File secretProgramFile(String fileName) { return secretProgramFile(getProgramID(), fileName); } static ThreadLocal> checkFileNotTooBigToRead_tl = new ThreadLocal(); static void checkFileNotTooBigToRead(File f) { callF(checkFileNotTooBigToRead_tl.get(), f); } static File newFile(File base, String... names) { for (String name : names) base = new File(base, name); return base; } static File newFile(String name) { return name == null ? null : new File(name); } static File newFile(String base, String... names) { return newFile(newFile(base), names); } static String _userHome; static String userHome() { if (_userHome == null) return actualUserHome(); return _userHome; } static File userHome(String path) { return new File(userDir(), path); } /** writes safely (to temp file, then rename) */ static File saveTextFile(String fileName, String contents) throws IOException { CriticalAction action = beginCriticalAction("Saving file " + fileName + " (" + l(contents) + " chars)"); try { File file = new File(fileName); mkdirsForFile(file); 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 = newFileOutputStream(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); vmBus_send("wroteFile", file); return file; } finally { action.done(); } } static File saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); return fileName; } catch (Exception __e) { throw rethrow(__e); } } static String aGlobalID() { return randomID(globalIDLength()); } static String aGlobalID(Random random) { return randomID(random, globalIDLength()); } static SecretValue secretValueOrNull(A a) { return a == null ? null : new SecretValue(a); } static boolean syncEmpty(Collection l) { if (l == null) return true; synchronized(l) { return l.isEmpty(); } } static void warnIfOddCount(Object... list) { if (odd(l(list))) printStackTrace("Odd list size: " + list); } static Object[] expandParams(Class c, Object[] params) { if (l(params) == 1) params = new Object[] { singleFieldName(c), params[0] }; else warnIfOddCount(params); return params; } // returns true if change static boolean _csetField(Concept c, String field, Object value) { try { Field f = setOpt_findField(c.getClass(), field); //print("cset: " + c.id + " " + field + " " + struct(value) + " " + f); if (value instanceof RC) value = c._concepts.getConcept((RC) value); value = deref(value); if (value instanceof String && l((String) value) >= concepts_internStringsLongerThan) value = intern((String) value); if (f == null) { // dynamic field (undeclared) assertIdentifier(field); Object oldVal = mapGet(c.fieldValues, field); if (value instanceof Concept) { if (oldVal instanceof Concept.Ref) // change existing reference return ((Concept.Ref) oldVal).set((Concept) value); else { // overwrite non-reference value if any, // create new reference dynamicObject_setRawFieldValue(c, field, c.new Ref((Concept) value)); c.change(); return true; } } else { // value is not a concept // if it was a reference, cleanly delete it if (oldVal instanceof Concept.Ref) ((Concept.Ref) oldVal).unindexAndDrop(); if (eq(oldVal, value)) return false; if (isConceptList(value) && nempty(((List) value))) { // TODO: clean-up etc dynamicObject_setRawFieldValue(c, field, c.new RefL(((List) value))); c.change(); return true; } if (value == null) { // delete field dynamicObject_dropRawField(c, field); } else { // update or create field if (!isPersistable(value)) throw fail("Can't persist: " + c + "." + field + " = "+ value); dynamicObject_setRawFieldValue(c, field, value); } c.change(); return true; } } else if (isSubtypeOf(f.getType(), Concept.Ref.class)) { // Concept.Ref magic ((Concept.Ref) f.get(c)).set((Concept) derefRef(value)); c.change(); return true; } else if (isSubtypeOf(f.getType(), Concept.RefL.class)) { // Concept.RefL magic ((Concept.RefL) f.get(c)).replaceWithList(lmap(__59 -> derefRef(__59), (List) value)); c.change(); return true; } else { Object old = f.get(c); if (neq(value, old)) { boolean isTransient = isTransient(f); if (!isTransient && !isPersistable(value)) throw fail("Can't persist: " + c + "." + field + " = "+ value); f.set(c, value); if (!isTransient) c.change(); return true; } } return false; } catch (Exception __e) { throw rethrow(__e); } } static HashMap findClass_cache = new HashMap(); // 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) { synchronized(findClass_cache) { if (findClass_cache.containsKey(name)) return findClass_cache.get(name); if (!isJavaIdentifier(name)) return null; Class c; try { c = Class.forName("main$" + name); } catch (ClassNotFoundException e) { c = null; } findClass_cache.put(name, c); return c; } } static Object nuObject(String className, Object... args) { try { return nuObject(classForName(className), args); } catch (Exception __e) { throw rethrow(__e); } } // too ambiguous - maybe need to fix some callers /*static O nuObject(O realm, S className, O... args) { ret nuObject(_getClass(realm, className), args); }*/ static A nuObject(Class c, Object... args) { try { if (args.length == 0) return nuObjectWithoutArguments(c); // cached! Constructor m = nuObject_findConstructor(c, args); makeAccessible(m); return (A) m.newInstance(args); } catch (Exception __e) { throw rethrow(__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 fail("Constructor " + c.getName() + getClasses(args) + " not found" + (args.length == 0 && (c.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0 ? " - hint: it's a non-static class!" : "")); } 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 int csetAll(Concept c, Object... values) { return cset(c, values); } static int csetAll(Iterable l, Object... values) { int n = 0; for (Concept c : unnullForIteration(l)) n += cset(c, values); return n; } static String rtrim_fromLines(Collection lines) { StringBuilder buf = new StringBuilder(); if (lines != null) { boolean first = true; for (Object line : lines) { if (first) first = false; else buf.append('\n'); buf.append(str(line)); } } return buf.toString(); } static String indentStructureString(String s) { return indentStructureString(100, s); } static String indentStructureString(int levels, String s) { if (s == null) return null; List tok = javaTokForStructure(s); StringBuilder buf = new StringBuilder(); int indent = 0; levels = clampToInt(levels*2L); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (isOpeningBracket(t)) { int j = i+5; if (containsClosingBracket(subList(tok, i+2, i+5))) { buf.append(joinSubList(tok, i, j)); i = j-1; } else { indent += 2; buf.append(t); if (indent <= levels) buf.append("\n").append(spaces(indent)); } } else if (isClosingBracket(t)) { indent -= 2; if (indent < levels) buf.append("\n").append(spaces(indent)); buf.append(t); } else if (indent <= levels && eq(t, ",")) { buf.append(t).append("\n").append(spaces(indent)); i++; } else buf.append(t); } return str(buf); } static void deleteConcepts(Collection conceptsOrIDs) { db_mainConcepts().deleteConcepts(asList(conceptsOrIDs)); } static List deleteConcepts(Class c, Object... params) { return deleteConcepts(db_mainConcepts(), c, params); } static List deleteConcepts(Concepts cc, Class c, Object... params) { List l = asList(findConceptsWhere(cc, c, params)); deleteConcepts(l); return l; } static void deleteConcepts(Class c, IF1 pred) { deleteConcepts(db_mainConcepts(), c, pred); } static void deleteConcepts(Concepts cc, Class c, IF1 pred) { deleteConcepts(filter(list(cc, c), pred)); } static List deleteConcepts(Concepts cc) { return deleteConcepts(cc, Concept.class); } static void deleteConcept(long id) { db_mainConcepts().deleteConcept(id); } static void deleteConcept(Concepts concepts, long id) { concepts.deleteConcept(id); } static void deleteConcept(Concept c) { if (c != null) c.delete(); } static void deleteConcept(Concept.Ref ref) { if (ref != null) deleteConcept(ref.get()); } static CharSequence subCharSequence(CharSequence s, int x) { return subCharSequence(s, x, s == null ? 0 : s.length()); } static CharSequence subCharSequence(CharSequence 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.subSequence(x, y); } static String fixNewLines(String s) { int i = indexOf(s, '\r'); if (i < 0) return s; int l = s.length(); StringBuilder out = new StringBuilder(l); out.append(s, 0, i); for (; i < l; i++) { char c = s.charAt(i); if (c != '\r') out.append(c); else { out.append('\n'); if (i+1 < l && s.charAt(i+1) == '\n') ++i; } } return out.toString(); } static ArrayList emptyList() { return new ArrayList(); //ret Collections.emptyList(); } static ArrayList emptyList(int capacity) { return new ArrayList(max(0, capacity)); } // Try to match capacity static ArrayList emptyList(Iterable l) { return l instanceof Collection ? emptyList(((Collection) l).size()) : emptyList(); } static ArrayList emptyList(Object[] l) { return emptyList(l(l)); } // get correct type at once static ArrayList emptyList(Class c) { return new ArrayList(); } static void addPair(Collection> c, A a, B b) { if (c != null) c.add(pair(a, b)); } static List pairsA(Collection> l) { return firstOfPairs(l); } static boolean isInstance(Class type, Object arg) { return type.isInstance(arg); } static List collectInstances(Iterable i, Class c) { List l = new ArrayList(); if (i == null) return l; c = primitiveToBoxedTypeOpt(c); for (Object o : i) if (isInstance(c, o)) l.add(o); return l; } static List collectInstances(Class c, Iterable i) { return collectInstances(i, c); } // makes dynamic & static modules - returns module ID static String dm_makeModule(String moduleLibID) { return (String) dm_callOS("makeModule", moduleLibID); } static A dm_ownResource(A resource) { dm_currentModuleMandatory().ownResource(resource); return resource; } static AutoCloseable vmBus_onMessage(String msg, IVF1 onMessage) { return vmBus_onMessage(msg, ivf1ToVF1(onMessage)); } static AutoCloseable vmBus_onMessage(final String msg, final VF1 onMessage) { Map map = vm_busListenersByMessage_live(); synchronized(map) { Set listeners = map.get(msg); if (listeners == null) map.put(msg, listeners = syncIdentityHashSet()); // We're technically violating the one-synchronized-object-per-thread rule, // but it should be OK here. // TODO: remove empty sets from map return tempAdd(listeners, new VF2() { public void get(String _msg, Object arg) { try { callF(onMessage, arg) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(onMessage, arg)"; }}); } } static AutoCloseable vmBus_onMessage(String msg, final VF2 onMessage) { return vmBus_onMessage(msg, new VF1() { public void get(Object[] o) { try { callF(onMessage, first(o), second(o)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(onMessage, first(o), second(o));"; }}); } static AutoCloseable vmBus_onMessage(String msg, final IVF2 onMessage) { return vmBus_onMessage(msg, new VF1() { public void get(Object[] o) { try { callF(onMessage, first(o), second(o)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(onMessage, first(o), second(o));"; }}); } static AutoCloseable vmBus_onMessage(String msg, Runnable onMessage) { return vmBus_onMessage(msg, runnableToVF1(onMessage)); } static VF1 ivf1ToVF1(IVF1 f) { return f == null ? null : new VF1() { public void get(A a) { try { f.get(a) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.get(a)"; }}; } static VF2 ivf2ToVF2(IVF2 f) { return f == null ? null : new VF2() { public void get(A a, B b) { try { f.get(a, b) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.get(a, b)"; }}; } static java.lang.management.OperatingSystemMXBean osMXBean() { return ManagementFactory.getOperatingSystemMXBean(); } static double systemCPULoad() { return advancedOSMXBean().getSystemCpuLoad(); } static void _handleError(Error e) { call(javax(), "_handleError", e); } static RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Throwable e) { throw asRuntimeException(e); } static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); } static RuntimeException fail(Object... objects) { throw new Fail(objects); } static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); } static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); } static String getType(Object o) { return getClassName(o); } static long getFileSize(String path) { return path == null ? 0 : new File(path).length(); } static long getFileSize(File f) { return f == null ? 0 : f.length(); } static ConceptFieldIndexCI getConceptFieldCIIndex(Class c, String field) { return getConceptFieldCIIndex(db_mainConcepts(), c, field); } static ConceptFieldIndexCI getConceptFieldCIIndex(Concepts concepts, Class c, String field) { return (ConceptFieldIndexCI) concepts.getCIFieldIndex(c, field); } static boolean isConceptFieldIndexed(Class c, String field) { return isConceptFieldIndexed(db_mainConcepts(), c, field); } static boolean isConceptFieldIndexed(Concepts concepts, Class c, String field) { return concepts.getFieldIndex(c, field) != null; } static Concepts newConceptsWithClassFinder(String progID) { Concepts cc = new Concepts(progID); cc.classFinder = _defaultClassFinder(); return cc; } static String getDBProgramID_id; static String getDBProgramID() { return nempty(getDBProgramID_id) ? getDBProgramID_id : programIDWithCase(); } static Set joinSets(Collection... l) { Set set = similarEmptySet(first(l)); for (Collection o : l) if (o != null) set.addAll(o); return set; } static IConceptIndex simpleConceptIndex(final Runnable r) { return new IConceptIndex() { public void update(Concept c) { pcallF(r); } public void remove(Concept c) { pcallF(r); } }; } // BREAKING CHANGE! // Also NOTE: Iterators of these sync-wrapped collections // after generally NOT thread-safe! // TODO: change that? static Set synchroLinkedHashSet() { return Collections.synchronizedSet(new CompactLinkedHashSet()); } static LinkedHashMap cloneLinkedHashMap(Map map) { return map == null ? new LinkedHashMap() : new LinkedHashMap(map); } static Map synchroHashMap() { return synchronizedMap(new HashMap()); } static Map synchronizedMap() { return synchroMap(); } static Map synchronizedMap(Map map) { return synchroMap(map); } static WeakReference newWeakReference(A a) { return a == null ? null : new WeakReference(a); } static void setFieldToSingleMethodProxy(Object o, String field, Object target, String methodName) { if (o == null || target == null) return; Class type = fieldType(o, field); if (type == null) throw fail("Field " + field + " not found in " + className(o)); set(o, field, singleObjectMethodProxy(type, target, methodName)); } static Map> callF_cache = newDangerousWeakHashMap(); static A callF(F0 f) { return f == null ? null : f.get(); } static B callF(F1 f, A a) { return f == null ? null : f.get(a); } static A callF(IF0 f) { return f == null ? null : f.get(); } static B callF(IF1 f, A a) { return f == null ? null : f.get(a); } static C callF(F2 f, A a, B b) { return f == null ? null : f.get(a, b); } static C callF(IF2 f, A a, B b) { return f == null ? null : f.get(a, b); } static void callF(VF1 f, A a) { if (f != null) f.get(a); } static Object callF(Runnable r) { { if (r != null) r.run(); } return null; } static Object callF(Object f, Object... args) { if (f instanceof String) return callMCWithVarArgs((String) f, args); // possible SLOWDOWN over callMC return safeCallF(f, args); } static Object safeCallF(Object f, Object... args) { if (f instanceof Runnable) { ((Runnable) f).run(); return null; } if (f == null) return null; Class c = f.getClass(); ArrayList methods; synchronized(callF_cache) { methods = callF_cache.get(c); if (methods == null) methods = callF_makeCache(c); } int n = l(methods); if (n == 0) { throw fail("No get method in " + getClassName(c)); } if (n == 1) return invokeMethod(methods.get(0), f, args); for (int i = 0; i < n; i++) { Method m = methods.get(i); if (call_checkArgs(m, args, false)) return invokeMethod(m, f, args); } throw fail("No matching get method in " + getClassName(c)); } // used internally static ArrayList callF_makeCache(Class c) { ArrayList l = new ArrayList(); Class _c = c; do { for (Method m : _c.getDeclaredMethods()) if (m.getName().equals("get")) { makeAccessible(m); l.add(m); } if (!l.isEmpty()) break; _c = _c.getSuperclass(); } while (_c != null); callF_cache.put(c, l); return l; } static Object call_withVarargs(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); Method me = cache.findStaticMethod(method, args); if (me != null) return invokeMethod(me, null, args); // try varargs List methods = cache.cache.get(method); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } { if (!(isStaticMethod(m))) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return invokeMethod(m, null, newArgs); } throw fail("Method " + c.getName() + "." + method + "(" + joinWithComma(classNames(args)) + ") not found"); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(method, args); if (me != null) return invokeMethod(me, o, args); // try varargs List methods = cache.cache.get(method); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return invokeMethod(m, o, newArgs); } throw fail("Method " + c.getName() + "." + method + "(" + joinWithComma(classNames(args)) + ") not found"); } } catch (Exception __e) { throw rethrow(__e); } } static Object pcallF(Object f, Object... args) { return pcallFunction(f, args); } static A pcallF(F0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { return null; } } static B pcallF(F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { return null; } } static void pcallF(VF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { _handleException(__e); } } static A pcallF(IF0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { return null; } } static B pcallF(IF1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { return null; } } static List synchroList() { return synchroList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } static Throwable printStackTrace2(Throwable e) { // we go to system.out now - system.err is nonsense print(getStackTrace2(e)); return e; } static void printStackTrace2() { printStackTrace2(new Throwable()); } static void printStackTrace2(String msg) { printStackTrace2(new Throwable(msg)); } static PersistableThrowable persistableThrowable(Throwable e) { return e == null ? null : new PersistableThrowable(e); } static Throwable innerException(Throwable e) { return getInnerException(e); } static String getStackTrace(Throwable throwable) { lastException(throwable); return getStackTrace_noRecord(throwable); } static String getStackTrace_noRecord(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); return hideCredentials(writer.toString()); } static String getStackTrace() { return getStackTrace_noRecord(new Throwable()); } static String getStackTrace(String msg) { return getStackTrace_noRecord(new Throwable(msg)); } static HashSet lithashset(A... items) { HashSet set = new HashSet(); for (A a : items) set.add(a); return set; } static A or(A a, A b) { return a != null ? a : b; } // TODO: extended multi-line strings static int javaTok_n, javaTok_elements; static boolean javaTok_opt = false; static List javaTok(String s) { ++javaTok_n; ArrayList tok = new ArrayList(); int l = s == null ? 0 : s.length(); int i = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !regionMatches(s, j, "*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(javaTok_substringN(s, i, j)); i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace // Special JavaX syntax: 'identifier if (c == '\'' && Character.isJavaIdentifierStart(d) && i+2 < l && "'\\".indexOf(s.charAt(i+2)) < 0) { j += 2; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j; } else if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { int c2 = s.charAt(j); if (c2 == opener || c2 == '\n' && opener == '\'') { // allow multi-line strings, but not for ' ++j; break; } else if (c2 == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // 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 (c == '[' && d == '[') { do ++j; while (j < l && !regionMatches(s, j, "]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !regionMatches(s, j, "]=]")); j = Math.min(j+3, l); } else ++j; tok.add(javaTok_substringC(s, i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); javaTok_elements += tok.size(); return tok; } static List javaTok(List tok) { return javaTokWithExisting(join(tok), tok); } static Object jparse_parser; static synchronized List jparse(String text, String className) { if (jparse_parser == null) { jparse_parser = run_overBot("#1002369"); setOpt(jparse_parser, "keepRules", true); } //setOpt(jparse_parser, "debug", true); Object parseResult = call(jparse_parser, "jparse", text); return (List) call(parseResult, "explain", className); } static String getString(Map map, Object key) { return map == null ? null : (String) map.get(key); } static String getString(List l, int idx) { return (String) get(l, idx); } static String getString(Object o, Object key) { if (o instanceof Map) return getString((Map) o, key); if (key instanceof String) return (String) getOpt(o, (String) key); throw fail("Not a string key: " + getClassName(key)); } static String getString(String key, Object o) { return getString(o, (Object) key); } static Class getMainClass() { return mc(); } static Class getMainClass(Object o) { try { if (o == null) return null; if (o instanceof Class && eq(((Class) o).getName(), "x30")) return (Class) o; ClassLoader cl = (o instanceof Class ? (Class) o : o.getClass()).getClassLoader(); if (cl == null) return null; String name = mainClassNameForClassLoader(cl); return loadClassFromClassLoader_orNull(cl, name); } catch (Exception __e) { throw rethrow(__e); } } static boolean structure_showTiming, structure_checkTokenCount; static String structure(Object o) { return structure(o, new structure_Data()); } static String structure(Object o, structure_Data d) { StringWriter sw = new StringWriter(); d.out = new PrintWriter(sw); structure_go(o, d); String s = str(sw); if (structure_checkTokenCount) { print("token count=" + d.n); assertEquals("token count", l(javaTokC(s)), d.n); } return s; } static void structure_go(Object o, structure_Data d) { structure_1(o, d); while (nempty(d.stack)) popLast(d.stack).run(); } static void structureToPrintWriter(Object o, PrintWriter out) { structureToPrintWriter(o, out, new structure_Data()); } static void structureToPrintWriter(Object o, PrintWriter out, structure_Data d) { d.out = out; structure_go(o, d); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; // info on how to serialize objects of a certain class static class structure_ClassInfo { Class c; List fields; Method customSerializer; IVF1 serializeObject; // can be set by caller of structure function boolean special = false; // various special classes boolean nullInstances = false; // serialize all instances as null (e.g. lambdas/anonymous classes) } static class structure_Data { PrintWriter out; int stringSizeLimit; int shareStringsLongerThan = 20; boolean noStringSharing = false; boolean storeBaseClasses = false; String mcDollar = actualMCDollar(); IdentityHashMap seen = new IdentityHashMap(); //new BitSet refd; HashMap strings = new HashMap(); HashSet concepts = new HashSet(); HashMap infoByClass = new HashMap(); HashMap persistenceInfo = new HashMap(); int n; // token count List stack = new ArrayList(); // append single token structure_Data append(String token) { out.print(token); ++n; return this; } structure_Data append(int i) { out.print(i); ++n; return this; } // append multiple tokens structure_Data append(String token, int tokCount) { out.print(token); n += tokCount; return this; } // extend last token structure_Data app(String token) { out.print(token); return this; } structure_Data app(int i) { out.print(i); return this; } structure_ClassInfo infoForClass(Class c) { structure_ClassInfo info = infoByClass.get(c); if (info == null) info = newClass(c); return info; } // called when a new class is detected // can be overridden by clients structure_ClassInfo newClass(Class c) { structure_ClassInfo info = new structure_ClassInfo(); info.c = c; infoByClass.put(c, info); if (isSyntheticOrAnonymous(c)) { info.special = info.nullInstances = true; return info; } if ((info.customSerializer = findMethodNamed(c, "_serialize")) != null) info.special = true; if (storeBaseClasses) { Class sup = c.getSuperclass(); if (sup != Object.class) { append("bc "); append(shortDynClassNameForStructure(c)); out.print(" "); append(shortDynClassNameForStructure(sup)); out.print(" "); infoForClass(sup); // transitively write out superclass relations } } return info; } void setFields(structure_ClassInfo info, List fields) { info.fields = fields; } void writeObject(Object o, String shortName, Map fv) { String singleField = fv.size() == 1 ? first(fv.keySet()) : null; append(shortName); n += countDots(shortName)*2; // correct token count int l = n; Iterator it = fv.entrySet().iterator(); stack.add(new Runnable() { public void run() { try { if (!it.hasNext()) { if (n != l) append(")"); } else { Map.Entry e = (Map.Entry) it.next(); append(n == l ? "(" : ", "); append((String) e.getKey()).append("="); stack.add(this); structure_1(e.getValue(), structure_Data.this); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!it.hasNext()) {\r\n if (n != l)\r\n append(\")\");\r\n } el..."; }}); } } static void structure_1(final Object o, final structure_Data d) { try { if (o == null) { d.append("null"); return; } Class c = o.getClass(); boolean concept = false; concept = o instanceof Concept; structure_ClassInfo info = d.infoForClass(c); List lFields = info.fields; if (lFields == null) { // these are never back-referenced (for readability) if (o instanceof Number) { PrintWriter out = d.out; if (o instanceof Integer) { int i = ((Integer) o).intValue(); out.print(i); d.n += i < 0 ? 2 : 1; return; } if (o instanceof Long) { long l = ((Long) o).longValue(); out.print(l); out.print("L"); d.n += l < 0 ? 2 : 1; return; } if (o instanceof Short) { short s = ((Short) o).shortValue(); d.append("sh "); out.print(s); d.n += s < 0 ? 2 : 1; return; } if (o instanceof Float) { d.append("fl ", 2); quoteToPrintWriter(str(o), out); return; } if (o instanceof Double) { d.append("d(", 3); quoteToPrintWriter(str(o), out); d.append(")"); return; } if (o instanceof BigInteger) { out.print("bigint("); out.print(o); out.print(")"); d.n += ((BigInteger) o).signum() < 0 ? 5 : 4; return; } } if (o instanceof Boolean) { d.append(((Boolean) o).booleanValue() ? "t" : "f"); return; } if (o instanceof Character) { d.append(quoteCharacter((Character) o)); return; } if (o instanceof File) { d.append("File ").append(quote(((File) o).getPath())); return; } // referencable objects follow Integer ref = d.seen.get(o); if (o instanceof String && ref == null) ref = d.strings.get((String) o); if (ref != null) { /*d.refd.set(ref);*/ d.append("t").app(ref); return; } if (!(o instanceof String)) d.seen.put(o, d.n); // record token number else { String s = d.stringSizeLimit != 0 ? shorten((String) o, d.stringSizeLimit) : (String) o; if (!d.noStringSharing) { if (d.shareStringsLongerThan == Integer.MAX_VALUE) d.seen.put(o, d.n); if (l(s) >= d.shareStringsLongerThan) d.strings.put(s, d.n); } quoteToPrintWriter(s, d.out); d.n++; return; } if (o instanceof Set) { /*O set2 = unwrapSynchronizedSet(o); if (set2 != o) { d.append("sync"); o = set2; } TODO */ if (((Set) o) instanceof TreeSet) { d.append(isCISet_gen((Set) o) ? "ciset" : "treeset"); structure_1(new ArrayList((Set) o), d); return; } // assume it's a HashSet or LinkedHashSet d.append(((Set) o) instanceof LinkedHashSet ? "lhs" : "hashset"); structure_1(new ArrayList((Set) o), d); return; } String name = c.getName(); if (o instanceof Collection && !isJavaXClassName(name) /* && neq(name, "main$Concept$RefL") */) { // it's a list if (name.equals("java.util.Collections$SynchronizedList") || name.equals("java.util.Collections$SynchronizedRandomAccessList")) { d.append("sync "); { structure_1(unwrapSynchronizedList(((List) o)), d); return; } } else if (name.equals("java.util.LinkedList")) d.append("ll"); d.append("["); final int l = d.n; final Iterator it = cloneList((Collection) o).iterator(); d.stack.add(new Runnable() { public void run() { try { if (!it.hasNext()) d.append("]"); else { d.stack.add(this); if (d.n != l) d.append(", "); structure_1(it.next(), d); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!it.hasNext())\r\n d.append(\"]\");\r\n else {\r\n d.sta..."; }}); return; } if (o instanceof Map && !startsWith(name, d.mcDollar)) { if (o instanceof LinkedHashMap) d.append("lhm"); else if (o instanceof HashMap) d.append("hm"); else if (o instanceof TreeMap) d.append(isCIMap_gen((TreeMap) o) ? "cimap" : "tm"); else if (name.equals("java.util.Collections$SynchronizedMap") || name.equals("java.util.Collections$SynchronizedSortedMap") || name.equals("java.util.Collections$SynchronizedNavigableMap")) { d.append("sync "); { structure_1(unwrapSynchronizedMap(((Map) o)), d); return; } } d.append("{"); final int l = d.n; final Iterator it = cloneMap((Map) o).entrySet().iterator(); d.stack.add(new Runnable() { boolean v = false; Map.Entry e; public void run() { if (v) { d.append("="); v = false; d.stack.add(this); structure_1(e.getValue(), d); } else { if (!it.hasNext()) d.append("}"); else { e = (Map.Entry) it.next(); v = true; d.stack.add(this); if (d.n != l) d.append(", "); structure_1(e.getKey(), d); } } } }); return; } if (c.isArray()) { if (o instanceof byte[]) { d.append("ba ").append(quote(bytesToHex((byte[]) o))); return; } final int n = Array.getLength(o); if (o instanceof boolean[]) { String hex = boolArrayToHex((boolean[]) o); int i = l(hex); while (i > 0 && hex.charAt(i-1) == '0' && hex.charAt(i-2) == '0') i -= 2; d.append("boolarray ").append(n).app(" ").append(quote(substring(hex, 0, i))); return; } String atype = "array"/*, sep = ", "*/; // sep is not used yet if (o instanceof int[]) { //ret "intarray " + quote(intArrayToHex((int[]) o)); atype = "intarray"; //sep = " "; } else if (o instanceof double[]) { atype = "dblarray"; //sep = " "; } else { Pair p = arrayTypeAndDimensions(c); if (p.a == int.class) atype = "intarray"; else if (p.a == byte.class) atype = "bytearray"; else if (p.a == boolean.class) atype = "boolarray"; else if (p.a == double.class) atype = "dblarray"; else if (p.a == String.class) { atype = "array S"; d.n++; } else atype = "array"; // fail("Unsupported array type: " + p.a); if (p.b > 1) { atype += "/" + p.b; // add number of dimensions d.n += 2; // 2 additional tokens will be written } } d.append(atype).append("{"); d.stack.add(new Runnable() { int i; public void run() { if (i >= n) d.append("}"); else { d.stack.add(this); if (i > 0) d.append(", "); structure_1(Array.get(o, i++), d); } } }); return; } if (o instanceof Class) { d.append("class(", 2).append(quote(((Class) o).getName())).append(")"); return; } if (o instanceof Throwable) { d.append("exception(", 2).append(quote(((Throwable) o).getMessage())).append(")"); return; } if (o instanceof BitSet) { BitSet bs = (BitSet) o; d.append("bitset{", 2); int l = d.n; for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { if (d.n != l) d.append(", "); d.append(i); } d.append("}"); return; } // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) { d.append("j ").append(quote(str(o))); return; // Hm. this is not unstructure-able } /*if (name.equals("main$Lisp")) { fail("lisp not supported right now"); }*/ if (info.special) { if (info.customSerializer != null) { // custom serialization (_serialize method) Object o2 = invokeMethod(info.customSerializer, o); d.append("cu "); String shortName = dropPrefix(d.mcDollar, name); d.append(shortName); d.out.append(' '); structure_1(o2, d); return; } else if (info.nullInstances) { d.append("null"); return; } else if (info.serializeObject != null) { info.serializeObject.get(o); return; } else throw fail("unknown special type"); } String dynName = shortDynClassNameForStructure(o); if (concept && !d.concepts.contains(dynName)) { d.concepts.add(dynName); d.append("c "); } // serialize an object with fields. // first, collect all fields and values in fv. TreeSet fields = new TreeSet(new Comparator() { public int compare(Field a, Field b) { return stdcompare(a.getName(), b.getName()); } }); Class cc = c; while (cc != Object.class) { for (Field field : getDeclaredFields_cached(cc)) { String fieldName = field.getName(); if (fieldName.equals("_persistenceInfo")) d.persistenceInfo.put(c, field); if ((field.getModifiers() & (java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.TRANSIENT)) != 0) continue; fields.add(field); // put special cases here...? } cc = cc.getSuperclass(); } // TODO: S fieldOrder = getOpt(c, "_fieldOrder"); lFields = asList(fields); // Render this$0/this$1 first because unstructure needs it for constructor call. int n = l(lFields); for (int i = 0; i < n; i++) { Field f = lFields.get(i); if (f.getName().startsWith("this$")) { lFields.remove(i); lFields.add(0, f); break; } } d.setFields(info, lFields); } // << if (lFields == null) else { // ref handling for lFields != null Integer ref = d.seen.get(o); if (ref != null) { /*d.refd.set(ref);*/ d.append("t").app(ref); return; } d.seen.put(o, d.n); // record token number } // get _persistenceInfo from field and/or dynamic field Field persistenceInfoField = (Field) (d.persistenceInfo.get(c)); Map persistenceInfo = persistenceInfoField == null ? null : (Map) persistenceInfoField.get(o); if (persistenceInfoField == null && o instanceof DynamicObject) persistenceInfo = (Map) getOptDynOnly(((DynamicObject) o), "_persistenceInfo"); LinkedHashMap fv = new LinkedHashMap(); for (Field f : lFields) { Object value; try { value = f.get(o); } catch (Exception e) { value = "?"; } if (value != null && (persistenceInfo == null || !Boolean.FALSE.equals(persistenceInfo.get(f.getName())))) fv.put(f.getName(), value); } String name = c.getName(); String shortName = dropPrefix("loadableUtils.utils$", dropPrefix(d.mcDollar, name)); if (startsWithDigit(shortName)) shortName = name; // for anonymous classes // Now we have fields & values. Process fieldValues if it's a DynamicObject. // omit field "className" if equal to class's name if (concept && eq(fv.get("className"), shortName)) fv.remove("className"); if (o instanceof DynamicObject) { putAll(fv, (Map) fv.get("fieldValues")); fv.remove("fieldValues"); shortName = shortDynClassNameForStructure(o); fv.remove("className"); } d.writeObject(o, shortName, fv); } catch (Exception __e) { throw rethrow(__e); } } static ArrayList litlist(A... a) { ArrayList l = new ArrayList(a.length); for (A x : a) l.add(x); return l; } // This is a bit rough... finds static and non-static methods. // It's usually just for error messages though, so no worries. static boolean hasMethodNamed(Object obj, String method) { if (obj == null) return false; if (obj instanceof Class) return hasMethodNamed((Class) obj, method); return hasMethodNamed(obj.getClass(), method); } static boolean hasMethodNamed(Class c, String method) { while (c != null) { for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method)) return true; c = c.getSuperclass(); } return false; } static String getClassName(Object o) { return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName(); } // binary legacy signature static Object[] toObjectArray(Collection c) { return toObjectArray((Iterable) c); } static Object[] toObjectArray(Iterable c) { List l = asList(c); return l.toArray(new Object[l.size()]); } // TODO: handle null values static String replaceDollarVars_js(String s, Object... __) { //ret replaceDollarVars_withCustomToString(s, o -> jsonEncode_extended(o), _); Map vars = paramsToMap_withNulls(__); if (empty(vars)) return s; var vars2 = mapKeys(__60 -> dropDollarPrefix(__60), vars); return replaceDollarVars_dyn(s, var -> { if (!vars2.containsKey(var)) return null; Object value = vars2.get(var); return jsonEncode_extended(value); }); } static Object dm_getModule(Object moduleOrID) { if (moduleOrID == null || eq(moduleOrID, "")) return null; if (isString(moduleOrID) && isIdentifier(((String) moduleOrID))) return dm_getService(((String) moduleOrID)); if (isStringOrIntOrLong(moduleOrID)) return dm_callOS("getDynModuleByID", str(moduleOrID)); return dm_resolveModule(moduleOrID); } static AutoCloseable dm_enter(Object mod) { return (AutoCloseable) callOpt(dm_getModule(mod), "enter"); } static String className(Object o) { return getClassName(o); } static void close_pcall(AutoCloseable c) { if (c != null) { try { c.close(); } catch (Throwable __e) { _handleException(__e); }} } static Collection values(Map map) { return map == null ? emptyList() : map.values(); } // convenience shortcut for values_gen static Collection values(Object map) { return values((Map) map); } static Collection values(MultiMap mm) { return mm == null ? emptyList() : concatLists(values(mm.data)); } static void preCleanUp(Object c) { if (c instanceof Collection) { for (Object o : ((Collection) c)) preCleanUp(o); return; } callOpt(c, "licensed_off"); setOpt(c, "ping_anyActions" , true); // so ping notices setOpt(c, "cleaningUp_flag" , true); } static Field setOpt_findField(Class c, String field) { HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } return map.get(field); } static void setOpt(Object o, String field, Object value) { try { if (o == null) return; Class c = o.getClass(); HashMap map; if (getOpt_cache == null) map = getOpt_makeCache(c); // in class init else synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } if (map == getOpt_special) { if (o instanceof Class) { setOpt((Class) o, field, value); return; } // It's probably a subclass of Map. Use raw method setOpt_raw(o, field, value); return; } Field f = map.get(field); if (f != null) { smartSet(f, o, value); return; } // possible improvement: skip setAccessible if (o instanceof DynamicObject) { setDyn(((DynamicObject) o), field, value); return; } if (o instanceof IMeta) setDyn(((IMeta) o), field, value); } catch (Exception __e) { throw rethrow(__e); } } static void setOpt(Class c, String field, Object value) { if (c == null) return; try { Field f = setOpt_findStaticField(c, field); // TODO: optimize 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() & java.lang.reflect.Modifier.STATIC) != 0) { makeAccessible(f); return f; } _c = _c.getSuperclass(); } while (_c != null); return null; } static void innerCleanUp(Object c) { // call custom cleanMeUp() and cleanMeUp_*() functions if (!isFalse(pcallOpt(c, "cleanMeUp"))) for (String name : sorted(methodsStartingWith(c, "cleanMeUp_"))) try { callOpt(c, name); } catch (Throwable e) { print("Error cleaning up: " + programID(c)); _handleException(e); } } static void innerCleanUp() { innerCleanUp(mc()); } static Object getOpt(Object o, String field) { return getOpt_cached(o, field); } static Object getOpt(String field, Object o) { return getOpt_cached(o, field); } static Object getOpt_raw(Object o, String field) { try { Field f = getOpt_findField(o.getClass(), field); if (f == null) return null; makeAccessible(f); return f.get(o); } catch (Exception __e) { throw rethrow(__e); } } // access of static fields is not yet optimized static Object getOpt(Class c, String field) { try { if (c == null) return null; Field f = getOpt_findStaticField(c, field); if (f == null) return null; makeAccessible(f); return f.get(null); } catch (Exception __e) { throw rethrow(__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() & java.lang.reflect.Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Object pcallOpt(Object o, String method, Object... args) { try { return callOpt(o, method, args); } catch (Throwable __e) { _handleException(__e); } return null; } static List registeredThreads(Object o) { Map map = (Map) (getOpt(o, "_registerThread_threads")); if (map == null) return ll(); map.size(); // force clean-up synchronized(map) { return asList(keys(map)); } } static List registeredThreads() { _registerThread_threads.size(); // force clean-up return asList(keys(_registerThread_threads)); } static String joinWithComma(Collection c) { return join(", ", c); } static String joinWithComma(String... c) { return join(", ", c); } static String joinWithComma(Pair p) { return p == null ? "" : joinWithComma(str(p.a), str(p.b)); } static List allToString(Iterable c) { List l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static List allToString(Object[] c) { List l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static void interruptThreads(Collection threads) { for (Thread t : unnull(threads)) interruptThread(t); } static void interruptThreads(Class mainClass) { interruptThreads(registeredThreads(mainClass)); } static void retireClassLoader(ClassLoader cl) { if (isJavaXClassLoader(cl)) setOptAll(cl, "retired" , true, "retiredMarker" , new DefunctClassLoader()); } static Map newWeakHashMap() { return _registerWeakMap(synchroMap(new WeakHashMap())); } static void closeAllAndClear(Collection l) { if (l == null) return; for (AutoCloseable c : cloneList(l)) { try { close(c); } catch (Throwable __e) { _handleException(__e); }} l.clear(); } static Field makeAccessible(Field f) { try { f.setAccessible(true); } catch (Throwable e) { // Note: The error reporting only works with Java VM option --illegal-access=deny vmBus_send("makeAccessible_error", e, f); } return f; } static Method makeAccessible(Method m) { try { m.setAccessible(true); } catch (Throwable e) { vmBus_send("makeAccessible_error", e, m); } return m; } static Constructor makeAccessible(Constructor c) { try { c.setAccessible(true); } catch (Throwable e) { vmBus_send("makeAccessible_error", e, c); } return c; } static void smartSet(Field f, Object o, Object value) throws Exception { try { f.set(o, value); } catch (Exception e) { Class type = f.getType(); // take care of common case (long to int) if (type == int.class && value instanceof Long) { f.set(o, ((Long) value).intValue()); return; } if (type == boolean.class && value instanceof String) { f.set(o, isTrueOrYes(((String) value))); return; } if (type == LinkedHashMap.class && value instanceof Map) { f.set(o, asLinkedHashMap((Map) value)); return; } try { if (f.getType() == Concept.Ref.class) { f.set(o, ((Concept) o).new Ref((Concept) value)); return; } if (o instanceof Concept.Ref) { f.set(o, ((Concept.Ref) o).get()); return; } } catch (Throwable _e) {} throw e; } } static String combinePrintParameters(String s, Object o) { return (endsWithLetterOrDigit(s) ? s + ": " : s) + o; } static void ping_okInCleanUp() { if (ping_pauseAll || ping_anyActions) ping_impl(true); } // this syntax should be removed... static Object getThreadLocal(Object o, String name) { ThreadLocal t = (ThreadLocal) (getOpt(o, name)); return t != null ? t.get() : null; } static A getThreadLocal(ThreadLocal tl) { return tl == null ? null : tl.get(); } static A getThreadLocal(ThreadLocal tl, A defaultValue) { return or(getThreadLocal(tl), defaultValue); } static ThreadLocal print_byThread_dontCreate() { return print_byThread; } static boolean isFalse(Object o) { return eq(false, o); } static void print_append(Appendable buf, String s, int max) { try { synchronized(buf) { buf.append(s); if (buf instanceof StringBuffer) rotateStringBuffer(((StringBuffer) buf), max); else if (buf instanceof StringBuilder) rotateStringBuilder(((StringBuilder) buf), max); } } catch (Exception __e) { throw rethrow(__e); } } static Class mc() { return main.class; } static AutoCloseable tempSetThreadLocal(final ThreadLocal tl, A a) { if (tl == null) return null; final A prev = setThreadLocal(tl, a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static AutoCloseable tempSetThreadLocalIfNecessary(ThreadLocal tl, A a) { if (tl == null) return null; A prev = tl.get(); if (eq(prev, a)) return null; tl.set(a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static AutoCloseable tempSetThreadLocalIfNecessary(x30_pkg.x30_util.BetterThreadLocal tl, A a) { if (tl == null) return null; A prev = tl.get(); if (eq(prev, a)) return null; tl.set(a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static Object jsonDecode(String text) { return new jsonDecode_Y(text).parse(); } // the miraculous class Y static class jsonDecode_Y { String text; List tok; boolean useOrderedMaps = false; int i = 1; jsonDecode_Y(String text) { this.text = text; tok = jsonTok(text); } transient IF1 fail; RuntimeException fail(String msg) { return fail != null ? fail.get(msg) : fail_base(msg); } final RuntimeException fail_fallback(IF1 _f, String msg) { return _f != null ? _f.get(msg) : fail_base(msg); } RuntimeException fail_base(String msg) { return main.fail(msg); } Object parse() { if (l(tok) == 1) return null; return parseExpr(); } Object parseExpr() { String t = tok.get(i); if (t.startsWith("\"") || t.startsWith("'")) { String s = unquote(tok.get(i)); i += 2; return s; } if (t.equals("{")) return parseMap(); if (t.equals("[")) return this.parseList(); // avoid loading standard function "parseList" if (t.equals("null")) { i += 2; return null; } if (t.equals("false")) { i += 2; return false; } if (t.equals("true")) { i += 2; return true; } boolean minus = false; if (t.equals("-")) { minus = true; i += 2; t = get(tok, i); } if (isInteger(t)) { int j = i; i += 2; if (eqOneOf(get(tok, i), ".", "e", "E")) { // rough parsing for doubles while (isInteger(get(tok, i)) || eqOneOf(get(tok, i), ".", "e", "E", "-")) i += 2; double d = parseDouble(joinSubList(tok, j, i-1)); if (minus) d = -d; return d; } else { long l = parseLong(t); return boxedIntOrLong(minus ? -l : l); } } throw fail("Unknown token " + (i+1) + ": " + t + ": " + text); } Object parseList() { consume("["); List list = new ArrayList(); while (!tok.get(i).equals("]")) { list.add(parseExpr()); if (tok.get(i).equals(",")) i += 2; } consume("]"); return list; } Object parseMap() { consume("{"); Map map = useOrderedMaps ? new LinkedHashMap() : new TreeMap(); while (!tok.get(i).equals("}")) { String key = unquote(tok.get(i)); i += 2; consume(":"); Object value = parseExpr(); 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; } } static Object collectionMutex(List l) { return l; } static Object collectionMutex(Object o) { if (o instanceof List) return o; String c = className(o); if (eq(c, "java.util.TreeMap$KeySet")) c = className(o = getOpt(o, "m")); else if (eq(c, "java.util.HashMap$KeySet")) c = className(o = get_raw(o, "this$0")); if (eqOneOf(c, "java.util.TreeMap$AscendingSubMap", "java.util.TreeMap$DescendingSubMap")) c = className(o = get_raw(o, "m")); return o; } static Map paramsFromURL(String url) { return decodeHQuery(getQueryFromURL(url)); } static boolean containsKey(Map map, A key) { return map != null && map.containsKey(key); } static String addAnchorToURL(String url, String anchor) { int i = smartIndexOf(url, '#'); url = takeFirst(url, i); if (nempty(anchor)) url += "#" + anchor; return url; } static String appendParamsToURL(String url, Map params) { return appendQueryToURL(url, params); } static String appendParamsToURL(String url, Object... data) { return appendQueryToURL(url, data); } static String urlWithoutQueryAndAnchor(String url) { return takeFirst(url, min(smartIndexOf(url, "?"), smartIndexOf(url, "#"))); } static String anchorFromURL(String url) { return substring(url, smartIndexOf(url, "#")); } //sbool ping_actions_shareable = true; static volatile boolean ping_pauseAll = false; static int ping_sleep = 100; // poll pauseAll flag every 100 static volatile boolean ping_anyActions = false; static Map ping_actions = newWeakHashMap(); static ThreadLocal ping_isCleanUpThread = new ThreadLocal(); // always returns true static boolean ping() { //ifdef useNewPing newPing(); //endifdef if (ping_pauseAll || ping_anyActions) ping_impl(true /* XXX */); //ifndef LeanMode ping_impl(); endifndef return true; } // returns true when it slept static boolean ping_impl(boolean okInCleanUp) { try { if (ping_pauseAll && !isAWTThread()) { do Thread.sleep(ping_sleep); while (ping_pauseAll); return true; } if (ping_anyActions) { // don't allow sharing ping_actions if (!okInCleanUp && !isTrue(ping_isCleanUpThread.get())) failIfUnlicensed(); Object action = null; synchronized(ping_actions) { if (!ping_actions.isEmpty()) { action = ping_actions.get(currentThread()); if (action instanceof Runnable) ping_actions.remove(currentThread()); if (ping_actions.isEmpty()) ping_anyActions = false; } } if (action instanceof Runnable) ((Runnable) action).run(); else if (eq(action, "cancelled")) throw fail("Thread cancelled."); } return false; } catch (Exception __e) { throw rethrow(__e); } } static Thread currentThread() { return Thread.currentThread(); } static Map vm_threadInterruptionReasonsMap() { return vm_generalWeakSubMap("Thread interruption reasons"); } static String strOr(Object o, String ifNull) { return o == null ? ifNull : str(o); } static A printStackTrace(A e) { // we go to system.out now - system.err is nonsense print(getStackTrace(e)); return e; } static void printStackTrace() { printStackTrace(new Throwable()); } static void printStackTrace(String msg) { printStackTrace(new Throwable(msg)); } static void printStackTrace(String msg, Throwable e) { printStackTrace(new Throwable(msg, e)); } static void lockOrFail(Lock lock, long timeout) { try { ping(); vmBus_send("locking", lock, "thread" , currentThread()); if (!lock.tryLock(timeout, TimeUnit.MILLISECONDS)) { String s = "Couldn't acquire lock after " + timeout + " ms."; if (lock instanceof ReentrantLock) { ReentrantLock l = (ReentrantLock) lock; s += " Hold count: " + l.getHoldCount() + ", owner: " + call(l, "getOwner"); } throw fail(s); } vmBus_send("locked", lock, "thread" , currentThread()); ping(); } catch (Exception __e) { throw rethrow(__e); } } static ReentrantLock fairLock() { return new ReentrantLock(true); } static int iteratorCount_int_close(Iterator i) { try { int n = 0; if (i != null) while (i.hasNext()) { i.next(); ++n; } if (i instanceof AutoCloseable) ((AutoCloseable) i).close(); return n; } catch (Exception __e) { throw rethrow(__e); } } static boolean hasConceptWhereIC(Class c, Object... params) { return conceptWhereIC(c, params) != null; } static boolean hasConceptWhereIC(Concepts cc, Class c, Object... params) { return conceptWhereIC(cc, c, params) != null; } static boolean regexpFindsIC(String pat, String s) { return regexpIC(pat, s).find(); } static Object vmBus_wrapArgs(Object... args) { return empty(args) ? null : l(args) == 1 ? args[0] : args; } static void pcallFAll_minimalExceptionHandling(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) { ping(); pcallF_minimalExceptionHandling(f, args); } } static void pcallFAll_minimalExceptionHandling(Iterator it, Object... args) { while (it.hasNext()) { ping(); pcallF_minimalExceptionHandling(it.next(), args); } } static Set vm_busListeners_live_cache; static Set vm_busListeners_live() { if (vm_busListeners_live_cache == null) vm_busListeners_live_cache = vm_busListeners_live_load(); return vm_busListeners_live_cache; } static Set vm_busListeners_live_load() { return vm_generalIdentityHashSet("busListeners"); } static Map vm_busListenersByMessage_live_cache; static Map vm_busListenersByMessage_live() { if (vm_busListenersByMessage_live_cache == null) vm_busListenersByMessage_live_cache = vm_busListenersByMessage_live_load(); return vm_busListenersByMessage_live_cache; } static Map vm_busListenersByMessage_live_load() { return vm_generalHashMap("busListenersByMessage"); } static int iceil(double d) { return (int) Math.ceil(d); } static String containerTag(String tag) { return containerTag(tag, ""); } static String containerTag(String tag, Object contents, Object... params) { String openingTag = hopeningTag(tag, params); String s = str(contents); return openingTag + s + ""; } static String hfulltag(String tag) { return hfulltag(tag, ""); } static String hfulltag(String tag, Object contents, Object... params) { return hopeningTag(tag, params) + str(contents) + ""; } static String htmlencode_noQuotes(String s) { if (s == null) return ""; int n = s.length(); StringBuilder out = null; for (int i = 0; i < n; i++) { char c = s.charAt(i); if (c == '<') { if (out == null) out = new StringBuilder(Math.max(16, n)).append(takeFirst(i, s)); out .append("<"); } else if (c == '>') { if (out == null) out = new StringBuilder(Math.max(16, n)).append(takeFirst(i, s)); out .append(">"); } else if (c > 127 || c == '&') { int cp = s.codePointAt(i); if (out == null) out = new StringBuilder(Math.max(16, n)).append(takeFirst(i, s)); out .append("&#x"); out.append(intToHex_flexLength(cp)); out.append(';'); i += Character.charCount(cp)-1; } else { if (out != null) out.append(c); } } return out == null ? s : out.toString(); } static String hcss(Object contents) { if (contents instanceof String && isRelativeOrAbsoluteURL((String) contents)) return hstylesheetsrc((String) contents); else return htag("style", contents); } // we used to have overflow: hidden, but visible should work // too and solve the cutting-off hPopDownButton problem static String hcss_responstable() { return hcss("\r\n .responstable {\r\n margin: 1em 0;\r\n width: 100%;\r\n overflow: visible;\r\n background: #FFF;\r\n color: #024457;\r\n border-radius: 10px;\r\n border: 1px solid #167F92;\r\n }\r\n \r\n .responstable tr {\r\n border: 1px solid #D9E4E6;\r\n }\r\n .responstable tr:nth-child(odd) {\r\n background-color: #EAF3F3;\r\n }\r\n .responstable th {\r\n display: none;\r\n border: 1px solid #FFF;\r\n background-color: #167F92;\r\n color: #FFF;\r\n padding: 1em;\r\n }\r\n .responstable th:first-child {\r\n display: table-cell;\r\n text-align: center;\r\n }\r\n .responstable th:nth-child(2) {\r\n display: table-cell;\r\n }\r\n .responstable th:nth-child(2) span {\r\n display: none;\r\n }\r\n .responstable th:nth-child(2):after {\r\n content: attr(data-th);\r\n }\r\n @media (min-width: 480px) {\r\n .responstable th:nth-child(2) span {\r\n display: block;\r\n }\r\n .responstable th:nth-child(2):after {\r\n display: none;\r\n }\r\n }\r\n .responstable td {\r\n display: block;\r\n word-wrap: break-word;\r\n max-width: 7em;\r\n }\r\n .responstable td:first-child {\r\n display: table-cell;\r\n text-align: center;\r\n border-right: 1px solid #D9E4E6;\r\n }\r\n @media (min-width: 480px) {\r\n .responstable td {\r\n border: 1px solid #D9E4E6;\r\n }\r\n }\r\n .responstable th, .responstable td {\r\n text-align: left;\r\n margin: .5em 1em;\r\n }\r\n @media (min-width: 480px) {\r\n .responstable th, .responstable td {\r\n display: table-cell;\r\n padding: 1em;\r\n }\r\n }\r\n "); } static String h3(String s, Object... params) { return tag("h3", s, params) + "\n"; } static Object[] concatArrays(Object[]... arrays) { int l = 0; for (Object[] a : arrays) l += l(a); Object[] x = new Object[l]; int i = 0; for (Object[] a : arrays) if (a != null) { System.arraycopy(a, 0, x, i, l(a)); i += l(a); } return x; } // grabs only the specified keys from the map. static String hiddenFields(Map map, Collection keys) { StringBuilder buf = new StringBuilder(); for (String key : keys) { String value = map.get(key); if (!empty(value)) buf.append(htag("input", "", "type", "hidden", "name", key, "value", value) + "\n"); } return str(buf); } static String hiddenFields(Map map, String... keys) { return hiddenFields(map, asList(keys)); } static String htag(String tag) { return htag(tag, ""); } static String htag(String tag, Object contents, Object... params) { String openingTag = hopeningTag(tag, params); String s = str(contents); if (empty(s) && neqic(tag, "script")) return dropLast(openingTag) + "/>"; return openingTag + s + ""; } static String htmlEncode_nlToBr(String s) { return nlToBr(htmlEncode2(s)); } static String tdTop(Object contents, Object... params) { return tag("td", contents, arrayPlus(params, "valign", "top")); } // first element of params can be the value static String htextinput(String name, Object... params) { Object value = ""; if (odd(l(params))) { value = params[0]; params = dropFirst(params); } params = html_massageAutofocusParam(params); return tag("input", "", concatArrays(new Object[] {"type", "text", "name", name, "value", value}, params)); } static String htextinput(String name) { return htextinput(name, ""); } // first element of params can be the value static String hpasswordfield(String name, Object... params) { Object value = ""; if (odd(l(params))) { value = params[0]; params = dropFirst(params); } params = html_massageAutofocusParam(params); return tag("input", "", concatArrays(new Object[] {"type", "password", "name", name, "value", value}, params)); } static String hpasswordfield(String name) { return hpasswordfield(name, ""); } static String strOrEmpty(Object o) { return o == null ? "" : str(o); } static boolean startsWithIgnoreCase(String a, String b) { return regionMatchesIC(a, 0, b, 0, b.length()); } static String dropSuffix(String suffix, String s) { return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static Object subBot_serve404() { return call(getMainBot(), "serve404"); } static Object subBot_serve404(String msg) { return call(getMainBot(), "serve404", msg); } static Object serveByteArray(byte[] data, String mimeType) { return subBot_serveByteArray(data, mimeType); } static byte[] toUtf8(String s) { try { return s.getBytes(utf8charset()); } catch (Exception __e) { throw rethrow(__e); } } static Object mainBot; static Object getMainBot() { return mainBot; } static Object serveJSON_shallowLineBreaks(Object data) { return serveText(jsonEncode_shallowLineBreaks(data)); } static Object jsonPrepareData(Object... data) { if (l(data) == 1) return data[0]; return litorderedmap(data); } static List lambdaMap(IF1 f, Iterable l) { return map(l, f); } static List lambdaMap(IF1 f, A[] l) { return map(l, f); } static A cDeref(Concept.Ref ref) { return ref == null ? null : ref.get(); } // unclear semantics as to whether return null on null static ArrayList asList(A[] a) { return a == null ? new ArrayList() : new ArrayList(Arrays.asList(a)); } static ArrayList asList(int[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (int i : a) l.add(i); return l; } static ArrayList asList(long[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (long i : a) l.add(i); return l; } static ArrayList asList(float[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (float i : a) l.add(i); return l; } static ArrayList asList(double[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (double i : a) l.add(i); return l; } static ArrayList asList(Iterator it) { ArrayList l = new ArrayList(); if (it != null) while (it.hasNext()) l.add(it.next()); return l; } // disambiguation static ArrayList asList(IterableIterator s) { return asList((Iterator) s); } 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; } static ArrayList asList(Producer p) { ArrayList l = new ArrayList(); A a; if (p != null) while ((a = p.next()) != null) l.add(a); return l; } static ArrayList asList(Enumeration e) { ArrayList l = new ArrayList(); if (e != null) while (e.hasMoreElements()) l.add(e.nextElement()); return l; } static List asList(Pair p) { return p == null ? null : ll(p.a, p.b); } static String dropLeadingSlash(String s) { return dropPrefix("/", s); } static String shortenClassName(String name) { if (name == null) return null; int i = lastIndexOf(name, "$"); if (i < 0) i = lastIndexOf(name, "."); return i < 0 ? name : substring(name, i+1); } static A highestByField(String field, Collection l) { A best = null; Object bestValue = null; if (l != null) for(A a : l) { Object val = getOpt(a, field); if (val != null && (bestValue == null || cmp(val, bestValue) > 0)) { best = a; bestValue = val; } } return best; } static A highestByField(Collection l, String field) { return highestByField(field, l); } // f: A -> Comparable static List sortedByCalculatedFieldDesc(Collection c, final Object f) { return sortByCalculatedFieldDesc(c, f); } static List sortedByCalculatedFieldDesc(Object f, Collection c) { return sortByCalculatedFieldDesc(f, c); } static List sortedByCalculatedFieldDesc(Iterable c, IF1 f) { return sortByCalculatedFieldDesc(c, f); } static List sortedByCalculatedFieldDesc(IF1 f, Iterable c) { return sortByCalculatedFieldDesc(f, c); } static boolean isTrue(Object o) { if (o instanceof Boolean) return ((Boolean) o).booleanValue(); if (o == null) return false; if (o instanceof ThreadLocal) // TODO: remove this return isTrue(((ThreadLocal) o).get()); throw fail(getClassName(o)); } static boolean isTrue(Boolean b) { return b != null && b.booleanValue(); } static List withoutNulls(Iterable l) { if (l instanceof List) if (!containsNulls((List) l)) return ((List) l); List l2 = new ArrayList(); for (A a : l) if (a != null) l2.add(a); return l2; } static Map withoutNulls(Map map) { Map map2 = similarEmptyMap(map); for (A a : keys(map)) if (a != null) { B b = map.get(a); if (b != null) map2.put(a, b); } return map2; } static List withoutNulls(A[] l) { List l2 = new ArrayList(); if (l != null) for (A a : l) if (a != null) l2.add(a); return l2; } static int[] subArray(int[] b, int start, int end) { int[] x = new int[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static byte[] subArray(byte[] b, int start, int end) { start = max(start, 0); end = min(end, l(b)); if (start >= end) return new byte[0]; byte[] x = new byte[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static short[] subArray(short[] b, int start, int end) { if (start <= 0 && end >= l(b)) return b; short[] x = new short[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static float[] subArray(float[] b, int start, int end) { float[] x = new float[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static Object[] subArray(Object[] b, int start) { return subArray(b, start, l(b)); } static Object[] subArray(Object[] b, int start, int end) { start = max(start, 0); end = min(end, l(b)); if (start >= end) return new Object[0]; Object[] x = new Object[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static List newSubListOrSame(List l, int startIndex) { return newSubListOrSame(l, startIndex, l(l)); } static List newSubListOrSame(List l, int startIndex, int endIndex) { if (l == null) return null; int n = l(l); startIndex = max(0, startIndex); endIndex = min(n, endIndex); if (startIndex >= endIndex) return ll(); if (startIndex == 0 && endIndex == n) return l; return cloneList(l.subList(startIndex, endIndex)); } static List newSubListOrSame(List l, IntRange r) { return newSubListOrSame(l, r.start, r.end); } static int[] takeFirstOfIntArray(int[] b, int n) { return subIntArray(b, 0, n); } static int[] takeFirstOfIntArray(int n, int[] b) { return takeFirstOfIntArray(b, n); } static void sort(T[] a, Comparator c) { if (a != null) Arrays.sort(a, c); } static void sort(T[] a) { if (a != null) Arrays.sort(a); } static void sort(int[] a) { if (a != null) Arrays.sort(a); } static void sort(List a, Comparator c) { if (a != null) Collections.sort(a, c); } static void sort(List a) { if (a != null) Collections.sort(a); } static Comparator descFieldComparator(final String field) { return new Comparator() { public int compare(A a, A b) { return cmp(getOpt(b, field), getOpt(a, field)); } }; } static Collection findConceptsWhereCI(Class c, Object... params) { return findConceptsWhereCI(db_mainConcepts(), c, params); } static List findConceptsWhereCI(String c, Object... params) { return findConceptsWhereCI(db_mainConcepts(), c, params); } static Collection findConceptsWhereCI(Concepts concepts, Class c, Object... params) { Collection l = findConceptsWhereCI_noParent(concepts, c, params); if (concepts.parent == null) return l; return concatCollections_conservative(l, findConceptsWhereCI(concepts.parent, c, params)); } static Collection findConceptsWhereCI_noParent(Concepts concepts, Class c, Object... params) { params = expandParams(c, params); // indexed if (concepts.ciFieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex index = concepts.getCIFieldIndex(c, (String) params[i]); if (index != null) { Collection rawList = index.getAll(params[i+1]); params = dropEntryFromParams(params, i); if (params == null) return rawList; List l = new ArrayList(); for (A x : rawList) if (checkConceptFieldsIC(x, params)) l.add(x); return l; } } // table scan return filterConceptsIC(concepts.list(c), params); } static List findConceptsWhereCI(Concepts concepts, String c, Object... params) { return filterConceptsIC(concepts.list(c), params); } static String hcommentSafe(String text) { return hcomment_unescaped(contains(text, "-->") ? htmlencode(text) : text); } static String joinNempties(String sep, Object... strings) { return joinStrings(sep, strings); } static String joinNempties(String sep, Iterable strings) { return joinStrings(sep, strings); } static String exceptionToStringShort(Throwable e) { lastException(e); e = getInnerException(e); String msg = hideCredentials(unnull(e.getMessage())); if (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0) return baseClassName(e) + prependIfNempty(": ", msg); else return msg; } public static String join(String glue, Iterable strings) { if (strings == null) return ""; if (strings instanceof Collection) { if (((Collection) strings).size() == 1) return str(first((Collection) 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)); } static String join(Iterable strings) { return join("", strings); } static String join(Iterable strings, String glue) { return join(glue, strings); } public static String join(String[] strings) { return join("", strings); } static String join(String glue, Pair p) { return p == null ? "" : str(p.a) + glue + str(p.b); } static String insertString(String a, int idx, String b) { return spliceString(a, idx, idx, b); } static int smartIndexOfSpaceEtc(String s) { int i = indexOfSpaceEtc(s); return i < 0 ? l(s) : i; } static boolean nemptyString(String s) { return s != null && s.length() > 0; } static int strL(String s) { return s == null ? 0 : s.length(); } static int listL(Collection l) { return l == null ? 0 : l.size(); } static String hjavascript(String scriptOrURL, Object... __) { if (isRelativeOrAbsoluteURL(scriptOrURL) && !startsWithOneOf(scriptOrURL, "//", "/*")) return hjavascript_src(scriptOrURL, __); else return tag("script", scriptOrURL, paramsPlus(__, "type" , "text/javascript")); } static A uniqueConcept(Class c, Object... params) { return uniqueConcept(db_mainConcepts(), c, params); } static A uniqueConcept(Concepts cc, Class c, Object... params) { AutoCloseable __1 = tempDBLock(cc); try { params = expandParams(c, params); A x = findConceptWhere(cc, c, params); if (x == null) { x = unlisted(c); csetAll(x, params); cc.register(x); } else { } return x; } finally { _close(__1); }} static AutoCloseable tempDBLock(Concepts concepts) { return tempLock(concepts.lock); // NO null propagation this time } static AutoCloseable tempDBLock() { return tempDBLock(db_mainConcepts()); } static A findConceptWhereCI(Class c, Object... params) { return findConceptWhereCI(db_mainConcepts(), c, params); } static A findConceptWhereCI(Concepts concepts, Class c, Object... params) { params = expandParams(c, params); // find smallest index to use Lowest>> bestIndex = new Lowest(); if (concepts.ciFieldIndices != null) for (int i = 0; i < l(params); i += 2) { ping(); Object val = params[i+1]; IFieldIndex index = concepts.getCIFieldIndex(c, (String) params[i]); if (index != null) { Collection l = index.getAll(val); bestIndex.put(() -> l, l(l)); } if (concepts.useBackRefsForSearches && val instanceof Concept) { // conservatively use full ref count as score until we store backRefs by class bestIndex.put(() -> findBackRefs(((Concept) val), c), ((Concept) val)._backRefCount()); } } if (bestIndex.has()) { Collection l = bestIndex.get().get(); if (l(params) == 2) return first(l); // nothing to filter, index should suffice for (A x : l) { ping(); if (checkConceptFieldsIC(x, params)) return x; } return null; } else { // table scan for (A x : concepts.list(c)) { ping(); if (checkConceptFieldsIC(x, params)) return x; } return null; } } static Concept findConceptWhereCI(Concepts concepts, String c, Object... params) { for (Concept x : concepts.list(c)) { ping(); if (checkConceptFieldsIC(x, params)) return x; } return null; } // make concept instance that is not connected to DB static A unlisted(Class c, Object... args) { concepts_unlisted.set(true); try { return nuObject(c, args); } finally { concepts_unlisted.set(null); } } static Concept unlisted(String name, Object... args) { Class cc = findClass(name); concepts_unlisted.set(true); try { return cc != null ? nuObject(cc) : new Concept(name); } finally { concepts_unlisted.set(null); } } static long round(double d) { return Math.round(d); } static String round(String s) { return roundBracket(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 String programID; static String getProgramID() { return nempty(programID) ? formatSnippetIDOpt(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)); } static String addPrefix(String prefix, String s) { return s.startsWith(prefix) ? s : prefix + s; } static String asString(Object o) { return o == null ? null : o.toString(); } static Map mapWithoutKey(Map map, A key) { if (map == null || !map.containsKey(key)) return map; Map m = cloneMap(map); m.remove(key); return m; } static Map mapWithoutKey(A key, Map map) { return mapWithoutKey(map, key); } static String beforeSlash_orAll(String s) { return takeFirst(smartIndexOf(s, '/'), s); } static int lCharSequence(CharSequence s) { return s == null ? 0 : s.length(); } static int[] emptyIntArray_a = new int[0]; static int[] emptyIntArray() { return emptyIntArray_a; } static char[] emptyCharArray = new char[0]; static char[] emptyCharArray() { return emptyCharArray; } static double[] emptyDoubleArray = new double[0]; static double[] emptyDoubleArray() { return emptyDoubleArray; } static Object[] emptyObjectArray_a = new Object[0]; static Object[] emptyObjectArray() { return emptyObjectArray_a; } static Symbol emptySymbol_value; static Symbol emptySymbol() { if (emptySymbol_value == null) emptySymbol_value = symbol(""); return emptySymbol_value; } static boolean isIdentifier(String s) { return isJavaIdentifier(s); } static List javaTokC(String s) { if (s == null) return null; int l = s.length(); ArrayList tok = new ArrayList(); int i = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // 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 (c == '[' && d == '[') { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && 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(javaTok_substringC(s, i, j)); i = j; } return tok; } static List codeTokens(List tok) { return codeTokensOnly(tok); } // TODO: test if android complains about this static boolean isAWTThread() { if (isAndroid()) return false; if (isHeadless()) return false; return isAWTThread_awt(); } static boolean isAWTThread_awt() { return SwingUtilities.isEventDispatchThread(); } static Object sleepQuietly_monitor = new Object(); static void sleepQuietly() { try { assertFalse(isAWTThread()); synchronized(sleepQuietly_monitor) { sleepQuietly_monitor.wait(); } } catch (Exception __e) { throw rethrow(__e); } } static int cmp(Number a, Number b) { return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue()); } static int cmp(double a, double b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(int a, int b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(long a, long b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(Object a, Object b) { if (a == null) return b == null ? 0 : -1; if (b == null) return 1; return ((Comparable) a).compareTo(b); } static List reverseInPlace(List l) { return reverseList(l); } static List sortByFieldInPlace(List l, String field) { sort(l, fieldComparator(field)); return l; } static List sortByFieldInPlace(String field, List l) { return sortByFieldInPlace(l, field); } static double elapsedSeconds(long time) { return toSeconds(sysNow()-time); } static String jsonEncode_breakAtLevels(Object o) { return jsonEncode_breakAtLevels(5, o); } static String jsonEncode_breakAtLevels(int levels, Object o) { StringBuilder buf = new StringBuilder(); jsonEncode_breakAtLevels(levels, o, buf, 1); return str(buf); } static void jsonEncode_breakAtLevels(int levels, Object o, StringBuilder buf, int level) { if (o == null) buf.append("null"); else if (o instanceof String) buf.append(quote((String) o)); else if (o instanceof Number || o instanceof Boolean) buf.append(o); else if (o instanceof Map) { Map map = (Map) o; buf.append("{"); boolean first = true; for (Object key : keys(map)) { if (first) first = false; else buf.append(","); if (level == 1) buf.append("\n" + spaces(level*2)); buf.append(quote((String) key)); buf.append(":"); jsonEncode_breakAtLevels(levels, map.get(key), buf, level+1); } if (level <= levels) buf.append("\n" + spaces((level-1)*2)); buf.append("}"); } else if (o instanceof Collection) { Collection l = (Collection) o; buf.append("["); boolean first = true; for (Object element : l) { if (first) first = false; else buf.append(","); if (level <= levels) buf.append("\n" + spaces(level*2)); jsonEncode_breakAtLevels(levels, element, buf, level+1); } if (level <= levels) buf.append("\n" + spaces((level-1)*2)); buf.append("]"); } else throw fail("Unknown object for JSON encoding: " + className(o)); } static Map mapKeyAndFunction(Iterable l, Object f) { return mapKeyAndFunction(f, l); } static Map mapKeyAndFunction(Object f, Iterable l) { HashMap map = new HashMap(); if (l != null) for (Object o : l) map.put(o, callF(f, o)); return map; } static Map mapKeyAndFunction(Map map, IF2 f) { HashMap map2 = new HashMap(); if (map != null) for (Map.Entry __0 : _entrySet( map)) { A key = __0.getKey(); B value = __0.getValue(); map2.put(key, callF(f, key, value)); } return map2; } static Map mapKeyAndFunction(Iterable l, IF1 f) { return mapKeyAndFunction(f, l); } // TODO: if field is a Ref<>, you can thoretically use findBackRefs static A findConceptWhere(Class c, Object... params) { return findConceptWhere(db_mainConcepts(), c, params); } static A findConceptWhere(Concepts concepts, Class c, Object... params) { ping(); params = expandParams(c, params); // indexed if (concepts.fieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex index = concepts.getFieldIndex(c, (String) params[i]); if (index != null) { for (A x : index.getAll(params[i+1])) if (checkConceptFields(x, params)) return x; return null; } } // table scan for (A x : concepts.list(c)) if (checkConceptFields(x, params)) return x; return null; } static Concept findConceptWhere(Concepts concepts, String c, Object... params) { for (Concept x : concepts.list(c)) if (checkConceptFields(x, params)) return x; return null; } static List immutableEmptyList() { return Collections.emptyList(); } static short[] emptyShortArray = new short[0]; static short[] emptyShortArray() { return emptyShortArray; } static Map immutableEmptyMap() { return Collections.emptyMap(); } static A optParam(ThreadLocal tl, A defaultValue) { return optPar(tl, defaultValue); } static A optParam(ThreadLocal tl) { return optPar(tl); } static Object optParam(String name, Map params) { return mapGet(params, name); } // now also takes a map as single array entry static A optParam(Object[] opt, String name, A defaultValue) { int n = l(opt); if (n == 1 && opt[0] instanceof Map) { Map map = (Map) (opt[0]); return map.containsKey(name) ? (A) map.get(name) : defaultValue; } if (!even(l(opt))) throw fail("Odd parameter length"); for (int i = 0; i < l(opt); i += 2) if (eq(opt[i], name)) return (A) opt[i+1]; return defaultValue; } static Object optParam(Object[] opt, String name) { return optParam(opt, name, null); } static Object optParam(String name, Object[] params) { return optParam(params, name); } static A optPar(ThreadLocal tl, A defaultValue) { A a = tl.get(); if (a != null) { tl.set(null); return a; } return defaultValue; } static A optPar(ThreadLocal tl) { return optPar(tl, null); } static Object optPar(Object[] params, String name) { return optParam(params, name); } static Object optPar(String name, Object[] params) { return optParam(params, name); } static Object optPar(String name, Map params) { return optParam(name, params); } static A optPar(Object[] params, String name, A defaultValue) { return optParam(params, name, defaultValue); } static A optPar(String name, Object[] params, A defaultValue) { return optParam(params, name, defaultValue); } static void setHeaders(URLConnection con) throws IOException { String computerID = getComputerID_quick(); if (computerID != null) try { con.setRequestProperty("X-ComputerID", computerID); con.setRequestProperty("X-OS", System.getProperty("os.name") + " " + System.getProperty("os.version")); } catch (Throwable e) { //printShortException(e); } } static int lUtf8(String s) { return l(utf8(s)); } static String hideCredentials(URL url) { return url == null ? null : hideCredentials(str(url)); } static String hideCredentials(String url) { try { if (startsWithOneOf(url, "http://", "https://") && isAGIBlueDomain(hostNameFromURL(url))) return url; } catch (Throwable e) { print("HideCredentials", e); } return url.replaceAll("([&?])(_pass|key|cookie)=[^&\\s\"]*", "$1$2="); } static String hideCredentials(Object o) { return hideCredentials(str(o)); } static URLConnection setURLConnectionTimeouts(URLConnection con, long timeout) { con.setConnectTimeout(toInt(timeout)); con.setReadTimeout(toInt(timeout)); if (con.getConnectTimeout() != timeout || con.getReadTimeout() != timeout) print("Warning: Timeouts not set by JDK."); return con; } static String loadPage_utf8(URL url) { return loadPage_utf8(url.toString()); } static String loadPage_utf8(String url) { AutoCloseable __1 = tempSetTL(loadPage_charset, "UTF-8"); try { return loadPage(url); } finally { _close(__1); }} static String loadPage_utf8(URLConnection con, URL url, boolean addHeaders) throws IOException { AutoCloseable __2 = tempSetTL(loadPage_charset, "UTF-8"); try { return loadPage(con, url, addHeaders); } finally { _close(__2); }} static CloseableIterableIterator emptyCloseableIterableIterator_instance = new CloseableIterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static CloseableIterableIterator emptyCloseableIterableIterator() { return emptyCloseableIterableIterator_instance; } static boolean ewic(String a, String b) { return endsWithIgnoreCase(a, b); } static boolean ewic(String a, String b, Matches m) { return endsWithIgnoreCase(a, b, m); } static CloseableIterableIterator linesFromReader(Reader r) { return linesFromReader(r, null); } static CloseableIterableIterator linesFromReader(Reader r, IResourceHolder resourceHolder) { final BufferedReader br = bufferedReader(r); return holdResource(resourceHolder, iteratorFromFunction_f0_autoCloseable(new F0() { public String get() { try { return readLineFromReaderWithClose(br); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret readLineFromReaderWithClose(br);"; }}, _wrapIOCloseable(r))); } static BufferedReader utf8bufferedReader(InputStream in) { try { return in == null ? null : bufferedReader(_registerIOWrap(new InputStreamReader(in, "UTF-8"), in)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedReader utf8bufferedReader(File f) { try { return utf8bufferedReader(newFileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static GZIPInputStream newGZIPInputStream(File f) { return gzInputStream(f); } static GZIPInputStream newGZIPInputStream(InputStream in) { return gzInputStream(in); } static boolean containsNewLine(String s) { return contains(s, '\n'); // screw \r, nobody needs it } static Object safeUnstructure(String s) { return unstructure(s, true); } static Object safeUnstructure(File f) { return safeUnstructureGZFile(f); } static boolean endsWith(String a, String b) { return a != null && a.endsWith(b); } static boolean endsWith(String a, char c) { return nempty(a) && lastChar(a) == c; } static boolean endsWith(String a, String b, Matches m) { if (!endsWith(a, b)) return false; m.m = new String[] {dropLast(l(b), a)}; return true; } static Set asSet(Object[] array) { HashSet set = new HashSet(); for (Object o : array) if (o != null) set.add(o); return set; } static Set asSet(String[] array) { TreeSet set = new TreeSet(); for (String o : array) if (o != null) set.add(o); return set; } static Set asSet(Iterable l) { if (l instanceof Set) return (Set) l; HashSet set = new HashSet(); for (A o : unnull(l)) if (o != null) set.add(o); return set; } // Note: does not clone the set (keeps multiset alive) static Set asSet(MultiSet ms) { return ms == null ? null : ms.asSet(); } static Map filterKeys(Map map, Object f) { return filterMapByFunctionOnKey(map, f); } static Map filterKeys(Object f, Map map) { return filterMapByFunctionOnKey(f, map); } static Map filterKeys(IF1 f, Map map) { return filterMapByFunctionOnKey((Object) f, map); } static Map filterKeys(Map map, IF1 f) { return filterKeys(f, map); } static boolean contains(Collection c, Object o) { return c != null && c.contains(o); } static boolean contains(Iterable it, Object a) { if (it != null) for (Object o : it) if (eq(a, o)) return true; return false; } static boolean contains(Object[] x, Object o) { if (x != null) for (Object a : x) if (eq(a, o)) return true; return false; } static boolean contains(String s, char c) { return s != null && s.indexOf(c) >= 0; } static boolean contains(String s, String b) { return s != null && s.indexOf(b) >= 0; } static boolean contains(BitSet bs, int i) { return bs != null && bs.get(i); } static boolean contains(Producer p, A a) { if (p != null && a != null) while (true) { A x = p.next(); if (x == null) break; if (eq(x, a)) return true; } return false; } // usually L static String fromLines(Iterable lines) { StringBuilder buf = new StringBuilder(); if (lines != null) for (Object line : lines) buf.append(str(line)).append('\n'); return buf.toString(); } static String fromLines(String... lines) { return fromLines(asList(lines)); } static IterableIterator toLines(File f) { return linesFromFile(f); } static List toLines(String s) { List lines = new ArrayList(); if (s == null) return lines; int start = 0; while (true) { int i = toLines_nextLineBreak(s, start); if (i < 0) { if (s.length() > start) lines.add(s.substring(start)); break; } lines.add(s.substring(start, i)); if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n') i += 2; else ++i; start = i; } return lines; } static int toLines_nextLineBreak(String s, int start) { int n = s.length(); for (int i = start; i < n; i++) { char c = s.charAt(i); if (c == '\r' || c == '\n') return i; } return -1; } static Object subBot_serve500() { return call(getMainBot(), "serve500"); } static Object subBot_serve500(String msg) { return call(getMainBot(), "serve500", msg); } static Object subBot_serve500(Throwable error) { return subBot_serve500(renderStackTrace(error)); } static String lines_possiblyRTrim(String originalText, List lines) { return endsWithNewLine(originalText) ? lines(lines) : lines_rtrim(lines); } static String lines_possiblyRTrim(List lines, String originalText) { return lines_possiblyRTrim(originalText, lines); } static List sortedAlphanumIC(Collection c) { List l = cloneList(c); Collections.sort(l, alphaNumComparatorIC()); return l; } // Use like this: renderVars(+x, +y) static String renderVars_str(Object... params) { List l = new ArrayList(); int i = 0; if (odd(l(params))) { l.add(strOrNull(first(params))); ++i; } for (; i+1 < l(params); i += 2) l.add(params[i] + "=" + params[i+1]); return trim(joinWithComma(l)); } static SimpleDateFormat simpleDateFormat_local(String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(localTimeZone()); return sdf; } static String linesPreservingTrailingNewLine(String original, List lines) { String s = lines(lines); return endsWith(original, "\n") ? s : dropSuffix("\n", s); } static List unquoteAll(List l) { List t = cloneList(l); for (int i = 0; i < l(t); i++) if (isQuoted(t.get(i))) t.set(i, unquote(t.get(i))); return t; } static String javascriptQuote(String s) { return quote(s); // use regular Java quoting } static String formatLocalDateWithSeconds(long time) { return localDateWithSeconds(time); } static String formatLocalDateWithSeconds() { return localDateWithSeconds(); } static String hrefBlank(String link, Object contents, Object... params) { return empty(link) ? strOrEmpty(contents) : tag("a", contents, concatArrays(new Object[] { "href" , link, "target" , "_blank" }, params)); } static String href(String link, Object contents, Object... params) { if (link == null) return str(contents); return hfulltag("a", contents, arrayPlus(params, "href", link)); } static String pUnlessEmpty(String s, Object... __) { return empty(s) ? "" : p(s, __); } static List replace(List l, A a, A b) { for (int i = 0; i < l(l); i++) if (eq(l.get(i), a)) l.set(i, b); return l; } static List replace(A a, A b, List l) { return replace(l, a, b); } // replace all occurrences of a in s with b static String replace(String s, String a, String b) { return s == null ? null : a == null || b == null ? s : s.replace(a, b); } static String replace(String s, char a, char b) { return s == null ? null : s.replace(a, b); } static String unicode_newLineArrow() { return charToString(0x21b5); } static BigInteger bigint(String s) { return new BigInteger(s); } static BigInteger bigint(long l) { return BigInteger.valueOf(l); } static List reversedList(Iterable l) { List x = cloneList(l); Collections.reverse(x); return x; } static String reversedString(String s) { return reverseString(s); } static A highestByFunction(Iterable l, Object f) { A best = null; Object bestValue = null; if (l != null) for (A a : l) { Object val = callF(f, a); if (best == null || cmp(val, bestValue) > 0) { best = a; bestValue = val; } } return best; } static A highestByFunction(Object f, Iterable l) { return highestByFunction(l, f); } static A highestByFunction(IF1 f, Iterable l) { return highestByFunction(l, (Object) f); } static A highestByFunction(Iterable l, IF1 f) { return highestByFunction(f, l); } static String squareBracket(String s) { return "[" + s + "]"; } static boolean containsIgnoreCase(Collection l, String s) { if (l != null) for (String x : l) if (eqic(x, s)) return true; return false; } static boolean containsIgnoreCase(String[] l, String s) { if (l != null) for (String x : l) if (eqic(x, s)) return true; return false; } static boolean containsIgnoreCase(String s, char c) { return indexOfIgnoreCase(s, String.valueOf(c)) >= 0; } static boolean containsIgnoreCase(String a, String b) { return indexOfIgnoreCase(a, b) >= 0; } static String hbuttonOnClick(String text, String onClick, Object... params) { return hfulltag("button", text, paramsPlus(params, "onClick" , onClick)); } static Object[] params_stylePlus(Map params, String style) { return paramsPlus(params, "style" , joinNemptiesWithSemicolon(stringPar("style", params), style)); } static Object[] params_stylePlus(Object[] params, String style) { return paramsPlus(params, "style" , joinNemptiesWithSemicolon(stringPar("style", params), style)); } static Object[] params_stylePlus(String style, Object... params) { return params_stylePlus(params, style); } static String spanTitle(String title, Object contents) { return empty(title) ? str(contents) : span(contents, "title", title); } 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 Object getOptDynOnly(DynamicObject o, String field) { if (o == null || o.fieldValues == null) return null; return o.fieldValues.get(field); } static RuntimeException asRuntimeException(Throwable t) { if (t instanceof Error) _handleError((Error) t); return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } // f : Matcher -> S static String regexReplace(String s, String pat, Object f) { Matcher m = Pattern.compile(pat).matcher(s); return regexReplace(m, f); } static String regexReplace(String s, String pat, String replacement) { return regexpReplace_direct(s, pat, replacement); } static String regexReplace(Matcher m, Object f) { StringBuffer buf = new StringBuffer(); while (m.find()) m.appendReplacement(buf, m.quoteReplacement(str(callF(f, m)))); m.appendTail(buf); return str(buf); } static String regexReplace(String s, String pat, IF1 f) { return regexReplace(s, pat, (Object) f); } static String regexpCaseInsensitivePrefix() { return "(?i)"; } // f: A -> Comparable static List sortByCalculatedField(Iterable c, final Object f) { List l = cloneList(c); sort(l, new Comparator() { public int compare(A a, A b) { return stdcompare(callF(f, a), callF(f, b)); } }); return l; } static int stdcompare(Number a, Number b) { return cmp(a, b); } static int stdcompare(String a, String b) { return cmp(a, b); } static int stdcompare(long a, long b) { return a < b ? -1 : a > b ? 1 : 0; } static int stdcompare(Object a, Object b) { return cmp(a, b); } static List keysList(Map map) { return cloneListSynchronizingOn(keys(map), map); } static List keysList(MultiSet ms) { return ms == null ? null : keysList(ms.map); } static List htmlTok(String s) { return htmlcoarsetok(s); } // tok must come from htmlTok // returns all container tags found (including content) as CNC // should be OK for both HTML and XML static List> findContainerTag(List tok, String tag) { List> l = new ArrayList(); for (int i = 1; i < l(tok); i += 2) if (isOpeningTag(tok.get(i), tag)) { int j, level = 1; for (j = i+2; j < tok.size(); j += 2) if (isOpeningTag(tok.get(j), tag)) ++level; else if (isTag(tok.get(j), "/" + tag)) { --level; if (level == 0) { l.add(subList(tok, i-1, j+2)); // actual CNC break; } } i = j; } return l; } static List> findContainerTag(String html, String tag) { return findContainerTag(htmlTok(html), tag); } static String addLineBreak(String s) { return addSuffix(s, "\n"); } static String hreconnectingWebSockets() { return hjavascript_src_snippet(hreconnectingWebSockets_snippetID()); } static String jsDropTrailingComments(String s) { return javaTokDropTrailingN(s); } static String divUnlessEmpty(String s, Object... params) { return empty(s) ? "" : div(s, params); } static String assertPossibleMD5(String s) { if (!possibleMD5(s)) throw fail("Not an MD5 signature: " + s); return 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 md5OfFile_verbose = false; static String md5OfFile(String path) { return md5OfFile(newFile(path)); } static String md5OfFile(File f) { try { if (!f.exists()) return "-"; if (md5OfFile_verbose) print("Getting MD5 of " + f); MessageDigest md5 = MessageDigest.getInstance("MD5"); FileInputStream in = new FileInputStream(f); try { byte buf[] = new byte[65536]; int l; while (true) { l = in.read(buf); if (l <= 0) break; md5.update(buf, 0, l); } return bytesToHex(md5.digest()); } finally { _close(in); }} catch (Exception __e) { throw rethrow(__e); } } static List itemPlusList(A a, Collection l) { return concatLists(ll(a), l); } static Map applyFunctionToMapValue(A key, Object f, Map map) { if (map.containsKey(key)) map.put(key, (B) callF(f, map.get(key))); return map; } static Object cloneIfList(Object o) { return o instanceof List ? cloneList((List) o) : o; } // MetaTransformer that understands Transformable and List static MetaTransformer metaTransformer_transformableAndList() { return new MetaTransformer(new MetaTransformer.StructureHandler() { public Object transform(Object o, IF1 recurse) { if (o instanceof Transformable) return ((Transformable) o).transformUsing(recurse); if (o instanceof List) return map_ping(recurse, ((List) o)); return null; } public void visit(Object o, IVF1 recurse) { if (o instanceof Visitable) ((Visitable) o).visitUsing(recurse); else if (o instanceof List) for (Object x : ((List) o)) { ping(); recurse.get(x); } } }); } static A printStructure(String prefix, A o) { if (endsWithLetter(prefix)) prefix += ": "; print(prefix + structureForUser(o)); return o; } static A printStructure(A o) { print(structureForUser(o)); return o; } static void logQuoted(String logFile, String line) { logQuoted(getProgramFile(logFile), line); } static void logQuoted(File logFile, String line) { appendToFile(logFile, quote(line) + "\n"); } static File getProgramFile(String progID, String fileName) { if (new File(fileName).isAbsolute()) return new File(fileName); return new File(getProgramDir(progID), fileName); } static File getProgramFile(String fileName) { return getProgramFile(getProgramID(), fileName); } static String jreplace(String s, String in, String out) { return jreplace(s, in, out, null); } static String jreplace(String s, String in, String out, Object condition) { List tok = javaTok(s); return jreplace(tok, in, out, condition) ? join(tok) : s; } // leaves tok properly tokenized // returns true iff anything was replaced static boolean jreplace(List tok, String in, String out) { return jreplace(tok, in, out, false, true, null); } static boolean jreplace(List tok, String in, String out, Object condition) { return jreplace(tok, in, out, false, true, condition); } static boolean jreplace(List tok, String in, String out, IF2, Integer, Boolean> condition) { return jreplace(tok, in, out, (Object) condition); } static boolean jreplace(List tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) { String[] toks = javaTokForJFind_array(in); int lTokin = toks.length*2+1; boolean anyChange = false; int i = -1; for (int n = 0; n < 10000; n++) { // TODO: don't need this check anymore i = findCodeTokens(tok, i+1, ignoreCase, toks, condition); if (i < 0) return anyChange; List subList = tok.subList(i-1, i+lTokin-1); // N to N String expansion = jreplaceExpandRefs(out, subList); int end = i+lTokin-2; clearAllTokens(tok, i, end); // C to C tok.set(i, expansion); if (reTok) // would this ever be false?? reTok(tok, i, end); i = end; anyChange = true; } throw fail("woot? 10000! " + quote(in) + " => " + quote(out)); } static boolean jreplace_debug = false; static Map paramsToMap(Object... params) { int n = l(params); if (l(params) == 1 && params[0] instanceof Map) return (Map) params[0]; LinkedHashMap map = new LinkedHashMap(); for (int i = 0; i+1 < n; i += 2) mapPut(map, params[i], params[i+1]); return map; } // returns from C to C static String jextract(String pat, String s) { return jextract(pat, javaTok(s)); } static String jextract(String pat, List tok) { List tokpat = javaTok(pat); jfind_preprocess(tokpat); int i = jfind(tok, tokpat); if (i < 0) return null; int j = i + l(tokpat) - 2; return joinSubList(tok, i, j); } static String stringPar(Object[] params, String name) { return stringOptPar(params, name); } static String stringPar(String name, Object[] params) { return stringOptPar(params, name); } static String stringPar(String name, Map params) { return (String) optPar(name, params); } static String stringPar(String name, Object[] params, String defaultValue) { return optPar(name, params, defaultValue); } static Object[] paramsMinus(Object[] a1, Object... keys) { return paramsWithout(a1, keys); } static String joinNemptiesWithSemicolon(String... strings) { return joinNempties("; ", strings); } static String joinNemptiesWithSemicolon(Collection strings) { return joinNempties("; ", strings); } static String joinNemptiesWithSpace(String... strings) { return joinNempties(" ", strings); } static String joinNemptiesWithSpace(Collection strings) { return joinNempties(" ", strings); } static String hsnippetimg(String imageID, Object... params) { return himg(snippetImageLink(imageID), params); } static int calcHeight(int w, int h, int newWidth) { return iround(doubleRatio(h, w)*newWidth); } static String hjavascript_src(String src, Object... __) { return hfulltag("script", "", paramsPlus(__, "src", src)); } static String renderHowLongAgo(Timestamp ts) { return renderHowLongAgo(timestampToLong(ts)); } static String renderHowLongAgo(long timestamp) { if (timestamp == 0) return "never"; int seconds = howManySecondsAgo(timestamp); if (seconds <= 0) return "just now"; if (seconds < 60) return n2(seconds, "second") + " ago"; int minutes = iround(seconds/60.0); if (minutes < 60) return n2(minutes, "minute") + " ago"; int hours = iround(minutes/60.0); if (hours < 24) return n2(hours, "hour") + " ago"; int days = iround(hours/24.0); return n2(days, "day") + " ago"; } // returns l(s) if not found static int smartIndexOf(String s, String sub, int i) { if (s == null) return 0; i = s.indexOf(sub, min(i, l(s))); return i >= 0 ? i : l(s); } static int smartIndexOf(String s, int i, char c) { return smartIndexOf(s, c, i); } static int smartIndexOf(String s, char c, int i) { if (s == null) return 0; i = s.indexOf(c, min(i, l(s))); return i >= 0 ? i : l(s); } static int smartIndexOf(String s, String sub) { return smartIndexOf(s, sub, 0); } static int smartIndexOf(String s, char c) { return smartIndexOf(s, c, 0); } static int smartIndexOf(List l, A sub) { return smartIndexOf(l, sub, 0); } static int smartIndexOf(List l, int start, A sub) { return smartIndexOf(l, sub, start); } static int smartIndexOf(List l, A sub, int start) { int i = indexOf(l, sub, start); return i < 0 ? l(l) : i; } static boolean boolPar(ThreadLocal tl) { return boolOptParam(tl); } // defaults to false static boolean boolPar(Object[] __, String name) { return boolOptParam(__, name); } static boolean boolPar(String name, Object[] __) { return boolOptParam(__, name); } static boolean boolPar(String name, Map __) { return boolOptParam(name, __); } static boolean boolPar(String name, Object[] params, boolean defaultValue) { return optParam(params, name, defaultValue); } static List llNonNulls(A... a) { List l = new ArrayList(); for (A x : a) if (x != null) l.add(x); return l; } static String[] flattenStringArray2(Object... a) { List l = new ArrayList(); if (a != null) for (Object x : a) if (x instanceof String[]) l.addAll(asList((String[]) x)); else if (x instanceof Collection) l.addAll((Collection) x); else l.add(x); return asStringArray(l); } // entries are HTML (e.g. links) // requires JQuery for click-outside-to-close trick // TODO: doesn't open on first click (does it still?) // Note: Make sure containing elements have overflow: visible, // otherwise pop-up can get cut off static String hPopDownButton(String... entries) { String id = "dropdown-" + aGlobalID(), outerID = "outer-" + id; String onclick = replaceDollarVars("\r\n var x = document.getElementById($id);\r\n x.style.display = window.getComputedStyle(x).display === \"none\" ? \"block\" : \"none\";\r\n console.log(\"popup \" + id + \" display: \" + x.style.display);\r\n ", "$id" , jsQuote(id)); return hstyle(replaceDollarVars("\r\n #$id ul {\r\n list-style-type: none;\r\n margin: 0;\r\n padding: 0;\r\n width: 200px;\r\n background-color: #f1f1f1;\r\n }\r\n \r\n #$id {\r\n position: absolute;\r\n display:none;\r\n background-color: #f9f9f9;\r\n width:auto;\r\n height:200px;\r\n overflow: auto;\r\n \r\n min-width: 160px;\r\n box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);\r\n \r\n z-index: 99;\r\n }\r\n \r\n #$id li a {\r\n text-decoration: none;\r\n color: #000;\r\n }\r\n\r\n #$id li { \r\n display: block;\r\n border-bottom: 1px solid #555;\r\n padding: 8px 16px;\r\n color: #000;\r\n }\r\n " /* #$id li:hover, #$id li:hover a { background-color: #3399ff; color: white; } /* exclude sub menus - works but submenu loses hover highlights *//* #$id li:hover ul li, #$id li:hover ul li a { background-color: #f9f9f9; color: #000; } */ // version 2 of the trick + "\r\n #$id li:hover, #$id li:hover > a {\r\n background-color: #3399ff;\r\n color: white;\r\n }\r\n ", "$id" , id)) + hdiv( span(htmlEncode2(unicode_smallDownPointingTriangle()), "onclick", onclick) + hdiv(ul(entries), "id", id), "style" , "position: relative; display: inline-block; cursor: context-menu", /*+onclick,*/ "id" , outerID) + hscript(replaceDollarVars("\r\n if (typeof $ !== 'undefined')\r\n $('body').click(function(e) {\r\n //console.log(\"target=\" + e.target + \", outerID=$outerID\");\r\n if (!$(e.target).closest('#$outerID').length) {\r\n //console.log(\"hiding $id\");\r\n $(\"#$id\").hide();\r\n }\r\n });\r\n ", "id", id, "outerID", outerID)); } static String hPopDownButton(Collection entries) { return hPopDownButton(toStringArray(entries)); } static Map mapPlus(Map m, Object... data) { m = cloneMap(m); litmap_impl(m, data); return m; } static Object[] assertEvenLength(Object[] a) { assertTrue(even(l(a))); return a; } static LinkedHashMap paramsToOrderedMap(Object... params) { return asLinkedHashMap(paramsToMap(params)); } static Object[] mapToParams(Map map) { return mapToObjectArray(map); } static void addAll(Collection c, Iterable b) { if (c != null && b != null) for (A a : b) c.add(a); } static boolean addAll(Collection c, Collection b) { return c != null && b != null && c.addAll(b); } static boolean addAll(Collection c, B... b) { return c != null && b != null && c.addAll(Arrays.asList(b)); } static Map addAll(Map a, Map b) { if (a != null && b != null) a.putAll(b); return a; } static Object derefRef(Object o) { if (o instanceof Concept.Ref) o = ((Concept.Ref) o).get(); return o; } static A derefRef(Concept.Ref r) { return r == null ? null : r.get(); } static File prepareProgramFile(String name) { return mkdirsForFile(getProgramFile(name)); } static File prepareProgramFile(String progID, String name) { return mkdirsForFile(getProgramFile(progID, name)); } static List> mapToPairs(Map map) { List> l = emptyList(l(map)); if (map != null) for (Map.Entry e : map.entrySet()) l.add(pair(e.getKey(), e.getValue())); return l; } static Matcher regexpIC(Pattern pat, String s) { return pat.matcher(unnull(s)); } static Matcher regexpIC(String pat, String s) { return compileRegexpIC(pat).matcher(unnull(s)); } static Pattern regexpIC(String pat) { return compileRegexpIC(pat); } static Object safeUnstructureAllowingClasses(String s, Class... allowedClasses) { final Set allowedClassesSet = mapToSet(__61 -> className(__61), allowedClasses); Object classFinder = new F1() { public Object get(String name) { try { if (!allowedClassesSet.contains(name)) throw fail("Class not allowed to unstructure: " + name); return classForName(name); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!allowedClassesSet.contains(name)) fail(\"Class not allowed to unstructure..."; }}; return unstructure(s, false, classFinder); } static List sortedAlphaNum(Collection c) { return sorted(c, new AlphanumComparator()); } static List uniquify(Collection l) { return uniquifyList(l); } static String upper(String s) { return s == null ? null : s.toUpperCase(); } static char upper(char c) { return Character.toUpperCase(c); } static B pairB(Pair p) { return p == null ? null : p.b; } static A pairA(Pair p) { return p == null ? null : p.a; } static Map mapKeys(Object func, Map map) { Map m = similarEmptyMap(map); // TODO: this might break when key type changes through func for (Object key : keys(map)) m.put(callF(func, key), map.get(key)); return m; } static Map mapKeys(Map map, Object func) { return mapKeys(func, map); } static Map mapKeys(Map map, IF1 func) { return mapKeys(map, (Object) func); } static Map mapKeys(IF1 func, Map map) { return mapKeys(map, func); } static String dropDollarPrefix(String s) { return dropPrefix("$", s); } static TreeMap litcimap(Object... x) { return litCIMap(x); } // f takes variable without $ sign. if it returns null, variable is kept static String replaceDollarVars_dyn(String s, IF1 f) { if (f == null) return s; return regexpReplaceIC(s, "\\$(\\w+)", matcher -> { String var = matcher.group(1); String val = f.get(var); return val == null ? matcher.group() : str(val); }); } static String strOrNull(Object o) { return o == null ? null : str(o); } static String formatWithThousands(long l) { return formatWithThousandsSeparator(l); } static double fraction(double d) { return d % 1; } static String n_fancy2(long l, String singular, String plural) { return formatWithThousandsSeparator(l) + " " + trim(l == 1 ? singular : plural); } static String n_fancy2(Collection l, String singular, String plural) { return n_fancy2(l(l), singular, plural); } static String n_fancy2(Map m, String singular, String plural) { return n_fancy2(l(m), singular, plural); } static String n_fancy2(Object[] a, String singular, String plural) { return n_fancy2(l(a), singular, plural); } static String n_fancy2(MultiSet ms, String singular, String plural) { return n_fancy2(l(ms), singular, plural); } static String formatDoubleFull(double d, int digits) { String format = digits <= 0 ? "0" : "0." + rep(digits, '0'); return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)).format(d); } static long toM(long l) { return (l+1024*1024-1)/(1024*1024); } static String toM(long l, int digits) { return formatDouble(toM_double(l), digits); } static MultiSet asCIMultiSet(Iterable l) { MultiSet ms = new MultiSet(); ms.map = ciMap(); ms.addAll(l); return ms; } static Map castMapToMapO(Map map) { return map; } static boolean fileNotEmpty(File f) { return isFile(f) && fileSize(f) > 0; } static List splitAtAmpersand(String s) { return s == null ? emptyList() : asList(s.split("&")); } static String urldecode(String x) { try { return URLDecoder.decode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } static boolean networkAllowanceTest(String url) { return isAllowed("networkAllowanceTest", url); } static String tb_mainServer_default = "https://code.botcompany.de:9898"; static Object tb_mainServer_override; // func -> S static String tb_mainServer() { if (tb_mainServer_override != null) return (String) callF(tb_mainServer_override); return trim(loadTextFile(tb_mainServer_file(), tb_mainServer_default)); } static File tb_mainServer_file() { return getProgramFile("#1001638", "mainserver.txt"); } static boolean tb_mainServer_isDefault() { return eq(tb_mainServer(), tb_mainServer_default); } static A printWithTime(A a) { return printWithTime("", a); } static A printWithTime(String s, A a) { print(hmsWithColons() + ": " + s, a); return a; } static A getAndClearThreadLocal(ThreadLocal tl) { A a = tl.get(); tl.set(null); return a; } static Map vm_generalSubMap(Object name) { synchronized(get(javax(), "generalMap")) { Map map = (Map) (vm_generalMap_get(name)); if (map == null) vm_generalMap_put(name, map = synchroMap()); return map; } } static InputStream urlConnection_getInputStream(URLConnection con) throws IOException { UnknownHostException lastException = null; for (int _repeat_0 = 0; _repeat_0 < 2; _repeat_0++) { try { if (con instanceof HttpURLConnection) if (((HttpURLConnection) con).getResponseCode() == 500) throw new IOException(joinNemptiesWithColonSpace("Server code 500", tryToReadErrorStreamFromURLConnection((HttpURLConnection) con))); return con.getInputStream(); } catch (UnknownHostException e) { lastException = e; print("Retrying because of: " + e); continue; } } throw lastException; } static String toHex(byte[] bytes) { return bytesToHex(bytes); } static String toHex(byte[] bytes, int ofs, int len) { return bytesToHex(bytes, ofs, len); } static byte[] utf8(String s) { return toUtf8(s); } static Matcher regexpMatcher(String pat, String s) { return compileRegexp(pat).matcher(unnull(s)); } static URLConnection openConnection(String url) { try { return openConnection(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } static URLConnection openConnection(URL url) { try { ping(); callOpt(javax(), "recordOpenURLConnection", str(url)); return url.openConnection(); } catch (Exception __e) { throw rethrow(__e); } } 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); if (o instanceof Boolean) return boolToInt((Boolean) 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; } static URLConnection setURLConnectionDefaultTimeouts(URLConnection con, long timeout) { if (con.getConnectTimeout() == 0) { con.setConnectTimeout(toInt(timeout)); if (con.getConnectTimeout() != timeout) print("Warning: URL connect timeout not set by JDK."); } if (con.getReadTimeout() == 0) { con.setReadTimeout(toInt(timeout)); if (con.getReadTimeout() != timeout) print("Warning: URL read timeout not set by JDK."); } return con; } static Matcher regexp(String pat, String s) { return regexp(compileRegexp(pat), unnull(s)); } static Matcher regexp(java.util.regex.Pattern pat, String s) { return pat.matcher(unnull(s)); } static java.util.regex.Pattern regexp(String pat) { return compileRegexp(pat); } static Map parseColonProperties(String text) { return parseColonProperties(text, new LinkedHashMap()); } static Map parseColonProperties(String text, Map map) { for (String s : tlft(text)) { int i = indexOf(s, ':'); if (i > 0) map.put(trimSubstring(s, 0, i), trimSubstring(s, i+1)); } return map; } static TreeMap ciMap() { return caseInsensitiveMap(); } static double toDouble(Object o) { if (o instanceof Number) return ((Number) o).doubleValue(); if (o instanceof BigInteger) return ((BigInteger) o).doubleValue(); if (o instanceof String) return parseDouble((String) o); if (o == null) return 0.0; throw fail(o); } static File loadBinarySnippet(String snippetID) { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadLibrary(snippetID); return loadBinarySnippet_noResourceLoader(snippetID); } static File loadBinarySnippet_noResourceLoader(String snippetID) { try { long id = parseSnippetID(snippetID); if (isImageServerSnippet(id)) return loadImageAsFile(snippetID); File f = DiskSnippetCache_getLibrary(id); if (fileSize(f) == 0) f = loadDataSnippetToFile_noResourceLoader(snippetID); return f; } catch (Exception __e) { throw rethrow(__e); } } static String gazelle_server() { return "https://gazelle.rocks/"; } static List similarEmptyList(Collection m) { return new ArrayList(); } static A printHidingCredentials(A o) { print(hideCredentials(str(o))); return o; } static void logQuotedWithDate(String s) { logQuotedWithTime(s); } static void logQuotedWithDate(String logFile, String s) { logQuotedWithTime(logFile, s); } static void logQuotedWithDate(File logFile, String s) { logQuotedWithTime(logFile, s); } static File infoBoxesLogFile() { return new File(javaxDataDir(), "Logs/infoBoxes.txt"); } static Boolean isHeadless_cache; static boolean isHeadless() { if (isHeadless_cache != null) return isHeadless_cache; if (isAndroid()) return isHeadless_cache = true; if (GraphicsEnvironment.isHeadless()) return isHeadless_cache = true; // Also check if AWT actually works. // If DISPLAY variable is set but no X server up, this will notice. try { SwingUtilities.isEventDispatchThread(); return isHeadless_cache = false; } catch (Throwable e) { return isHeadless_cache = true; } } static void swingAndWait(Runnable r) { try { if (isAWTThread()) r.run(); else EventQueue.invokeAndWait(addThreadInfoToRunnable(r)); } catch (Exception __e) { throw rethrow(__e); } } static Object swingAndWait(final Object f) { if (isAWTThread()) return callF(f); else { final Var result = new Var(); swingAndWait(new Runnable() { public void run() { try { result.set(callF(f)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "result.set(callF(f));"; }}); return result.get(); } } static JWindow showWindow(Component c) { JWindow w = new JWindow(); w.add(wrap(c)); return w; } static JPanel infoMessage_makePanel(String text) { final JTextArea ta = wrappedTextArea(text); onClick(ta, new Runnable() { public void run() { try { disposeWindow(ta) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "disposeWindow(ta)"; }}); int size = 14; if (l(text) <= 50) size *= 2; else if (l(text) < 100) size = iround(size*1.5); ta.setFont(typeWriterFont(size)); JScrollPane sp = jscroll(ta); return withMargin(sp); } static int moveToTopRightCorner_inset = 20; static A moveToTopRightCorner(A a) { return moveToTopRightCorner(moveToTopRightCorner_inset, moveToTopRightCorner_inset, a); } static A moveToTopRightCorner(int insetX, int insetY, A a) { Window w = getWindow(a); if (w != null) w.setLocation(getScreenSize().width-w.getWidth()-insetX, insetY); return a; } static boolean vmBus_noObjections(String msg, Object... args) { return !vmBus_anyFalse(msg, args); } static A disposeWindowAfter(int delay, final A w) { if (w != null) swingLater(delay, new Runnable() { public void run() { try { w.dispose(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "w.dispose();"; }}); return w; } static A disposeWindowAfter(A w, double seconds) { return disposeWindowAfter(toMS_int(seconds), w); } static A disposeWindowAfter(double seconds, A w) { return disposeWindowAfter(w, seconds); } static String defaultThreadName_name; static String defaultThreadName() { if (defaultThreadName_name == null) defaultThreadName_name = "A thread by " + programID(); return defaultThreadName_name; } static Runnable wrapAsActivity(Object r) { if (r == null) return null; Runnable r2 = toRunnable(r); Object mod = dm_current_generic(); if (mod == null) return r2; return new Runnable() { public void run() { try { AutoCloseable c = (AutoCloseable) (rcall("enter", mod)); AutoCloseable __1 = c; try { r2.run(); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "AutoCloseable c = (AutoCloseable) (rcall enter(mod));\r\n temp c;\r\n r2.r..."; }}; } // runnable = Runnable or String (method name) static Thread newThread(Object runnable) { return new BetterThread(_topLevelErrorHandling(toRunnable(runnable))); } static Thread newThread(Object runnable, String name) { if (name == null) name = defaultThreadName(); return new BetterThread(_topLevelErrorHandling(toRunnable(runnable)), name); } static Thread newThread(String name, Object runnable) { return newThread(runnable, name); } static Runnable toRunnable(final Object o) { if (o instanceof Runnable) return (Runnable) o; return new Runnable() { public void run() { try { callF(o) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(o)"; }}; } static Map _registerThread_threads; static Object _onRegisterThread; // voidfunc(Thread) static Thread _registerThread(Thread t) { if (_registerThread_threads == null) _registerThread_threads = newWeakHashMap(); _registerThread_threads.put(t, true); vm_generalWeakSubMap("thread2mc").put(t, weakRef(mc())); callF(_onRegisterThread, t); return t; } static void _registerThread() { _registerThread(Thread.currentThread()); } static boolean confirmOKCancel(final Component owner, final String msg) { return isTrue(swingAndWait(new F0() { public Object get() { try { return JOptionPane.showConfirmDialog(owner, msg, "JavaX", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret JOptionPane.showConfirmDialog(owner,\r\n msg, \"JavaX\", JOptionPane.OK_..."; }})); } static boolean confirmOKCancel(String msg) { return confirmOKCancel(null, msg); } // one array plus more elements static Object[] arrayPlus(Object[] a1, Object... a2) { return concatArrays(a1, a2); } //static final Map> getOpt_cache = newDangerousWeakHashMap(f getOpt_special_init); static class getOpt_Map extends WeakHashMap { getOpt_Map() { if (getOpt_special == null) getOpt_special = new HashMap(); clear(); } public void clear() { super.clear(); //print("getOpt clear"); put(Class.class, getOpt_special); put(String.class, getOpt_special); } } static final Map> getOpt_cache = _registerDangerousWeakMap(synchroMap(new getOpt_Map())); //static final Map> getOpt_cache = _registerWeakMap(synchroMap(new getOpt_Map)); static HashMap getOpt_special; // just a marker /*static void getOpt_special_init(Map map) { map.put(Class.class, getOpt_special); map.put(S.class, getOpt_special); }*/ static Map getOpt_getFieldMap(Object o) { Class c = _getClass(o); HashMap map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); return map; } static Object getOpt_cached(Object o, String field) { try { if (o == null) return null; Map map = getOpt_getFieldMap(o); if (map == getOpt_special) { if (o instanceof Class) return getOpt((Class) o, field); /*if (o instanceof S) ret getOpt(getBot((S) o), field);*/ if (o instanceof Map) return ((Map) o).get(field); } Field f = map.get(field); if (f != null) return f.get(o); if (o instanceof DynamicObject) return syncMapGet2(((DynamicObject) o).fieldValues, field); return null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap getOpt_makeCache(Class c) { HashMap map; if (isSubtypeOf(c, Map.class)) map = getOpt_special; else { map = new HashMap(); if (!reflection_classesNotToScan().contains(c.getName())) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) { makeAccessible(f); String name = f.getName(); if (!map.containsKey(name)) map.put(name, f); } _c = _c.getSuperclass(); } while (_c != null); } } if (getOpt_cache != null) getOpt_cache.put(c, map); return map; } static ThreadLocal print_byThread() { synchronized(print_byThread_lock) { if (print_byThread == null) print_byThread = new ThreadLocal(); } return print_byThread; } // f can return false to suppress regular printing // call print_raw within f to actually print something static AutoCloseable tempInterceptPrint(F1 f) { return tempSetThreadLocal(print_byThread(), f); } static File getSecretProgramDir() { return getSecretProgramDir(actualProgramID()); } static File getSecretProgramDir(String snippetID) { if (empty(snippetID)) return javaxSecretDir(); return newFile(javaxSecretDir(), formatSnippetID(snippetID)); } static String actualUserHome_value; static String actualUserHome() { if (actualUserHome_value == null) { if (isAndroid()) actualUserHome_value = "/storage/emulated/0/"; else actualUserHome_value = System.getProperty("user.home"); } return actualUserHome_value; } static File actualUserHome(String sub) { return newFile(new File(actualUserHome()), sub); } static File userDir() { return new File(userHome()); } static File userDir(String path) { return new File(userHome(), path); } static List beginCriticalAction_inFlight = synchroList(); static class CriticalAction { String description; CriticalAction() {} CriticalAction(String description) { this.description = description;} void done() { beginCriticalAction_inFlight.remove(this); } } static CriticalAction beginCriticalAction(String description) { ping(); CriticalAction c = new CriticalAction(description); beginCriticalAction_inFlight.add(c); return c; } static void cleanMeUp_beginCriticalAction() { int n = 0; while (nempty(beginCriticalAction_inFlight)) { int m = l(beginCriticalAction_inFlight); if (m != n) { n = m; try { print("Waiting for " + n2(n, "critical actions") + ": " + join(", ", collect(beginCriticalAction_inFlight, "description"))); } catch (Throwable __e) { _handleException(__e); } } sleepInCleanUp(10); } } public static File mkdirsForFile(File file) { File dir = file.getParentFile(); if (dir != null) { // is null if file is in current dir dir.mkdirs(); if (!dir.isDirectory()) if (dir.isFile()) throw fail("Please delete the file " + f2s(dir) + " - it is supposed to be a directory!"); else throw fail("Unknown IO exception during mkdirs of " + f2s(file)); } return file; } public static String mkdirsForFile(String path) { mkdirsForFile(new File(path)); return path; } static File copyFile(File src, File dest) { try { FileInputStream inputStream = new FileInputStream(src.getPath()); FileOutputStream outputStream = newFileOutputStream(dest.getPath()); try { copyStream(inputStream, outputStream); inputStream.close(); } finally { outputStream.close(); } return dest; } catch (Exception __e) { throw rethrow(__e); } } static FileOutputStream newFileOutputStream(File path) throws IOException { return newFileOutputStream(path.getPath()); } static FileOutputStream newFileOutputStream(String path) throws IOException { return newFileOutputStream(path, false); } static FileOutputStream newFileOutputStream(File path, boolean append) throws IOException { return newFileOutputStream(path.getPath(), append); } static FileOutputStream newFileOutputStream(String path, boolean append) throws IOException { mkdirsForFile(path); FileOutputStream f = new FileOutputStream(path, append); _registerIO(f, path, true); return f; } static int randomID_defaultLength = 12; static String randomID(int length) { return makeRandomID(length); } static String randomID(Random r, int length) { return makeRandomID(r, length); } static String randomID() { return randomID(randomID_defaultLength); } static String randomID(Random r) { return randomID(r, randomID_defaultLength); } static int globalIDLength() { return 16; } static boolean odd(int i) { return (i & 1) != 0; } static boolean odd(long i) { return (i & 1) != 0; } static boolean odd(BigInteger i) { return odd(toInt(i)); } static String singleFieldName(Class c) { Set l = listFields(c); if (l(l) != 1) throw fail("No single field found in " + c + " (have " + n(l(l), "fields") + ")"); return first(l); } static Object deref(Object o) { if (o instanceof IRef) return ((IRef) o).get(); return o; } static String intern(String s) { return fastIntern(s); } static String assertIdentifier(String s) { return assertIsIdentifier(s); } static String assertIdentifier(String msg, String s) { return assertIsIdentifier(msg, s); } static void dynamicObject_setRawFieldValue(DynamicObject o, Object key, Object value) { if (o == null) return; // double sync, but should be OK here because of locking order o > o.fieldValues synchronized(o) { o.fieldValues = syncMapPut2_createLinkedHashMap((LinkedHashMap) o.fieldValues, key, value); } } static boolean isConceptList(Object o) { if (!(o instanceof List)) return false; List l = (List) o; for (Object x : l) if (!(x instanceof Concept)) return false; return true; } static void dynamicObject_dropRawField(DynamicObject o, Object key) { if (o == null) return; // double sync, but should be OK here because of locking order o > o.fieldValues synchronized(o) { // can drop the inner synchronization when we migrated all users // of fieldValues to synchronizing on the object too o.fieldValues = (LinkedHashMap) syncMapRemove_deleteMapIfEmpty((Map) o.fieldValues, key); } } static boolean isPersistable(Object o) { return !isInAnonymousClass(o); } static boolean isSubtypeOf(Class a, Class b) { return b.isAssignableFrom(a); // << always hated that method, let's replace it! } static boolean isTransient(Field f) { return (f.getModifiers() & java.lang.reflect.Modifier.TRANSIENT) != 0; } static boolean isJavaIdentifier(String s) { if (empty(s) || !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 Map classForName_cache = synchroHashMap(); static Class classForName(String name) { return classForName(name, null); } static Class classForName(String name, Object classFinder) { // first clause is when we're in class init if (classForName_cache == null || classFinder != null) return classForName_uncached(name, classFinder); Class c = classForName_cache.get(name); if (c == null) classForName_cache.put(name, c = classForName_uncached(name, null)); return c; } static Class classForName_uncached(String name, Object classFinder) { try { if (classFinder != null) return (Class) callF(classFinder, name); return Class.forName(name); } catch (Exception __e) { throw rethrow(__e); } } static Map nuObjectWithoutArguments_cache = newDangerousWeakHashMap(); static Object nuObjectWithoutArguments(String className) { try { return nuObjectWithoutArguments(classForName(className)); } catch (Exception __e) { throw rethrow(__e); } } static A nuObjectWithoutArguments(Class c) { try { if (nuObjectWithoutArguments_cache == null) // in class init return (A) nuObjectWithoutArguments_findConstructor(c).newInstance(); Constructor m = nuObjectWithoutArguments_cache.get(c); if (m == null) nuObjectWithoutArguments_cache.put(c, m = nuObjectWithoutArguments_findConstructor(c)); return (A) m.newInstance(); } catch (Exception __e) { throw rethrow(__e); } } static Constructor nuObjectWithoutArguments_findConstructor(Class c) { for (Constructor m : c.getDeclaredConstructors()) if (empty(m.getParameterTypes())) { makeAccessible(m); return m; } throw fail("No default constructor found in " + c.getName()); } static List getClasses(Object[] array) { List l = emptyList(l(array)); for (Object o : array) l.add(_getClass(o)); return l; } 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 List javaTokForStructure(String s) { return javaTok_noMLS(s); } static int clampToInt(long l) { return (int) clamp(l, Integer.MIN_VALUE, Integer.MAX_VALUE); } static boolean isOpeningBracket(String s) { return eqOneOf(s, "(", "{", "["); } static boolean containsClosingBracket(List l) { return any(__62 -> isClosingBracket(__62), l); } static List subList(List l, int startIndex) { return subList(l, startIndex, l(l)); } static List subList(int startIndex, List l) { return subList(l, startIndex); } static List subList(int startIndex, int endIndex, List l) { return subList(l, startIndex, endIndex); } static List subList(List l, int startIndex, int endIndex) { if (l == null) return null; int n = l(l); startIndex = Math.max(0, startIndex); endIndex = Math.min(n, endIndex); if (startIndex > endIndex) return ll(); if (startIndex == 0 && endIndex == n) return l; return l.subList(startIndex, endIndex); } static List subList(List l, IntRange r) { return subList(l, r.start, r.end); } static String joinSubList(List l, int i, int j) { return join(subList(l, i, j)); } static String joinSubList(List l, int i) { return join(subList(l, i)); } static String joinSubList(List l, IntRange r) { return r == null ? null : joinSubList(l, r.start, r.end); } static String spaces(int n) { return rep(' ', n); } static boolean isClosingBracket(String s) { return eqOneOf(s, ")", "}", "]"); } static Collection findConceptsWhere(Class c, Object... params) { return findConceptsWhere(db_mainConcepts(), c, params); } static Collection findConceptsWhere(String c, Object... params) { return findConceptsWhere(db_mainConcepts(), c, params); } static Collection findConceptsWhere(Concepts concepts, Class c, Object... params) { ping(); params = expandParams(c, params); // indexed if (concepts.fieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex index = concepts.getFieldIndex(c, (String) params[i]); if (index != null) { Collection rawList = index.getAll(params[i+1]); params = dropEntryFromParams(params, i); if (params == null) return rawList; List l = new ArrayList(); for (A x : rawList) if (checkConceptFields(x, params)) l.add(x); return l; } } // table scan return filterConcepts(concepts.list(c), params); } static Collection findConceptsWhere(Concepts concepts, String c, Object... params) { return filterConcepts(concepts.list(c), params); } static List firstOfPairs(Collection> l) { List out = new ArrayList(); for (Pair p : unnull(l)) out.add(firstOfPair(p)); return out; } static Class primitiveToBoxedTypeOpt(Class type) { return or(primitiveToBoxedType(type), type); } static Object dm_callOS(String functionName, Object... args) { return call(dm_os(), functionName, args); } static DynModule dm_currentModuleMandatory() { return dm_current_mandatory(); } static Set syncIdentityHashSet() { return (Set) synchronizedSet(identityHashSet()); } static A second(List l) { return get(l, 1); } static A second(Iterable l) { if (l == null) return null; Iterator it = iterator(l); if (!it.hasNext()) return null; it.next(); return it.hasNext() ? it.next() : null; } static A second(A[] bla) { return bla == null || bla.length <= 1 ? null : bla[1]; } static B second(Pair p) { return p == null ? null : p.b; } static B second(T3 t) { return t == null ? null : t.b; } static A second(Producer p) { if (p == null) return null; if (p.next() == null) return null; return p.next(); } static char second(String s) { return charAt(s, 1); } static B second(Either e) { return e == null ? null : e.bOpt(); } static A third(List l) { return _get(l, 2); } static A third(Iterable l) { if (l == null) return null; Iterator it = iterator(l); for (int _repeat_0 = 0; _repeat_0 < 2; _repeat_0++) { if (!it.hasNext()) return null; it.next(); } return it.hasNext() ? it.next() : null; } static A third(Producer p) { if (p == null) return null; for (int _repeat_1 = 0; _repeat_1 < 2; _repeat_1++) { if (p.next() == null) return null; } return p.next(); } static A third(A[] bla) { return bla == null || bla.length <= 2 ? null : bla[2]; } static C third(T3 t) { return t == null ? null : t.c; } static A last(List l) { return empty(l) ? null : l.get(l.size()-1); } static char last(String s) { return empty(s) ? '#' : s.charAt(l(s)-1); } static int last(int[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static double last(double[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static A last(A[] a) { return l(a) != 0 ? a[l(a)-1] : null; } static A last(Iterator it) { A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static A last(Collection l) { if (l == null) return null; if (l instanceof List) return (A) last((List) l); if (l instanceof SortedSet) return (A) last((SortedSet) l); Iterator it = iterator(l); A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static A last(SortedSet l) { return l == null ? null : l.last(); } static A last(CompactLinkedHashSet set) { return set == null ? null : set.last(); } static VF1 runnableToVF1(final Runnable r) { return r == null ? null : new VF1() { public void get(A a) { try { r.run() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "r.run()"; }}; } static com.sun.management.OperatingSystemMXBean advancedOSMXBean() { return optCast(com.sun.management.OperatingSystemMXBean.class, osMXBean()); } static Class javax() { return getJavaX(); } static Object _defaultClassFinder_value = defaultDefaultClassFinder(); static Object _defaultClassFinder() { return _defaultClassFinder_value; } static String programIDWithCase() { return nempty(caseID()) ? programID() + "/" + quoteUnlessIdentifierOrInteger(caseID()) : programID(); } static Set similarEmptySet(Iterable m) { if (m instanceof TreeSet) return new TreeSet(((TreeSet) m).comparator()); if (m instanceof LinkedHashSet) return new LinkedHashSet(); return new HashSet(); } static Set similarEmptySet(Map m) { if (m instanceof TreeMap) return new TreeSet(((TreeMap) m).comparator()); if (m instanceof LinkedHashMap) return new LinkedHashSet(); return new HashSet(); } static Map synchroMap() { return synchroHashMap(); } static Map synchroMap(Map map) { return Collections.synchronizedMap(map); } static Class fieldType(Object o, String field) { Field f = getField(o, field); return f == null ? null : f.getType(); } static A singleObjectMethodProxy(Class intrface, Object object, String methodName) { return proxyFromInvocationHandler(intrface, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) { String mname = method.getName(); if (mname.equals(methodName)) return call(object, methodName, unnull(args)); if (mname.equals("toString")) return "proxy for " + methodName; return null; } }); } static Map newDangerousWeakHashMap() { return _registerDangerousWeakMap(synchroMap(new WeakHashMap())); } // initFunction: voidfunc(Map) - is called initially, and after clearing the map static Map newDangerousWeakHashMap(Object initFunction) { return _registerDangerousWeakMap(synchroMap(new WeakHashMap()), initFunction); } static Object callMCWithVarArgs(String method, Object... args) { return call_withVarargs(mc(), method, args); } static Object invokeMethod(Method m, Object o, Object... args) { try { try { return m.invoke(o, args); } catch (InvocationTargetException e) { throw rethrow(getExceptionCause(e)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(e.getMessage() + " - was calling: " + m + ", args: " + joinWithSpace(classNames(args))); } } catch (Exception __e) { throw rethrow(__e); } } static boolean call_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != args.length) { if (debug) print("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) { Object arg = args[i]; if (!(arg == null ? !types[i].isPrimitive() : isInstanceX(types[i], arg))) { if (debug) print("Bad parameter " + i + ": " + arg + " vs " + types[i]); return false; } } return true; } static final Map callOpt_cache = newDangerousWeakHashMap(); static Object callOpt_cached(Object o, String methodName, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); // TODO: (super-rare) case where method exists static and non-static // with different args Method me = cache.findMethod(methodName, args); if (me == null || (me.getModifiers() & Modifier.STATIC) == 0) return null; return invokeMethod(me, null, args); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(methodName, args); if (me == null) return null; return invokeMethod(me, o, args); } } catch (Exception __e) { throw rethrow(__e); } } // no longer synchronizes! (see #1102990) static _MethodCache callOpt_getCache(Class c) { _MethodCache cache = callOpt_cache.get(c); if (cache == null) callOpt_cache.put(c, cache = new _MethodCache(c)); return cache; } static boolean isStaticMethod(Method m) { return methodIsStatic(m); } static Object[] massageArgsForVarArgsCall(Method m, Object[] args) { Class[] types = m.getParameterTypes(); int n = types.length-1, nArgs = args.length; if (nArgs < n) return null; for (int i = 0; i < n; i++) if (!argumentCompatibleWithType(args[i], types[i])) return null; Class varArgType = types[n].getComponentType(); for (int i = n; i < nArgs; i++) if (!argumentCompatibleWithType(args[i], varArgType)) return null; Object[] newArgs = new Object[n+1]; arraycopy(args, 0, newArgs, 0, n); Object[] varArgs = arrayOfType(varArgType, nArgs-n); arraycopy(args, n, varArgs, 0, nArgs-n); newArgs[n] = varArgs; return newArgs; } static List classNames(Collection l) { return getClassNames(l); } static List classNames(Object[] l) { return getClassNames(Arrays.asList(l)); } static Object pcallFunction(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable __e) { _handleException(__e); } return null; } static String getStackTrace2(Throwable e) { return hideCredentials(getStackTrace(unwrapTrivialExceptionWraps(e)) + replacePrefix("java.lang.RuntimeException: ", "FAIL: ", hideCredentials(str(innerException2(e)))) + "\n"); } static Throwable getInnerException(Throwable e) { if (e == null) return null; while (e.getCause() != null) e = e.getCause(); return e; } static Throwable getInnerException(Runnable r) { return getInnerException(getException(r)); } // PersistableThrowable doesn't hold GC-disturbing class references in backtrace static volatile PersistableThrowable lastException_lastException; static PersistableThrowable lastException() { return lastException_lastException; } static void lastException(Throwable e) { lastException_lastException = persistableThrowable(e); } static boolean regionMatches(String a, int offsetA, String b, int offsetB, int len) { return a != null && b != null && a.regionMatches(offsetA, b, offsetB, len); } static boolean regionMatches(String a, int offsetA, String b) { return regionMatches(a, offsetA, b, 0, l(b)); } static String javaTok_substringN(String s, int i, int j) { if (i == j) return ""; if (j == i+1 && s.charAt(i) == ' ') return " "; return s.substring(i, j); } static String javaTok_substringC(String s, int i, int j) { return s.substring(i, j); } static List javaTokWithExisting(String s, List existing) { ++javaTok_n; int nExisting = javaTok_opt && existing != null ? existing.size() : 0; ArrayList 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, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } if (n < nExisting && javaTokWithExisting_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(javaTok_substringN(s, i, j)); ++n; i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace // Special JavaX syntax: 'identifier if (c == '\'' && Character.isJavaIdentifierStart(d) && i+2 < l && "'\\".indexOf(s.charAt(i+2)) < 0) { j += 2; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j; } else if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener /*|| s.charAt(j) == '\n'*/) { // allow multi-line strings ++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 (c == '[' && d == '[') { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && 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 && javaTokWithExisting_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(javaTok_substringC(s, i, j)); ++n; i = j; } if ((tok.size() % 2) == 0) tok.add(""); javaTok_elements += tok.size(); return tok; } static boolean javaTokWithExisting_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 Class run_overBot(String progID) { Class main = hotwire_overBot(progID); callMain(main); return main; } static String mainClassNameForClassLoader(ClassLoader cl) { return or((String) callOpt(cl, "mainClassName"), "main"); } static Class loadClassFromClassLoader_orNull(ClassLoader cl, String name) { try { return cl == null ? null : cl.loadClass(name); } catch (ClassNotFoundException e) { return null; } } static A assertEquals(Object x, A y) { return assertEquals("", x, y); } static A assertEquals(String msg, Object x, A y) { if (assertVerbose()) return assertEqualsVerbose(msg, x, y); if (!(x == null ? y == null : x.equals(y))) throw fail((msg != null ? msg + ": " : "") + y + " != " + x); return y; } static void assertEquals(Scorer scorer, Object x, Object y) { assertEquals(scorer, "", x, y); } static void assertEquals(Scorer scorer, String msg, Object x, Object y) { if (scorer == null) { assertEquals(msg, x, y); return; } scorer.add(eq(x, y), nullIfEmpty(msg)); } static A popLast(List l) { return liftLast(l); } static List popLast(int n, List l) { return liftLast(n, l); } static String actualMCDollar() { return actualMC().getName() + "$"; } static boolean isSyntheticOrAnonymous(Class c) { return c != null && (c.isSynthetic() || isAnonymousClassName(c.getName())); } // This is a bit rough... finds static and non-static methods. static Method findMethodNamed(Object obj, String method) { if (obj == null) return null; if (obj instanceof Class) return findMethodNamed((Class) obj, method); return findMethodNamed(obj.getClass(), method); } static Method findMethodNamed(Class c, String method) { while (c != null) { for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method)) { makeAccessible(m); return m; } c = c.getSuperclass(); } return null; } // keeps package names for dynamic code (package dyn.*) static String shortDynClassNameForStructure(Object o) { if (o instanceof DynamicObject && ((DynamicObject) o).className != null) return ((DynamicObject) o).className; if (o == null) return null; Class c = o instanceof Class ? (Class) o : o.getClass(); String name = c.getName(); return name.startsWith("dyn.") ? classNameToVM(name) : shortenClassName(name); } static int countDots(String s) { int n = l(s), count = 0; for (int i = 0; i < n; i++) if (s.charAt(i) == '.') ++count; return count; } static void quoteToPrintWriter(String s, PrintWriter out) { if (s == null) { out.print("null"); return; } out.print('"'); int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') { out.print('\\'); out.print(c); } else if (c == '\r') out.print("\\r"); else if (c == '\n') out.print("\\n"); else if (c == '\0') out.print("\\0"); else out.print(c); } out.print('"'); } static String quoteCharacter(char c) { if (c == '\'') return "'\\''"; if (c == '\\') return "'\\\\'"; if (c == '\r') return "'\\r'"; if (c == '\n') return "'\\n'"; if (c == '\t') return "'\\t'"; return "'" + c + "'"; } static boolean isCISet_gen(Iterable l) { return l instanceof TreeSet && className(((TreeSet) l).comparator()).contains("CIComp"); } static boolean isJavaXClassName(String s) { return startsWithOneOf(s, "main$", "loadableUtils."); } static List unwrapSynchronizedList(List l) { if (eqOneOf(className(l), "java.util.Collections$SynchronizedList", "java.util.Collections$SynchronizedRandomAccessList")) return (List) get_raw(l, "list"); return l; } static boolean isCIMap_gen(Map map) { return map instanceof TreeMap && className(((TreeMap) map).comparator()).contains("CIComp"); } // works for both java.util-wrapped maps as well as our own static Map unwrapSynchronizedMap(Map map) { if (eqOneOf(shortClassName(map), "SynchronizedMap", "SynchronizedSortedMap", "SynchronizedNavigableMap")) return (Map) get_raw(map, "m"); return map; } static String boolArrayToHex(boolean[] a) { return bytesToHex(boolArrayToBytes(a)); } static Pair arrayTypeAndDimensions(Object o) { return arrayTypeAndDimensions(_getClass(o)); } static Pair arrayTypeAndDimensions(Class c) { if (c == null || !c.isArray()) return null; Class elem = c.getComponentType(); if (elem.isArray()) return mapPairB(arrayTypeAndDimensions(elem), dim -> dim+1); return pair(elem, 1); } static Iterator emptyIterator() { return Collections.emptyIterator(); } static String dropPrefix(String prefix, String s) { return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s; } static Map getDeclaredFields_cache = newDangerousWeakHashMap(); static Field[] getDeclaredFields_cached(Class c) { Field[] fields; synchronized(getDeclaredFields_cache) { fields = getDeclaredFields_cache.get(c); if (fields == null) { getDeclaredFields_cache.put(c, fields = c.getDeclaredFields()); for (Field f : fields) makeAccessible(f); } } return fields; } static boolean startsWithDigit(String s) { return nempty(s) && isDigit(s.charAt(0)); } static Map paramsToMap_withNulls(Object... params) { int n = l(params); if (l(params) == 1 && params[0] instanceof Map) return (Map) params[0]; LinkedHashMap map = new LinkedHashMap(); for (int i = 0; i+1 < n; i += 2) map.put(params[i], params[i+1]); return map; } static String jsonEncode_extended(Object o) { StringBuilder buf = new StringBuilder(); jsonEncode_extended(o, buf); return str(buf); } static void jsonEncode_extended(Object o, StringBuilder buf) { if (o == null) buf.append("null"); else if (o instanceof JS) buf.append(((JS) o).get()); else if (o instanceof String) buf.append(quote((String) o)); else if (o instanceof Number || o instanceof Boolean) buf.append(o); else if (o instanceof Map) { Map map = (Map) o; buf.append("{"); boolean first = true; for (Object key : keys(map)) { if (first) first = false; else buf.append(","); buf.append(quote((String) key)); buf.append(":"); jsonEncode_extended(map.get(key), buf); } buf.append("}"); } else if (o instanceof Collection) { Collection l = (Collection) o; buf.append("["); boolean first = true; for (Object element : l) { if (first) first = false; else buf.append(","); jsonEncode_extended(element, buf); } buf.append("]"); } else throw fail("Unknown object for JSON encoding: " + className(o)); } static boolean isString(Object o) { return o instanceof String; } static Object dm_getService(String serviceName) { return empty(serviceName) ? null : dm_getModule(vmBus_query(assertIdentifier(serviceName))); } static boolean isStringOrIntOrLong(Object o) { return o instanceof String || o instanceof Integer || o instanceof Long; } static Object dm_resolveModule(Object moduleOrStem) { return dm_callOS("resolveModule", moduleOrStem); } static Object callOpt(Object o) { return callF(o); } static Object callOpt(Object o, String method, Object... args) { return callOpt_withVarargs(o, method, args); } static List concatLists(Iterable... lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) addAll(l, list); return l; } static List concatLists(Collection> lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) addAll(l, list); return l; } static void setOpt_raw(Object o, String field, Object value) { try { if (o == null) return; if (o instanceof Class) setOpt_raw((Class) o, field, value); else { Field f = setOpt_raw_findField(o.getClass(), field); if (f != null) { makeAccessible(f); smartSet(f, o, value); } } } catch (Exception __e) { throw rethrow(__e); } } static void setOpt_raw(Class c, String field, Object value) { try { if (c == null) return; Field f = setOpt_raw_findStaticField(c, field); if (f != null) { makeAccessible(f); smartSet(f, null, value); } } catch (Exception __e) { throw rethrow(__e); } } static Field setOpt_raw_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Field setOpt_raw_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 A setDyn(A o, String key, Object value) { setDynObjectValue(o, key, value); return o; } static void setDyn(IMeta o, String key, Object value) { metaMapPut(o, key, value); } static List sorted(Collection c, Object comparator) { List l = cloneList(c); sort(l, makeComparator(comparator)); return l; } static List sorted(Collection c) { List l = cloneList(c); sort(l); return l; } static List sorted(Comparator comparator, Collection c) { List l = cloneList(c); sort(l, comparator); return l; } static List methodsStartingWith(Object o, final String prefix) { return filter(allMethodNames(o), new F1() { public Object get(String s) { try { return startsWith(s, prefix); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "startsWith(s, prefix)"; }}); } static String programID() { return getProgramID(); } static String programID(Object o) { return getProgramID(o); } static boolean interruptThread_verbose = false; static void interruptThread(Thread t) { if (t == null) return; if (interruptThread_verbose) print("Interrupting thread " + t); // note reason in global map vm_threadInterruptionReasonsMap().put(t, getStackTrace()); t.interrupt(); URLConnection c = (URLConnection) (vm_generalSubMap("URLConnection per thread").get(t)); if (c != null) { try { print("Closing URLConnection of interrupted thread."); call(c, "disconnect"); } catch (Throwable __e) { _handleException(__e); }} } static boolean isJavaXClassLoader(ClassLoader cl) { return startsWithOneOf(className(cl), "main$JavaXClassLoader", "x30$JavaXClassLoader"); } static void setOptAll(Object o, Map fields) { if (fields == null) return; for (String field : keys(fields)) setOpt/*_flex*/(o, field, fields.get(field)); } static void setOptAll(Object o, Object... values) { //values = expandParams(c.getClass(), values); warnIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; setOpt(o, field, value); } } static List _registerWeakMap_preList; static A _registerWeakMap(A map) { if (javax() == null) { // We're in class init if (_registerWeakMap_preList == null) _registerWeakMap_preList = synchroList(); _registerWeakMap_preList.add(map); return map; } try { call(javax(), "_registerWeakMap", map); } catch (Throwable e) { printException(e); print("Upgrade JavaX!!"); } return map; } static void _onLoad_registerWeakMap() { assertNotNull(javax()); if (_registerWeakMap_preList == null) return; for (Object o : _registerWeakMap_preList) _registerWeakMap(o); _registerWeakMap_preList = null; } static void close(AutoCloseable c) { _close(c); } static boolean isTrueOrYes(Object o) { return isTrueOpt(o) || o instanceof String && (eqicOneOf(((String) o), "1", "t", "true") || isYes(((String) o))); } static LinkedHashMap asLinkedHashMap(Map map) { if (map instanceof LinkedHashMap) return (LinkedHashMap) map; LinkedHashMap m = new LinkedHashMap(); if (map != null) synchronized(collectionMutex(map)) { m.putAll(map); } return m; } static boolean endsWithLetterOrDigit(String s) { return s != null && s.length() > 0 && Character.isLetterOrDigit(s.charAt(s.length()-1)); } static void rotateStringBuffer(StringBuffer buf, int max) { try { if (buf == null) return; synchronized(buf) { if (buf.length() <= max) return; 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); } buf.trimToSize(); } } catch (Exception __e) { throw rethrow(__e); } } static void rotateStringBuilder(StringBuilder buf, int max) { try { if (buf == null) return; synchronized(buf) { if (buf.length() <= max) return; 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); } buf.trimToSize(); } } catch (Exception __e) { throw rethrow(__e); } } static A setThreadLocal(ThreadLocal tl, A value) { if (tl == null) return null; A old = tl.get(); tl.set(value); return old; } static List jsonTok(String s) { List tok = new ArrayList(); int l = l(s); 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 // scan for non-whitespace (json strings, "null" identifier, numbers. everything else automatically becomes a one character token.) if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener) { ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isLetter(c)) do ++j; while (j < l && Character.isLetter(s.charAt(j))); else if (Character.isDigit(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 Class main() { return getMainClass(); } static double parseDouble(String s) { return empty(s) ? 0.0 : Double.parseDouble(s); } static Number boxedIntOrLong(long l) { return l != (int) l ? boxed(l) : boxed((int) l); } static String getQueryFromURL(String url) { return dropAfterSubstring(substring(url, smartIndexOf(url, "?")+1), "#"); } static void newPing() { var tl = newPing_actionTL(); Runnable action = tl == null ? null : tl.get(); { if (action != null) action.run(); } } static void failIfUnlicensed() { assertTrue("license off", licensed()); } static Map vm_generalWeakSubMap(Object name) { synchronized(get(javax(), "generalMap")) { Map map = (Map) (vm_generalMap_get(name)); if (map == null) vm_generalMap_put(name, map = newWeakMap()); return map; } } static Object pcallF_minimalExceptionHandling(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable e) { System.out.println(getStackTrace(e)); _storeException(e); } return null; } static Set vm_generalIdentityHashSet(Object name) { synchronized(get(javax(), "generalMap")) { Set set = (Set) (vm_generalMap_get(name)); if (set == null) vm_generalMap_put(name, set = syncIdentityHashSet()); return set; } } static Map vm_generalHashMap(Object name) { synchronized(get(javax(), "generalMap")) { Map m = (Map) (vm_generalMap_get(name)); if (m == null) vm_generalMap_put(name, m = syncHashMap()); return m; } } static String hopeningTag(String tag, Map params) { return hopeningTag(tag, mapToParams(params)); } static String hopeningTag(String tag, Object... params) { StringBuilder buf = new StringBuilder(); buf.append("<" + tag); params = unrollParams(params); for (int i = 0; i < l(params); i += 2) { String name = (String) get(params, i); Object val = get(params, i+1); if (nempty(name) && val != null) { if (eqOneOf(val, html_valueLessParam(), true)) buf.append(" " + name); else { String s = str(val); if (!empty(s)) buf.append(" " + name + "=" + htmlQuote(s)); } } } buf.append(">"); return str(buf); } static String intToHex_flexLength(int i) { return Integer.toHexString(i); } static boolean isRelativeOrAbsoluteURL(String s) { return isAbsoluteURL(s) || isRelativeURL(s); } static String hstylesheetsrc(String src) { return tag("link", "", "rel" , "stylesheet", "href" , src); } static boolean neqic(String a, String b) { return !eqic(a, b); } static boolean neqic(char a, char b) { return !eqic(a, b); } static A[] dropLast(A[] a) { return dropLast(a, 1); } static A[] dropLast(A[] a, int n) { if (a == null) return null; n = Math.min(n, a.length); A[] b = arrayOfSameType(a, a.length-n); System.arraycopy(a, 0, b, 0, b.length); return b; } static List dropLast(List l) { return subList(l, 0, l(l)-1); } static List dropLast(int n, List l) { return subList(l, 0, l(l)-n); } static List dropLast(Iterable l) { return dropLast(asList(l)); } static String dropLast(String s) { return substring(s, 0, l(s)-1); } static String dropLast(String s, int n) { return substring(s, 0, l(s)-n); } static String dropLast(int n, String s) { return dropLast(s, n); } static String nlToBr(String s) { return s.replace("\n", "
\n"); } static String[] dropFirst(int n, String[] a) { return drop(n, a); } static String[] dropFirst(String[] a) { return drop(1, a); } static Object[] dropFirst(Object[] a) { return drop(1, a); } static
List dropFirst(List l) { return dropFirst(1, l); } static List dropFirst(int n, Iterable i) { return dropFirst(n, toList(i)); } static List dropFirst(Iterable i) { return dropFirst(toList(i)); } static List dropFirst(int n, List l) { return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size())); } static List dropFirst(List l, int n) { return dropFirst(n, l); } static String dropFirst(int n, String s) { return substring(s, n); } static String dropFirst(String s, int n) { return substring(s, n); } static String dropFirst(String s) { return substring(s, 1); } static Object[] html_massageAutofocusParam(Object[] params) { Object autofocus = optPar("autofocus", params); return changeParam(params, "autofocus" , eqOneOf(autofocus, html_valueLessParam(), true, 1, "1", "autofocus") ? html_valueLessParam() : null); } static boolean regionMatchesIC(String a, int offsetA, String b, int offsetB, int len) { return a != null && a.regionMatches(true, offsetA, b, offsetB, len); } static Object subBot_serveByteArray(byte[] data, String mimeType) { return call(mainBot(), "serveByteArray", data, mimeType); } static Charset utf8charset_cache; static Charset utf8charset() { if (utf8charset_cache == null) utf8charset_cache = utf8charset_load(); return utf8charset_cache; } static Charset utf8charset_load() { return Charset.forName("UTF-8"); } static String jsonEncode_shallowLineBreaks(Object o) { return jsonEncode_breakAtLevel1(o); } static void jsonEncode_shallowLineBreaks(Object o, StringBuilder buf, int level) { jsonEncode_breakAtLevel1(o, buf, level); } static int lastIndexOf(String a, String b) { return a == null || b == null ? -1 : a.lastIndexOf(b); } static int lastIndexOf(String a, char b) { return a == null ? -1 : a.lastIndexOf(b); } // starts searching from i-1 static int lastIndexOf(List l, int i, A a) { if (l == null) return -1; for (i = min(l(l), i)-1; i >= 0; i--) if (eq(l.get(i), a)) return i; return -1; } static int lastIndexOf(List l, A a) { if (l == null) return -1; for (int i = l(l)-1; i >= 0; i--) if (eq(l.get(i), a)) return i; return -1; } // f: A -> Comparable static List sortByCalculatedFieldDesc(Collection c, final Object f) { return sortByCalculatedFieldDesc_inPlace(cloneList(c), f); } static List sortByCalculatedFieldDesc(Object f, Collection c) { return sortByCalculatedFieldDesc(c, f); } static List sortByCalculatedFieldDesc(Iterable c, IF1 f) { List l = cloneList(c); sort(l, new Comparator() { public int compare(A a, A b) { return stdcompare(f.get(b), f.get(a)); } }); return l; } static List sortByCalculatedFieldDesc(IF1 f, Iterable c) { return sortByCalculatedFieldDesc(c, f); } static boolean containsNulls(Collection c) { return contains(c, null); } static Map similarEmptyMap(Map m) { if (m instanceof TreeMap) return new TreeMap(((TreeMap) m).comparator()); if (m instanceof LinkedHashMap) return new LinkedHashMap(); // default to a hash map return new HashMap(); } static Map similarEmptyMap(Iterable m) { if (m instanceof TreeSet) return new TreeMap(((TreeSet) m).comparator()); if (m instanceof LinkedHashSet) return new LinkedHashMap(); return new HashMap(); } static int[] subIntArray(int[] b, int start) { return subIntArray(b, start, l(b)); } static int[] subIntArray(int[] b, int start, int end) { start = max(start, 0); end = min(end, l(b)); if (start == 0 && end == l(b)) return b; if (start >= end) return new int[0]; int[] x = new int[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static int[] subIntArray(int[] a, IntRange r) { return r == null ? null : subIntArray(a, r.start, r.end); } static Collection concatCollections_conservative(Collection a, Collection b) { if (empty(a)) return b; if (empty(b)) return a; return concatLists(a, b); } static Object[] dropEntryFromParams(Object[] params, int i) { int n = l(params); if (i < 0 || i >= n) return params; if (n == 2) return null; Object[] p = new Object[n-2]; System.arraycopy(params, 0, p, 0, i); System.arraycopy(params, i+2, p, i, n-i-2); return p; } static boolean checkConceptFieldsIC(Concept x, Object... data) { for (int i = 0; i < l(data); i += 2) if (!eqicOrEq(cget(x, (String) data[i]), deref(data[i+1]))) return false; return true; } static List filterConceptsIC(Collection list, Object... params) { List l = new ArrayList(); if (list != null) for (A x : list) if (checkConceptFieldsIC(x, params)) l.add(x); return l; } static String hcomment_unescaped(String text) { return ""; } static String htmlencode(Object o) { return htmlencode(str(o)); } static String htmlencode(String s) { if (s == null) return ""; StringBuilder out = new StringBuilder(Math.max(16, s.length())); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127 || c == '"' || c == '<' || c == '>' || c == '&') { int cp = s.codePointAt(i); out.append("&#x"); out.append(intToHex_flexLength(cp)); out.append(';'); i += Character.charCount(cp)-1; } else out.append(c); } return out.toString(); } static String joinStrings(String sep, Object... strings) { return joinStrings(sep, Arrays.asList(strings)); } static String joinStrings(String sep, Iterable strings) { StringBuilder buf = new StringBuilder(); for (Object o : unnull(strings)) { String s = strOrNull(o); if (nempty(s)) { if (nempty(buf)) buf.append(sep); buf.append(s); } } return str(buf); } static String baseClassName(String className) { return substring(className, className.lastIndexOf('.')+1); } static String baseClassName(Object o) { return baseClassName(getClassName(o)); } static String prependIfNempty(String prefix, String s) { return empty(s) ? unnull(s) : prefix + s; } static String spliceString(String a, int from, int to, String b) { return substring(a, 0, from) + b + substring(a, Math.max(from, to)); } static String spliceString(String a, int from, int to, char b) { return spliceString(a, from, to, str(b)); } static int indexOfSpaceEtc(String s) { int n = l(s); for (int i = 0; i < n; i++) if (isSpace(s.charAt(i))) return i; return -1; } static boolean startsWithOneOf(String s, String... l) { for (String x : l) if (startsWith(s, x)) return true; return false; } static boolean startsWithOneOf(String s, Matches m, String... l) { for (String x : l) if (startsWith(s, x, m)) return true; return false; } // accept purpose argument so we are a drop-in for tempVerboseLock static AutoCloseable tempLock(Lock lock) { return tempLock("", lock); } static AutoCloseable tempLock(String purpose, Lock lock) { if (lock == null) return null; lock(lock); return new AutoCloseable() { public String toString() { return "unlock(lock);"; } public void close() throws Exception { unlock(lock); }}; } static List findBackRefs(Collection concepts, Class type) { IdentityHashMap l = new IdentityHashMap(); for (Concept c : concepts) if (c.backRefs != null) for (Concept.Ref r : c.backRefs) if (instanceOf(r.concept(), type)) l.put((A) r.concept(), true); return asList(keys(l)); } // TODO: sort by ID? static List findBackRefs(Concept c, Class type) { IdentityHashMap l = new IdentityHashMap(); if (c != null && c.backRefs != null) for (Concept.Ref r : c.backRefs) if (instanceOf(r.concept(), type)) l.put((A) r.concept(), true); return asList(keys(l)); } static List findBackRefs(Class type, Concept c) { return findBackRefs(c, type); } static Collection findBackRefs(Concept c) { return findBackRefs(c, Concept.class); } 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 formatSnippetIDOpt(String s) { return isSnippetID(s) ? formatSnippetID(s) : s; } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } static WeakHasherMap symbol_map = new WeakHasherMap(new Hasher() { public int hashCode(Symbol symbol) { return symbol.text.hashCode(); } public boolean equals(Symbol a, Symbol b) { if (a == null) return b == null; return b != null && eq(a.text, b.text); } }); static Symbol symbol(String s) { if (s == null) return null; synchronized(symbol_map) { // TODO: avoid object creation by passing the string to findKey Symbol symbol = new Symbol(s, true); Symbol existingSymbol = symbol_map.findKey(symbol); if (existingSymbol == null) symbol_map.put(existingSymbol = symbol, true); return existingSymbol; } } static Symbol symbol(CharSequence s) { if (s == null) return null; if (s instanceof Symbol) return (Symbol) s; if (s instanceof String) return symbol((String) s); return symbol(str(s)); } static Symbol symbol(Object o) { return symbol((CharSequence) o); } static List codeTokensOnly(List tok) { int n = l(tok); List l = emptyList(n/2); for (int i = 1; i < n; i += 2) l.add(tok.get(i)); return l; } static int isAndroid_flag; static boolean isAndroid() { if (isAndroid_flag == 0) isAndroid_flag = System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0 ? 1 : -1; return isAndroid_flag > 0; } static void assertFalse(Object o) { if (!(eq(o, false) /*|| isFalse(pcallF(o))*/)) throw fail(str(o)); } static boolean assertFalse(boolean b) { if (b) throw fail("oops"); return b; } static boolean assertFalse(String msg, boolean b) { if (b) throw fail(msg); return b; } static List reverseList(List l) { Collections.reverse(l); return l; } static Comparator fieldComparator(final String field) { return new Comparator() { public int compare(A a, A b) { return cmp(getOpt(a, field), getOpt(b, field)); } }; } static double toSeconds(long ms) { return ms/1000.0; } static String toSeconds(long ms, int digits) { return formatDouble(toSeconds(ms), digits); } static double toSeconds(double ms) { return ms/1000.0; } static String toSeconds(double ms, int digits) { return formatDouble(toSeconds(ms), digits); } static boolean checkConceptFields(Concept x, Object... data) { for (int i = 0; i < l(data); i += 2) if (neq(cget(x, (String) data[i]), deref(data[i+1]))) return false; return true; } static boolean even(int i) { return (i & 1) == 0; } static boolean even(long i) { return (i & 1) == 0; } static boolean even(BigInteger n) { return even(n.intValue()); } static String getComputerID_quick() { return computerID(); } static boolean isAGIBlueDomain(String domain) { return domainIsUnder(domain, theAGIBlueDomain()); } static String hostNameFromURL(String url) { try { return new URL(url).getHost(); } catch (Exception __e) { throw rethrow(__e); } } static boolean endsWithIgnoreCase(String a, String b) { int la = l(a), lb = l(b); return la >= lb && regionMatchesIC(a, la-lb, b, 0, lb); } static boolean endsWithIgnoreCase(String a, String b, Matches m) { if (!endsWithIgnoreCase(a, b)) return false; if (m != null) m.m = new String[] { substring(a, 0, l(a)-l(b)) }; return true; } static BufferedReader bufferedReader(Reader r) { return bufferedReader(r, 8192); } static BufferedReader bufferedReader(Reader r, int bufSize) { if (r == null) return null; return r instanceof BufferedReader ? (BufferedReader) r : _registerIOWrap(new BufferedReader(r, bufSize), r); } static A holdResource(IResourceHolder holder, A a) { { if (holder != null) holder.add(a); } return a; } static CloseableIterableIterator iteratorFromFunction_f0_autoCloseable(final F0 f, final AutoCloseable closeable) { class IFF2 extends CloseableIterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } public void close() throws Exception { if (closeable != null) closeable.close(); } }; return new IFF2(); } static String readLineFromReaderWithClose(BufferedReader r) { try { String s = r.readLine(); if (s == null) r.close(); return s; } catch (Exception __e) { throw rethrow(__e); } } static AutoCloseable _wrapIOCloseable(final AutoCloseable c) { return c == null ? null : new AutoCloseable() { public String toString() { return "c.close();\r\n _registerIO(c, null, false);"; } public void close() throws Exception { c.close(); _registerIO(c, null, false); }}; } static A _registerIOWrap(A wrapper, Object wrapped) { return wrapper; } static FileInputStream newFileInputStream(File path) throws IOException { return newFileInputStream(path.getPath()); } static FileInputStream newFileInputStream(String path) throws IOException { FileInputStream f = new FileInputStream(path); _registerIO(f, path, true); return f; } static int gzInputStream_defaultBufferSize = 65536; static GZIPInputStream gzInputStream(File f) { try { return gzInputStream(new FileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static GZIPInputStream gzInputStream(File f, int bufferSize) { try { return gzInputStream(new FileInputStream(f), bufferSize); } catch (Exception __e) { throw rethrow(__e); } } static GZIPInputStream gzInputStream(InputStream in) { return gzInputStream(in, gzInputStream_defaultBufferSize); } static GZIPInputStream gzInputStream(InputStream in, int bufferSize) { try { return _registerIOWrap(new GZIPInputStream(in, gzInputStream_defaultBufferSize), in); } catch (Exception __e) { throw rethrow(__e); } } // TODO: cyclic structures involving certain lists & sets static Object unstructure(String text) { return unstructure(text, false); } static Object unstructure(String text, boolean allDynamic) { return unstructure(text, allDynamic, null); } static Object unstructure(String text, IF1 classFinder) { return unstructure(text, false, classFinder); } static int structure_internStringsLongerThan = 50; static int unstructure_unquoteBufSize = 100; static int unstructure_tokrefs; // stats abstract static class unstructure_Receiver { abstract void set(Object o); } // classFinder: func(name) -> class (optional) static Object unstructure(String text, boolean allDynamic, Object classFinder) { if (text == null) return null; return unstructure_tok(javaTokC_noMLS_iterator(text), allDynamic, classFinder); } static Object unstructure_reader(BufferedReader reader) { return unstructure_tok(javaTokC_noMLS_onReader(reader), false, null); } static Object unstructure_tok(final Producer tok, final boolean allDynamic, final Object _classFinder) { final boolean debug = unstructure_debug; final class X { int i = -1; final Object classFinder = _classFinder != null ? _classFinder : _defaultClassFinder(); String mcDollar = actualMCDollar(); // use Eclipse primitive collection if possible (smaller & hopefully faster?) HashMap refs = new HashMap(); HashMap tokrefs = new HashMap(); HashSet concepts = new HashSet(); HashMap classesMap = new HashMap(); List stack = new ArrayList(); Map baseClassMap = new HashMap(); HashMap innerClassConstructors = new HashMap(); String curT; char[] unquoteBuf = new char[unstructure_unquoteBufSize]; X() { try { Class mc = (Class) (callF(_classFinder, "
")); if (mc != null) mcDollar = mc.getName() + "$"; } catch (Throwable __e) { _handleException(__e); } } Class findAClass(String fullClassName) { try { return classFinder != null ? (Class) callF(classFinder, fullClassName) : findClass_fullName(fullClassName); } catch (Throwable __e) { return null; } } String unquote(String s) { return unquoteUsingCharArray(s, unquoteBuf); } // look at current token String t() { return curT; } // get current token, move to next String tpp() { String t = curT; consume(); return t; } void parse(final unstructure_Receiver out) { String t = t(); int refID; if (structure_isMarker(t, 0, l(t))) { refID = parseInt(t.substring(1)); consume(); } else refID = -1; // if (debug) print("parse: " + quote(t)); final int tokIndex = i; parse_inner(refID, tokIndex, new unstructure_Receiver() { void set(Object o) { if (refID >= 0) refs.put(refID, o); if (o != null) tokrefs.put(tokIndex, o); out.set(o); } }); } void parse_inner(int refID, int tokIndex, unstructure_Receiver out) { String t = t(); // if (debug) print("parse_inner: " + quote(t)); String cname = t; Class c = classesMap.get(cname); if (c == null) { if (t.startsWith("\"")) { String s = internIfLongerThan(unquote(tpp()), structure_internStringsLongerThan); out.set(s); return; } if (t.startsWith("'")) { out.set(unquoteCharacter(tpp())); return; } if (t.equals("bigint")) { out.set(parseBigInt()); return; } if (t.equals("d")) { out.set(parseDouble()); return; } if (t.equals("fl")) { out.set(parseFloat()); return; } if (t.equals("sh")) { consume(); t = tpp(); if (t.equals("-")) { t = tpp(); out.set((short) (-parseInt(t))); return; } out.set((short) parseInt(t)); return; } if (t.equals("-")) { consume(); t = tpp(); out.set(isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t))); return; } if (isInteger(t) || isLongConstant(t)) { consume(); //if (debug) print("isLongConstant " + quote(t) + " => " + isLongConstant(t)); if (isLongConstant(t)) { out.set(parseLong(t)); return; } long l = parseLong(t); boolean isInt = l == (int) l; out.set(isInt ? (Object) Integer.valueOf((int) l) : (Object) Long.valueOf(l)); return; } if (t.equals("false") || t.equals("f")) { consume(); out.set(false); return; } if (t.equals("true") || t.equals("t")) { consume(); out.set(true); return; } if (t.equals("-")) { consume(); t = tpp(); out.set(isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t))); return; } if (isInteger(t) || isLongConstant(t)) { consume(); //if (debug) print("isLongConstant " + quote(t) + " => " + isLongConstant(t)); if (isLongConstant(t)) { out.set(parseLong(t)); return; } long l = parseLong(t); boolean isInt = l == (int) l; out.set(isInt ? (Object) Integer.valueOf((int) l) : (Object) Long.valueOf(l)); return; } if (t.equals("File")) { consume(); File f = new File(unquote(tpp())); out.set(f); return; } if (t.startsWith("r") && isInteger(t.substring(1))) { consume(); int ref = Integer.parseInt(t.substring(1)); Object o = refs.get(ref); if (o == null) warn("unsatisfied back reference " + ref); out.set(o); return; } if (t.startsWith("t") && isInteger(t.substring(1))) { consume(); int ref = Integer.parseInt(t.substring(1)); Object o = tokrefs.get(ref); if (o == null) warn("unsatisfied token reference " + ref + " at " + tokIndex); out.set(o); return; } if (t.equals("hashset")) { parseHashSet(out); return; } if (t.equals("lhs")) { parseLinkedHashSet(out); return; } if (t.equals("treeset")) { parseTreeSet(out); return; } if (t.equals("ciset")) { parseCISet(out); return; } if (eqOneOf(t, "hashmap", "hm")) { consume(); parseMap(new HashMap(), out); return; } if (t.equals("lhm")) { consume(); parseMap(new LinkedHashMap(), out); return; } if (t.equals("tm")) { consume(); parseMap(new TreeMap(), out); return; } if (t.equals("cimap")) { consume(); parseMap(ciMap(), out); return; } if (t.equals("ll")) { consume(); LinkedList l = new LinkedList(); if (refID >= 0) refs.put(refID, l); { parseList(l, out); return; } } if (t.equals("syncLL")) { // legacy consume(); { parseList(synchroLinkedList(), out); return; } } if (t.equals("sync")) { consume(); { parse(new unstructure_Receiver() { void set(Object value) { if (value instanceof Map) { // Java 7 if (value instanceof NavigableMap) { out.set(synchroNavigableMap((NavigableMap) value)); return; } if (value instanceof SortedMap) { out.set(synchroSortedMap((SortedMap) value)); return; } { out.set(synchroMap((Map) value)); return; } } else { out.set(synchroList((List) value)); return; } } }); return; } } if (t.equals("{")) { parseMap(out); return; } if (t.equals("[")) { ArrayList l = new ArrayList(); if (refID >= 0) refs.put(refID, l); this.parseList(l, out); return; } if (t.equals("bitset")) { parseBitSet(out); return; } if (t.equals("array") || t.equals("intarray") || t.equals("dblarray")) { parseArray(out); return; } if (t.equals("ba")) { consume(); String hex = unquote(tpp()); out.set(hexToBytes(hex)); return; } if (t.equals("boolarray")) { consume(); int n = parseInt(tpp()); String hex = unquote(tpp()); out.set(boolArrayFromBytes(hexToBytes(hex), n)); return; } if (t.equals("class")) { out.set(parseClass()); return; } if (t.equals("l")) { parseLisp(out); return; } if (t.equals("null")) { consume(); out.set(null); return; } if (eq(t, "c")) { consume(); t = t(); assertTrue(isJavaIdentifier(t)); concepts.add(t); } // custom deserialization (new static method method) if (eq(t, "cu")) { consume(); t = tpp(); assertTrue(isJavaIdentifier(t)); String fullClassName = mcDollar + t; Class _c = findAClass(fullClassName); if (_c == null) throw fail("Class not found: " + fullClassName); parse(new unstructure_Receiver() { void set(Object value) { out.set(call(_c, "_deserialize", value)); } }); return; } } if (eq(t, "j")) { consume(); out.set(parseJava()); return; } if (eq(t, "bc")) { consume(); String c1 = tpp(); String c2 = tpp(); baseClassMap.put(c1, c2); { parse_inner(refID, i, out); return; } } // add more tokens here // Now we want to find our target class c // Have we failed to look up the class before? //bool seenBefore = classesMap.containsKey(cname); // If we have seen the class before, we skip all of this // and simply leave c as null // TODO - how do we fill className? //if (!seenBefore) { if (c == null && !isJavaIdentifier(t)) throw new RuntimeException("Unknown token " + (i+1) + ": " + quote(t)); // any other class name (or package name) consume(); String className, fullClassName; // Is it a package name? if (eq(t(), ".")) { consume(); className = fullClassName = t + "." + assertIdentifier(tpp()); } else { className = t; fullClassName = mcDollar + t; } if (c == null && !allDynamic) { // First, find class c = findAClass(fullClassName); classesMap.put(className, c); } // check for existing base class if (c == null && !allDynamic) { Set seen = new HashSet(); String parent = className; while (true) { String baseName = baseClassMap.get(parent); if (baseName == null) break; if (!seen.add(baseName)) throw fail("Cyclic superclass info: " + baseName); c = findAClass(mcDollar + baseName); if (c == null) print("Base class " + baseName + " of " + parent + " doesn't exist either"); else if (isAbstract(c)) print("Can't instantiate abstract base class: " + c); else { printVars_str("Reverting to base class", "className", className, "baseName", baseName, "c", c); classesMap.put(className, c); break; } parent = baseName; } } //} // Check if it has an outer reference boolean hasBracket = eq(t(), "("); if (hasBracket) consume(); boolean hasOuter = hasBracket && startsWith(t(), "this$"); DynamicObject dO = null; Object o = null; final String thingName = t; if (c != null) { if (hasOuter) try { Constructor ctor = innerClassConstructors.get(c); if (ctor == null) innerClassConstructors.put(c, ctor = nuStubInnerObject_findConstructor(c, classFinder)); o = ctor.newInstance(new Object[] {null}); } catch (Exception e) { print("Error deserializing " + c + ": " + e); o = nuEmptyObject(c); } else o = nuEmptyObject(c); if (o instanceof DynamicObject) dO = (DynamicObject) o; } else { if (concepts.contains(t) && (c = findAClass(mcDollar + "Concept")) != null) o = dO = (DynamicObject) nuEmptyObject(c); else dO = new DynamicObject(); dO.className = className; } // Save in references list early because contents of object // might link back to main object if (refID >= 0) refs.put(refID, o != null ? o : dO); tokrefs.put(tokIndex, o != null ? o : dO); // NOW parse the fields! HashMap fields = new HashMap(); // no longer preserving order (why did we do this?) Object _o = o; DynamicObject _dO = dO; if (hasBracket) { stack.add(new Runnable() { public void run() { try { if (eq(t(), ",")) consume(); if (eq(t(), ")")) { consume(")"); objRead(_o, _dO, fields, hasOuter); out.set(_o != null ? _o : _dO); } else { final String key = unquote(tpp()); String t = tpp(); if (!eq(t, "=")) throw fail("= expected, got " + t + " after " + quote(key) + " in object " + thingName /*+ " " + sfu(fields)*/); stack.add(this); parse(new unstructure_Receiver() { void set(Object value) { fields.put(key, value); /*ifdef unstructure_debug print("Got field value " + value + ", next token: " + t()); endifdef*/ //if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef unstructure_debug\r\n print(\"in object values, token: \" + t())..."; }}); } else { objRead(o, dO, fields, hasOuter); out.set(o != null ? o : dO); } } void objRead(Object o, DynamicObject dO, Map fields, boolean hasOuter) { // translate between diferent compilers (this$0 vs this$1) Object outer = fields.get("this$0"); if (outer != null) fields.put("this$1", outer); else { outer = fields.get("this$1"); if (outer != null) fields.put("this$0", outer); } if (o != null) { if (dO != null) { setOptAllDyn_pcall(dO, fields); } else { setOptAll_pcall(o, fields); } if (hasOuter) fixOuterRefs(o); } else for (Map.Entry e : fields.entrySet()) setDynObjectValue(dO, intern(e.getKey()), e.getValue()); if (o != null) pcallOpt_noArgs(o, "_doneLoading"); } void parseSet(final Set set, final unstructure_Receiver out) { this.parseList(new ArrayList(), new unstructure_Receiver() { void set(Object o) { set.addAll((List) o); out.set(set); } }); } void parseLisp(final unstructure_Receiver out) { throw fail("class Lisp not included"); } void parseBitSet(final unstructure_Receiver out) { consume("bitset"); consume("{"); final BitSet bs = new BitSet(); stack.add(new Runnable() { public void run() { try { if (eq(t(), "}")) { consume("}"); out.set(bs); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { bs.set((Integer) o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"}\")) {\r\n consume(\"}\");\r\n out.set(bs);\r\n ..."; }}); } void parseList(final List list, final unstructure_Receiver out) { tokrefs.put(i, list); consume("["); stack.add(new Runnable() { public void run() { try { if (eq(t(), "]")) { consume(); out.set(list); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { //if (debug) print("List element type: " + getClassName(o)); list.add(o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"]\")) {\r\n consume();\r\n ifdef unstructure_debug\r..."; }}); } void parseArray(unstructure_Receiver out) { String _type = tpp(); int dims; if (eq(t(), "S")) { // string array _type = "S"; consume(); } if (eq(t(), "/")) { // multi-dimensional array consume(); dims = parseInt(tpp()); } else dims = 1; consume("{"); List list = new ArrayList(); String type = _type; stack.add(new Runnable() { public void run() { try { if (eq(t(), "}")) { consume("}"); if (dims > 1) { Class atype; if (type.equals("intarray")) atype = int.class; else if (type.equals("S")) atype = String.class; else throw todo("multi-dimensional arrays of other types"); out.set(list.toArray((Object[]) newMultiDimensionalOuterArray(atype, dims, l(list)))); } else out.set( type.equals("intarray") ? toIntArray(list) : type.equals("dblarray") ? toDoubleArray(list) : type.equals("S") ? toStringArray(list) : list.toArray()); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { list.add(o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"}\")) {\r\n consume(\"}\");\r\n if (dims > 1) {\r\n ..."; }}); } Object parseClass() { consume("class"); consume("("); String name = unquote(tpp()); consume(")"); Class c = allDynamic ? null : findAClass(name); if (c != null) return c; DynamicObject dO = new DynamicObject(); dO.className = "java.lang.Class"; name = dropPrefix(mcDollar, name); dO.fieldValues.put("name", name); return dO; } Object parseBigInt() { consume("bigint"); consume("("); String val = tpp(); if (eq(val, "-")) val = "-" + tpp(); consume(")"); return new BigInteger(val); } Object parseDouble() { consume("d"); consume("("); String val = unquote(tpp()); consume(")"); return Double.parseDouble(val); } Object parseFloat() { consume("fl"); String val; if (eq(t(), "(")) { consume("("); val = unquote(tpp()); consume(")"); } else { val = unquote(tpp()); } return Float.parseFloat(val); } void parseHashSet(unstructure_Receiver out) { consume("hashset"); parseSet(new HashSet(), out); } void parseLinkedHashSet(unstructure_Receiver out) { consume("lhs"); parseSet(new LinkedHashSet(), out); } void parseTreeSet(unstructure_Receiver out) { consume("treeset"); parseSet(new TreeSet(), out); } void parseCISet(unstructure_Receiver out) { consume("ciset"); parseSet(ciSet(), out); } void parseMap(unstructure_Receiver out) { parseMap(new TreeMap(), out); } Object parseJava() { String j = unquote(tpp()); Matches m = new Matches(); if (jmatch("java.awt.Color[r=*,g=*,b=*]", j, m)) return nuObject("java.awt.Color", parseInt(m.unq(0)), parseInt(m.unq(1)), parseInt(m.unq(2))); else { warn("Unknown Java object: " + j); return null; } } void parseMap(final Map map, final unstructure_Receiver out) { consume("{"); stack.add(new Runnable() { boolean v = false; Object key; public void run() { if (v) { v = false; stack.add(this); if (!eq(tpp(), "=")) throw fail("= expected, got " + t() + " in map of size " + l(map)); parse(new unstructure_Receiver() { void set(Object value) { map.put(key, value); if (eq(t(), ",")) consume(); } }); } else { if (eq(t(), "}")) { consume("}"); out.set(map); } else { v = true; stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { key = o; } }); } } // if v else } // run() }); } /*void parseSub(unstructure_Receiver out) { int n = l(stack); parse(out); while (l(stack) > n) stack }*/ void consume() { curT = tok.next(); ++i; } void consume(String s) { if (!eq(t(), s)) { /*S prevToken = i-1 >= 0 ? tok.get(i-1) : ""; S nextTokens = join(tok.subList(i, Math.min(i+2, tok.size()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")");*/ throw fail(quote(s) + " expected, got " + quote(t())); } consume(); } // outer wrapper function getting first token and unwinding the stack void parse_initial(unstructure_Receiver out) { consume(); // get first token parse(out); while (nempty(stack)) popLast(stack).run(); } } ThreadLocal tlLoading = dynamicObjectIsLoading_threadLocal(); Boolean b = tlLoading.get(); tlLoading.set(true); try { final Var v = new Var(); X x = new X(); x.parse_initial(new unstructure_Receiver() { void set(Object o) { v.set(o); } }); unstructure_tokrefs = x.tokrefs.size(); return v.get(); } finally { tlLoading.set(b); } } static boolean unstructure_debug = false; static Object safeUnstructureGZFile(File f) { try { if (!fileExists(f)) return null; BufferedReader reader = utf8BufferedReader(gzInputStream(f)); return unstructure_tok(javaTokC_noMLS_onReader(reader), true, null); } catch (Exception __e) { throw rethrow(__e); } } static char lastChar(String s) { return empty(s) ? '\0' : s.charAt(l(s)-1); } static Map filterMapByFunctionOnKey(Map map, Object f) { Map m2 = similarEmptyMap(map); for (A a : keys(map)) { if (isTrue(callF(f, a))) m2.put(a, map.get(a)); } return m2; } static Map filterMapByFunctionOnKey(Object f, Map map) { return filterMapByFunctionOnKey(map, f); } static String renderStackTrace(StackTraceElement[] st) { return stackTraceToString(st); } static String renderStackTrace(Throwable e) { return stackTraceToString(e); } static String renderStackTrace(String msg) { return renderStackTrace(new Throwable(msg)); } static boolean endsWithNewLine(String s) { return endsWith(s, "\n"); } static AlphanumComparator alphaNumComparatorIC_instance; static Comparator alphaNumComparatorIC() { if (alphaNumComparatorIC_instance == null) { AlphanumComparator ac = new AlphanumComparator(); ac.ignoreCase = true; alphaNumComparatorIC_instance = ac; } return alphaNumComparatorIC_instance; } static TimeZone localTimeZone() { return getTimeZone(standardTimeZone()); // TimeZone.getDefault()? } // supports the usual quotings (", variable length double brackets) except ' quoting static boolean isQuoted(String s) { if (isNormalQuoted(s)) return true; // use the exact version return isMultilineQuoted(s); } static String localDateWithSeconds(long time) { SimpleDateFormat format = simpleDateFormat_local("yyyy/MM/dd HH:mm:ss"); return format.format(time); } static String localDateWithSeconds() { return localDateWithSeconds(now()); } static String a(String noun) { if (eq(noun, "")) return "?"; return ("aeiou".indexOf(noun.charAt(0)) >= 0 ? "an " : "a ") + noun; } static String a(String contents, Object... params) { return hfulltag("a", contents, params); } static String charToString(char c) { return String.valueOf(c); } static String charToString(int c) { return String.valueOf((char) c); } static String reverseString(String s) { return empty(s) ? s : new StringBuilder(s).reverse().toString(); } // works on lists and strings and null static int indexOfIgnoreCase(List a, String b) { return indexOfIgnoreCase(a, b, 0); } static int indexOfIgnoreCase(List a, String b, int i) { int n = a == null ? 0 : a.size(); for (; i < n; i++) if (eqic(a.get(i), b)) return i; return -1; } static int indexOfIgnoreCase(String a, String b) { return indexOfIgnoreCase_manual(a, b); /*Matcher m = Pattern.compile(b, Pattern.CASE_INSENSITIVE + Pattern.LITERAL).matcher(a); if (m.find()) return m.start(); else ret -1;*/ } static int indexOfIgnoreCase(String a, String b, int i) { return indexOfIgnoreCase_manual(a, b, i); } static ArrayList cloneListSynchronizingOn(Collection l, Object mutex) { if (l == null) return new ArrayList(); synchronized(mutex) { return new ArrayList(l); } } // TODO: process CDATA, scripts static List htmlcoarsetok(String s) { List tok = new ArrayList(); int l = s == null ? 0 : s.length(); int i = 0; while (i < l) { int j = i; char c; // scan for non-tags while (j < l) { if (s.charAt(j) != '<') // regular character ++j; else if (s.substring(j, Math.min(j+4, l)).equals("")); j = Math.min(j+3, l); } else { char d = charAt(s, j+1); // character after < if (d == '/' || isLetter(d)) // it's a tag break; else ++j; } } tok.add(s.substring(i, j)); // add non-tag content i = j; if (i >= l) break; c = s.charAt(i); // scan over tag if (c == '<') { ++j; while (j < l && s.charAt(j) != '>') ++j; // TODO: strings in tag? if (j < l) ++j; } tok.add(s.substring(i, j)); // add tag i = j; } if ((tok.size() & 1) == 0) tok.add(""); return tok; } static boolean isOpeningTag(String token, String tag) { return isTag(token, tag) && !token.endsWith("/>"); } static boolean isOpeningTag(String token) { return token.startsWith("<") && token.endsWith(">") && !token.endsWith("/>") && isLetter(token.charAt(1)); } static boolean isTag(String token, String tag) { return token.regionMatches(true, 0, "<" + tag + " ", 0, tag.length()+2) || token.regionMatches(true, 0, "<" + tag + ">", 0, tag.length()+2); } static String addSuffix(String s, String suffix) { return s == null || s.endsWith(suffix) ? s : s + suffix; } static String hjavascript_src_snippet(String snippetID, Object... __) { return hfulltag("script", "", paramsPlus(__, "src" , snippetRawURL(snippetID, "text/javascript"))); } static String hreconnectingWebSockets_snippetID() { return "#2000597"; } static String javaTokDropTrailingN(String s) { List tok = javaTok(s); return empty(last(tok)) ? unnull(s) : join(dropLast(tok)); } static boolean possibleMD5(String s) { return isMD5(s); } static List map_ping(Iterable l, Object f) { return map_ping(f, l); } static List map_ping(Object f, Iterable l) { List x = emptyList(l); if (l != null) for (Object o : l) x.add(callF(f, o)); return x; } static List map_ping(Iterable l, F1 f) { return map_ping(f, l); } static List map_ping(F1 f, Iterable l) { List x = emptyList(l); if (l != null) for (A o : l) x.add(callF(f, o)); return x; } static List map_ping(IF1 f, Iterable l) { return map_ping(l, f); } static List map_ping(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map_ping(IF1 f, A[] l) { return map_ping(l, f); } static List map_ping(A[] l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map_ping(Object f, Object[] l) { return map_ping(f, asList(l)); } static List map_ping(Object[] l, Object f) { return map_ping(f, l); } static List map_ping(Object f, Map map) { return map_ping(map, f); } // map_ping: func(key, value) -> list element static List map_ping(Map map, Object f) { List x = new ArrayList(); if (map != null) for (Object _e : map.entrySet()) { ping(); Map.Entry e = (Map.Entry) _e; x.add(callF(f, e.getKey(), e.getValue())); } return x; } static List map_ping(Map map, IF2 f) { return map_ping(map, (Object) f); } static boolean endsWithLetter(String s) { return nempty(s) && isLetter(last(s)); } static String structureForUser(Object o) { return beautifyStructure(struct_noStringSharing(o)); } static Lock appendToFile_lock = lock(); static boolean appendToFile_keepOpen = false; static HashMap appendToFile_writers = new HashMap(); static void appendToFile(String path, String s) { try { Lock __0 = appendToFile_lock; lock(__0); try { // Let's just generally synchronize this to be safe. mkdirsForFile(new File(path)); path = getCanonicalPath(path); Writer writer = appendToFile_writers.get(path); if (writer == null) { //print("[Logging to " + path + "]"); writer = new BufferedWriter(new OutputStreamWriter( newFileOutputStream(path, true), "UTF-8")); if (appendToFile_keepOpen) appendToFile_writers.put(path, writer); } writer.write(s); if (!appendToFile_keepOpen) writer.close(); } finally { unlock(__0); } } catch (Exception __e) { throw rethrow(__e); } } static void appendToFile(File path, String s) { if (path != null) appendToFile(path.getPath(), s); } static void cleanMeUp_appendToFile() { AutoCloseable __3 = tempCleaningUp(); try { Lock __1 = appendToFile_lock; lock(__1); try { closeAllWriters(values(appendToFile_writers)); appendToFile_writers.clear(); } finally { unlock(__1); } } finally { _close(__3); }} static File getProgramDir() { return programDir(); } static File getProgramDir(String snippetID) { return programDir(snippetID); } static Map javaTokForJFind_array_cache = synchronizedMRUCache(1000); static String[] javaTokForJFind_array(String s) { String[] tok = javaTokForJFind_array_cache.get(s); if (tok == null) javaTokForJFind_array_cache.put(s, tok = codeTokensAsStringArray(jfind_preprocess(javaTok(s)))); return tok; } // Note: In the transpiler, this version is used: #1025802 static int findCodeTokens(List tok, String... tokens) { return findCodeTokens(tok, 1, false, tokens); } static int findCodeTokens(List tok, boolean ignoreCase, String... tokens) { return findCodeTokens(tok, 1, ignoreCase, tokens); } static int findCodeTokens(List tok, int startIdx, boolean ignoreCase, String... tokens) { return findCodeTokens(tok, startIdx, ignoreCase, tokens, null); } static HashSet findCodeTokens_specials = lithashset("*", "", "", "", "\\*"); static int findCodeTokens_bails, findCodeTokens_nonbails; static interface findCodeTokens_Matcher { boolean get(String token); } static int findCodeTokens(List tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) { int end = tok.size()-tokens.length*2+2, nTokens = tokens.length; int i = startIdx | 1; if (i >= end) return -1; // bail out early if first token not found (works great with IndexedList) String firstToken = tokens[0]; if (!ignoreCase && !findCodeTokens_specials.contains(firstToken)) { // quickly scan for first token while (i < end && !firstToken.equals(tok.get(i))) i += 2; } findCodeTokens_Matcher[] matchers = new findCodeTokens_Matcher[nTokens]; for (int j = 0; j < nTokens; j++) { String p = tokens[j]; findCodeTokens_Matcher matcher; if (p.equals("*")) matcher = t -> true; else if (p.equals("")) matcher = t -> isQuoted(t); else if (p.equals("")) matcher = t -> isIdentifier(t); else if (p.equals("")) matcher = t -> isInteger(t); else if (p.equals("\\*")) matcher = t -> t.equals("*"); else if (ignoreCase) matcher = t -> eqic(p, t); else matcher = t -> t.equals(p); matchers[j] = matcher; } outer: for (; i < end; i += 2) { for (int j = 0; j < nTokens; j++) if (!matchers[j].get(tok.get(i+j*2))) continue outer; if (condition == null || checkTokCondition(condition, tok, i-1)) // pass N index return i; } return -1; } // "$1" is first code token, "$2" second code token etc. static String jreplaceExpandRefs(String s, List tokref) { if (!contains(s, '$')) return s; List tok = javaTok(s); for (int i = 1; i < l(tok); i += 2) { String t = tok.get(i); if (t.startsWith("$") && isInteger(t.substring(1))) { String x = tokref.get(-1+parseInt(t.substring(1))*2); tok.set(i, x); } else if (t.equals("\\")) { tok.set(i, ""); i += 2; } } return join(tok); } static void clearAllTokens(List tok) { for (int i = 0; i < tok.size(); i++) tok.set(i, ""); } static void clearAllTokens(List tok, int i, int j) { for (; i < j; i++) tok.set(i, ""); } static List reTok(List tok) { replaceCollection(tok, javaTok(tok)); return tok; } static List reTok(List tok, int i) { return reTok(tok, i, i+1); } static List reTok(List tok, int i, int j) { // extend i to an "N" token // and j to "C" (so j-1 is an "N" token) i = max(i & ~1, 0); j = min(l(tok), j | 1); if (i >= j) return tok; List t = javaTok(joinSubList(tok, i, j)); replaceListPart(tok, i, j, t); // fallback to safety // reTok(tok); return tok; } static List reTok(List tok, IntRange r) { if (r != null) reTok(tok, r.start, r.end); return tok; } static void mapPut(Map map, A key, B value) { if (map != null && key != null && value != null) map.put(key, value); } static void mapPut(Map map, Pair p) { if (map != null && p != null) map.put(p.a, p.b); } static int jfind(String s, String in) { return jfind(javaTok(s), in); } static int jfind(List tok, String in) { return jfind(tok, 1, in); } static int jfind(List tok, int startIdx, String in) { return jfind(tok, startIdx, in, null); } static int jfind(List tok, String in, Object condition) { return jfind(tok, 1, in, condition); } static int jfind(List tok, String in, ITokCondition condition) { return jfind(tok, 1, in, condition); } static int jfind(List tok, int startIndex, String in, ITokCondition condition) { return jfind(tok, startIndex, in, (Object) condition); } static int jfind(List tok, int startIdx, String in, Object condition) { //LS tokin = jfind_preprocess(javaTok(in)); return jfind(tok, startIdx, javaTokForJFind_array(in), condition); } // assumes you preprocessed tokin static int jfind(List tok, List tokin) { return jfind(tok, 1, tokin); } static int jfind(List tok, int startIdx, List tokin) { return jfind(tok, startIdx, tokin, null); } static int jfind(List tok, int startIdx, String[] tokinC, Object condition) { return findCodeTokens(tok, startIdx, false, tokinC, condition); } static int jfind(List tok, int startIdx, List tokin, Object condition) { return jfind(tok, startIdx, codeTokensAsStringArray(tokin), condition); } static List jfind_preprocess(List tok) { for (String type : litlist("quoted", "id", "int")) replaceSublist(tok, ll("<", "", type, "", ">"), ll("<" + type + ">")); replaceSublist(tok, ll("\\", "", "*"), ll("\\*")); return tok; } static String stringOptPar(Object[] params, String name) { return (String) optPar(params, name); } static Object[] paramsWithout(Object[] a1, Object... keys) { if (l(a1) == 1 && a1[0] instanceof Map) return new Object[] { mapMinus((Map) a1[0], keys) }; Set set = lithashset(keys); List l = new ArrayList(); int n = l(a1); for (int i = 0; i < n; i += 2) if (i == n-1) l.add(a1[i]); else if (!set.contains(a1[i])) { l.add(a1[i]); l.add(a1[i+1]); } return toObjectArray(l); } static String himg(String src, Object... params) { return tag("img", "", arrayPlus(params, "src", isSnippetID(src) ? snippetImageLink(src) : src)); } static String himg(BufferedImage img, Object... params) { return himg(dataURL(jpegMimeType(), toJPEG(img)), params); } static String snippetImageLink(String snippetID) { return snippetImageURL(snippetID); } static double doubleRatio(double x, double y) { return y == 0 ? 0 : x/y; } static long timestampToLong(Timestamp ts) { return ts == null ? 0 : ts.date; } static int howManySecondsAgo(long timestamp) { return iround(toSeconds(now()-timestamp)); } static boolean boolOptParam(ThreadLocal tl) { return isTrue(optPar(tl)); } // defaults to false static boolean boolOptParam(Object[] __, String name) { return isTrue(optParam(__, name)); } static boolean boolOptParam(String name, Object[] __) { return boolOptParam(__, name); } static boolean boolOptParam(String name, Map __) { return isTrue(optPar(name, __)); } static String[] asStringArray(Collection c) { return toStringArray(c); } static String[] asStringArray(Object o) { return toStringArray(o); } static String hstyle(Object contents) { return hcss(contents); } static String hdiv(Object contents, Object... params) { return div(contents, params); } static String unicode_smallDownPointingTriangle() { return charToString(0x25BE); } static String[] toStringArray(Collection c) { String[] a = new String[l(c)]; Iterator it = c.iterator(); for (int i = 0; i < l(a); i++) a[i] = it.next(); return a; } static String[] toStringArray(Object o) { if (o instanceof String[]) return (String[]) o; else if (o instanceof Collection) return toStringArray((Collection) o); else throw fail("Not a collection or array: " + getClassName(o)); } static void assertTrue(Object o) { if (!(eq(o, true) /*|| isTrue(pcallF(o))*/)) throw fail(str(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 Object[] mapToObjectArray(Map map) { List l = new ArrayList(); for (Object o : keys(map)) { l.add(o); l.add(map.get(o)); } return toObjectArray(l); } static Object[] mapToObjectArray(Object f, Collection l) { int n = l(l); Object[] array = new Object[n]; if (n != 0) { Iterator it = iterator(l); for (int i = 0; i < n; i++) array[i] = callF(f, it.next()); } return array; } static Object[] mapToObjectArray(Object f, Object[] l) { int n = l(l); Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = callF(f, l[i]); return array; } static Object[] mapToObjectArray(Collection l, IF1 f) { return mapToObjectArray(f, l); } static Object[] mapToObjectArray(A[] l, IF1 f) { return mapToObjectArray(f, l); } static Object[] mapToObjectArray(IF1 f, A[] l) { int n = l(l); Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = f.get(l[i]); return array; } static Map compileRegexpIC_cache = syncMRUCache(10); static java.util.regex.Pattern compileRegexpIC(String pat) { java.util.regex.Pattern p = compileRegexpIC_cache.get(pat); if (p == null) { try { compileRegexpIC_cache.put(pat, p = java.util.regex.Pattern.compile(pat, Pattern.CASE_INSENSITIVE)); } catch (PatternSyntaxException e) { throw rethrow(wrapPatternSyntaxException(e)); } } return p; } static HashSet mapToSet(Object f, Iterable l) { return mapToHashSet(f, l); } static HashSet mapToSet(IF1 f, Iterable l) { return mapToSet((Object) f, l); } static HashSet mapToSet(IF1 f, A[] l) { return mapToSet(f, asList(l)); } static HashSet mapToSet(Iterable l, IF1 f) { return mapToSet((Object) f, l); } static List uniquifyList(Collection l) { if (l == null) return null; if (l(l) < 2) return asList(l); HashSet set = new HashSet(); List out = new ArrayList(); for (A a : l) if (set.add(a)) out.add(a); return out; } static TreeMap litCIMap(Object... x) { TreeMap map = caseInsensitiveMap(); litmap_impl(map, x); return map; } // f : Matcher -> S static String regexpReplaceIC(String s, String pat, Object f) { return regexReplaceIC(s, pat, f); } static String regexpReplaceIC(String s, String pat, String replacement) { return regexReplaceIC(s, pat, replacement); } static String regexpReplaceIC(String s, String pat, IF1 f) { return regexReplaceIC(s, pat, f); } static String formatWithThousandsSeparator(long l) { return NumberFormat.getInstance(new Locale("en_US")).format(l); } static String rep(int n, char c) { return repeat(c, n); } static String rep(char c, int n) { return repeat(c, n); } static List rep(A a, int n) { return repeat(a, n); } static List rep(int n, A a) { return repeat(n, a); } static String formatDouble(double d, int digits) { String format = digits <= 0 ? "0" : "0." + rep(digits, '#'); return decimalFormatEnglish(format, d); } static String formatDouble(double d) { return str(d); } static double toM_double(long l) { return l/(1024*1024.0); } static boolean isFile(File f) { return f != null && f.isFile(); } static boolean isFile(String path) { return isFile(newFile(path)); } static long fileSize(String path) { return getFileSize(path); } static long fileSize(File f) { return getFileSize(f); } static volatile Object isAllowed_function; // func(S, O[]) -> bool static volatile boolean isAllowed_all = true; static boolean isAllowed(String askingMethod, Object... args) { // check on VM level Object f = vm_generalMap_get("isAllowed_function"); if (f != null && !isTrue(callF(f, askingMethod, args))) return false; // check locally return isAllowed_all || isTrue(callF(isAllowed_function, askingMethod, args)); } static String hmsWithColons() { return hmsWithColons(now()); } static String hmsWithColons(long time) { return new SimpleDateFormat("HH:mm:ss").format(time); } static Object vm_generalMap_get(Object key) { return vm_generalMap().get(key); } static Object vm_generalMap_put(Object key, Object value) { return mapPutOrRemove(vm_generalMap(), key, value); } static String joinNemptiesWithColonSpace(String... strings) { return joinNempties(": ", strings); } static String joinNemptiesWithColonSpace(Collection strings) { return joinNempties(": ", strings); } static String tryToReadErrorStreamFromURLConnection(URLConnection conn) { try { if (conn instanceof HttpURLConnection) return stream2string(((HttpURLConnection) conn).getErrorStream()); // TODO: ensure some max length return null; } catch (Throwable __e) { return null; } } static Map compileRegexp_cache = syncMRUCache(10); static java.util.regex.Pattern compileRegexp(String pat) { java.util.regex.Pattern p = compileRegexp_cache.get(pat); if (p == null) { compileRegexp_cache.put(pat, p = java.util.regex.Pattern.compile(pat)); } return p; } static int parseInt(String s) { return emptyString(s) ? 0 : Integer.parseInt(s); } static int parseInt(char c) { return Integer.parseInt(str(c)); } static int boolToInt(boolean b) { return b ? 1 : 0; } static List tlft(String s) { return toLinesFullTrim(s); } static List tlft(File f) { return toLinesFullTrim(f); } static String trimSubstring(String s, int x) { return trim(substring(s, x)); } static String trimSubstring(String s, int x, int y) { return trim(substring(s, x, y)); } static String trimSubstring(String s, IntRange r) { return trim(substring(s, r)); } static String trimSubstring(int x, String s) { return trimSubstring(s, x); } static TreeMap caseInsensitiveMap() { return new TreeMap(caseInsensitiveComparator()); } static IResourceLoader vm_getResourceLoader() { return proxy(IResourceLoader.class, vm_generalMap_get("_officialResourceLoader")); } static boolean isImageServerSnippet(long id) { return id >= 1100000 && id < 1200000; } static File loadImageAsFile(String snippetIDOrURL) { try { if (isURL(snippetIDOrURL)) throw fail("not implemented"); if (!isSnippetID(snippetIDOrURL)) throw fail("Not a URL or snippet ID: " + snippetIDOrURL); String snippetID = "" + parseSnippetID(snippetIDOrURL); File file = imageSnippetCacheFile(snippetID); if (fileSize(file) > 0) return file; String imageURL = snippetImageURL_noHttps(snippetID); System.err.println("Loading image: " + imageURL); byte[] data = loadBinaryPage(imageURL); saveBinaryFile(file, data); return file; } catch (Exception __e) { throw rethrow(__e); } } // If you change this, also change DiskSnippetCache_fileToLibID static File DiskSnippetCache_file(long snippetID) { return new File(getGlobalCache(), "data_" + snippetID + ".jar"); } // Data files are immutable, use centralized cache public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException { File file = DiskSnippetCache_file(snippetID); return file.exists() ? file : null; } public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException { saveBinaryFile(DiskSnippetCache_file(snippetID), data); } static byte[] loadDataSnippetImpl(String snippetID) throws IOException { byte[] data; try { URL url = new URL(dataSnippetLink(snippetID)); print("Loading library: " + hideCredentials(url)); try { data = loadBinaryPage(url.openConnection()); } catch (RuntimeException e) { data = null; } if (data == null || data.length == 0) { url = new URL(tb_mainServer() + "/blobs/" + parseSnippetID(snippetID)); print("Loading library: " + hideCredentials(url)); data = loadBinaryPage(url.openConnection()); } print("Bytes loaded: " + data.length); } catch (FileNotFoundException e) { throw new IOException("Binary snippet #" + snippetID + " not found or not public"); } return data; } static File loadDataSnippetToFile(String snippetID) { try { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadLibrary(snippetID); return loadDataSnippetToFile_noResourceLoader(snippetID); } catch (Exception __e) { throw rethrow(__e); } } static File loadDataSnippetToFile_noResourceLoader(String snippetID) { try { snippetID = fsI(snippetID); File f = DiskSnippetCache_file(parseSnippetID(snippetID)); List urlsTried = new ArrayList(); List errors = new ArrayList(); try { URL url = addAndReturn(urlsTried, new URL(dataSnippetLink(snippetID))); print("Loading library: " + hideCredentials(url)); try { loadBinaryPageToFile(openConnection(url), f); if (fileSize(f) == 0) throw fail(); } catch (Throwable e) { errors.add(e); url = addAndReturn(urlsTried, new URL(tb_mainServer() + "/blobs/" + psI(snippetID))); print(e); print("Trying other server: " + hideCredentials(url)); loadBinaryPageToFile(openConnection(url), f); print("Got bytes: " + fileSize(f)); } // TODO: check if we hit the "LOADING" message if (fileSize(f) == 0) throw fail(); System.err.println("Bytes loaded: " + fileSize(f)); } catch (Throwable e) { //printStackTrace(e); errors.add(e); throw fail("Binary snippet " + snippetID + " not found or not public. URLs tried: " + allToString(urlsTried) + ", errors: " + allToString(errors)); } return f; } catch (Exception __e) { throw rethrow(__e); } } static void logQuotedWithTime(String s) { logQuotedWithTime(standardLogFile(), s); } static void logQuotedWithTime(File logFile, String s) { logQuoted(logFile, logQuotedWithTime_format(s)); } static void logQuotedWithTime(String logFile, String s) { logQuoted(logFile, logQuotedWithTime_format(s)); } static String logQuotedWithTime_format(String s) { return /*formatGMTWithDate_24*/(now()) + " " + s; } 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 File javaxDataDir(String... subs) { return newFile(javaxDataDir(), subs); } static Runnable addThreadInfoToRunnable(final Object r) { final Object info = _threadInfo(); return info == null ? asRunnable(r) : new Runnable() { public void run() { try { _inheritThreadInfo(info); callF(r); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_inheritThreadInfo(info); callF(r);"; }}; } // c = Component or something implementing swing() static JComponent wrap(Object swingable) { return _recordNewSwingComponent(wrap_2(swingable)); } static JComponent wrap_2(Object swingable) { if (swingable == null) return null; JComponent c; if (swingable instanceof Component) c = componentToJComponent((Component) swingable); else c = componentToJComponent((Component) callOpt(swingable, "swing")); if (c instanceof JTable || c instanceof JList || c instanceof JTextArea || c instanceof JEditorPane || c instanceof JTextPane || c instanceof JTree) return jscroll(c); return c == null ? jlabel(str(swingable)) : c; } static JTextArea wrappedTextArea(final JTextArea ta) { enableWordWrapForTextArea(ta); return ta; } static JTextArea wrappedTextArea() { return wrappedTextArea(jtextarea()); } static JTextArea wrappedTextArea(String text) { JTextArea ta = wrappedTextArea(); setText(ta, text); return ta; } static A onClick(final A c, final Object runnable) { if (c != null) { swing(new Runnable() { public void run() { try { c.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { callF(runnable, e); } }); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "c.addMouseListener(new MouseAdapter {\r\n public void mouseClicked(MouseEv..."; }}); } return c; } // re-interpreted for buttons static void onClick(JButton btn, final Object runnable) { onEnter(btn, runnable); } static void disposeWindow(final Window window) { if (window != null) { swing(new Runnable() { public void run() { try { window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING)); // call listeners myFrames_list.remove(window); window.dispose(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING)); //..."; }}); } } static void disposeWindow(final Component c) { disposeWindow(getWindow(c)); } static void disposeWindow(Object o) { if (o != null) disposeWindow(((Component) o)); } static void disposeWindow() { disposeWindow(heldInstance(Component.class)); } static Font typeWriterFont() { return typeWriterFont(iround(14*getSwingFontScale())); } static Font typeWriterFont(int size) { return new Font("Courier", Font.PLAIN, size); } static JScrollPane jscroll(final Component c) { return swing(new F0() { public JScrollPane get() { try { return new JScrollPane(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret new JScrollPane(c);"; }}); } static int withMargin_defaultWidth = 6; static JPanel withMargin(Component c) { return withMargin(withMargin_defaultWidth, c); } static JPanel withMargin(int w, Component c) { return withMargin(w, w, c); } static JPanel withMargin(int w, int h, Component c) { return withMargin(w, h, w, h, c); } static JPanel withMargin(final int top, final int left, final int bottom, final int right, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createEmptyBorder(top, left, bottom, right)); p.add(c); return p; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel p = new JPanel(new BorderLayout);\r\n p.setBorder(BorderFactory.creat..."; }}); } static Window getWindow(Object o) { if (!(o instanceof Component)) return null; return swing(() -> { Component c = (Component) o; while (c != null) { if (c instanceof Window) return ((Window) c); c = c.getParent(); } return null; }); } static Dimension getScreenSize() { return Toolkit.getDefaultToolkit().getScreenSize(); } static boolean vmBus_anyFalse(String msg, Object... args) { return contains(vmBus_queryAll(msg, args), false); } static void swingLater(long delay, final Object r) { javax.swing.Timer timer = new javax.swing.Timer(toInt(delay), actionListener(wrapAsActivity(r))); timer.setRepeats(false); timer.start(); } static void swingLater(Object r) { SwingUtilities.invokeLater(toRunnable(r)); } static int toMS_int(double seconds) { return toInt_checked((long) (seconds*1000)); } static Object dm_current_generic() { return getWeakRef(dm_current_generic_tl().get()); } static Runnable _topLevelErrorHandling(Runnable r) { if (r == null) return null; // maybe we don't want this anymore. just dm_current_generic() Object info = _threadInfo(); Object mod = dm_current_generic(); if (info == null && mod == null) return r; return new Runnable() { public void run() { try { AutoCloseable __1 = (AutoCloseable) (rcall("enter", mod)); try { _threadInheritInfo(info); r.run(); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp (AutoCloseable) rcall enter(mod);\r\n _threadInheritInfo(info);\r\n r...."; }}; } static boolean loadBufferedImage_useImageCache = true; static BufferedImage loadBufferedImage(String snippetIDOrURLOrFile) { try { ping(); if (snippetIDOrURLOrFile == null) return null; if (isURL(snippetIDOrURLOrFile)) return imageIO_readURL(snippetIDOrURLOrFile); if (isAbsolutePath(snippetIDOrURLOrFile)) return loadBufferedImage(new File(snippetIDOrURLOrFile)); if (!isSnippetID(snippetIDOrURLOrFile)) throw fail("Not a URL or snippet ID or file: " + snippetIDOrURLOrFile); String snippetID = "" + parseSnippetID(snippetIDOrURLOrFile); IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return loadBufferedImage(rl.loadLibrary(snippetID)); File dir = imageSnippetsCacheDir(); 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_http(snippetID); print("Loading image: " + imageURL); BufferedImage image = imageIO_readURL(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 (Exception __e) { throw rethrow(__e); } } static BufferedImage loadBufferedImage(File file) { return loadBufferedImageFile(file); } static Map myFrames_list = weakHashMap(); static List myFrames() { return swing(new F0>() { public List get() { try { return keysList(myFrames_list); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret keysList(myFrames_list);"; }}); } static void clear(Collection c) { if (c != null) c.clear(); } static void clear(Map map) { if (map != null) map.clear(); } static void put(Map map, A a, B b) { if (map != null) map.put(a, b); } static void put(List l, int i, A a) { if (l != null && i >= 0 && i < l(l)) l.set(i, a); } static List _registerDangerousWeakMap_preList; static A _registerDangerousWeakMap(A map) { return _registerDangerousWeakMap(map, null); } static A _registerDangerousWeakMap(A map, Object init) { callF(init, map); if (init instanceof String) { final String f = (String) init; init = new VF1() { public void get(Map map) { try { callMC(f, map) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callMC(f, map)"; }}; } if (javax() == null) { // We're in class init if (_registerDangerousWeakMap_preList == null) _registerDangerousWeakMap_preList = synchroList(); _registerDangerousWeakMap_preList.add(pair(map, init)); return map; } call(javax(), "_registerDangerousWeakMap", map, init); return map; } static void _onLoad_registerDangerousWeakMap() { assertNotNull(javax()); if (_registerDangerousWeakMap_preList == null) return; for (Pair p : _registerDangerousWeakMap_preList) _registerDangerousWeakMap(p.a, p.b); _registerDangerousWeakMap_preList = null; } static Class _getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; // could optimize this } } static Class _getClass(Object o) { return o == null ? null : o instanceof Class ? (Class) o : o.getClass(); } static Class _getClass(Object realm, String name) { try { return classLoaderForObject(realm).loadClass(classNameToVM(name)); } catch (ClassNotFoundException e) { return null; // could optimize this } } static B syncMapGet2(Map map, A a) { if (map == null) return null; synchronized(collectionMutex(map)) { return map.get(a); } } static B syncMapGet2(A a, Map map) { return syncMapGet2(map, a); } static Set reflection_classesNotToScan_value = litset( "jdk.internal.loader.URLClassPath" ); static Set reflection_classesNotToScan() { return reflection_classesNotToScan_value; } static String actualProgramID() { //try answer getProgramIDOrNull(realMC()); return programID(); } static List collect(Iterable c, String field) { return collectField(c, field); } static List collect(String field, Iterable c) { return collectField(c, field); } /*ifclass Concept static L collect(Class c, S field) { ret collect(list(c), field); } endif TODO: make translator ignore stuff in ifclass until resolved */ static void sleepInCleanUp(long ms) { try { if (ms < 0) return; Thread.sleep(ms); } catch (Exception __e) { throw rethrow(__e); } } static String f2s(File f) { return f == null ? null : f.getAbsolutePath(); } static String f2s(String s) { return f2s(newFile(s)); } static String f2s(java.nio.file.Path p) { return p == null ? null : f2s(p.toFile()); } static void copyStream(InputStream in, OutputStream out) { try { byte[] buf = new byte[65536]; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); } } catch (Exception __e) { throw rethrow(__e); } } static void _registerIO(Object object, String path, boolean opened) { } static String makeRandomID(int length) { return makeRandomID(length, defaultRandomGenerator()); } static String makeRandomID(int length, Random 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 String makeRandomID(Random r, int length) { return makeRandomID(length, r); } // This is for main classes that are all static. // (We don't go to base classes.) static Set listFields(Object c) { TreeSet fields = new TreeSet(); for (Field f : _getClass(c).getDeclaredFields()) fields.add(f.getName()); return fields; } static String n(long l, String name) { return l + " " + trim(l == 1 ? singular(name) : getPlural(name)); } static String n(Collection l, String name) { return n(l(l), name); } static String n(Map m, String name) { return n(l(m), name); } static String n(Object[] a, String name) { return n(l(a), name); } static String n(MultiSet ms, String name) { return n(l(ms), name); } static Method fastIntern_method; static String fastIntern(String s) { try { if (s == null) return null; if (fastIntern_method == null) { fastIntern_method = findMethodNamed(javax(), "internPerProgram"); if (fastIntern_method == null) upgradeJavaXAndRestart(); } return (String) fastIntern_method.invoke(null, s); } catch (Exception __e) { throw rethrow(__e); } } static String assertIsIdentifier(String s) { if (!isIdentifier(s)) throw fail("Not an identifier: " + quote(s)); return s; } static String assertIsIdentifier(String msg, String s) { if (!isIdentifier(s)) throw fail(msg + " - Not an identifier: " + quote(s)); return s; } static LinkedHashMap syncMapPut2_createLinkedHashMap(LinkedHashMap map, A key, B value) { if (key != null) if (value != null) { if (map == null) map = new LinkedHashMap(); synchronized(collectionMutex(map)) { map.put(key, value); } } else if (map != null) synchronized(collectionMutex(map)) { map.remove(key); } return map; } static > C syncMapRemove_deleteMapIfEmpty(C map, A key) { if (map != null && key != null) synchronized(collectionMutex(map)) { map.remove(key); if (map.isEmpty()) return null; } return map; } static boolean isInAnonymousClass(Object o) { if (o == null) return false; return isAnonymousClassName(className(o)); } static List javaTok_noMLS(String s) { ArrayList tok = new ArrayList(); int l = s == null ? 0 : s.length(); int i = 0, n = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(javaTok_substringN(s, i, j)); ++n; i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { int c2 = s.charAt(j); if (c2 == opener || c2 == '\n' && opener == '\'') { // allow multi-line strings, but not for ' ++j; break; } else if (c2 == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))); 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 ++j; tok.add(javaTok_substringC(s, i, j)); ++n; i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } static float clamp(float x, float a, float b) { return x < a ? a : x > b ? b : x; } static double clamp(double x, double a, double b) { return x < a ? a : x > b ? b : x; } static int clamp(int x, int a, int b) { return x < a ? a : x > b ? b : x; } static long clamp(long x, long a, long b) { return x < a ? a : x > b ? b : x; } static boolean any(Object pred, Iterable l) { if (l != null) for (A a : l) if (isTrue(callF(pred, a))) return true; return false; } static boolean any(IF1 pred, Iterable l) { return any((Object) pred, l); } static boolean any(Iterable l, IF1 pred) { return any(pred, l); } static boolean any(A[] l, IF1 pred) { if (l != null) for (A a : l) if (pred.get(a)) return true; return false; } static boolean any(Iterable l) { if (l != null) for (Boolean a : l) if (isTrue(a)) return true; return false; } static List filterConcepts(List list, Object... params) { if (empty(params)) return list; List l = new ArrayList(); for (A x : list) if (checkConceptFields(x, params)) l.add(x); return l; } static A firstOfPair(Pair p) { return p == null ? null : p.a; } static Class primitiveToBoxedType(Class type) { if (type == boolean.class) return Boolean.class; if (type == int.class) return Integer.class; if (type == long.class) return Long.class; if (type == float.class) return Float.class; if (type == short.class) return Short.class; if (type == char.class) return Character.class; if (type == byte.class) return Byte.class; if (type == double.class) return Double.class; return null; } static Object dm_os() { { Object __1= vm_generalMap_get("stefansOS"); if (__1 != null) return __1; } return creator(); } static DynModule dm_current_mandatory() { return assertNotNull("No module set!", dm_currentModule()); } static Set synchronizedSet() { return synchroHashSet(); } static Set synchronizedSet(Set set) { return Collections.synchronizedSet(set); } static Set identityHashSet() { return Collections.newSetFromMap(new IdentityHashMap()); } static Iterator iterator(Iterable c) { return c == null ? emptyIterator() : c.iterator(); } static char charAt(String s, int i) { return s != null && i >= 0 && i < s.length() ? s.charAt(i) : '\0'; } static A _get(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } static Object _get(Object o, String field) { return get(o, field); } static Object _get(String field, Object o) { return get(o, field); } static A _get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } static Class __javax; static Class getJavaX() { try { return __javax; } catch (Exception __e) { throw rethrow(__e); } } static Object defaultDefaultClassFinder() { return new F1() { public Class get(String name) { Class c = get2(name); return c; } Class get2(String name) { // special invocation to find main class irrelevant of name if (eq(name, "
")) return mc(); { Class c = findClass_fullName(name); if (c != null) return c; } if (startsWithAny(name, "loadableUtils.utils$", "main$", mcDollar())) for (String pkg : ll("loadableUtils.utils$", mcDollar())) { String newName = pkg + afterDollar(name); { Class c = findClass_fullName(newName); if (c != null) return c; } } return null; } }; } static volatile String caseID_caseID; static String caseID() { return caseID_caseID; } static void caseID(String id) { caseID_caseID = id; } static String quoteUnlessIdentifierOrInteger(String s) { return quoteIfNotIdentifierOrInteger(s); } static Field getField(Object o, String field) { if (o == null) return null; return setOpt_findField(_getClass(o), field); } static Object getField(Field field, Object o) { return fieldGet(field, o); } static A proxyFromInvocationHandler(Class intrface, InvocationHandler handler) { return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, handler); } static Throwable getExceptionCause(Throwable e) { Throwable c = e.getCause(); return c != null ? c : e; } static boolean methodIsStatic(Method m) { return (m.getModifiers() & Modifier.STATIC) != 0; } static boolean argumentCompatibleWithType(Object arg, Class type) { return arg == null ? !type.isPrimitive() : isInstanceX(type, arg); } static void arraycopy(Object[] a, Object[] b) { if (a != null && b != null) arraycopy(a, 0, b, 0, Math.min(a.length, b.length)); } static void arraycopy(Object src, int srcPos, Object dest, int destPos, int n) { if (n != 0) System.arraycopy(src, srcPos, dest, destPos, n); } static A[] arrayOfType(Class type, int n) { return makeArray(type, n); } static A[] arrayOfType(int n, Class type) { return arrayOfType(type, n); } static List getClassNames(Collection l) { List out = new ArrayList(); if (l != null) for (Object o : l) out.add(o == null ? null : getClassName(o)); return out; } static Object callFunction(Object f, Object... args) { return callF(f, args); } static Throwable unwrapTrivialExceptionWraps(Throwable e) { if (e == null) return e; while (e.getClass() == RuntimeException.class && e.getCause() != null && eq(e.getMessage(), str(e.getCause()))) e = e.getCause(); return e; } static String replacePrefix(String prefix, String replacement, String s) { if (!startsWith(s, prefix)) return s; return replacement + substring(s, l(prefix)); } static Throwable innerException2(Throwable e) { if (e == null) return null; while (empty(e.getMessage()) && e.getCause() != null) e = e.getCause(); return e; } static Throwable getException(Runnable r) { try { callF(r); return null; } catch (Throwable e) { return e; } } static Class hotwire_overBot(String src) { return hotwire(src); } static A callMain(A c, String... args) { callOpt(c, "main", new Object[] {args}); return c; } static void callMain() { callMain(mc()); } static ThreadLocal assertVerbose_value = new ThreadLocal(); static void assertVerbose(boolean b) { assertVerbose_value.set(b); } static boolean assertVerbose() { return isTrue(assertVerbose_value.get()); } static A assertEqualsVerbose(Object x, A y) { assertEqualsVerbose((String) null, x, y); return y; } // x = expected, y = actual static A assertEqualsVerbose(String msg, Object x, A y) { if (!eq(x, y)) { throw fail((nempty(msg) ? msg + ": " : "") + "expected: "+ x + ", got: " + y); } else print("OK" + (empty(msg) ? "" : " " + msg) + ": " + /*sfu*/(x)); return y; } static void assertEqualsVerbose(Scorer scorer, Object x, Object y) { assertEqualsVerbose(scorer, "", x, y); } static void assertEqualsVerbose(Scorer scorer, String msg, Object x, Object y) { if (scorer == null) { assertEqualsVerbose(x, y); return; } if (!eq(x, y)) { print(appendColonIfNempty(msg) + y + " != " + x); scorer.add(false); } else { print("OK: " + appendColonIfNempty(msg) + x); scorer.add(true); } } static String nullIfEmpty(String s) { return isEmpty(s) ? null : s; } static Map nullIfEmpty(Map map) { return isEmpty(map) ? null : map; } static List nullIfEmpty(List l) { return isEmpty(l) ? null : l; } static A liftLast(List l) { if (empty(l)) return null; int i = l(l)-1; A a = l.get(i); l.remove(i); return a; } static List liftLast(int n, List l) { int i = l(l)-n; List part = cloneSubList(l, i); removeSubList(l, i); return part; } static Class actualMC() { return or((Class) realMC(), mc()); } static boolean isAnonymousClassName(String s) { for (int i = 0; i < l(s); i++) if (s.charAt(i) == '$' && Character.isDigit(s.charAt(i+1))) return true; return false; } // Note: This is actually broken. Inner classes must stay with a $ separator static String classNameToVM(String name) { return name.replace(".", "$"); } static byte[] boolArrayToBytes(boolean[] a) { byte[] b = new byte[(l(a)+7)/8]; for (int i = 0; i < l(a); i++) if (a[i]) b[i/8] |= 1 << (i & 7); return b; } static List> mapPairB(final Object f, Iterable> l) { return map(l, new F1, Pair>() { public Pair get(Pair p) { try { return p == null ? null : pair(p.a, (C) callF(f, p.b)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "p == null ? null : pair(p.a, (C) callF(f, p.b))"; }}); } static List> mapPairB(final F1 f, Iterable> l) { return mapPairB((Object) f, l); } static List> mapPairB(final IF1 f, Iterable> l) { return mapPairB((Object) f, l); } static List> mapPairB(Iterable> l, IF1 f) { return mapPairB((Object) f, l); } static Pair mapPairB(IF1 f, Pair p) { return pairMapB(f, p); } static Pair mapPairB(Pair p, IF1 f) { return pairMapB(f, p); } static boolean isDigit(char c) { return Character.isDigit(c); } static Object vmBus_query(String msg, Object... args) { Object arg = vmBus_wrapArgs(args); { Object __1= pcallFAll_returnFirstNotNull(vm_busListeners_live(), msg, arg); if (__1 != null) return __1; } return pcallFAll_returnFirstNotNull(vm_busListenersByMessage_live().get(msg), msg, arg); } static Object vmBus_query(String msg) { return vmBus_query(msg, (Object) null); } static Object callOpt_withVarargs(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(method, args); if (me == null) { // TODO: varargs return null; } if ((me.getModifiers() & Modifier.STATIC) == 0) return null; return invokeMethod(me, null, args); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(method, args); if (me != null) return invokeMethod(me, o, args); // try varargs List methods = cache.cache.get(method); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return invokeMethod(m, o, newArgs); } return null; } } catch (Exception __e) { throw rethrow(__e); } } static void setDynObjectValue(DynamicObject o, String field, Object value) { dynamicObject_setRawFieldValue(o, field, value); } static void metaMapPut(IMeta o, Object key, Object value) { if (o == null || key == null) return; Map map = convertObjectMetaToMap(o); syncMapPutOrRemove(map, key, value); } // untested... /*svoid metaMapPut(IMeta o, O key, O value, IMapImplementationPolicy policy) { if (o == null || key == null) ret; if (policy == null) ret with metaMapPut(o, key, value); // no shortcuts in this function, always get mutex & lock temp var mutex = tempMetaMutex(o); synchronized(mutex!) { O meta = o._getMeta(); // create map if necessary Map map = null; if (meta cast Map) map = meta; else if (meta != null) map = policy.put(map, previousMeta := meta); // putOrRemove through policy if (value != null) map = policy.put(map, key, value); else map = policy.remove(map, key); if (map != meta) o._setMeta(map); } }*/ static Comparator makeComparator(final Object f) { if (f instanceof Comparator) return (Comparator) f; return new Comparator() { public int compare(Object a, Object b) { return (Integer) callF(f, a, b); } }; } static List allMethodNames(Object o) { Class c = _getClass(o); TreeSet names = new TreeSet(); while (c != null) { for (Method m : c.getDeclaredMethods()) names.add(m.getName()); c = c.getSuperclass(); } return asList(names); } static A printException(A e) { printStackTrace(e); return e; } static A assertNotNull(A a) { assertTrue(a != null); return a; } static A assertNotNull(String msg, A a) { assertTrue(msg, a != null); return a; } static A assertNotNull(Scorer scorer, String msg, A a) { if (scorer == null) return assertNotNull(msg, a); if (a == null) { print("BAD - " + msg + " is null: " + a); scorer.add(false); } else { print("OK, " + msg + " not null: " + a); scorer.add(true); } return a; } static boolean isTrueOpt(Object o) { if (o instanceof Boolean) return ((Boolean) o).booleanValue(); return false; } static boolean isTrueOpt(String field, Object o) { return isTrueOpt(getOpt(field, o)); } static List isYes_yesses = litlist("y", "yes", "yeah", "y", "yup", "yo", "corect", "sure", "ok", "afirmative"); // << collapsed words, so "corect" means "correct" static boolean isYes(String s) { return isYes_yesses.contains(collapseWord(toLowerCase(firstWord2(s)))); } static Integer boxed(int i) { return i; } static Long boxed(long l) { return l; } static String dropAfterSubstring(String s, String b) { return substring(s, 0, smartIndexOf(s, b)); } static x30_pkg.x30_util.BetterThreadLocal newPing_actionTL; static x30_pkg.x30_util.BetterThreadLocal newPing_actionTL() { if (newPing_actionTL == null) newPing_actionTL = vm_generalMap_getOrCreate("newPing_actionTL", () -> new x30_pkg.x30_util.BetterThreadLocal()); return newPing_actionTL; } static volatile boolean licensed_yes = true; static boolean licensed() { if (!licensed_yes) return false; ping_okInCleanUp(); return true; } static void licensed_off() { licensed_yes = false; } static Map newWeakMap() { return newWeakHashMap(); } static Throwable _storeException_value; static void _storeException(Throwable e) { _storeException_value = e; } static Map syncHashMap() { return synchroHashMap(); } static Object[] unrollParams(Object[] params) { if (l(params) == 1 && params[0] instanceof Map) return mapToParams((Map) params[0]); return params; } static Object html_valueLessParam_cache; static Object html_valueLessParam() { if (html_valueLessParam_cache == null) html_valueLessParam_cache = html_valueLessParam_load(); return html_valueLessParam_cache; } static Object html_valueLessParam_load() { return new Object(); } static String htmlQuote(String s) { return "\"" + htmlencode_forParams(s) + "\""; } static boolean isAbsoluteURL(String s) { return isURL(s); } static boolean isRelativeURL(String s) { return startsWithOneOf(s, "/", "./", "../"); } static A[] arrayOfSameType(A[] a, int n) { return newObjectArrayOfSameType(a, n); } static String[] drop(int n, String[] a) { n = Math.min(n, a.length); String[] b = new String[a.length-n]; System.arraycopy(a, n, b, 0, b.length); return b; } static Object[] drop(int n, Object[] a) { n = Math.min(n, a.length); Object[] b = new Object[a.length-n]; System.arraycopy(a, n, b, 0, b.length); return b; } static ArrayList toList(A[] a) { return asList(a); } static ArrayList toList(int[] a) { return asList(a); } static ArrayList toList(Set s) { return asList(s); } static ArrayList toList(Iterable s) { return asList(s); } static Object[] changeParam(Object[] params, String name, Object value) { if (eq(optPar(params, name), value)) return params; Map map = paramsToOrderedMap(params); map.put(name, value); return mapToParams(map); } static Object mainBot() { return getMainBot(); } static String jsonEncode_breakAtLevel1(Object o) { StringBuilder buf = new StringBuilder(); jsonEncode_breakAtLevel1(o, buf, 1); return str(buf); } static void jsonEncode_breakAtLevel1(Object o, StringBuilder buf, int level) { if (o == null) buf.append("null"); else if (o instanceof String) buf.append(quote((String) o)); else if (o instanceof Number || o instanceof Boolean) buf.append(o); else if (o instanceof Map) { Map map = (Map) o; buf.append("{"); boolean first = true; for (Object key : keys(map)) { if (first) first = false; else buf.append(","); if (level == 1) buf.append("\n "); buf.append(quote((String) key)); buf.append(":"); jsonEncode_breakAtLevel1(map.get(key), buf, level+1); } if (level == 1) buf.append("\n"); buf.append("}"); } else if (o instanceof Collection) { Collection l = (Collection) o; buf.append("["); boolean first = true; for (Object element : l) { if (first) first = false; else buf.append(","); if (level == 1) buf.append("\n "); jsonEncode_breakAtLevel1(element, buf, level+1); } if (level == 1) buf.append("\n"); buf.append("]"); } else throw fail("Unknown object for JSON encoding: " + className(o)); } // f: A -> Comparable static List sortByCalculatedFieldDesc_inPlace(List l, final Object f) { sort(l, new Comparator() { public int compare(A b, A a) { return stdcompare((Object) callF(f, a), (Object) callF(f, b)); } }); return l; } static List sortByCalculatedFieldDesc_inPlace(Object f, List c) { return sortByCalculatedFieldDesc_inPlace(c, f); } static boolean eqicOrEq(Object a, Object b) { return a instanceof String && b instanceof String ? eqic((String) a, (String) b) : eq(a, b); } static boolean isSpace(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } // not a very good one static boolean instanceOf(Object o, String className) { if (o == null) return false; String c = o.getClass().getName(); return eq(c, className) || eq(c, "main$" + className); } // better static boolean instanceOf(Object o, Class c) { if (c == null) return false; return c.isInstance(o); } static boolean instanceOf(Class c, Object o) { return instanceOf(o, c); } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } static String _computerID; static Lock computerID_lock = lock(); public static String computerID() { if (_computerID == null) { Lock __0 = computerID_lock; lock(__0); try { if (_computerID != null) return _computerID; File file = computerIDFile(); _computerID = loadTextFile(file.getPath()); if (_computerID == null) { // legacy load _computerID = loadTextFile(userDir(".tinybrain/computer-id")); if (_computerID == null) _computerID = makeRandomID(12, new SecureRandom()); saveTextFile(file, _computerID); } } finally { unlock(__0); } } return _computerID; } static boolean domainIsUnder(String domain, String mainDomain) { return eqic(domain, mainDomain) || ewic(domain, "." + mainDomain); } static String theAGIBlueDomain() { return "agi.blue"; } static Producer javaTokC_noMLS_iterator(final String s) { return javaTokC_noMLS_iterator(s, 0); } static Producer javaTokC_noMLS_iterator(final String s, final int startIndex) { return new Producer() { final int l = s.length(); int i = startIndex; public String next() { if (i >= l) return null; int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } i = j; if (i >= l) return null; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // 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))); 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 ++j; String t = quickSubstring(s, i, j); i = j; return t; } }; } static Producer javaTokC_noMLS_onReader(final BufferedReader r) { final class X implements Producer { StringBuilder buf = new StringBuilder(); // stores from "i" char c, d, e = 'x'; // just not '\0' X() { // fill c, d and e nc(); nc(); nc(); } // get next character(s) into c, d and e void nc() { try { c = d; d = e; if (e == '\0') return; int i = r.read(); e = i < 0 ? '\0' : i == '\0' ? '_' // shouldn't happen anymore : (char) i; } catch (Exception __e) { throw rethrow(__e); } } void ncSave() { if (c != '\0') { buf.append(c); nc(); } } public String next() { // scan for whitespace while (c != '\0') { if (c == ' ' || c == '\t' || c == '\r' || c == '\n') nc(); else if (c == '/' && d == '*') { do nc(); while (c != '\0' && !(c == '*' && d == '/')); nc(); nc(); } else if (c == '/' && d == '/') { do nc(); while (c != '\0' && "\r\n".indexOf(c) < 0); } else break; } if (c == '\0') return null; // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ncSave(); while (c != '\0') { if (c == opener || c == '\n') { // end at \n to not propagate unclosed string literal errors ncSave(); break; } else if (c == '\\') { ncSave(); ncSave(); } else ncSave(); } } else if (Character.isJavaIdentifierStart(c)) do ncSave(); while (Character.isJavaIdentifierPart(c) || c == '\''); // for stuff like "don't" else if (Character.isDigit(c)) { do ncSave(); while (Character.isDigit(c)); if (c == 'L') ncSave(); // Long constants like 1L } else ncSave(); String t = buf.toString(); buf.setLength(0); return t; } } return new X(); } static HashMap findClass_fullName_cache = new HashMap(); // returns null on not found // this is the simple version that is not case-tolerant static Class findClass_fullName(String name) { synchronized(findClass_fullName_cache) { if (findClass_fullName_cache.containsKey(name)) return findClass_fullName_cache.get(name); Class c; try { c = Class.forName(name); } catch (ClassNotFoundException e) { c = null; } findClass_fullName_cache.put(name, c); return c; } } static String unquoteUsingCharArray(String s, char[] buf) { if (s == null) return null; if (startsWith(s, '[')) { 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.length() > 1) { char c = s.charAt(0); if (c == '\"' || c == '\'') { int l = endsWith(s, c) ? s.length()-1 : s.length(); if (l > buf.length) return unquote(s); // fallback int n = 0; for (int i = 1; i < l; i++) { char ch = s.charAt(i); if (ch == '\\') { char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; } } buf[n++] = (char) Integer.parseInt(code, 8); continue; } switch (nextChar) { case '\"': ch = '\"'; break; 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; // Hex Unicode: u???? case 'u': if (i >= l - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.charAt(i + 5), 16); char[] x = Character.toChars(code); int lx = x.length; for (int j = 0; j < lx; j++) buf[n++] = x[j]; i += 5; continue; default: ch = nextChar; // added by Stefan } i++; } buf[n++] = ch; } return new String(buf, 0, n); } } return s; // not quoted - return original } static boolean structure_isMarker(String s, int i, int j) { if (i >= j) return false; if (s.charAt(i) != 'm') return false; ++i; while (i < j) { char c = s.charAt(i); if (c < '0' || c > '9') return false; ++i; } return true; } static String internIfLongerThan(String s, int l) { return s == null ? null : l(s) >= l ? intern(s) : s; } 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 BigInteger parseBigInt(String s) { return new BigInteger(s); } static float parseFloat(String s) { return Float.parseFloat(s); } static boolean isLongConstant(String s) { if (!s.endsWith("L")) return false; s = s.substring(0, l(s)-1); return isInteger(s); } static boolean warn_on = true; static ThreadLocal> warn_warnings = new ThreadLocal(); static void warn(String s) { if (warn_on) print("Warning: " + s); } static void warn(String s, List warnings) { warn(s); if (warnings != null) warnings.add(s); addToCollection(warn_warnings.get(), s); } static List parseList(String s) { return (List) safeUnstructure(s); } static List synchroLinkedList() { return Collections.synchronizedList(new LinkedList()); } static NavigableMap synchroNavigableMap(NavigableMap map) { return (NavigableMap) call(Collections.class, "synchronizedNavigableMap", map); } static SortedMap synchroSortedMap(SortedMap map) { return Collections.synchronizedSortedMap(map); } static byte[] hexToBytes(String s) { if (odd(l(s))) throw fail("Hex string has odd length: " + quote(shorten(10, s))); int n = l(s) / 2; byte[] bytes = new byte[n]; for (int i = 0; i < n; i++) { int a = parseHexChar(s.charAt(i*2)); int b = parseHexChar(s.charAt(i*2+1)); if (a < 0 || b < 0) throw fail("Bad hex byte: " + quote(substring(s, i*2, i*2+2)) + " at " + i*2 + "/" + l(s)); bytes[i] = (byte) ((a << 4) | b); } return bytes; } static boolean[] boolArrayFromBytes(byte[] a, int n) { boolean[] b = new boolean[n]; int m = min(n, l(a)*8); for (int i = 0; i < m; i++) b[i] = (a[i/8] & 1 << (i & 7)) != 0; return b; } static boolean isAbstract(Class c) { return (c.getModifiers() & Modifier.ABSTRACT) != 0; } static boolean isAbstract(Method m) { return (m.getModifiers() & Modifier.ABSTRACT) != 0; } static Constructor nuStubInnerObject_findConstructor(Class c) { return nuStubInnerObject_findConstructor(c, null); } static Constructor nuStubInnerObject_findConstructor(Class c, Object classFinder) { try { Class outerType = getOuterClass(c, classFinder); Constructor m = c.getDeclaredConstructor(outerType); makeAccessible(m); return m; } catch (Exception __e) { throw rethrow(__e); } } static Map nuEmptyObject_cache = newDangerousWeakHashMap(); static A nuEmptyObject(Class c) { try { Constructor ctr; synchronized(nuEmptyObject_cache) { ctr = nuEmptyObject_cache.get(c); if (ctr == null) { nuEmptyObject_cache.put(c, ctr = nuEmptyObject_findConstructor(c)); makeAccessible(ctr); } } try { return (A) ctr.newInstance(); } catch (InstantiationException e) { if (empty(e.getMessage())) if ((c.getModifiers() & Modifier.ABSTRACT) != 0) throw fail("Can't instantiate abstract class " + className(c), e); else throw fail("Can't instantiate " + className(c), e); else throw rethrow(e); } } catch (Exception __e) { throw rethrow(__e); } } static Constructor nuEmptyObject_findConstructor(Class c) { for (Constructor m : c.getDeclaredConstructors()) if (m.getParameterTypes().length == 0) return m; throw fail("No default constructor declared in " + c.getName()); } static void setOptAllDyn_pcall(DynamicObject o, Map fields) { if (fields == null || o == null) return; HashMap fieldMap = instanceFieldsMap(o); for (Map.Entry e : fields.entrySet()) { try { String field = e.getKey(); Object val = e.getValue(); Field f = fieldMap.get(field); if (f != null) smartSet(f, o, val); else { dynamicObject_setRawFieldValue(o, intern(field), val); } } catch (Throwable __e) { _handleException(__e); }} } static void setOptAll_pcall(Object o, Map fields) { if (fields == null) return; for (String field : keys(fields)) try { setOpt(o, field, fields.get(field)); } catch (Throwable __e) { print(exceptionToStringShort(__e)); } } static void setOptAll_pcall(Object o, Object... values) { //values = expandParams(c.getClass(), values); warnIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; try { setOpt(o, field, value); } catch (Throwable __e) { print(exceptionToStringShort(__e)); } } } static void fixOuterRefs(Object o) { try { if (o == null) return; Field[] l = thisDollarOneFields(o.getClass()); if (l.length <= 1) return; Object father = null; for (Field f : l) { father = f.get(o); if (father != null) break; } if (father == null) return; for (Field f : l) f.set(o, father); } catch (Exception __e) { throw rethrow(__e); } } static void pcallOpt_noArgs(Object o, String method) { try { callOpt_noArgs(o, method); } catch (Throwable __e) { _handleException(__e); } } static RuntimeException todo() { throw new RuntimeException("TODO"); } static RuntimeException todo(Object msg) { throw new RuntimeException("TODO: " + msg); } static Object newMultiDimensionalOuterArray(Class elementType, int dimensions, int length) { int[] dims = new int[dimensions]; dims[0] = length; return Array.newInstance(elementType, dims); } static int[] toIntArray(Collection l) { int[] a = new int[l(l)]; int i = 0; if (a.length != 0) for (int x : l) a[i++] = x; return a; } static double[] toDoubleArray(Collection l) { double[] a = new double[l(l)]; int i = 0; if (a.length != 0) for (double x : l) a[i++] = x; return a; } static TreeSet ciSet() { return caseInsensitiveSet(); } // DIFFERENCES to jfind: always ignores case, doesn't recognize etc // You probably want jmatch2 static boolean jmatch(String pat, String s) { return jmatch(pat, s, null); } static boolean jmatch(String pat, String s, Matches matches) { if (s == null) return false; return jmatch(pat, javaTok(s), matches); } static boolean jmatch(String pat, List toks) { return jmatch(pat, toks, null); } static boolean jmatch(String pat, List toks, Matches matches) { List tokpat = javaTok(pat); 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 ThreadLocal DynamicObject_loading = or((ThreadLocal) get(getClass("x30_pkg.x30_util"), "DynamicObject_loading"), new ThreadLocal()); static ThreadLocal dynamicObjectIsLoading_threadLocal() { return DynamicObject_loading; } static BufferedReader utf8BufferedReader(InputStream in) { return utf8bufferedReader(in); } static BufferedReader utf8BufferedReader(File f) { return utf8bufferedReader(f); } static String stackTraceToString(StackTraceElement[] st) { return lines(st); } static String stackTraceToString(Throwable e) { return getStackTrace_noRecord(e); } static TimeZone getTimeZone(String name) { return TimeZone.getTimeZone(name); } static String standardTimeZone_name = "Europe/Berlin"; static String standardTimeZone() { return standardTimeZone_name; } static boolean isNormalQuoted(String s) { int l = l(s); if (!(l >= 2 && s.charAt(0) == '"' && lastChar(s) == '"')) return false; int j = 1; while (j < l) if (s.charAt(j) == '"') return j == l-1; else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; return false; } static boolean isMultilineQuoted(String s) { if (!startsWith(s, "[")) return false; int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; return i < s.length() && s.charAt(i) == '['; } static int indexOfIgnoreCase_manual(String a, String b) { return indexOfIgnoreCase_manual(a, b, 0); } static int indexOfIgnoreCase_manual(String a, String b, int i) { int la = strL(a), lb = strL(b); if (la < lb) return -1; int n = la-lb; loop: for (; i <= n; i++) { for (int j = 0; j < lb; j++) { char c1 = a.charAt(i+j), c2 = b.charAt(j); if (!eqic(c1, c2)) continue loop; } return i; } return -1; } static boolean isLetter(char c) { return Character.isLetter(c); } static String snippetRawURL(String snippetID) { return snippetRawURL(snippetID, null); } static String snippetRawURL(String snippetID, String contentType) { return "https://botcompany.de/serve/" + psI(snippetID) + htmlQuery("ct" , contentType); } static boolean isMD5(String s) { return l(s) == 32 && isLowerHexString(s); } static String beautifyStructure(String s) { List tok = javaTokForStructure(s); structure_addTokenMarkers(tok); jreplace(tok, "lhm", ""); return join(tok); } static String struct_noStringSharing(Object o) { structure_Data d = new structure_Data(); d.noStringSharing = true; return structure(o, d); } static String getCanonicalPath(File f) { try { return f == null ? null : f.getCanonicalPath(); } catch (Exception __e) { throw rethrow(__e); } } static String getCanonicalPath(String path) { return getCanonicalPath(newFile(path)); } static AutoCloseable tempCleaningUp() { return tempSetTL(ping_isCleanUpThread, true); } static void closeAllWriters(Collection l) { for (Writer w : unnull(l)) { try { w.close(); } catch (Throwable __e) { _handleException(__e); }} } static File programDir_mine; // set this to relocate program's data static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { boolean me = sameSnippetID(snippetID, programID()); if (programDir_mine != null && me) return programDir_mine; File dir = new File(javaxDataDir(), formatSnippetIDOpt(snippetID)); if (me) { String c = caseID(); if (nempty(c)) dir = newFile(dir, c); } return dir; } static File programDir(String snippetID, String subPath) { return new File(programDir(snippetID), subPath); } static Map synchronizedMRUCache(int maxSize) { return synchroMap(new MRUCache(maxSize)); } static String[] codeTokensAsStringArray(List tok) { int n = max(0, (l(tok)-1)/2); String[] out = new String[n]; for (int i = 0; i < n; i++) out[i] = tok.get(i*2+1); return out; } static boolean checkTokCondition(Object condition, List tok, int i) { if (condition instanceof TokCondition) return ((TokCondition) condition).get(tok, i); return checkCondition(condition, tok, i); } static void replaceCollection(Collection dest, Collection src) { if (dest == src) return; dest.clear(); if (src != null) dest.addAll(src); } static void replaceListPart(List l, int i, int j, List l2) { replaceSublist(l, i, j, l2); } // syntax 1: replace all occurrences of x in l with y static List replaceSublist(List l, List x, List y) { if (x == null) return l; int i = 0; while (true) { i = indexOfSubList(l, x, i); if (i < 0) break; replaceSublist(l, i, i+l(x), y); i += l(y); } return l; } // syntax 2: splice l at fromIndex-toIndex and replace middle part with y static List replaceSublist(List l, int fromIndex, int toIndex, List y) { int n = y.size(), toIndex_new = fromIndex+n; if (toIndex_new < toIndex) { removeSubList(l, toIndex_new, toIndex); copyListPart(y, 0, l, fromIndex, n); } else { copyListPart(y, 0, l, fromIndex, toIndex-fromIndex); if (toIndex_new > toIndex) l.addAll(toIndex, subList(y, toIndex-fromIndex)); } return l; } static List replaceSublist(List l, IntRange r, List y) { return replaceSublist(l, r.start, r.end, y); } static Map mapMinus(Map map, Object... keys) { if (empty(keys)) return map; Map m2 = cloneMap(map); for (Object key : keys) m2.remove(key); return m2; } static String dataURL(String mimeType, byte[] data) { return "data:" + mimeType + ";base64," + base64(data); } static String jpegMimeType() { return "image/jpeg"; } static byte[] toJPEG(BufferedImage img) { try { ByteArrayOutputStream stream = new ByteArrayOutputStream(); // need to drop alpha channel to avoid JDK bug ImageIO.write(dropAlphaChannelFromBufferedImage(img), "jpeg", stream); return stream.toByteArray(); } catch (Exception __e) { throw rethrow(__e); } } static byte[] toJPEG(File f) { return toJPEG(loadImage2(f)); } static String snippetImageURL(long snippetID) { return snippetImageURL(fsI(snippetID)); } static String snippetImageURL(String snippetID) { return snippetImageURL(snippetID, "png"); } static String snippetImageURL(String snippetID, String contentType) { if (snippetID == null || isURL(snippetID)) return snippetID; long id = parseSnippetID(snippetID); String url; if (isImageServerSnippet(id)) url = imageServerLink(id); else //url = "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + id + "&contentType=image/" + contentType; url = "https://botcompany.de/img/" + id; return url; } static Map syncMRUCache(int size) { return synchroMap(new MRUCache(size)); } static RuntimeException wrapPatternSyntaxException(PatternSyntaxException e) { if (e == null) return null; String pat = e.getPattern(); int i = e.getIndex(); return new RuntimeException("Regular expression error between " + multiLineQuoteWithSpaces(substring(pat, 0, i)) + " and " + multiLineQuoteWithSpaces(substring(pat, i)) + " - " + e.getMessage()); } static HashSet mapToHashSet(Object f, Iterable l) { HashSet x = new HashSet(l(l)); if (l != null) for (Object o : l) x.add(callF(f, o)); return x; } static HashSet mapToHashSet(IF1 f, Iterable l) { return mapToHashSet((Object) f, l); } // f : Matcher -> S static String regexReplaceIC(String s, String pat, Object f) { return regexReplace(regexpMatcherIC(pat, s), f); } static String regexReplaceIC(String s, String pat, String replacement) { return regexpReplaceIC_direct(s, pat, replacement); } static String repeat(char c, int n) { n = Math.max(n, 0); char[] chars = new char[n]; for (int i = 0; i < n; i++) chars[i] = c; return new String(chars); } static List repeat(A a, int n) { n = Math.max(n, 0); List l = new ArrayList(n); for (int i = 0; i < n; i++) l.add(a); return l; } static List repeat(int n, A a) { return repeat(a, n); } static String decimalFormatEnglish(String format, double d) { return decimalFormatEnglish(format).format(d); } static java.text.DecimalFormat decimalFormatEnglish(String format) { return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)); } static Map vm_generalMap_map; static Map vm_generalMap() { if (vm_generalMap_map == null) vm_generalMap_map = (Map) get(javax(), "generalMap"); return vm_generalMap_map; } static B mapPutOrRemove(Map map, A key, B value) { if (map != null && key != null) if (value != null) return map.put(key, value); else return map.remove(key); return null; } static String stream2string(InputStream in) { return utf8streamToString(in); } static boolean emptyString(String s) { return s == null || s.length() == 0; } static List toLinesFullTrim(String s) { List l = new ArrayList(); for (String line : toLines(s)) if (nempty(line = trim(line))) l.add(line); return l; } static List toLinesFullTrim(File f) { List l = new ArrayList(); for (String line : linesFromFile(f)) if (nempty(line = trim(line))) l.add(line); return l; } static Comparator caseInsensitiveComparator() { return betterCIComparator(); } static boolean isURL(String s) { return startsWithOneOf(s, "http://", "https://", "file:"); } static File imageSnippetCacheFile(String snippetID) { File dir = imageSnippetsCacheDir(); if (!loadBufferedImage_useImageCache) return null; return new File(dir, parseSnippetID(snippetID) + ".png"); } static String snippetImageURL_noHttps(String snippetID) { return snippetImageURL_noHttps(snippetID, "png"); } static String snippetImageURL_noHttps(String snippetID, String contentType) { return snippetImageURL(snippetID, contentType) .replace("https://www.botcompany.de:8443/", "http://www.botcompany.de:8080/") .replace("https://botcompany.de/", "http://botcompany.de/"); } static ThreadLocal>> loadBinaryPage_responseHeaders = new ThreadLocal(); static ThreadLocal> loadBinaryPage_extraHeaders = new ThreadLocal(); static byte[] loadBinaryPage(String url) { try { print("Loading " + url); return loadBinaryPage(loadPage_openConnection(new URL(url))); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage(URLConnection con) { try { Map extraHeaders = getAndClearThreadLocal(loadBinaryPage_extraHeaders); setHeaders(con); for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key)); return loadBinaryPage_noHeaders(con); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage_noHeaders(URLConnection con) { try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); loadBinaryPage_responseHeaders.set(con.getHeaderFields()); long len = 0; try { len = con.getContentLength/*Long*/(); } catch (Throwable e) { printStackTrace(e); } int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) println(" " + n + (len != 0 ? "/" + len : "") + " bytes loaded."); } inputStream.close(); return buf.toByteArray(); } catch (Exception __e) { throw rethrow(__e); } } /** writes safely (to temp file, then rename) */ public static byte[] saveBinaryFile(String fileName, byte[] contents) { try { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; FileOutputStream fileOutputStream = newFileOutputStream(tempFileName); fileOutputStream.write(contents); fileOutputStream.close(); if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); vmBus_send("wroteFile", file); return contents; } catch (Exception __e) { throw rethrow(__e); } } static byte[] saveBinaryFile(File fileName, byte[] contents) { return saveBinaryFile(fileName.getPath(), contents); } static File getGlobalCache() { File file = new File(javaxCachesDir(), "Binary Snippets"); file.mkdirs(); return file; } static String dataSnippetLink(String snippetID) { long id = parseSnippetID(snippetID); if (id >= 1100000 && id < 1200000) return imageServerURL() + id; if (id >= 1200000 && id < 1300000) { // Woody files, actually String pw = muricaPassword(); if (empty(pw)) throw fail("Please set 'murica password by running #1008829"); return "https://botcompany.de/files/" + id + "?_pass=" + pw; // XXX, although it typically gets hidden when printing } return fileServerURL() + "/" + id /*+ "?_pass=" + muricaPassword()*/; } static String fsI(String id) { return formatSnippetID(id); } static String fsI(long id) { return formatSnippetID(id); } static A addAndReturn(Collection c, A a) { if (c != null) c.add(a); return a; } static void loadBinaryPageToFile(String url, File file) { try { print("Loading " + url); loadBinaryPageToFile(openConnection(new URL(url)), file); } catch (Exception __e) { throw rethrow(__e); } } static void loadBinaryPageToFile(URLConnection con, File file) { try { setHeaders(con); loadBinaryPageToFile_noHeaders(con, file); } catch (Exception __e) { throw rethrow(__e); } } static void loadBinaryPageToFile_noHeaders(URLConnection con, File file) { try { File ftemp = new File(f2s(file) + "_temp"); FileOutputStream buf = newFileOutputStream(mkdirsFor(ftemp)); try { InputStream inputStream = con.getInputStream(); long len = 0; try { len = con.getContentLength/*Long*/(); } catch (Throwable e) { printStackTrace(e); } String pat = " {*}" + (len != 0 ? "/" + len : "") + " bytes loaded."; copyStreamWithPrints(inputStream, buf, pat); inputStream.close(); buf.close(); file.delete(); renameFile_assertTrue(ftemp, file); } finally { if (buf != null) buf.close(); } } catch (Exception __e) { throw rethrow(__e); } } static long psI(String snippetID) { return parseSnippetID(snippetID); } static File standardLogFile() { return getProgramFile("log"); } static List> _threadInfo_makers = synchroList(); static Object _threadInfo() { if (empty(_threadInfo_makers)) return null; HashMap map = new HashMap(); pcallFAll(_threadInfo_makers, map); return map; } static Runnable asRunnable(Object o) { return toRunnable(o); } static void _inheritThreadInfo(Object info) { _threadInheritInfo(info); } static A _recordNewSwingComponent(A c) { if (c != null) callF((Object) vm_generalMap_get("newSwingComponentRegistry"), (Object) c); return c; } static JComponent componentToJComponent(Component c) { if (c instanceof JComponent) return (JComponent) c; if (c instanceof JFrame) return ((JFrame) c).getRootPane(); if (c == null) return null; throw fail("boohoo " + getClassName(c)); } static JLabel jlabel(final String text) { return swingConstruct(BetterLabel.class, text); } static JLabel jlabel() { return jlabel(" "); } static JTextArea enableWordWrapForTextArea(JTextArea ta) { return enableWordWrapForTextArea(ta, true); } static JTextArea enableWordWrapForTextArea(JTextArea ta, boolean enabled) { if (ta != null) { swing(new Runnable() { public void run() { try { ta.setLineWrap(enabled); ta.setWrapStyleWord(true); // Haven't found a way to reliable enable word-wrapping for // an already visible text area //revalidate(enclosingScrollPane(ta)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ta.setLineWrap(enabled);\r\n ta.setWrapStyleWord(true);\r\n // Haven't foun..."; }}); } return ta; } static JTextArea jtextarea() { return jTextArea(); } static JTextArea jtextarea(String text) { return jTextArea(text); } static boolean setText_opt = true; // optimize by calling getText first static A setText(A c, Object text) { setText((JComponent) c, text); return c; } static A setText(final A c, Object text) { // only for editable combo boxes at this point final String s = strUnnull(text); { swing(new Runnable() { public void run() { try { c.getEditor().setItem(s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "c.getEditor().setItem(s);"; }}); } return c; } static void setText(JLabel c, Object text) { setText((JComponent) c, text); } static JButton setText(JButton c, Object text) { setText((JComponent) c, jlabel_textAsHTML_center_ifNeeded(strUnnull(text))); return c; } static A setText(final A c, Object text) { if (c == null) return null; final String s = strUnnull(text); { swing(new Runnable() { public void run() { try { if (!setText_opt || neq(callOpt(c, "getText"), s)) call(c, "setText", s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!setText_opt || neq(callOpt(c, \"getText\"), s))\r\n call(c, \"setText\", s);"; }}); } return c; } static Object swing(Object f) { return swingAndWait(f); } static A swing(F0 f) { return (A) swingAndWait(f); } static A swing(IF0 f) { return (A) swingAndWait(f); } static JTextField onEnter(final JTextField tf, final Object action) { if (action == null || tf == null) return tf; tf.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { tf.selectAll(); callF(action); } catch (Throwable __e) { messageBox(__e); }}}); return tf; } static JButton onEnter(JButton btn, final Object action) { if (action == null || btn == null) return btn; btn.addActionListener(actionListener(action)); return btn; } static JList onEnter(JList list, Object action) { list.addKeyListener(enterKeyListener(rCallOnSelectedListItem(list, action))); return list; } static JComboBox onEnter(final JComboBox cb, final Object action) { { swing(new Runnable() { public void run() { try { if (cb.isEditable()) { JTextField text = (JTextField) cb.getEditor().getEditorComponent(); onEnter(text, action); } else { cb.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "enter"); cb.getActionMap().put("enter", abstractAction("", new Runnable() { public void run() { try { cb.hidePopup(); callF(action); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "cb.hidePopup(); callF(action);"; }})); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (cb.isEditable()) {\r\n JTextField text = (JTextField) cb.getEditor().g..."; }}); } return cb; } static JTable onEnter(final JTable table, final Object action) { table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter"); table.getActionMap().put("Enter", new AbstractAction() { public void actionPerformed(ActionEvent e) { callF(action, table.getSelectedRow()); } }); return table; } /*static JTextArea onEnter(final JTextArea ta, fO action) { addKeyListener(ta, enterKeyListener(action)); ret ta; }*/ static JTextField onEnter(Object action, JTextField tf) { return onEnter(tf, action); } static A heldInstance(Class c) { List l = holdInstance_l.get(); for (int i = l(l)-1; i >= 0; i--) { Object o = l.get(i); if (isInstanceOf(o, c)) return (A) o; } throw fail("No instance of " + className(c) + " held"); } static float getSwingFontScale() { return or((Float) vm_generalMap_get("swingFontScale_value"), 1f); } static List vmBus_queryAll(String msg, Object... args) { Object arg = vmBus_wrapArgs(args); List out = new ArrayList(); for (Object o : unnullForIteration(vm_busListeners_live())) addIfNotNull(out, pcallF(o, msg, arg)); for (Object o : unnullForIteration(vm_busListenersByMessage_live().get(msg))) addIfNotNull(out, pcallF(o, msg, arg)); return out; } static ActionListener actionListener(final Object runnable) { return actionListener(runnable, null); } static ActionListener actionListener(final Object runnable, final Object instanceToHold) { if (runnable instanceof ActionListener) return (ActionListener) runnable; final Object info = _threadInfo(); return new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { _threadInheritInfo(info); AutoCloseable __1 = holdInstance(instanceToHold); try { callF(runnable); } finally { _close(__1); }} catch (Throwable __e) { messageBox(__e); }}}; } static int toInt_checked(long l) { if (l != (int) l) throw fail("Too large for int: " + l); return (int) l; } static A getWeakRef(Reference ref) { return ref == null ? null : ref.get(); } static x30_pkg.x30_util.BetterThreadLocal dm_current_generic_tl; static x30_pkg.x30_util.BetterThreadLocal dm_current_generic_tl() { if (dm_current_generic_tl == null) dm_current_generic_tl = vm_generalMap_getOrCreate("currentModule", () -> new x30_pkg.x30_util.BetterThreadLocal()); return dm_current_generic_tl; } static List> _threadInheritInfo_retrievers = synchroList(); static void _threadInheritInfo(Object info) { if (info == null) return; pcallFAll(_threadInheritInfo_retrievers, (Map) info); } static ThreadLocal> holdInstance_l = new ThreadLocal(); static AutoCloseable holdInstance(Object o) { if (o == null) return null; listThreadLocalAdd(holdInstance_l, o); return new AutoCloseable() { public void close() { listThreadLocalPopLast(holdInstance_l); } }; } static BufferedImage imageIO_readURL(String url) { try { return ImageIO.read(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } static boolean isAbsolutePath(String s) { return s != null && new File(s).isAbsolute(); } static boolean isAbsolutePath(File f) { return f != null && f.isAbsolute(); } static File imageSnippetsCacheDir() { return javaxCachesDir("Image-Snippets"); } static String snippetImageURL_http(String snippetID) { return snippetImageURL_http(snippetID, "png"); } static String snippetImageURL_http(String snippetID, String contentType) { return replacePrefix("https://", "http://", snippetImageURL(snippetID, contentType)).replace(":8443", ":8080"); } static BufferedImage loadBufferedImageFile(File file) { try { return isFile(file) ? ImageIO.read(file) : null; } catch (Exception __e) { throw rethrow(__e); } } static Map weakHashMap() { return newWeakHashMap(); } static HashMap> callMC_cache = new HashMap(); static String callMC_key; static Method callMC_value; // varargs assignment fixer for a single string array argument static Object callMC(String method, String[] arg) { return callMC(method, new Object[] {arg}); } static Object callMC(String method, Object... args) { try { Method me; if (callMC_cache == null) callMC_cache = new HashMap(); // initializer time workaround synchronized(callMC_cache) { me = method == callMC_key ? callMC_value : null; } if (me != null) try { return invokeMethod(me, null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e); } List m; synchronized(callMC_cache) { m = callMC_cache.get(method); } if (m == null) { if (callMC_cache.isEmpty()) { callMC_makeCache(); m = callMC_cache.get(method); } if (m == null) throw fail("Method named " + method + " not found in main"); } int n = m.size(); if (n == 1) { me = m.get(0); synchronized(callMC_cache) { callMC_key = method; callMC_value = me; } try { return invokeMethod(me, null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e); } } for (int i = 0; i < n; i++) { me = m.get(i); if (call_checkArgs(me, args, false)) return invokeMethod(me, null, args); } throw fail("No method called " + method + " with arguments (" + joinWithComma(getClasses(args)) + ") found in main"); } catch (Exception __e) { throw rethrow(__e); } } static void callMC_makeCache() { synchronized(callMC_cache) { callMC_cache.clear(); Class _c = (Class) mc(), c = _c; while (c != null) { for (Method m : c.getDeclaredMethods()) if ((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) { makeAccessible(m); multiMapPut(callMC_cache, m.getName(), m); } c = c.getSuperclass(); } } } static ClassLoader classLoaderForObject(Object o) { if (o instanceof ClassLoader) return ((ClassLoader) o); if (o == null) return null; return _getClass(o).getClassLoader(); } static List collectField(Iterable c, String field) { List l = new ArrayList(); if (c != null) for (Object a : c) l.add(getOpt(a, field)); return l; } static List collectField(String field, Iterable c) { return collectField(c, field); } static Random defaultRandomGenerator() { return ThreadLocalRandom.current(); } static Map singular_specials = litmap( "children", "child", "images", "image", "chess", "chess"); static Set singular_specials2 = litciset("time", "machine", "line", "rule"); static String singular(String s) { if (s == null) return null; { String __1 = singular_specials.get(s); if (!empty(__1)) return __1; } //try answer hippoSingulars().get(lower(s)); if (singular_specials2.contains(dropSuffix("s", afterLastSpace(s)))) return dropSuffix("s", s); if (s.endsWith("ness")) return s; if (s.endsWith("ges")) return dropSuffix("s", s); if (endsWith(s, "bases")) return dropLast(s); s = dropSuffix("es", s); s = dropSuffix("s", s); return s; } static List getPlural_specials = ll("sheep", "fish"); static String getPlural(String s) { if (containsIgnoreCase(getPlural_specials, s)) return s; if (ewic(s, "y")) return dropSuffixIgnoreCase("y", s) + "ies"; if (ewicOneOf(s, "ss", "ch")) return s + "es"; if (ewic(s, "s")) return s; return s + "s"; } static void upgradeJavaXAndRestart() { run("#1001639"); restart(); sleep(); } static WeakReference creator_class; static Object creator() { return creator_class == null ? null : creator_class.get(); } static DynModule dm_currentModule() { return optCast(DynModule.class, dm_current_generic()); } // TODO: OurSyncCollections static Set synchroHashSet() { return Collections.synchronizedSet(new HashSet()); } static Object get2(Object o, String field1, String field2) { return get(get(o, field1), field2); } static boolean startsWithAny(String a, Collection b) { for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix)) return true; return false; } static boolean startsWithAny(String a, String... b) { if (b != null) for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix)) return true; return false; } static boolean startsWithAny(String a, Collection b, Matches m) { for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix, m)) return true; return false; } static String mcDollar() { return mcName() + "$"; } static String afterDollar(String s) { return substring(s, smartIndexOf(s, '$')+1); } static String quoteIfNotIdentifierOrInteger(String s) { if (s == null) return null; return isJavaIdentifier(s) || isInteger(s) ? s : quote(s); } static Object fieldGet(Field f, Object o) { try { return f == null ? null : f.get(o); } catch (Exception __e) { throw rethrow(__e); } } static A[] makeArray(Class type, int n) { return (A[]) Array.newInstance(type, n); } // custom mainClass only works with hotwire_here static Class hotwire(String src) { return hotwire(src, __1 -> mainClassNameForClassLoader(__1)); } static Class hotwire(String src, IF1 calculateMainClass) { assertFalse(_inCore()); Class j = getJavaX(); if (isAndroid()) { synchronized(j) { // hopefully this goes well... List libraries = new ArrayList(); File srcDir = (File) call(j, "transpileMain", src, libraries); if (srcDir == null) throw fail("transpileMain returned null (src=" + quote(src) + ")"); Object androidContext = get(j, "androidContext"); return (Class) call(j, "loadx2android", srcDir, src); } } else { Class c = (Class) (call(j, "hotwire", src)); hotwire_copyOver(c); return c; } } static String appendColonIfNempty(String s) { return empty(s) ? "" : s + ": "; } 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(byte[] a) { return a == null || a.length == 0; } static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } static List cloneSubList(List l, int startIndex, int endIndex) { return newSubList(l, startIndex, endIndex); } static List cloneSubList(List l, int startIndex) { return newSubList(l, startIndex); } static void removeSubList(List l, int from, int to) { if (l != null) subList(l, from, to).clear(); } static void removeSubList(List l, int from) { if (l != null) subList(l, from).clear(); } static Object realMC() { return getThreadLocal(realMC_tl()); } static Pair pairMapB(Object f, Pair p) { return p == null ? null : pair(p.a, callF(f, p.b)); } static Pair pairMapB(IF1 f, Pair p) { return p == null ? null : pair(p.a, f.get(p.b)); } static Pair pairMapB(Pair p, Object f) { return pairMap(f, p); } static Object pcallFAll_returnFirstNotNull(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) { Object __1= pcallF(f, args); if (__1 != null) return __1; } return null; } static Object pcallFAll_returnFirstNotNull(Iterator it, Object... args) { while (it.hasNext()) { Object __2= pcallF(it.next(), args); if (__2 != null) return __2; } return null; } static Map convertObjectMetaToMap(IMeta o) { return convertObjectMetaToMap(o, () -> synchroLinkedHashMap()); } static Map convertObjectMetaToMap(IMeta o, IF0 createEmptyMap) { if (o == null) return null; // The following shortcut depends on the assumption that a meta field never reverts // to null when it was a map Object meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta); // non-shortcut path (create meta) var mutex = tempMetaMutex(o); try { var actualMutex = mutex.get(); synchronized(actualMutex) { meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta); Map map = createEmptyMap.get(); if (meta != null) map.put("previousMeta" , meta); o._setMeta(map); return map; } } finally { _close(mutex); }} static void syncMapPutOrRemove(Map map, A key, B value) { syncMapPut2(map, key, value); } static String collapseWord(String s) { if (s == null) return ""; StringBuilder buf = new StringBuilder(); for (int i = 0; i < l(s); i++) if (i == 0 || !charactersEqualIC(s.charAt(i), s.charAt(i-1))) buf.append(s.charAt(i)); return buf.toString(); } static List toLowerCase(List strings) { List x = new ArrayList(); for (String s : strings) x.add(s.toLowerCase()); return x; } static String[] toLowerCase(String[] strings) { String[] x = new String[l(strings)]; for (int i = 0; i < l(strings); i++) x[i] = strings[i].toLowerCase(); return x; } static String toLowerCase(String s) { return s == null ? "" : s.toLowerCase(); } static String firstWord2(String s) { s = xltrim(s); if (empty(s)) return ""; if (isLetterOrDigit(first(s))) return takeCharsWhile(__63 -> isLetterOrDigit(__63), s); else return "" + first(s); } static A vm_generalMap_getOrCreate(Object key, F0 create) { return vm_generalMap_getOrCreate(key, f0ToIF0(create)); } static A vm_generalMap_getOrCreate(Object key, IF0 create) { Map generalMap = vm_generalMap(); if (generalMap == null) return null; // must be x30 init synchronized(generalMap) { // should switch to locks here A a = (A) (vm_generalMap_get(key)); if (a == null) vm_generalMap_put(key, a = create == null ? null : create.get()); return a; } } // this should be on by default now I think, but it may break // legacy code... static ThreadLocal htmlencode_forParams_useV2 = new ThreadLocal(); static String htmlencode_forParams(String s) { if (s == null) return ""; if (isTrue(htmlencode_forParams_useV2.get())) return htmlencode_forParams_v2(s); StringBuilder out = new StringBuilder(Math.max(16, s.length())); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127 || c == '"' || c == '<' || c == '>') { out.append("&#"); out.append((int) c); out.append(';'); } else out.append(c); } return out.toString(); } static A[] newObjectArrayOfSameType(A[] a) { return newObjectArrayOfSameType(a, a.length); } static A[] newObjectArrayOfSameType(A[] a, int n) { return (A[]) Array.newInstance(a.getClass().getComponentType(), n); } static File computerIDFile() { return javaxDataDir("Basic Info/computer-id.txt"); } static String quickSubstring(String s, int i, int j) { if (i == j) return ""; return s.substring(i, j); } static boolean addToCollection(Collection c, A a) { return c != null && c.add(a); } static int parseHexChar(char c) { if (c >= '0' && c <= '9') return charDiff(c, '0'); if (c >= 'a' && c <= 'f') return charDiff(c, 'a')+10; if (c >= 'A' && c <= 'F') return charDiff(c, 'A')+10; return -1; } static Class getOuterClass(Class c) { return getOuterClass(c, null); } static Class getOuterClass(Class c, Object classFinder) { try { String s = c.getName(); int i = s.lastIndexOf('$'); String name = substring(s, 0, i); return classForName(name, classFinder); } catch (Exception __e) { throw rethrow(__e); } } static Class getOuterClass(Object o) { return getOuterClass(o, null); } static Class getOuterClass(Object o, Object classFinder) { return getOuterClass(_getClass(o), classFinder); } static HashMap instanceFieldsMap(Object o) { return (HashMap) getOpt_getFieldMap(o); } static Map thisDollarOneFields_cache = newDangerousWeakHashMap(); static Field[] thisDollarOneFields(Class c) { synchronized(thisDollarOneFields_cache) { Field[] l = thisDollarOneFields_cache.get(c); if (l == null) thisDollarOneFields_cache.put(c, l = thisDollarOneFields_uncached(c)); return l; } } static Field[] thisDollarOneFields_uncached(Class c) { List fields = new ArrayList(); do { for (Field f : c.getDeclaredFields()) if (f.getName().startsWith("this$")) fields.add(makeAccessible(f)); c = c.getSuperclass(); } while (c != null); return toArray(new Field[l(fields)], fields); } static Map> callOpt_noArgs_cache = newDangerousWeakHashMap(); static Object callOpt_noArgs(Object o, String method) { try { if (o == null) return null; if (o instanceof Class) return callOpt(o, method); // not optimized Class c = o.getClass(); HashMap map; synchronized(callOpt_noArgs_cache) { map = callOpt_noArgs_cache.get(c); if (map == null) map = callOpt_noArgs_makeCache(c); } Method m = map.get(method); return m != null ? m.invoke(o) : null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap callOpt_noArgs_makeCache(Class c) { HashMap map = new HashMap(); Class _c = c; do { for (Method m : c.getDeclaredMethods()) if (m.getParameterTypes().length == 0 && !reflection_isForbiddenMethod(m)) { makeAccessible(m); String name = m.getName(); if (!map.containsKey(name)) map.put(name, m); } _c = _c.getSuperclass(); } while (_c != null); callOpt_noArgs_cache.put(c, map); return map; } static TreeSet caseInsensitiveSet() { return caseInsensitiveSet_treeSet(); } static TreeSet caseInsensitiveSet(Collection c) { return caseInsensitiveSet_treeSet(c); } // 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()) { return null; } for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(i); if (eq(p, "*")) result.add(t); else if (!equalsIgnoreCase(unquote(p), unquote(t))) // bold change - match quoted and unquoted now. TODO: should remove return null; } return result.toArray(new String[result.size()]); } static Class getClass(String name) { return _getClass(name); } static Class getClass(Object o) { return _getClass(o); } static Class getClass(Object realm, String name) { return _getClass(realm, name); } static String htmlQuery(Map params) { return empty(params) ? "" : "?" + makePostData(params); } static String htmlQuery(Object... data) { return empty(data) ? "" : "?" + makePostData(data); } static boolean isLowerHexString(String s) { for (int i = 0; i < l(s); i++) { char c = s.charAt(i); if (c >= '0' && c <= '9' || c >= 'a' && c <= 'f') { // ok } else return false; } return true; } static String structure_addTokenMarkers(String s) { return join(structure_addTokenMarkers(javaTokForStructure(s))); } static List structure_addTokenMarkers(List tok) { // find references TreeSet refs = new TreeSet(); for (int i = 1; i < l(tok); i += 2) { String t = tok.get(i); if (t.startsWith("t") && isInteger(t.substring(1))) refs.add(parseInt(t.substring(1))); } if (empty(refs)) return tok; // add markers for (int i : refs) { int idx = i*2+1; if (idx >= l(tok)) continue; // broken structure String t = ""; if (endsWithLetterOrDigit(tok.get(idx-1))) t = " "; tok.set(idx, t + "m" + i + " " + tok.get(idx)); } return tok; } static boolean sameSnippetID(String a, String b) { if (!isSnippetID(a) || !isSnippetID(b)) return false; return parseSnippetID(a) == parseSnippetID(b); } static boolean checkCondition(Object condition, Object... args) { return isTrue(callF(condition, args)); } static boolean checkCondition(IF1 condition, A arg) { return isTrue(callF(condition, arg)); } static int indexOfSubList(List x, List y) { return indexOfSubList(x, y, 0); } static int indexOfSubList(List x, List y, int i) { outer: for (; i+l(y) <= l(x); i++) { for (int j = 0; j < l(y); j++) if (neq(x.get(i+j), y.get(j))) continue outer; return i; } return -1; } static int indexOfSubList(List x, A[] y, int i) { outer: for (; i+l(y) <= l(x); i++) { for (int j = 0; j < l(y); j++) if (neq(x.get(i+j), y[j])) continue outer; return i; } return -1; } static void copyListPart(List a, int i1, List b, int i2, int n) { if (a == null || b == null) return; for (int i = 0; i < n; i++) b.set(i2+i, a.get(i1+i)); } static String base64(byte[] a) { return base64encode(a); } static BufferedImage dropAlphaChannelFromBufferedImage(BufferedImage img) { if (img == null || img.getType() == BufferedImage.TYPE_INT_RGB) return img; int w = img.getWidth(), h = img.getHeight(); BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); int[] rgb = img.getRGB(0, 0, w, h, null, 0, w); newImage.setRGB(0, 0, w, h, rgb, 0, w); return newImage; } static BufferedImage loadImage2(String snippetIDOrURL) { return loadBufferedImage(snippetIDOrURL); } static BufferedImage loadImage2(File file) { return loadBufferedImage(file); } static String imageServerLink(String md5OrID) { if (possibleMD5(md5OrID)) return "https://botcompany.de/images/md5/" + md5OrID; return imageServerLink(parseSnippetID(md5OrID)); } static String imageServerLink(long id) { return "https://botcompany.de/images/" + id; } static String multiLineQuoteWithSpaces(String s) { return multiLineQuote(" " + s + " "); } static Matcher regexpMatcherIC(String pat, String s) { return compileRegexpIC(pat).matcher(unnull(s)); } static String regexpReplaceIC_direct(String s, String pat, String replacement) { Matcher m = regexpIC(pat, s); StringBuffer buf = new StringBuffer(); while (m.find()) m.appendReplacement(buf, replacement); m.appendTail(buf); return str(buf); } static String utf8streamToString(InputStream in) { return readerToString(utf8bufferedReader(in)); } static betterCIComparator_C betterCIComparator_instance; static betterCIComparator_C betterCIComparator() { if (betterCIComparator_instance == null) betterCIComparator_instance = new betterCIComparator_C(); return betterCIComparator_instance; } final static class betterCIComparator_C implements Comparator { public int compare(String s1, String s2) { if (s1 == null) return s2 == null ? 0 : -1; if (s2 == null) return 1; int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; } } static A println(A a) { return print(a); } static File javaxCachesDir_dir; // can be set to work on different base dir static File javaxCachesDir() { return javaxCachesDir_dir != null ? javaxCachesDir_dir : new File(userHome(), "JavaX-Caches"); } static File javaxCachesDir(String sub) { return newFile(javaxCachesDir(), sub); } static String imageServerURL() { return or2(trim(loadTextFile(javaxDataDir("image-server-url.txt"))), "http://botcompany.de/images/raw/"); } static volatile boolean muricaPassword_pretendNotAuthed = false; static String muricaPassword() { if (muricaPassword_pretendNotAuthed) return null; return trim(loadTextFile(muricaPasswordFile())); } static String fileServerURL() { return "https://botcompany.de/files"; } public static File mkdirsFor(File file) { return mkdirsForFile(file); } static void copyStreamWithPrints(InputStream in, OutputStream out, String pat) { try { byte[] buf = new byte[65536]; int total = 0; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); if ((total+n)/100000 > total/100000) print(pat.replace("{*}", str(roundDownTo(100000, total)))); total += n; } } catch (Exception __e) { throw rethrow(__e); } } static File renameFile_assertTrue(File a, File b) { try { if (a.equals(b)) return b; // no rename necessary if (!a.exists()) throw fail("Source file not found: " + f2s(a)); if (b.exists()) throw fail("Target file exists: " + f2s(b)); mkdirsForFile(b); if (!a.renameTo(b)) throw fail("Can't rename " + f2s(a) + " to " + f2s(b)); return b; } catch (Exception __e) { throw rethrow(__e); } } static A swingConstruct(final Class c, final Object... args) { return swing(new F0() { public A get() { try { return nuObject(c, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret nuObject(c, args);"; }}); } static JTextArea jTextArea() { return jTextArea(""); } static JTextArea jTextArea(final String text) { return jTextAreaWithUndo(text); } static String strUnnull(Object o) { return o == null ? "" : str(o); } static String jlabel_textAsHTML_center_ifNeeded(String text) { if (swic(text, "") && ewic(text, "")) return text; if (!containsNewLines(text)) return text; return jlabel_textAsHTML_center(text); } static void messageBox(final String msg) { if (headless()) print(msg); else { swing(new Runnable() { public void run() { try { JOptionPane.showMessageDialog(null, msg, "JavaX", JOptionPane.INFORMATION_MESSAGE); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JOptionPane.showMessageDialog(null, msg, \"JavaX\", JOptionPane.INFORMATION_MES..."; }}); } } static void messageBox(Throwable e) { //showConsole(); printStackTrace(e); messageBox(hideCredentials(innerException2(e))); } static KeyListener enterKeyListener(final Object action) { return new KeyAdapter() { public void keyPressed(KeyEvent ke) { if (ke.getKeyCode() == KeyEvent.VK_ENTER) pcallF(action); } }; } static Runnable rCallOnSelectedListItem(final JList list, final Object action) { return new Runnable() { public void run() { try { pcallF(action, getSelectedItem(list)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(action, getSelectedItem(list))"; }}; } static AbstractAction abstractAction(String name, final Object runnable) { return new AbstractAction(name) { public void actionPerformed(ActionEvent evt) { pcallF(runnable); } }; } static boolean isInstanceOf(Object o, Class type) { return type.isInstance(o); } static boolean addIfNotNull(Collection l, A a) { return a != null && l != null & l.add(a); } static void addIfNotNull(MultiSet ms, A a) { if (a != null && ms != null) ms.add(a); } static void listThreadLocalAdd(ThreadLocal> tl, A a) { List l = tl.get(); if (l == null) tl.set(l = new ArrayList()); l.add(a); } static A listThreadLocalPopLast(ThreadLocal> tl) { List l = tl.get(); if (l == null) return null; A a = popLast(l); if (empty(l)) tl.set(null); return a; } static void multiMapPut(Map> map, A a, B b) { List l = map.get(a); if (l == null) map.put(a, l = new ArrayList()); l.add(b); } static void multiMapPut(MultiMap mm, A key, B value) { if (mm != null && key != null && value != null) mm.put(key, value); } static TreeSet litciset(String... items) { TreeSet set = caseInsensitiveSet(); for (String a : items) set.add(a); return set; } static TreeSet litciset(Symbol... items) { TreeSet set = treeSet(); // HashSet would also do, but we might have the return type fixed somewhere, and they might want a NavigableMap. for (Symbol a : items) set.add(a); return set; } static String afterLastSpace(String s) { return s == null ? null : substring(s, s.lastIndexOf(' ')+1); } static String dropSuffixIgnoreCase(String suffix, String s) { return ewic(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static boolean ewicOneOf(String s, String... l) { if (s != null) for (String x : l) if (ewic(s, x)) return true; return false; } static Class run(String progID, String... args) { Class main = hotwire(progID); callMain(main, args); return main; } static void restart() { Object j = getJavaX(); call(j, "cleanRestart", get(j, "fullArgs")); } static String mcName() { return mc().getName(); } static boolean _inCore() { return false; } static List hotwire_copyOver_after = synchroList(); static void hotwire_copyOver(Class c) { // TODO: make a mechanism for making such "inheritable" fields for (String field : ll("print_log", "print_silent", "androidContext", "_userHome")) setOptIfNotNull(c, field, getOpt(mc(), field)); setOptIfNotNull(c, "mainBot" , getMainBot()); setOpt(c, "creator_class" , new WeakReference(mc())); pcallFAll(hotwire_copyOver_after, c); } static List newSubList(List l, int startIndex, int endIndex) { return cloneList(subList(l, startIndex, endIndex)); } static List newSubList(List l, int startIndex) { return cloneList(subList(l, startIndex)); } static ThreadLocal realMC_tl_tl = new ThreadLocal(); static ThreadLocal realMC_tl() { return realMC_tl_tl; } static Pair pairMap(Object f, Pair p) { return p == null ? null : pair(callF(f, p.a), callF(f, p.b)); } static Pair pairMap(IF1 f, Pair p) { return p == null ? null : pair(callF(f, p.a), callF(f, p.b)); } static Pair pairMap(Pair p, Object f) { return pairMap(f, p); } static Map synchroLinkedHashMap() { return Collections.synchronizedMap(new LinkedHashMap()); } static IAutoCloseableF0 tempMetaMutex(IMeta o) { return o == null ? null : o._tempMetaMutex(); } static void syncMapPut2(Map map, A key, B value) { if (map != null && key != null) synchronized(collectionMutex(map)) { if (value != null) map.put(key, value); else map.remove(key); } } static boolean charactersEqualIC(char c1, char c2) { if (c1 == c2) return true; char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); if (u1 == u2) return true; return Character.toLowerCase(u1) == Character.toLowerCase(u2); } static String xltrim(String s) { int i = 0, n = l(s); while (i < n && contains(" \t\r\n", s.charAt(i))) ++i; return substr(s, i); } static boolean isLetterOrDigit(char c) { return Character.isLetterOrDigit(c); } // pred: char -> bool static String takeCharsWhile(String s, Object pred) { int i = 0; while (i < l(s) && isTrue(callF(pred, s.charAt(i)))) ++i; return substring(s, 0, i); } static String takeCharsWhile(IF1 f, String s) { return takeCharsWhile(s, f); } static IF0 f0ToIF0(F0 f) { return f == null ? null : () -> f.get(); } static String htmlencode_forParams_v2(String s) { if (s == null) return ""; StringBuilder out = new StringBuilder(Math.max(16, s.length())); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127 || c == '"' || c == '<' || c == '>' || c == '&') { out.append("&#"); out.append((int) c); out.append(';'); } else out.append(c); } return out.toString(); } static int charDiff(char a, char b) { return (int) a-(int) b; } static int charDiff(String a, char b) { return charDiff(stringToChar(a), b); } static Object[] toArray(Collection c) { return toObjectArray(c); } static A[] toArray(Class type, Iterable c) { return toArray(c, type); } static A[] toArray(Iterable c, Class type) { A[] a = arrayOfType(l(c), type); if (a.length == 0) return a; asList(c).toArray(a); return a; } // array must have correct length and will be filled static A[] toArray(A[] array, Collection c) { if (array == null || c == null) return null; asList(c).toArray(array); return array; } static boolean reflection_isForbiddenMethod(Method m) { return m.getDeclaringClass() == Object.class && eqOneOf(m.getName(), "finalize", "clone", "registerNatives"); } static TreeSet caseInsensitiveSet_treeSet() { return new TreeSet(caseInsensitiveComparator()); } static TreeSet caseInsensitiveSet_treeSet(Collection c) { return toCaseInsensitiveSet_treeSet(c); } static boolean equalsIgnoreCase(String a, String b) { return eqic(a, b); } static boolean equalsIgnoreCase(char a, char b) { return eqic(a, b); } static String base64encode(byte[] a) { int aLen = a.length; int numFullGroups = aLen/3; int numBytesInPartialGroup = aLen - 3*numFullGroups; int resultLen = 4*((aLen + 2)/3); StringBuffer result = new StringBuffer(resultLen); char[] intToAlpha = intToBase64; // Translate all full groups from byte array elements to Base64 int inCursor = 0; for (int i=0; i> 2]); result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]); result.append(intToAlpha[byte2 & 0x3f]); } // Translate partial group if present if (numBytesInPartialGroup != 0) { int byte0 = a[inCursor++] & 0xff; result.append(intToAlpha[byte0 >> 2]); if (numBytesInPartialGroup == 1) { result.append(intToAlpha[(byte0 << 4) & 0x3f]); result.append("=="); } else { // assert numBytesInPartialGroup == 2; int byte1 = a[inCursor++] & 0xff; result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); result.append(intToAlpha[(byte1 << 2)&0x3f]); result.append('='); } } // assert inCursor == a.length; // assert result.length() == resultLen; return result.toString(); } /** * This array is a lookup table that translates 6-bit positive integer * index values into their "Base64 Alphabet" equivalents as specified * in Table 1 of RFC 2045. */ static final char intToBase64[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static String multiLineQuote(String s) { for (int i = 0; ; i++) { String closer = "]" + rep('=', i) + "]"; if (!contains(s, closer)) return "[" + rep('=', i) + "[" + s + closer; } } static String readerToString(Reader r) { try { if (r == null) return null; try { StringBuilder buf = new StringBuilder(); int n = 0; while (true) { int ch = r.read(); if (ch < 0) break; buf.append((char) ch); ++n; //if ((n % loadPage_verboseness) == 0) print(" " + n + " chars read"); } return buf.toString(); } finally { r.close(); } } catch (Exception __e) { throw rethrow(__e); } } static File muricaPasswordFile() { return new File(javaxSecretDir(), "murica/muricaPasswordFile"); } // TODO: optimize to x-(x%n) in case that's the same thing // (or x-mod(x,n)?) static int roundDownTo(int n, int x) { return x/n*n; } static long roundDownTo(long n, long x) { return x/n*n; } static JTextArea jTextAreaWithUndo() { return jTextAreaWithUndo(""); } static JTextArea jTextAreaWithUndo(final String text) { return jenableUndoRedo(swingNu(JTextArea.class, text)); } static boolean containsNewLines(String s) { return containsNewLine(s); } static String jlabel_textAsHTML_center(String text) { return "
" + replace(htmlencode2(text), "\n", "
") + "
"; } static boolean headless() { return isHeadless(); } static String getSelectedItem(JList l) { return (String) l.getSelectedValue(); } static String getSelectedItem(JComboBox cb) { return strOrNull(cb.getSelectedItem()); } static
TreeSet treeSet() { return new TreeSet(); } static void setOptIfNotNull(Object o, String field, Object value) { if (value != null) setOpt(o, field, value); } static String substr(String s, int x) { return substring(s, x); } static String substr(String s, int x, int y) { return substring(s, x, y); } static char stringToChar(String s) { if (l(s) != 1) throw fail("bad stringToChar: " + s); return firstChar(s); } static TreeSet toCaseInsensitiveSet_treeSet(Iterable c) { if (isCISet(c)) return (TreeSet) c; TreeSet set = caseInsensitiveSet_treeSet(); addAll(set, c); return set; } static TreeSet toCaseInsensitiveSet_treeSet(String... x) { TreeSet set = caseInsensitiveSet_treeSet(); addAll(set, x); return set; } static A jenableUndoRedo(final A textcomp) { { swing(new Runnable() { public void run() { try { final UndoManager undo = new UndoManager(); vm_generalWeakSet("Undo Managers").add(undo); textcomp.getDocument().addUndoableEditListener(new UndoableEditListener() { public void undoableEditHappened(UndoableEditEvent evt) { undo.addEdit(evt.getEdit()); } }); textcomp.getActionMap().put("Undo", abstractAction("Undo", new Runnable() { public void run() { try { if (undo.canUndo()) undo.undo() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (undo.canUndo()) undo.undo()"; }})); textcomp.getActionMap().put("Redo", abstractAction("Redo", new Runnable() { public void run() { try { if (undo.canRedo()) undo.redo() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (undo.canRedo()) undo.redo()"; }})); textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo"); textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final new UndoManager undo;\r\n vm_generalWeakSet(\"Undo Managers\").add(undo)..."; }}); } return textcomp; } static A swingNu(final Class c, final Object... args) { return swingConstruct(c, args); } static String htmlencode2(String s) { return htmlencode_noQuotes(s); } static char firstChar(String s) { return s.charAt(0); } static boolean isCISet(Iterable l) { return l instanceof TreeSet && ((TreeSet) l).comparator() == caseInsensitiveComparator(); } static Set vm_generalWeakSet(Object name) { synchronized(get(javax(), "generalMap")) { Set set = (Set) (vm_generalMap_get(name)); if (set == null) vm_generalMap_put(name, set = newWeakHashSet()); return set; } } static Set newWeakHashSet() { return synchroWeakHashSet(); } static Set synchroWeakHashSet() { return Collections.newSetFromMap((Map) newWeakHashMap()); } static abstract class VF1 implements IVF1 { public abstract void get(A a); } // Meta - a "minimal" approach to adding meta-level to Java objects static class Meta implements IMeta { // We allocate one extra field for each Java object to make it // reasoning-compatible. We couldn't go for 0 extra fields and // there are no half fields in Java... so there you go. // Also, if you don't use any meta data, you are probably not // reasoning about anything. The point of reasoning in JavaX is // to attach information to objects directly used in the program. // Possible information contained in the meta field: // Origin, destination, security level, sender, cost center, // purpose, list of reifications, ... // So here it is. THE FIELD YOU HAVE BEEN WAITING FOR // We also have IMeta to retrofit foreign classes (rare but // probably useful) //////////// // "meta" // //////////// // Generic meta value of any kind, but the typical case is it's a // Map with extra field values for the object etc. // "meta" is volatile to avoid synchronization; but you can also synchronize on // _tempMetaMutex() which is usually the object itself. Collections // and maps are exempt from using the collections's monitor as the meta // mutex because their monitor tends to be held for long operations // (e.g. cloneList). For those we use a substantially more complex // algorithm using a weakMap. Probably overkill. I may reconsider. volatile Object meta; // The meta field is not transient, thus by default it will be // persisted like anything else unless you customize your object // to suppress or modulate this. // ...and the interface methods public void _setMeta(Object meta) { this.meta = meta; } public Object _getMeta() { return meta; } // That was layer 1... // ...and here is the convenience stuff - just use the meta field like a regular map. final void metaPut(Object key, Object value) { metaSet(key, value); } void metaSet(Object key, Object value) { metaMapPut(this, key, value); } Object metaGet(Object key) { return metaMapGet(this, key); } } static abstract class VF2 { abstract void get(A a, B b); } // Transpile #1008705 after changing this and Very-Fresh #1001167 // TODO: use Map attributesForCharRanges // and generate HTML properly from that static class JavaXHyperlinker { boolean targetBlank = false; // only a single token allowed on LHS Map shortFor = litmap( "BigInt", "BigInteger", "$1", "m.unq(0)", "$2", "m.unq(1)", "$3", "m.unq(2)", "LS", "List", "sclass", "static class", "asclass", "abstract static class", "svoid", "static void", "SS", "Map", "S", "String", "ret", "return", "L", "List", "Cl", "Collection", "O", "Object", "sO", "static Object", "sS", "static String", "fO", "final Object", "fS", "final String", "sS", "static String", "sbool", "static boolean", "fbool", "final boolean", "bool", "boolean", "Int", "Integer", "cast", "casting to the type required on the left-hand side", "°", "()", "ItIt", "IterableIterator", "ES", "Ext (string plus extended information)", "CloseableItIt", "CloseableIterableIterator", "LPairS", "List>", "ISegmenter", "IF1>"); String explFunc = "anonymous function declaration (similar to Java 8 lambdas)"; String explPNoconsole = "Main program including Substance L&F, started in non-AWT thread, hiding the console"; // any number of tokens allowed on LHS Map tokenExplanations = litmap( "cm", "cm summons the last version of a module in Stefan's OS (cm originally stood for 'compact module')", "~.", "short for accessing a field or calling a method by reflection", "beaConcept", "a BEA concept is just a concept (database-enabled Java class) derived from BEAObject (the AGI base class)", "== null ?:", "short for: == null ? null :", "p-exp {", "main program with automatic upgrade", "mapLike", "a function that takes a global function name as first argument and can be called like this: (...)", "mapMethodLike", "a function that takes a method or field name as first argument and can be called like this: (...)", "func(", explFunc, "func{", explFunc, //"f(", explFunc, "f{", explFunc, "voidfunc", "anonymous function declaration without return value (similar to Java 8 lambdas)", "ctex", "ctex rethrows exceptions as RuntimeExceptions so you don't have to declare them", "p {", "short for: public static void main(String[] args)", "p-experiment {", "Main program with a nice big console & auto-restart", "p-substance {", "Main program including Substance L&F, started in AWT thread", "p-subst {", "Main program including Substance L&F, started in non-AWT thread", "p-noconsole {", explPNoconsole, "pn {", explPNoconsole, "answer {", "Answer function - static S answer(S s) { new Matches m; ...; null; }", "time {", "Run the code block and print how long it took", "cprint {", "A module with default class name derived from DynPrintLog", "semiauto {", "In a semiauto block, JavaX automatically adds a ; to (almost) every line", "autosemi {", "In an autosemi block, JavaX automatically adds a ; to (almost) every line", "Clusters", "Clusters is short for Map>", "temp", "temp is like try (...) extending to the end of the current block", "pcall {", "protected call - try { ... } catch { print exception }", "r {", "r { ... } is short for: new Runnable() { public void run() { ... } }", "runnable {", "runnable { ... } is short for: new Runnable() { public void run() { ... } }", "pcall-short {", "protected call, shorter output - try { ... } catch { printShortException }", "LPair<", "LPair<...> is short for: L>", "visualize {", "Visualisation method for an OS module; returns a Swing component", "visualize as", "define a visualization for the module", "enhanceFrame {", "Method that changes properties of an OS module's frame", "run {", "short for: public void run()", "start {", "Method that is run when an OS module is loaded", "enter {", "Methods in a module should be marked 'enter' for bookkeeping", "vf<", "reference to function as voidfunc", "compact module", "try to save memory by reusing code between modules", "cmodule", "cmodule = compact module for Stefan's OS. compact = try to save memory by reusing code between modules", "cmodule2", "cmodule2 = compact module for Stefan's OS (newer version). compact = try to save memory by reusing code between modules", "rThread {", "Runnable in a new thread", "shit(", "short for: return with print", "shit:", "short for: return with print", ":=", "the := operator translates to a simple comma, but converts an identifier on its left-hand side into a string", "noeq", "don't auto-generate equals+hashCode methods", "for single (", "for single is for ping + singletonUnlessNull", "selfType", "selfType is replaced with the enclosing class's name", "runnable class", "short for: class X implementing Runnable { run { ... }}", "aka", "In JavaX, methods can have multiple names"); Map sc, sf; StringTree2 tokenExplanationsTree; JavaXHyperlinker() { clearCaches(); tokenExplanationsTree = stringTree2_javaTok(tokenExplanations); } String codeToHTML(String code) { return codeToHTML(code, true); } String codeToHTML(String code, boolean real) { List tok = javaTok(code); Map links = new HashMap(); // token index -> sf snippet ID Map explanations = new HashMap(); // token index -> explanation text putMultipleKeys(explanations, allPlus(4, jfindAll(tok, "void q {")), "Function will execute in module's queue"); // Go over all tokens and colorize Map styles = new HashMap(); tokensToColors(tok, styles); // Go over code tokens, generate links and explanations Matches m = new Matches(); for (int i = 1; i < l(tok); i += 2) { String t = tok.get(i); String prev = get(tok, i-2), prevPrev = get(tok, i-4); String next = get(tok, i+2); String sfID = sf.get(t); if (startsWith(t, "lambda", m) && isInteger(m.rest()) && isIdentifier(next)) explanations.put(i, ("lambda reference to a " + (m.rest()) + "-argument function")); if (eq(t, "is") && isIdentifier(next)) explanations.put(i, "synonym of 'implements'"); if (eq(t, "methodLambda0") && isIdentifier(next)) explanations.put(i, ("short for: x -> x." + next + "() (with a fresh variable instead of x)")); if (eq(t, "swappable") && isIdentifier(next)) explanations.put(i, "swappable functions can be exchanged per object instance"); if (eq(t, "main") && isIdentifier(next)) explanations.put(i, "reference to the current 'main' class (where all the standard functions are held, not always called 'main')"); if (eq(t, "autoDispose") && isIdentifier(next)) explanations.put(i, "autoDispose adds a cleanMeUp method that properly disposes of this variable"); if (eq(t, "macro") && isIdentifier(next)) explanations.put(i, "define a macro called '" + next + "', visible until the end of the enclosing block (or until redefined)"); if (eq(t, "virtual") && isIdentifier(next)) explanations.put(i, "\"virtual\" represents a type that is not visible in this realm, so it is transpiled to just Object"); if (eq(t, "concept") && isIdentifier(next)) explanations.put(i, "A concept is like a Java class, but persistable"); if (eq(t, "flexeq") && isIdentifier(next)) explanations.put(i, "flexeq is a fix for records inside parameterized classes"); if (eq(t, "switchable") && isIdentifier(next)) explanations.put(i, "A field that can be changed through the module's popup menu"); if (eq(t, "embedded") && isIdentifier(next)) explanations.put(i, "\"embedded\" allows you to put a function where they would not normally be allowed"); if (eq(t, "visual") && isIdentifier(next)) explanations.put(i, "short definition of the visualize() function"); if (eq(t, "dm_q") && isIdentifier(next)) explanations.put(i, "Function reference delegating to module queue"); if (eq(t, "!") && isIdentifier(prev) && neq(next, "=")) explanations.put(i, "! is short for .get()"); /*if (eq(t, "!") && containsNewLine(get(tok, i-1)) && isInteger(next)) explanations.put(i, "Translator invocation");*/ if (eq(t, "#") && isIdentifier(next)) explanations.put(i, "# makes an identifier local to the scope"); if (eq(t, "f") && isIdentifier(next)) explanations.put(i, "f references a static function in the main class"); if (eq(t, "r") && isIdentifier(next)) explanations.put(i, "short for: r { " + next + "() }"); if (eqOneOf(t, "rThread", "rThreadEnter") && isIdentifier(next)) explanations.put(i, "short for: " + t + " { " + next + "() }"); if (eq(t, "dispose") && isIdentifier(next)) explanations.put(i, "short for: cleanUp(" + next + "); " + next + " = null;"); if (eq(t, "*") && eq(next, "(")) explanations.put(i, "Short syntax for a constructor declaration"); if (eq(t, "thread") && eq(next, "{")) explanations.put(i, "Start a new thread with the following code"); if (eq(t, "module") && isIdentifier(next)) explanations.put(i, "A module is a class that can be loaded in Stefan's OS"); if (eq(t, "record") && isIdentifier(next)) explanations.put(i, "A record is a value-based class"); if (eq(t, "srecord") && isIdentifier(next)) explanations.put(i, "An srecord is a static value-based class"); if (eqOneOf(t, "cached", "simplyCached") && isIdentifier(next)) explanations.put(i, "A function that caches its return value"); if (eq(t, "thread") && isQuoted(next)) explanations.put(i, "Start a new thread with the following name & code"); if (eq(t, "if") && isQuoted(next)) { try { List tok2 = tok_subListWithoutBorderNTokens(tok, i, i+3); tok_expandIfQuoted(tok2); explanations.put(i, "short for: " + join(tok2)); } catch (Throwable __e) { _handleException(__e); }} if (eq(t, "html") && eq(next, "{")) explanations.put(i, "short for: static Object html(String uri, final Map params) ctex {"); if (eq(t, "try") && eq(next, "answer")) doublePut(explanations, i, i+2, "\"try answer\" returns the expression if it isn't null or empty"); if (isSingleQuoteIdentifier(t)) explanations.put(i, "string constant, " + quote(fromSingleQuoteIdentifier(t))); if (eq(t, "event") && isIdentifier(next)) explanations.put(i, "declare a function called " + next + "() plus helpers for adding listeners"); if (eqOneOf(t, "null", "false", "true", "this") && eq(next, ";") && tok_tokenBeforeLonelyReturnValue(tok, i-2)) doublePut(explanations, i, i+2, "short for: return " + t + ";"); String e = shortFor.get(t); if (e != null) mapPut(explanations, i, "short for: " + e); // link to standard function if (!explanations.containsKey(i) && sfID != null) { if (eqOneOf(prev, "f", "r", "rThread") || startsWith(prev, "lambda") || isIdentifier(next) && eqGet(tok, i+4, "(") || eqOneOf(next, "(", "°") && (neq(prev, ".") || eq(prevPrev, "main") && neq(get(tok, i-6), ".")) || eq(prev, "{") && eq(next, "}") && eq(prevPrev, "postProcess") || eq(prev, ":") && eq(prevPrev, ":")) links.put(i, sfID); } List fewTokens = codeTokens(subList(tok, i-1, i+2*5)); Pair p = stringTreeLeafValue2(tokenExplanationsTree, fewTokens); if (p != null) { //print(struct(p)); int lastCodeToken = i+p.b*2-2; if (eq(get(tok, lastCodeToken), "{")) lastCodeToken -= 2; mapPutInRange(explanations, i, lastCodeToken+1, p.a); } if (isQuoted(t) && eq(prev, "(") && isIdentifier(prevPrev) && isMechFunction(prevPrev)) mapPut(links, i, neatMechListURL(unquote(t))); //mapPut(explanations, i, tokenExplanations.get(t)); String id = sc.get(t); if (id != null) links.put(i, id); } // go over tokens, output HTML characters StringBuilder out = new StringBuilder(); Map titles = getSnippetTitles(filter(__64 -> isSnippetID(__64), values(links))); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (empty(t)) continue; String id = links.get(i), ex = explanations.get(i); String style = styles.get(i); if (t.startsWith("[[") && t.endsWith("]]")) { String explanation = "[[...]] denotes a multi-line string constant (as in Lua)"; out.append(dottedSpan("[[", explanation)); String inner = htmlencode(dropPrefix("[[", dropSuffix("]]", t))); out.append(span(inner, "style" , "background-color: #77FF77")); out.append(dottedSpan("]]", explanation)); continue; } if (t.startsWith("[=[") && t.endsWith("]=]")) { String explanation = "[=[...]=] denotes a multi-line string constant (as in Lua)"; out.append(dottedSpan("[=[", explanation)); String inner = htmlencode(dropPrefix("[=[", dropSuffix("]=]", t))); out.append(span(inner, "style" , "background-color: #77FF77")); out.append(dottedSpan("]=]", explanation)); continue; } String enc = htmlencode(t); out.append(id != null ? ahref(makeLink(real, id), enc, "title" , isSnippetID(id) ? titles.get(fsI(id)) : ex, "style" , "text-decoration: none; color: black; border-bottom: dotted 1px", "target" , targetBlank ? "_blank" : null) : ex != null ? dottedSpan(enc, ex) : style != null ? span(enc, "style", style) : enc); } String html = str(out); html = dynamize_noEncode(html); return html; } // id can be a URL String makeLink(boolean real, String id) { if (isURL(id)) return id; if (real) return longSnippetLink(id); return "/" + psI(id); } void clearCaches() { stdFunctions_clearCache(); sc = standardClassesMap(); sf = stdFunctions_cached(); //sf.putAll(tok_findStandardFunctionDefinitions(javaTokSnippet(#1022367))); } boolean isMechFunction(String s) { return startsWithOneOf(s, "mech", "mL"); } void tokensToColors(List tok, Map styles) { for (int i = 0; i < l(tok); i += 2) if (tok_whitespaceContainsJavaComments(tok.get(i))) styles.put(i, "color: #666666"); } } static class Var implements IVar, ISetter { Var() {} Var(A v) { this.v = v;} A v; // you can access this directly if you use one thread public synchronized void set(A a) { if (v != a) { v = a; notifyAll(); } } public synchronized A get() { return v; } public synchronized boolean has() { return v != null; } public synchronized void clear() { v = null; } public String toString() { return str(this.get()); } } static interface Visitable { void visitUsing(IVF1 f); } static class DefunctClassLoader {} static interface ITokCondition { boolean get(List tok, int i); // i = N Index } static class HTML implements IF0 , IFieldsToList{ String html; HTML() {} HTML(String html) { this.html = html;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + html + ")"; } public boolean equals(Object o) { if (!(o instanceof HTML)) return false; HTML __1 = (HTML) o; return eq(html, __1.html); } public int hashCode() { int h = 2228139; h = boostHashCombine(h, _hashCode(html)); return h; } public Object[] _fieldsToList() { return new Object[] {html}; } public String get() { return html; } } static abstract class TokCondition implements ITokCondition { public abstract boolean get(List tok, int i); // i = N Index } // Note: doesn't have value-semantics // i.e. eq(SecretValue(x), SecretValue(x)) will be false static class SecretValue extends Var { SecretValue() {} SecretValue(A a) { super(a); } public String toString() { return "Secret value"; } } // one instance should only be used for one page at a time static class HCRUD extends HAbstractRenderable { HCRUD_Data data; // normally an instance of HCRUD_Concepts (#1026002) // yeah these fields are a mess... boolean mutationRights = true; boolean allowCreateOrDelete = true; boolean allowCreate = true; boolean allowEdit = true; boolean singleton = false; boolean cmdsLeft = false; // put commands on the left instead of the right boolean showEntryCountInTitle = false; boolean allowFieldRenaming = false; int defaultTextFieldCols = 80; int valueDisplayLength = 1000; String tableClass; // CSS class for table String formTableClass; // CSS class for form String checkBoxClass = "crud_chkbox"; String customTitle; boolean showTextFieldsAsAutoExpandingTextAreas = false; Set unshownFields; // not shown in table or form Set uneditableFields; // not shown in form Set unlistedFields; // not shown in table boolean showCheckBoxes = false; boolean cleanItemIDs = false; boolean haveJQuery, haveSelectizeJS, haveSelectizeClickable; boolean needsJQuery = false; // unused boolean paginate = false; boolean sortable = false; boolean buttonsOnTop, buttonsOnBottom = true; boolean duplicateInNewTab = false; String formID = "crudForm"; boolean showQuickSaveButton = false; // needs hnotificationPopups() boolean enableMultiSelect = true; // enables shift+click on check boxes. needs haveJQuery boolean cellColumnToolTips = false; // give each table cell a tooltip showing its column name boolean showSearchField = false; String searchQuery; Map params; HTMLPaginator paginator = new HTMLPaginator(); // sort options String sortByField = "id"; String sortParameter = "sort"; boolean descending = false; int flexibleLengthListLeeway = 5; // how many empty rows to display at the end of each flexible length list // set internally after update/create or from "selectObj" param to highlight an item in the list (or show only that item) Object objectIDToHighlight; boolean showOnlySelected = false; String fieldPrefix = "f_"; int entryCount; HCRUD() {} HCRUD(HCRUD_Data data) { this.data = data;} HCRUD(String baseLink, HCRUD_Data data) { this.data = data; this.baseLink = baseLink;} String newLink() { return appendQueryToURL(baseLink, "cmd" , "new"); } String deleteLink(Object id) { return appendQueryToURL(baseLink, "delete_" + id, 1); } String editLink(Object id) { return appendQueryToURL(baseLink, "edit" , id); } String duplicateLink(Object id) { return appendQueryToURL(baseLink, "duplicate" , id); } void setParams(Map params) { this.params = params; if (objectIDToHighlight == null) objectIDToHighlight = params.get("selectObj"); if (eq("1", params.get("showOnlySelected"))) showOnlySelected = true; } // also handles commands if withCmds=true // you probably want to call renderPage() instead to handle all commands String render(boolean withCmds, Map params) { //print("HCRUD render"); setParams(params); if (!withCmds) return renderTable(false); { String __3 = handleCommands(params); if (!empty(__3)) return __3; } return renderMsgs(params) + divUnlessEmpty(nav()) + renderTable(withCmds); } transient IF0 nav; String nav() { return nav != null ? nav.get() : nav_base(); } final String nav_fallback(IF0 _f) { return _f != null ? _f.get() : nav_base(); } String nav_base() { List l = new ArrayList(); if (actuallyAllowCreate()) l.add(ahref(newLink(), "New " + itemName())); if (showSearchField) l.add(hInlineSearchForm("search", searchQuery, "")); return joinWithVBar(l); } String handleCommands(Map params) { List msgs = new ArrayList(); if (eqGet(params, "action", "create")) { if (!actuallyAllowCreate()) throw fail("Creating objects not allowed"); processRenames(params); Object id = data.createObject(preprocessUpdateParams(params), fieldPrefix); msgs.add(itemName() + " created (ID: " + id + ")"); objectIDToHighlight = id; } if (eqGet(params, "action", "update")) { if (!actuallyAllowEdit()) throw fail("Editing objects not allowed"); String id = params.get("id"); processRenames(params); msgs.add(data.updateObject(id, preprocessUpdateParams(params), fieldPrefix)); objectIDToHighlight = id; } List toDeleteList = keysDeprefixNemptyValue(params, "delete_"); if (eq(params.get("bulkAction"), "deleteSelected")) toDeleteList.addAll(keysDeprefixNemptyValue(params, "obj_")); for (String toDelete : toDeleteList) { if (!actuallyAllowDelete()) throw fail("Deleting objects not allowed"); msgs.add(data.deleteObject(toDelete)); } return empty(msgs) ? "" : refreshAfterCommand(params, msgs); } transient IF2, List, String> refreshAfterCommand; String refreshAfterCommand(Map params, List msgs) { return refreshAfterCommand != null ? refreshAfterCommand.get(params, msgs) : refreshAfterCommand_base(params, msgs); } final String refreshAfterCommand_fallback(IF2, List, String> _f, Map params, List msgs) { return _f != null ? _f.get(params, msgs) : refreshAfterCommand_base(params, msgs); } String refreshAfterCommand_base(Map params, List msgs) { String redirectAfterSave = mapGet(params, "redirectAfterSave"); if (nempty(redirectAfterSave)) return hrefresh(redirectAfterSave); return refreshWithMsgs(msgs, "anchor" , objectIDToHighlight == null ? null : "obj" + objectIDToHighlight, "params" , objectIDToHighlight == null ? null : litmap("selectObj" , objectIDToHighlight)); } String encodeField(String s) { return or(data.fieldNameToHTML(s), s); } // in table transient IF2 renderValue; String renderValue(String field, Object value) { return renderValue != null ? renderValue.get(field, value) : renderValue_base(field, value); } final String renderValue_fallback(IF2 _f, String field, Object value) { return _f != null ? _f.get(field, value) : renderValue_base(field, value); } String renderValue_base(String field, Object value) { if (value instanceof HTML) return ((HTML) value).html; value = deref(value); if (value instanceof SecretValue) return hhiddenStuff(renderValue_inner(((SecretValue) value).get())); return renderValue_inner(value); } transient IF1 renderValue_inner; String renderValue_inner(Object value) { return renderValue_inner != null ? renderValue_inner.get(value) : renderValue_inner_base(value); } final String renderValue_inner_fallback(IF1 _f, Object value) { return _f != null ? _f.get(value) : renderValue_inner_base(value); } String renderValue_inner_base(Object value) { if (value instanceof Boolean) return yesNo_short((Boolean) value); return htmlEncode_nlToBr_withIndents(shorten(valueDisplayLength, strOrEmpty(value))); } String renderTable(boolean withCmds) { return renderTable(withCmds, data.list()); } String valueToSortable(Object value) { if (value instanceof HTML) return ((HTML) value).get(); return strOrNull(value); } // l = list of maps as it comes from data object String renderTable(boolean withCmds, List> l) { //print("HCRUD renderTable"); entryCount = l(l); if (empty(l)) return p("No entries"); if (!eq(data.defaultSortField(), pair(sortByField, descending))) { if (nempty(sortByField)) { print("Sorting " + nEntries(l) + " by " + sortByField); l = sortByTransformedMapKey_alphaNum(__105 -> valueToSortable(__105), l, sortByField); } if (descending) l = reversed(l); } // l2 is the rendered map. use keyEncoding to get from l keys to l2 keys Map keyEncoding = new HashMap(); List> l2 = lazyMap(l, _map -> { Object id = itemID(_map); return data.new Item(id) { public Map calcFullMap() { Map map = _map; map = mapMinusKeys(map, joinSets(unshownFields, unlistedFields)); Map map2 = postProcessTableRow(map, mapToMap( (key, value) -> pair(mapPut_returnValue(keyEncoding, key, encodeField(key)), renderValue(key, value)), map)); if (singleton) map2.remove(encodeField(data.idField())); // don't show ID in table in singleton mode if (withCmds) map2 = addCmdsToTableRow(map, map2); // add anchor to row map2.put(firstKey(map2), aname("obj" + id, firstValue(map2))); return map2; } }; }); List out = new ArrayList(); if (paginate) { paginator.processParams(params); paginator.baseLink = addParamsToURL(baseLink, filterKeys(__65 -> keepParamInPagination(__65), params)); paginator.max = l(l2); out.add(divUnlessEmpty(paginator.renderNav())); List> l3 = subListOrFull(l2, paginator.visibleRange()); //printVars_str(+objectIDToHighlight, visible := l(l3), first := first(l3), firstID := mapToID(first(l3))); // if highlighted object is not on page or user wants to see only this object, show only this object if (objectIDToHighlight != null && (showOnlySelected || !any(__66 -> isHighlighted(__66), l3))) { l3 = llNonNulls(firstThat(__67 -> isHighlighted(__67), l2)); //printVars_str(+objectIDToHighlight, total := l(l2), found := l(l3)); } l2 = l3; } Map replaceHeaders = new HashMap(); if (sortable && !singleton) for (Map.Entry __1 : _entrySet( keyEncoding)) { String key = __1.getKey(); String html = __1.getValue(); boolean sortedByField = eq(sortByField, key); boolean showDescendingLink = sortedByField && !descending; String htmlOld = html; if (sortedByField) { String title = showDescendingLink ? "Click here to sort descending" : "Click here to sort ascending"; String titleSorted = "Sorted by this field (" + (descending ? "descending" : "ascending") + ")"; title = titleSorted + ". " + title; html = span_title(title, unicode_downOrUpPointingTriangle(descending)) + " " + html; } String sortLink = appendQueryToURL(baseLink, sortParameter, showDescendingLink ? "-" + key : key); replaceHeaders.put(htmlOld, /*html + " " + ahref(sortLink, unicode_smallDownOrUpPointingTriangle(showDescendingLink), +title)*/ ahref(sortLink, html)); } Map paramsByColName = null; if (cellColumnToolTips) { paramsByColName = new HashMap(); for (Map map : l2) for (String key : keys(map)) if (!paramsByColName.containsKey(key)) paramsByColName.put(key, litobjectarray("title" , nullIfEmpty(htmldecode_dropTagsAndComments(key)))); } out.add(hpostform( htmlTable2_noHtmlEncode(l2, paramsPlus(tableParams(), "replaceHeaders", replaceHeaders, "paramsByColName", paramsByColName)) + (!withCmds || !showCheckBoxes ? "" : "\n" + divUnlessEmpty(renderBulkCmds())), "action" , baseLink)); if (showCheckBoxes && haveJQuery && enableMultiSelect) out.add(hCheckBoxMultiSelect_v2()); return lines_rtrim(out); } Object mapToID(Map item) { return item == null ? null : dropAllTags(strOrNull(item.get(encodeField(idField())))); } // item is after encodeField boolean isHighlighted(Map item) { Object id = mapToID(item); //print("isHighlighted ID: " + toStringWithClass(id) + " / " + toStringWithClass(objectIDToHighlight)); return eq(id, objectIDToHighlight); } transient IF0 renderBulkCmds; String renderBulkCmds() { return renderBulkCmds != null ? renderBulkCmds.get() : renderBulkCmds_base(); } final String renderBulkCmds_fallback(IF0 _f) { return _f != null ? _f.get() : renderBulkCmds_base(); } String renderBulkCmds_base() { return "Bulk action: " + hselect("bulkAction", litorderedmap("" , "", "deleteSelected" , "Delete selected")) + " " + hsubmit("OK", "onclick" , "return confirm('Are you sure?')"); } Map addCmdsToTableRow(Map map, Map map2) { if (showCheckBoxes) { Object id = itemID(map); map2.put(checkBoxKey(), hcheckbox("obj_" + id, false, "title" , "Select this object for a bulk action", "class" , checkBoxClass)); map2 = putKeysFirst(map2, checkBoxKey()); } map2.put(cmdsKey(), renderCmds(map)); if (cmdsLeft) map2 = putKeysFirst(map2, cmdsKey()); return map2; } /*swappable*/ Object[] tableParams() { return litparams( "tdParams" , litparams("valign" , "top"), "tableParams" , litparams("class" , tableClass)); } String renderForm(Map map) { AutoCloseable __5 = tempSetTL(htmlencode_forParams_useV2, true); try { //print("renderForm: filteredFields=" + data.filteredFields()); map = mapMinusKeys(map, joinSets(unshownFields, uneditableFields, data.filteredFields())); Map mapWithoutID = mapWithoutKey(map, data.idField()); List> matrix = map(mapWithoutID, (field, value) -> { String help = data.fieldHelp(field); return ll( allowFieldRenaming ? hinputfield("rename_" + field, field, "class" , "field-rename", "style" , "border: none; text-align: right", "title" , "Edit this to rename field " + quote(field) + " or clear to delete field") : encodeField(field), addHelpText(help, renderInput(field, value)) ); }); massageFormMatrix(map, matrix); return htableRaw_valignTop(matrix , empty(formTableClass) ? litparams("border" , 1, "cellpadding" , 4) : litparams("class" , formTableClass)); } finally { _close(__5); }} String renderInput(String field, Object value) { String name = fieldPrefix + field; return renderInput(name, data.getRenderer(field, value), value); } String renderInput(String name, HCRUD_Data.Renderer r, Object value) { //print("Renderer for " + name + ": " + r); if (r != null) value = r.preprocessValue(value); String meta = r == null ? "" : renderMetaInfo(r.metaInfo, name); // switch by renderer type if (r instanceof HCRUD_Data.AceEditor) { //ret meta + hAceEditor(strOrEmpty(value), style := "width: " + r.cols + "ch; height: " + r.rows + "em", +name); HTMLAceEditor ace = new HTMLAceEditor(strOrEmpty(value)); ace.name = name; ace.divParams.put("style" , "width: " + ((HCRUD_Data.AceEditor) r).cols + "ch; height: " + ((HCRUD_Data.AceEditor) r).rows + "em"); customizeACEEditor(ace); return meta + ace.headStuff() + ace.html(); } if (r instanceof HCRUD_Data.TextArea) return meta + htextarea(strOrEmpty(value), "name", name, "cols" , ((HCRUD_Data.TextArea) r).cols, "rows" , ((HCRUD_Data.TextArea) r).rows); if (r instanceof HCRUD_Data.TextField) return meta + renderTextField(name, strOrEmpty(value), ((HCRUD_Data.TextField) r).cols); if (r instanceof HCRUD_Data.ComboBox) return meta + renderComboBox(name, ((HCRUD_Data.ComboBox) r).valueToEntry(value), ((HCRUD_Data.ComboBox) r).entries, ((HCRUD_Data.ComboBox) r).editable); if (r instanceof HCRUD_Data.DynamicComboBox) return meta + renderDynamicComboBox(name, ((HCRUD_Data.DynamicComboBox) r).valueToEntry(value), ((HCRUD_Data.DynamicComboBox) r).info, ((HCRUD_Data.DynamicComboBox) r).editable, ((HCRUD_Data.DynamicComboBox) r).url); if (r instanceof HCRUD_Data.CheckBox) //ret hcheckbox(name, isTrue(value)); return meta + htrickcheckboxWithText(name, "", isTrue(value)); if (r instanceof HCRUD_Data.FlexibleLengthList) { List list = (List) value; List rows = new ArrayList(); int n = l(list)+flexibleLengthListLeeway; for (int i = 0; i < n; i++) { Object item = _get(list, i); //print("Item: " + item); rows.add(tr(td(i+1 + ".", "align" , "right") + td(renderInput(name + "_" + i, ((HCRUD_Data.FlexibleLengthList) r).itemRenderer, item)))); } return meta + htag("table", lines(rows)); } if (r instanceof HCRUD_Data.NotEditable) return "Not editable"; return renderInput_default(name, value); } transient IVF1 customizeACEEditor; void customizeACEEditor(HTMLAceEditor ace) { if (customizeACEEditor != null) customizeACEEditor.get(ace); else customizeACEEditor_base(ace); } final void customizeACEEditor_fallback(IVF1 _f, HTMLAceEditor ace) { if (_f != null) _f.get(ace); else customizeACEEditor_base(ace); } void customizeACEEditor_base(HTMLAceEditor ace) {} String renderMetaInfo(String metaInfo, String name) { if (empty(metaInfo)) return ""; return hhidden("metaInfo_" + dropPrefix(fieldPrefix, name), metaInfo); } String renderInput_default(String name, Object value) { return renderTextField(name, strOrEmpty(value), defaultTextFieldCols); } String renderTextField(String name, String value, int cols) { if (showTextFieldsAsAutoExpandingTextAreas) { return htextarea(value, "name", name, "class" , "auto-expand", "style" , "width: " + cols + "ch", "autofocus" , eq(mapGet(params, "autofocus"), name) ? html_valueLessParam() : null, "onkeydown" , jquery_submitFormOnCtrlEnter()); } return htextfield(name, value, "size" , cols, "style" , "font-family: monospace"); } String renderNewForm() { return renderNewForm(data.emptyObject()); } // pre-populate fields from request parameters String renderNewFormWithParams(Map params) { Map filteredMap = subMapStartingWith_dropPrefix(params, fieldPrefix); Map map = joinMaps(data.emptyObject(), (Map) filteredMap); data.rawFormValues = params; // pre-populate list fields for (Map.Entry __0 : _entrySet( filteredMap)) { String key = __0.getKey(); String value = __0.getValue(); List l = splitAt(key, "_"); if (l(l) == 2) { String field = first(l); int idx = parseInt(second(l)); List list = (List) (map.get(field)); if (!(list instanceof ArrayList)) map.put(field, list = new ArrayList()); listPut(list, idx, value); } } return renderNewForm(map); } String renderNewForm(Map map) { //printStruct("renderNewForm", map); String buttons = p(hsubmit("Create")); return hpostform( hhidden("action", "create") + formExtraHiddens() + stringIf(buttonsOnTop, buttons) + renderForm(map) + stringIf(buttonsOnBottom, buttons), paramsPlus(formParameters(), "action" , baseLink)); } transient IF0 formParameters; Object[] formParameters() { return formParameters != null ? formParameters.get() : formParameters_base(); } final Object[] formParameters_fallback(IF0 _f) { return _f != null ? _f.get() : formParameters_base(); } Object[] formParameters_base() { return litparams("id" , formID); } String idField() { return data.idField(); } String renderEditForm(String id) { if (!actuallyAllowEdit()) return "Can't edit objects in this table"; if (!data.objectCanBeEdited(id)) return htmlEncode2("Object " + id + " can't be edited"); Map map = data.getObjectForEdit(id); if (map == null) return htmlEncode2("Entry " + id + " not found"); String onlyFields = mapGet(params, "onlyFields"); if (nempty(onlyFields)) map = onlyKeys(map, itemPlus(idField(), tok_identifiersOnly(onlyFields))); String buttons = p_vbar( hsubmit("Save changes"), !showQuickSaveButton ? "" : hbuttonOnClick_noSubmit("Save & keep editing", "\r\n $.ajax({\r\n type: 'POST',\r\n url: $('#crudForm').attr('action'),\r\n data: $('#crudForm').serialize(), \r\n success: function(response) { successNotification(\"Saved\"); },\r\n }).error(function() { errorNotification(\"Couldn't save\"); });\r\n "), deleteObjectHTML(id)); return hpostform( hhidden("action", "update") + formExtraHiddens() + hhidden("id", id) + p("Object ID: " + htmlEncode2(id)) + stringIf(buttonsOnTop, buttons) + renderForm(map) + stringIf(buttonsOnBottom, buttons), paramsPlus(formParameters(), "action" , baseLink + "#obj" + id)); } String renderPage(Map params) { //print("HCRUD renderPage"); setParams(params); { String __4 = handleComboSearch(params); if (!empty(__4)) return __4; } if (eqGet(params, "cmd", "new")) { if (!actuallyAllowCreate()) return "Can't create objects in ths table"; return frame(customTitleOr("New " + itemName()), renderNewFormWithParams(params)); } if (nempty(params.get("edit"))) return frame("Edit " + itemName(), renderEditForm(params.get("edit"))); if (nempty(params.get("duplicate"))) return frame("New " + itemName(), renderNewForm(data.getObjectForDuplication(params.get("duplicate")))); String rendered = render(mutationRights, params); // handle commands, render list String title = null; if (singleton) title = ahref(baseLink, firstToUpper(data.itemName())); else { if (objectIDToHighlight != null) title = data.titleForObjectID(objectIDToHighlight); if (empty(title)) title = (showEntryCountInTitle ? n2(entryCount) + " " : "") + ahref(baseLink, firstToUpper(data.itemNamePlural())); } return frame(customTitleOr(title), rendered); } HCRUD makeFrame(MakeFrame makeFrame) { super.makeFrame(makeFrame); return this; } String cmdsKey() { return ""; } String checkBoxKey() { return ""; } String itemName() { return data.itemName(); } transient IF2, Map, Map> postProcessTableRow; Map postProcessTableRow(Map data, Map rendered) { return postProcessTableRow != null ? postProcessTableRow.get(data, rendered) : postProcessTableRow_base(data, rendered); } final Map postProcessTableRow_fallback(IF2, Map, Map> _f, Map data, Map rendered) { return _f != null ? _f.get(data, rendered) : postProcessTableRow_base(data, rendered); } Map postProcessTableRow_base(Map data, Map rendered) { return rendered; } Object itemID(Map item) { Object id = mapGet(item, data.idField()); // getVarOpt decodes HTML record if (cleanItemIDs) id = htmlDecode_dropTags(strOrNull(getVarOpt(id))); return id; } long itemIDAsLong(Map item) { return parseLong(itemID(item)); } // return list of HTMLs for commands in pop down button transient IF1, List> additionalCmds; List additionalCmds(Map item) { return additionalCmds != null ? additionalCmds.get(item) : additionalCmds_base(item); } final List additionalCmds_fallback(IF1, List> _f, Map item) { return _f != null ? _f.get(item) : additionalCmds_base(item); } List additionalCmds_base(Map item) { return null; } transient IF1, String> renderCmds; String renderCmds(Map item) { return renderCmds != null ? renderCmds.get(item) : renderCmds_base(item); } final String renderCmds_fallback(IF1, String> _f, Map item) { return _f != null ? _f.get(item) : renderCmds_base(item); } String renderCmds_base(Map item) { Object id = itemID(item); List additionalCmds = additionalCmds(item); return joinNemptiesWithVBar( !actuallyAllowEdit() || !data.objectCanBeEdited(id) ? null : ahref(editLink(id), "EDIT"), deleteObjectHTML(id), !actuallyAllowCreate() ? null : targetBlankIf(duplicateInNewTab, duplicateLink(id), "dup", "title" , "duplicate"), empty(additionalCmds) ? null : hPopDownButton(additionalCmds) ); } boolean actuallyAllowCreate() { return !singleton && allowCreateOrDelete && allowCreate; } boolean actuallyAllowEdit() { return allowCreateOrDelete && allowEdit; } boolean actuallyAllowDelete() { return !singleton && allowCreateOrDelete; } // e.g. for adding rows to edit/create form transient IVF2, List>> massageFormMatrix; void massageFormMatrix(Map map, List> matrix) { if (massageFormMatrix != null) massageFormMatrix.get(map, matrix); else massageFormMatrix_base(map, matrix); } final void massageFormMatrix_fallback(IVF2, List>> _f, Map map, List> matrix) { if (_f != null) _f.get(map, matrix); else massageFormMatrix_base(map, matrix); } void massageFormMatrix_base(Map map, List> matrix) { } String renderComboBox(String name, String value, List entries, boolean editable) { if (haveSelectizeJS) { // coolest option - use selectize.js String id = aGlobalID(); return hselect_list(entries, value, "name", name, "id", id) + hjs("$('#" + id + "').selectize" + "\r\n ({\r\n searchField: 'text',\r\n openOnFocus: true,\r\n dropdownParent: 'body',\r\n create: " + jsBool(editable) + "\r\n /*allowEmptyOption: true*/\r\n " + unnull(moreSelectizeOptions2(name)) + "\r\n });\r\n ") + selectizeLayoutFix(); } if (haveJQuery) { // make searchable list if JQuery is available // (functional, but looks quite poor on Firefox) // TODO: this seems to always be editable String id = aGlobalID(); return tag("datalist", mapToLines(__68 -> hoption(__68), entries), "id", id) + tag("input", "", "name", name, "list" , id); } // standard non-searchable list if (editable) return hinputfield(name, value); // no editable combo box possible without selectize.js return hselect_list(entries, value, "name", name); } String renderDynamicComboBox(String name, String value, String info, boolean editable) { return renderDynamicComboBox(name, value, info, editable, null); } String renderDynamicComboBox(String name, String value, String info, boolean editable, String url) { assertTrue("haveSelectizeJS", haveSelectizeJS); String id = aGlobalID(); String ajaxURL = or2(url, baseLink); return hselect_list(llNempties(value), value, "name", name, "id", id) + hjs("$('#" + id + "').selectize" + "\r\n ({\r\n searchField: 'text',\r\n valueField: 'text',\r\n labelField: 'text',\r\n openOnFocus: true,\r\n dropdownParent: 'body',\r\n create: " + jsBool(editable) + ",\r\n load: function(query, callback) {\r\n if (!query.length) return callback();\r\n var data = {\r\n comboSearchInfo: " + jsQuote(info) + ",\r\n comboSearchQuery: query\r\n };\r\n console.log(\"Loading \" + " + jsQuote(baseLink) + " + \" with \" + JSON.stringify(data));\r\n $.ajax({\r\n url: " + jsQuote(ajaxURL) + ",\r\n type: 'GET',\r\n dataType: 'json',\r\n data: data,\r\n error: function() {\r\n console.log(\"Got error\");\r\n callback();\r\n },\r\n success: function(res) {\r\n //console.log(\"Got data: \" + res);\r\n var converted = res.map(x => { return {text: x}; });\r\n //console.log(\"Converted: \" + converted);\r\n callback(converted);\r\n }\r\n });\r\n }\r\n /*allowEmptyOption: true*/\r\n " + moreSelectizeOptions2(name) + "\r\n });\r\n ") + selectizeLayoutFix(); } void processSortParameter(Map params) { String sort = mapGet(params, sortParameter); //sortByField = null; if (nempty(sort)) if (startsWith(sort, "-")) { descending = true; sortByField = substring(sort, 1); } else { descending = false; sortByField = sort; } } String deleteObjectHTML(Object id) { return !actuallyAllowDelete() ? null : !data.objectCanBeDeleted(id) ? // TODO: custom msg! span_title("Object can't be deleted, either there are references to it or you are not authorized", htmlEncode2(unicode_DEL())) : ahrefWithConfirm( "Really delete item " + id + "?", deleteLink(id), htmlEncode2(unicode_DEL()), "title" , "delete"); } // helpText is also HTML String addHelpText(String helpText, String html) { return empty(helpText) ? html : html + p(small(helpText), "style" , "text-align: right"); } String moreSelectizeOptions2(String name) { return unnull(moreSelectizeOptions(name)) + (!haveSelectizeClickable ? "" : "\r\n , plugins: ['clickable']\r\n , render: {\r\n option: function(item) {\r\n var id = item.text.match(/\\d+/)[0];\r\n return '';\r\n }\r\n }\r\n "); } transient IF1 moreSelectizeOptions; String moreSelectizeOptions(String name) { return moreSelectizeOptions != null ? moreSelectizeOptions.get(name) : moreSelectizeOptions_base(name); } final String moreSelectizeOptions_fallback(IF1 _f, String name) { return _f != null ? _f.get(name) : moreSelectizeOptions_base(name); } String moreSelectizeOptions_base(String name) { return ""; } // deliver content for dynamic search in combo boxes String handleComboSearch(Map params) { String query = params.get("comboSearchQuery"); if (nempty(query)) { String info = params.get("comboSearchInfo"); return jsonEncode_shallowLineBreaks(data.comboBoxSearch(info, query)); } return null; } // for update or create transient IF1, Map> preprocessUpdateParams; Map preprocessUpdateParams(Map params) { return preprocessUpdateParams != null ? preprocessUpdateParams.get(params) : preprocessUpdateParams_base(params); } final Map preprocessUpdateParams_fallback(IF1, Map> _f, Map params) { return _f != null ? _f.get(params) : preprocessUpdateParams_base(params); } Map preprocessUpdateParams_base(Map params) { return params; } void disableAllMutationRights() { mutationRights = allowCreateOrDelete = allowCreate = allowEdit = false; } transient IF1 keepParamInPagination; boolean keepParamInPagination(String name) { return keepParamInPagination != null ? keepParamInPagination.get(name) : keepParamInPagination_base(name); } final boolean keepParamInPagination_fallback(IF1 _f, String name) { return _f != null ? _f.get(name) : keepParamInPagination_base(name); } boolean keepParamInPagination_base(String name) { return eq(name, "search"); } String customTitleOr(String title) { return or2(customTitle, or2(mapGet(params, "title"), title)); } transient IF0 selectizeLayoutFix; String selectizeLayoutFix() { return selectizeLayoutFix != null ? selectizeLayoutFix.get() : selectizeLayoutFix_base(); } final String selectizeLayoutFix_fallback(IF0 _f) { return _f != null ? _f.get() : selectizeLayoutFix_base(); } String selectizeLayoutFix_base() { // dirty CSS quick-fix (TODO: send only once) return hcss(".selectize-input, .selectize-control { min-width: 300px }"); } String formExtraHiddens() { String redirectAfterSave = mapGet(params, "redirectAfterSave"); return empty(redirectAfterSave) ? "" : hhidden("redirectAfterSave", redirectAfterSave); } void processRenames(Map params) { if (!allowFieldRenaming) return; for (String key1 : keysList(params)) { String field = dropPrefixOrNull("rename_", key1); if (field == null) continue; String newName = trim(params.get(key1)); if (newName == null || eq(field, newName)) continue; print("Renaming " + field + " to " + or2(newName, "")); params.remove("rename_" + field); // Try to catch f_myField, metaInfo_myField, f_myField_1... String re = "^([^_]+_)" + regexpQuote(field) + "(_[^_]+)?$"; for (String key : keysList(params)) { List groups = regexpGroups(re, key); if (groups != null) { String newKey = empty(newName) ? null : first(groups) + newName + unnull(second(groups)); mapPut(params, newKey, params.get(key)); params.put(key, ""); // this should delete stuff (if empty strings are converted to null) print("Renaming key: " + key + " => " + newKey); } } } } } final static class ConceptFieldIndexDesc extends ConceptFieldIndexBase { ConceptFieldIndexDesc(Class cc, String field) { super(cc, field); } ConceptFieldIndexDesc(Concepts concepts, Class cc, String field) { super(concepts, cc, field); } void init() { valueToObject = treeMultiSetMap(reverseOrder()); } void register() { concepts.addFieldIndex(cc, field, this); } public void close() { concepts.removeFieldIndex(cc, field, this); super.close(); } synchronized List objectsWithValueGreaterThan(Object value) { SortedMap> subMap = ((SortedMap>) valueToObject.data).headMap(value); return concatLists(values(subMap)); } } // -has fast nextElement() and prevElement() // -design allows for more functions like reordering the list // -Saves up to 34% in space over LinkedHashSet // (e.g. 22% for a set of 1,000 Ints) static class CompactLinkedHashSet extends AbstractSet { UnsynchronizedCompactHashSet> entries = new UnsynchronizedCompactHashSet(); Entry head, tail; static class Entry { A value; Entry prev, next; public int hashCode() { return _hashCode(value); } // "magic" equals function for CompactHashSet lookup without temp object public boolean equals(Object o) { return o == this || eq(value, o); } } public boolean add(A a) { if (entries.contains(a)) return false; Entry n = new Entry(); n.value = a; n.prev = tail; if (tail != null) tail.next = n; tail = n; if (head == null) head = n; entries.add(n); return true; } public boolean remove(Object a) { return remove(entries.find(a)); } public boolean remove(Entry node) { if (node == null) return false; if (node.next != null) node.next.prev = node.prev; else tail = node.prev; if (node.prev != null) node.prev.next = node.next; else head = node.next; entries.remove(node); return true; } public int size() { return entries.size(); } public IterableIterator iterator() { return new IterableIterator() { Entry entry = head, prev = null; public boolean hasNext() { return entry != null; } public A next() { A a = entry.value; prev = entry; entry = entry.next; return a; } // untested public void remove() { if (prev == null) throw new IllegalStateException(); CompactLinkedHashSet.this.remove(prev); prev = null; } }; } public void clear() { entries.clear(); head = tail = null; } public boolean contains(Object a) { return entries.contains(a); } public A find(Object o) { Entry e = entries.find(o); return e == null ? null : e.value; } public A prevElement(A a) { Entry e = entries.find(a); if (e == null || e.prev == null) return null; return e.prev.value; } public A nextElement(A a) { Entry e = entries.find(a); if (e == null || e.next == null) return null; return e.next.value; } public A first() { return head == null ? null : head.value; } public A last() { return tail == null ? null : tail.value; } boolean removeIfSame(Object o) { A value = find(o); if (value == o) { remove(value); return true; } return false; } } static abstract class DynModule extends DynamicObject { // we really shouldn't have used up these variable names // (subclasses can't overload them due to how the persistence works) String name, toolTip; // module name, module tool tip in task bar PersistableThrowable _error; Map> mechLists; Map _persistenceInfo; transient Object _host; transient Map timers = newWeakHashMap(); // all kinds of resources actually; value is closer helper transient Set _resources = synchroHashSet(); transient Lock lock; // set by stem transient boolean persistOnChangedField = true; transient int changeCount; transient Object changeCountSync = new Object(); transient List onChange; transient List onTransientChange; transient boolean verboseTimers = false; transient ReliableSingleThread rstUpdate; transient Set componentFieldsToKeep; // TODO: use DynamicObject for this transient Map transientGeneralMap = synchroHashMap(); transient Q q; // module command queue transient List onFieldChange; // also for non-persistent fields. L transient boolean _hasPreviousBounds = false; transient StringBuffer _printLog = new StringBuffer(); transient boolean deleted = false; DynModule() { if (useErrorHandling()) dm_initErrorHandling(); // TODO: DynModule is now shared between modules, so not good setMainDesktopPane((JDesktopPane) getCreatorOpt("desktop")); } boolean useErrorHandling() { return true; } boolean isVisible() { return isTrue(getOpt(_host, "visible")); } String moduleName() { return name; } void setModuleName(String name) { String oldName = this.name; if (!eq(name, oldName)) { setField("name", name); possiblyInternalFrameTitle(vis(), name); vmBus_send("moduleNameChange", this, oldName, name); } } void setModuleToolTip(String toolTip) { this.toolTip = toolTip; } JComponent vis() { return (JComponent) getOpt(_host, "vis"); } A ownResource(A a) { if (a != null) _resources.add(a); return a; } A ownTimer(A timer) { if (timer instanceof AutoCloseable) ownResource((AutoCloseable) timer); ownTimer(timer, "cancelTimerOrInterruptThread"); return timer; } void ownTimer(Object timer, Object closerHelper) { timers.put(timer, closerHelper); } void singleTimer(java.util.Timer timer) { stopAllTimers(); ownTimer(timer); } void stopAllTimers() { for (AutoCloseable resource : getAndClearList(_resources)) { if (verboseTimers) print("Releasing resource: " + resource); try { resource.close(); } catch (Throwable __e) { _handleException(__e); } } for (Map.Entry __0 : _entrySet( getAndClearMap(timers))) { Object timer = __0.getKey(); Object closerHelper = __0.getValue(); if (verboseTimers) print("Stopping timer: " + closerHelper + " / " + timer); pcallFInRealOrMyMC(this, closerHelper, timer); } } boolean isDeleted() { return deleted; } boolean deleted() { return deleted; } void cleanMeUp_dynModule() { deleted = true; stopAllTimers(); } void persistMe() { synchronized(changeCountSync) { ++changeCount; } pcallFAll(onChange); callOpt(_host, "_change"); updateMe(); } void fireChangeWithoutPersisting() { pcallFAll(onChange); } void fireTransientChange() { pcallFAll(onTransientChange); } void _change() { persistMe(); } void change() { persistMe(); } void updateMe() { rstUpdate().trigger(); } void changeAndUpdate() { _change(); updateMe(); } boolean setField_noPersist(String name, Object value) { return setField(name, value, true); } boolean _setField(String name, Object value) { return setField(name, value); } boolean setField(String name, Object value) { return setField(name, value, false); } boolean setField(String name, Object value, boolean noPersist) { AutoCloseable __5 = enter(); try { try { // some really weird classes fail on equals() (BCEL JavaClass, I'm looking at you) if (eq(get(this, name), value)) return false; } catch (Throwable __e) { _handleException(__e); } return setField_noCheck(name, value, noPersist); } finally { _close(__5); }} // always set even if equal // TODO: why are we not checking for transientness? boolean setField_noCheck(String name, Object value) { return setField_noCheck(name, value, false); } boolean setField_noCheck(String name, Object value, boolean noPersist) { AutoCloseable __6 = enter(); try { set(this, name, value); pcallFAll(onFieldChange, name); if (!noPersist && persistOnChangedField //&& !isTransientField(this, name) && !isFalse(mapGet(_persistenceInfo, name))) _change(); return true; } finally { _close(__6); }} A setFieldAndReturn(String name, A value) { setField(name, value); return value; } boolean setFields(Object... params) { boolean change = false; for (int i = 0; i < l(params); i += 2) if (setField((String) params[i], params[i+1])) change = true; return change; } void start() { _hasPreviousBounds = dm_getBounds() != null; if (hasMethod_onTypes(this, "onTopInput", String.class)) dm_onTopInput_q(new VF1() { public void get(String s) { try { call(module(), "onTopInput", s) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "call(module(), 'onTopInput, s)"; }}); } void revisualize() { call(creator(), "revisualizeModule", _host); } boolean useEnterShortcut() { return false; } AutoCloseable enter() { AutoCloseable c = tempSetBetterThreadLocalIfNecessary_weakRef(dm_current_generic_tl(), this); /// XXX shortcut, testing now (doesn't seem to work yet) if (c == null && useEnterShortcut()) return null; // assume we are entered (so the rest of what we do in this function is also done) Object realMC = getMainClass(this); if (printToModule()) c = combineAutoCloseables(c, tempInterceptPrintIfNotIntercepted(_printToModuleInterceptor())); if (realMC != mc()) { if (printToModule()) c = combineAutoCloseables(c, (AutoCloseable) callOpt(realMC, "tempInterceptPrintIfNotIntercepted", _printToModuleInterceptor())); else c = combineAutoCloseables(c, tempInterceptPrintIfNotIntercepted(new F1() { public Boolean get(String s) { try { { call(realMC, "print", s); return false; } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret false with call(realMC, 'print, s);"; }})); c = combineAutoCloseables(c, tempSetTL(realMC_tl(), realMC)); } return c; } F1 _printToModuleInterceptor() { return new F1() { public Boolean get(String s) { try { if (print_preprocess != null) s = (String) callF(print_preprocess, s); s = fixNewLines(s); Appendable loc = _printLog; Appendable 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 false; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (print_preprocess != null) s = (S) callF(print_preprocess, s);\r\n s = ..."; }}; } boolean printToModule() { return true; } // on by default now AutoCloseable enterAndLock() { return combineAutoCloseables(enter(), tempLock(lock)); } boolean setError(Throwable e) { setField("_error" , persistableThrowable(e)); return true; } void clearError() { setField("_error" , null); } void onChange(Runnable r) { Lock __1 = lock; lock(__1); try { if (onChange == null) onChange = synchroList(); addIfNotThere(onChange, r); } finally { unlock(__1); } } void onFieldChange(VF1 r) { Lock __2 = lock; lock(__2); try { if (onFieldChange == null) onFieldChange = synchroList(); addIfNotThere(onFieldChange, r); } finally { unlock(__2); } } void onChangeAndNow(Runnable r) { onChange(r); callF(r); } // forward to main class void onChangeAndNow(JComponent c, Object r) { onUpdateAndNow(c, r); } /*static*/ A onChange(A tc, Object r) { onUpdate(tc, r); return tc; } void update() {} void unvisualize() { zeroAllFieldsOfTypeExcept(this, Component.class, componentFieldsToKeep); } // return L (legacy) // or litobjectarray(text, Runnable, text, Runnable, ...) Object menuItems() { return null; } void enhanceFrame(Container f) { Object items = menuItems(); if (items instanceof Object[]) items = paramsToAbstractActions((Object[]) items); internalFramePopupMenuFromActions_threaded(f, (List) items); String switchableFields = (String) (callOpt(this, "switchableFields")); Set set = asLinkedHashSet(splitAtSpace(switchableFields)); Matches m = new Matches(); for (String field : allFields(getClass())) if (startsWith(field, "_switchableField_", m)) set.add(m.rest()); for (String field : set) { Class type = fieldType(this, field); if (eq(boolean.class, type)) dm_boolFieldMenuItem(f, field); else if (eq(int.class, type)) dm_intFieldMenuItem(f, field); else if (eq(long.class, type)) dm_longFieldMenuItem(f, field); else if (eq(double.class, type)) dm_doubleFieldMenuItem(f, field); else if (eq(float.class, type)) dm_floatFieldMenuItem(f, field); else if (eq(String.class, type)) dm_stringFieldMenuItem(f, field); else if (eq(File.class, type)) dm_fileFieldMenuItem(f, field); } } // assume this is called in start(), so no locking ReliableSingleThread rstUpdate() { if (rstUpdate == null) rstUpdate = dm_rst(this, new Runnable() { public void run() { try { AutoCloseable __7 = enter(); try { update(); } finally { _close(__7); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); update();"; }}); return rstUpdate; } Q q() { AutoCloseable __8 = enter(); try { if (q == null) { Lock __3 = lock; lock(__3); try { if (q == null) q = dm_startQ(); } finally { unlock(__3); } } return q; } finally { _close(__8); }} A withUpdate(A a) { rstUpdate().trigger(); return a; } DynModule module() { return this; } DynModule me() { return this; } JComponent visualize() { return dm_noVisualisation(); } String programTitle_cache; String programTitle() { if (programTitle_cache == null) programTitle_cache = snippetTitle(programID(mainClass(this))); return programTitle_cache; } // field must be non-transient void doPersist(String field) { _persistenceInfo = mapMinus(_persistenceInfo, field); } void dontPersist(String field) { _persistenceInfo = mapPlus(_persistenceInfo, "field" , false); } Appendable _actualPrintLog() { return printToModule() ? _printLog : print_log; } boolean addAndChange(Collection cl, A a) { if (cl == null || !cl.add(a)) return false; change(); return true; } void clearAndChange(Collection cl) { if (cl == null) return; cl.clear(); change(); } } // end of DynModule static void _registerTimer(java.util.Timer timer) { _registerTimer_original(timer); dm_currentModule().ownTimer(timer); } /* TODO: thread {} should enter the module svoid _onLoad_moduleThreadInfo { _threadInfo_addMakerAndRetriever( voidfunc(Map map) { mapPut(map, dm_current := dm_currentModule()) }, voidfunc(Map map) { dm_enter((S) map.get('dm_current)) }); } */ static class Fail extends RuntimeException implements IFieldsToList{ Object[] objects; Fail() {} Fail(Object... objects) { this.objects = objects;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + objects + ")"; }public Object[] _fieldsToList() { return new Object[] {objects}; } } // A concept should be an object, not just a string. // Functions that should always be there for child processes: static int concepts_internStringsLongerThan = 10; static ThreadLocal concepts_unlisted = new ThreadLocal(); // BREAKING CHANGE 2021/6/7 set to true static boolean concepts_unlistedByDefault = true; // true = we can create instances of concepts with "new" without registering them automatically interface IConceptIndex { void update(Concept c); // also for adding void remove(Concept c); } interface IFieldIndex { Collection getAll(Val val); List allValues(); // returns a cloned list MultiSet allValues_multiSet(); IterableIterator objectIterator(); } // Approach to persisting the Concepts object itself (in normal // DB operation, this is not done): For simplification, speed and // compactness, we make almost all the fields transient and store only // the concepts and the idCounter. To unstructure the Concepts object, // use unstructureConcepts() or postUnstructureConcepts(), then // re-set up any indices, listeners etc. // base class indicating nothing static class ConceptsChange {} // change of a single concept static class ConceptCreate extends ConceptsChange implements IFieldsToList{ Concept c; ConceptCreate() {} ConceptCreate(Concept c) { this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ")"; } public boolean equals(Object o) { if (!(o instanceof ConceptCreate)) return false; ConceptCreate __4 = (ConceptCreate) o; return eq(c, __4.c); } public int hashCode() { int h = -1751266972; h = boostHashCombine(h, _hashCode(c)); return h; } public Object[] _fieldsToList() { return new Object[] {c}; } } // change of a single concept static class ConceptChange extends ConceptsChange implements IFieldsToList{ Concept c; ConceptChange() {} ConceptChange(Concept c) { this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ")"; } public boolean equals(Object o) { if (!(o instanceof ConceptChange)) return false; ConceptChange __5 = (ConceptChange) o; return eq(c, __5.c); } public int hashCode() { int h = -1760609256; h = boostHashCombine(h, _hashCode(c)); return h; } public Object[] _fieldsToList() { return new Object[] {c}; } } // removal of a single concept // c.id is going to become 0 at some point, so we pass the // id separately static class ConceptDelete extends ConceptsChange implements IFieldsToList{ static final String _fieldOrder = "id c"; long id; Concept c; ConceptDelete() {} ConceptDelete(long id, Concept c) { this.c = c; this.id = id;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + id + ", " + c + ")"; } public boolean equals(Object o) { if (!(o instanceof ConceptDelete)) return false; ConceptDelete __6 = (ConceptDelete) o; return id == __6.id && eq(c, __6.c); } public int hashCode() { int h = -1734431213; h = boostHashCombine(h, _hashCode(id)); h = boostHashCombine(h, _hashCode(c)); return h; } public Object[] _fieldsToList() { return new Object[] {id, c}; } } // unknown change anywhere in concepts; consider it all dirty // (this one should not be used except for batch jobs) static class FullChange extends ConceptsChange implements IFieldsToList{ FullChange() {} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ")"; } public boolean equals(Object o) { return o instanceof FullChange; } public int hashCode() { int h = 733452095; return h; } public Object[] _fieldsToList() { return null; } } static class Concepts implements AutoCloseable { Map concepts = synchroTreeMap(); long idCounter; transient HashMap perClassData; transient Map miscMap; // don't use directly, call miscMap... methods to access // set to "-" for non-persistent (possibly not implemented) // also, can include a case ID ("#123/1") // TODO: have an actual directory instead transient String programID; transient Concepts parent; // new mechanism transient volatile long changes, changesWritten, lastChange; transient volatile java.util.Timer autoSaver; transient volatile boolean dontSave = false; transient volatile boolean savingConcepts, noXFullGrab; transient boolean vmBusSend = true; transient boolean initialSave = false; // set to false to avoid initial useless saving transient int autoSaveInterval = -1000; // 1 second + wait logic transient boolean useGZIP = true, quietSave; transient ReentrantLock lock = new ReentrantLock(true); transient ReentrantLock saverLock = new ReentrantLock(true); transient long lastSaveTook = -1, lastSaveWas, loadTook, uncompressedSize; transient float maxAutoSavePercentage = 10; transient List conceptIndices; transient Map, Map> fieldIndices; transient Map, Map> ciFieldIndices; //transient L saveActions = synchroList(); transient List preSave; transient Object classFinder = _defaultClassFinder(); transient List onAllChanged = synchroList(); // list of runnables transient Set onChange = new HashSet(); transient Object saveWrapper; // VF1, to profile saving transient boolean modifyOnCreate = false; // set _modified == created initially transient boolean modifyOnBackRef = false; // set modified if back refs change transient boolean useFileLock = true; // instead of locking by bot // OLD - not done anymore. transient bool collectForwardRefs transient FileBasedLock fileLock; transient boolean storeBaseClassesInStructure = false; // helps with schema evolution when concept subclasses disappear transient boolean useBackRefsForSearches = false; // assume backRefs are sane in order to speed up searches transient boolean defunct = false; transient int newBackupEveryXMinutes = 60; // add more fields for Concepts here Concepts() {} Concepts(String programID) { this.programID = programID;} synchronized long internalID() { do { ++idCounter; } while (hasConcept(idCounter)); return idCounter; } synchronized HashMap perClassData() { if (perClassData == null) perClassData = new HashMap(); return perClassData; } void initProgramID() { if (programID == null) programID = getDBProgramID(); } // Now tries to load from bot first, then go to disk. Concepts load() { return load(false); } Concepts safeLoad() { return load(true); } Concepts load(boolean allDynamic) { initProgramID(); // try custom grabber Object dbGrabber = miscMapGet("dbGrabber"); if (dbGrabber != null && !isFalse(callF(dbGrabber))) return this; try { if (tryToGrab(allDynamic)) return this; } catch (Throwable e) { if (!exceptionMessageContains(e, "no xfullgrab")) printShortException(e); print("xfullgrab failed - loading DB of " + programID + " from disk"); } return loadFromDisk(allDynamic); } Concepts loadFromDisk() { return loadFromDisk(false); } Concepts loadFromDisk(boolean allDynamic) { if (nempty(concepts)) clearConcepts(); //DynamicObject_loading.set(true); // now done in unstructure() //try { // minimal crash recovery restoreLatestBackupIfConceptsFileEmpty(programID, "doIt" , true); long time = now(); Map _concepts = concepts; // empty map readLocally2_allDynamic.set(allDynamic); AutoCloseable __3 = tempSetTL(readLocally2_classFinder, classFinder); try { readLocally2(this, programID, "concepts"); Map __concepts = concepts; concepts = _concepts; concepts.putAll(__concepts); int l = readLocally_stringLength; int tokrefs = unstructure_tokrefs; assignConceptsToUs(); loadTook = now()-time; done("Loaded " + n(l(concepts), "concepts"), time); try { if (fileSize(getProgramFile(programID, "idCounter.structure")) != 0) readLocally2(this, programID, "idCounter"); else calcIdCounter(); } catch (Throwable e) { _handleException(e); calcIdCounter(); } /*} finally { DynamicObject_loading.set(null); }*/ return this; } finally { _close(__3); }} Concepts loadConcepts() { return load(); } boolean tryToGrab(boolean allDynamic) { if (sameSnippetID(programID, getDBProgramID())) return false; RemoteDB db = connectToDBOpt(programID); try { if (db != null) { loadGrab(db.fullgrab(), allDynamic); return true; } return false; } finally { _close(db); }} Concepts load(String grab) { return loadGrab(grab, false); } Concepts safeLoad(String grab) { return loadGrab(grab, true); } Concepts loadGrab(String grab, boolean allDynamic) { clearConcepts(); DynamicObject_loading.set(true); try { Map map = (Map) unstructure(grab, allDynamic, classFinder); concepts.putAll(map); assignConceptsToUs(); for (long l : map.keySet()) idCounter = max(idCounter, l); } finally { DynamicObject_loading.set(null); } //XXX allChanged(); // Nobody is listening at this point anyway return this; } void assignConceptsToUs() { // fix unstructure bugs for (Pair p: mapToPairs((Map) (Map) concepts)) if (!(p.b instanceof Concept)) { print("DROPPING non-existant concept " + p.a + ": " + dynShortName(p.b)); concepts.remove(p.a); } for (Concept c : values(concepts)) c._concepts = this; for (Concept c : values(concepts)) c._doneLoading2(); // doneLoading2 is called on all concepts after all concepts are loaded } String progID() { return programID == null ? getDBProgramID() : programID; } Concept getConcept(String id) { return empty(id) ? null : getConcept(parseLong(id)); } Concept getConcept(long id) { return (Concept) concepts.get((long) id); } Concept getConcept(RC ref) { return ref == null ? null : getConcept(ref.longID()); } boolean hasConcept(long id) { return concepts.containsKey((long) id); } void deleteConcept(long id) { Concept c = getConcept(id); if (c == null) print("Concept " + id + " not found"); else c.delete(); } void calcIdCounter() { long id_ = 0; for (long id : keys(concepts)) id_ = max(id_, id); idCounter = id_+1; saveLocally2(this, programID, "idCounter"); } File conceptsFile() { return getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure"); } // used for locking when useFileLock is activated File lockFile() { return getProgramFile(programID, "concepts.lock"); } FileBasedLock fileLock() { if (fileLock == null) fileLock = new FileBasedLock(lockFile()); return fileLock; } void saveConceptsIfDirty() { saveConcepts(); } void save() { saveConcepts(); } void saveConcepts() { vmBus_send("saveConceptsCalled", Concepts.this); if (dontSave) return; initProgramID(); saverLock.lock(); savingConcepts = true; long start = now(), time; try { String s = null; //synchronized(main.class) { long _changes = changes; if (_changes == changesWritten) return; File f = conceptsFile(); lock.lock(); long fullTime = now(); try { saveLocally2(this, programID, "idCounter"); vmBus_send("idCounterWritten", Concepts.this, programID, "idCounter"); if (useGZIP) { vmBus_send("callingSaveWrapper", Concepts.this, saveWrapper); callRunnableWithWrapper(saveWrapper, new Runnable() { public void run() { try { vmBus_send("callingPreSave", Concepts.this, preSave); callFAll(preSave); vmBus_send("writingFile", Concepts.this, f); uncompressedSize = saveGZStructureToFile(f, cloneMap(concepts), makeStructureData()); vmBus_send("gzFileSaved", Concepts.this, f, uncompressedSize); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "vmBus_send callingPreSave(Concepts.this, preSave);\r\n callFAll(preS..."; }}); getProgramFile(programID, "concepts.structure").delete(); } else s = fullStructure(); } finally { lock.unlock(); } /*while (nempty(saveActions)) pcallF(popFirst(saveActions));*/ changesWritten = _changes; // only update when structure didn't fail if (!useGZIP) { time = now()-start; if (!quietSave) print("Saving " + toM(l(s)) + "M chars (" /*+ changesWritten + ", "*/ + time + " ms)"); start = now(); saveTextFile(f, javaTokWordWrap(s)); getProgramFile(programID, "concepts.structure.gz").delete(); } File backupFile = getProgramFile(programID, "backups/concepts.structure" + (useGZIP ? ".gz" : "") + ".backup" + ymd() + "-" + formatInt(hours(), 2) + (newBackupEveryXMinutes >= 60 ? "" : formatInt(roundDownTo_rev(minutes(), newBackupEveryXMinutes), 2))); copyFile(f, backupFile); time = now()-start; if (!quietSave) print(programID + ": Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)"); lastSaveWas = fullTime; lastSaveTook = now()-fullTime; } finally { savingConcepts = false; saverLock.unlock(); } } void _autoSaveConcepts() { if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) { long pivotTime = Math.round(lastSaveWas+lastSaveTook*100.0/maxAutoSavePercentage); if (now() < pivotTime) { //print("Skipping auto-save (last save took " + lastSaveTook + ")"); return; } } try { saveConcepts(); } catch (Throwable e) { print("Concept save failed, will try again"); printStackTrace(e); } } String fullStructure() { return structure(cloneMap(concepts), makeStructureData()); } transient IF0 makeStructureData; structure_Data makeStructureData() { return makeStructureData != null ? makeStructureData.get() : makeStructureData_base(); } final structure_Data makeStructureData_fallback(IF0 _f) { return _f != null ? _f.get() : makeStructureData_base(); } structure_Data makeStructureData_base() { return finishStructureData(new structure_Data()); } structure_Data finishStructureData(structure_Data data) { if (storeBaseClassesInStructure) data.storeBaseClasses = true; return data; } void clearConcepts() { for (Concept c : allConcepts()) c.delete(); //concepts.clear(); //allChanged(); } void fireLegacyChangeEvent() { synchronized(this) { ++changes; lastChange = sysNow(); } if (vmBusSend) vmBus_send("conceptsChanged", this); pcallFAll(onAllChanged); } // auto-save every second if dirty synchronized void autoSaveConcepts() { if (autoSaver == null) { if (isTransient()) throw fail("Can't persist transient database"); autoSaver = doEvery_daemon("Concepts Saver for " + programID, abs(autoSaveInterval), new Runnable() { public void run() { try { _autoSaveConcepts() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_autoSaveConcepts()"; }}); // print("Installed auto-saver (" + autoSaveInterval + " ms, " + progID() + ")"); } } public void close() { cleanMeUp(); } void cleanMeUp() { try { defunct = true; boolean shouldSave = autoSaver != null; if (autoSaver != null) { autoSaver.cancel(); autoSaver = null; } while (savingConcepts) sleepInCleanUp(10); if (shouldSave) saveConceptsIfDirty(); } catch (Throwable __e) { _handleException(__e); } { cleanUp(fileLock); fileLock = null; } } Map getIDsAndNames() { Map map = new HashMap(); Map cloned = cloneMap(concepts); for (long id : keys(cloned)) map.put(id, cloned.get(id).className); return map; } void deleteConcepts(List l) { ping(); if (l != null) for (Object o : cloneList(l)) if (o instanceof Long) { Concept c = concepts.get(o); if (c != null) c.delete(); } else if (o instanceof Concept) ((Concept) o).delete(); else warn("Can't delete " + getClassName(o)); } A conceptOfType(Class type) { IConceptCounter counter = conceptCounterForClass(type); if (counter != null) return (A) first(counter.allConcepts()); return firstOfType(allConcepts(), type); } List conceptsOfType(Class type) { List l = conceptsOfType_noParent(type); if (parent == null) return l; return concatLists_conservative(l, parent.conceptsOfType(type)); } List conceptsOfType_noParent(Class type) { ping(); IConceptCounter counter = conceptCounterForClass(type); if (counter != null) return (List) cloneList(counter.allConcepts()); return filterByType(allConcepts(), type); } List listConcepts(Class type) { return conceptsOfType(type); } List list(Class type) { return conceptsOfType(type); } List list_noParent(Class type) { return conceptsOfType_noParent(type); } // TODO: would be better to make this Cl (indices may return sets) List list(String type) { return conceptsOfType(type); } List conceptsOfType(String type) { return filterByDynamicType(allConcepts(), "main$" + type); } boolean hasConceptOfType(Class type) { return hasType(allConcepts(), type); } void persistConcepts() { loadConcepts(); autoSaveConcepts(); } // We love synonyms void conceptPersistence() { persistConcepts(); } Concepts persist() { persistConcepts(); return this; } void persist(Integer interval) { if (interval != null) autoSaveInterval = interval; persist(); } // Runs r if there is no concept of that type A ensureHas(Class c, Runnable r) { A a = conceptOfType(c); if (a == null) { r.run(); a = conceptOfType(c); if (a == null) throw fail("Concept not made by " + r + ": " + shortClassName(c)); } return a; } // Ensures that every concept of type c1 is ref'd by a concept of // type c2. // Type of func: voidfunc(concept) void ensureHas(Class c1, Class c2, Object func) { for (Concept a : conceptsOfType(c1)) { Concept b = findBackRef(a, c2); if (b == null) { callF(func, a); b = findBackRef(a, c2); if (b == null) throw fail("Concept not made by " + func + ": " + shortClassName(c2)); } } } // Type of func: voidfunc(concept) void forEvery(Class type, Object func) { for (Concept c : conceptsOfType(type)) callF(func, c); } int deleteAll(Class type) { List l = (List) conceptsOfType(type); for (Concept c : l) c.delete(); return l(l); } // always returns a new list (callers depend on this) Collection allConcepts() { synchronized(concepts) { return new ArrayList(values(concepts)); } } IConceptCounter conceptCounterForClass(Class c) { for (IFieldIndex idx : values(mapGet(fieldIndices, c))) if (idx instanceof IConceptCounter) return ((IConceptCounter) idx); for (IFieldIndex idx : values(mapGet(ciFieldIndices, c))) if (idx instanceof IConceptCounter) return ((IConceptCounter) idx); return null; } int countConcepts(Class c, Object... params) { int n = countConcepts_noParent(c, params); if (parent == null) return n; return n+parent.countConcepts(c, params); } int countConcepts_noParent(Class c, Object... params) { ping(); if (empty(params)) { IConceptCounter counter = conceptCounterForClass(c); if (counter != null) return counter.countConcepts(); return l(list_noParent(c)); } int n = 0; for (A x : list_noParent(c)) if (checkConceptFields(x, params)) ++n; return n; } int countConcepts(String c, Object... params) { ping(); if (empty(params)) return l(list(c)); int n = 0; for (Concept x : list(c)) if (checkConceptFields(x, params)) ++n; return n; } int countConcepts() { return l(concepts); } synchronized List clonedConceptIndices() { return cloneList(conceptIndices); } synchronized void addConceptIndex(IConceptIndex index) { if (conceptIndices == null) conceptIndices = new ArrayList(); conceptIndices.add(index); } synchronized void removeConceptIndex(IConceptIndex index) { if (conceptIndices == null) return; conceptIndices.remove(index); if (empty(conceptIndices)) conceptIndices = null; } synchronized void addFieldIndex(Class c, String field, IFieldIndex index) { if (fieldIndices == null) fieldIndices = new HashMap(); Map map = fieldIndices.get(c); if (map == null) fieldIndices.put(c, map = new HashMap()); map.put(field, index); } synchronized void removeFieldIndex(Class c, String field, IFieldIndex index) { Map map = mapGet(fieldIndices, c); mapRemove(map, field); } synchronized IFieldIndex getFieldIndex(Class c, String field) { if (fieldIndices == null) return null; Map map = fieldIndices.get(c); return map == null ? null : map.get(field); } synchronized IFieldIndex getAnyIndexForClass(Class c) { return firstValue(fieldIndices == null ? null : fieldIndices.get(c)); } synchronized void addCIFieldIndex(Class c, String field, IFieldIndex index) { if (ciFieldIndices == null) ciFieldIndices = new HashMap(); Map map = ciFieldIndices.get(c); if (map == null) ciFieldIndices.put(c, map = new HashMap()); map.put(field, index); } synchronized void removeCIFieldIndex(Class c, String field) { Map map = mapGet(ciFieldIndices, c); mapRemove(map, field); } synchronized IFieldIndex getCIFieldIndex(Class c, String field) { if (ciFieldIndices == null) return null; Map map = ciFieldIndices.get(c); return map == null ? null : map.get(field); } // inter-process methods RC xnew(String name, Object... values) { return new RC(cnew(name, values)); } void xset(long id, String field, Object value) { xset(new RC(id), field, value); } void xset(RC c, String field, Object value) { if (value instanceof RC) value = getConcept((RC) value); cset(getConcept(c), field, value); } Object xget(long id, String field) { return xget(new RC(id), field); } Object xget(RC c, String field) { return xgetPost(cget(getConcept(c), field)); } Object xgetPost(Object o) { o = deref(o); if (o instanceof Concept) return new RC((Concept) o); return o; } void xdelete(long id) { xdelete(new RC(id)); } void xdelete(RC c) { getConcept(c).delete(); } void xdelete(List l) { for (RC c : l) xdelete(c); } List xlist() { return map("toPassRef", allConcepts()); } List xlist(String className) { return map("toPassRef", conceptsOfType(className)); } boolean isTransient() { return eq(programID, "-"); } String xfullgrab() { if (noXFullGrab) throw fail("no xfullgrab (DB too large)"); Lock __1 = lock(); lock(__1); try { if (changes == changesWritten && !isTransient()) return loadConceptsStructure(programID); return fullStructure(); } finally { unlock(__1); } } /* dev. Either xfullgrabGZipped() { lock lock(); if (changes == changesWritten && !isTransient()) ret loadConceptsStructure(programID); ret fullStructure(); }*/ void xshutdown() { // Killing whole VM if someone wants this DB to shut down cleanKillVM(); } long xchangeCount() { return changes; } int xcount() { return countConcepts(); } void register(Concept c) { ping(); if (c._concepts == this) return; if (c._concepts != null) throw fail("Can't re-register"); c.id = internalID(); c.created = now(); if (modifyOnCreate) c._setModified(c.created); register_phase2(c); vmBus_send("conceptCreated", c); fireChange(new ConceptCreate(c)); } // also called by replaceConceptAndUpdateRefs void register_phase2(Concept c) { c._concepts = this; concepts.put((long) c.id, c); for (Concept.Ref r : c._refs()) r.index(); c.change(); c._onRegistered(); } void registerKeepingID(Concept c) { if (c._concepts == this) return; if (c._concepts != null) throw fail("Can't re-register"); c._concepts = this; concepts.put((long) c.id, c); c.change(); } void conceptChanged(Concept c) { fireChange(new ConceptChange(c)); if (conceptIndices != null) for (IConceptIndex index : clonedConceptIndices()) index.update(c); } boolean hasUnsavedData() { return changes != changesWritten || savingConcepts; } synchronized Object miscMapGet(Object key) { return mapGet(miscMap, key); } synchronized Object miscMapPut(Object key, Object value) { if (miscMap == null) miscMap = new HashMap(); return miscMap.put(key, value); } synchronized void miscMapRemove(Object key) { mapRemove(miscMap, key); } // Note: auto-typing can fool you, make sure create returns // a wide enough type synchronized A miscMapGetOrCreate(Object key, IF0 create) { if (containsKey(miscMap, key)) return (A) miscMap.get(key); A value = create.get(); miscMapPut(key, value); return value; } void setParent(Concepts parent) { this.parent = parent; } void fireChange(ConceptsChange change) { if (change == null) return; pcallFAll(onChange, change); fireLegacyChangeEvent(); } final void onChange(IVF1 l) { addChangeListener(l); } void addChangeListener(IVF1 l) { syncAdd(onChange, l); } void removeChangeListener(IVF1 l) { syncRemove(onChange, l); } void addPreSave(Runnable r) { preSave = syncAddOrCreate(preSave, r); } public String toString() { return nConcepts(concepts) + " (" + programID + ", hash: " + identityHashCode(this) + ")"; } } // end of Concepts static class Concept extends DynamicObject { transient Concepts _concepts; // Where we belong long id; long created, _modified; List refs; List backRefs; // used only internally (cnew) Concept(String className) { super(className); _created(); } Concept() { if (!_loading()) { //className = shortClassName(this); // XXX - necessary? //print("New concept of type " + className); _created(); } } Concept(boolean unlisted) { if (!unlisted) _created(); } public String toString() { return shortDynamicClassName(this) + " " + id; } static boolean loading() { return _loading(); } static boolean _loading() { return dynamicObjectIsLoading(); } void _created() { if (!concepts_unlistedByDefault && !eq(concepts_unlisted.get(), true)) db_mainConcepts().register(this); } // base class + required interface. experimental class TypedRef extends Ref { TypedRef() {} //Class aType; Class bType; TypedRef(Class bType) { this.bType = bType;} TypedRef(Class bType, B value) { this.bType = bType; set((A) value); } TypedRef(B value) { set((A) value); } public boolean set(A a) { return super.set(checkValue(a)); } void check() { checkValue(get()); } C checkValue(C a) { if (bType != null && a != null) assertIsInstance(a, bType); return a; } B b() { return (B) value; } } class Ref implements IRef { A value; Ref() { if (!dynamicObjectIsLoading()) registerRef(); } void registerRef() { vmBus_send("registeringConceptRef", this); } Ref(A value) { this.value = value; registerRef(); index(); } // get owning concept (source) Concept concept() { return Concept.this; } // get target public A get() { return value; } public boolean has() { return value != null; } boolean set(A a) { if (a == value) return false; unindex(); value = a; index(); change(); return true; } void setIfEmpty(A a) { if (!has()) set(a); } public void set(Ref ref) { set(ref.get()); } public void clear() { set((A) null); } boolean validRef() { return value != null && _concepts != null && _concepts == value._concepts; } // TODO: sync all the indexing and unindexing!? void index() { if (validRef()) { value._addBackRef(this); change(); } } Ref unindex() { if (validRef()) { value._removeBackRef(this); change(); } return this; } void unindexAndDrop() { unindex(); _removeRef(this); } void change() { Concept.this.change(); } public String toString() { return str(value); } } class RefL extends AbstractList { List> l = new ArrayList(); RefL() {} RefL(List l) { replaceWithList(l); } public void clear() { while (!isEmpty()) removeLast(this); } public void replaceWithList(List l) { clear(); for (A a : unnullForIteration(l)) add(a); } public A set(int i, A o) { Ref ref = syncGet(l, i); A prev = ref.get(); ref.set(o); return prev; } public void add(int i, A o) { syncAdd(l, i, new Ref(o)); } public A get(int i) { return syncGet(l, i).get(); } public A remove(int i) { return syncRemove(l, i).get(); } public int size() { return syncL(l); } public boolean contains(Object o) { if (o instanceof Concept) for (Ref r : l) if (eq(r.get(), o)) return true; return super.contains(o); } } void delete() { //name = "[defunct " + name + "]"; //defunct = true; //energy = 0; // clean refs for (Ref r : unnullForIteration(_refs())) r.unindex(); refs = null; // set back refs to null for (Ref r : cloneList(backRefs)) r.set((Concept) null); backRefs = null; var _concepts = this._concepts; if (_concepts != null) { _concepts.concepts.remove(id); _concepts.fireChange(new ConceptDelete(id, this)); if (_concepts.conceptIndices != null) for (IConceptIndex index : _concepts.conceptIndices) index.remove(this); this._concepts = null; } id = 0; } BaseXRef export() { return new BaseXRef(_concepts.progID(), id); } // notice system of a change in this object void change() { _setModified(now()); _change_withoutUpdatingModifiedField(); } void _setModified(long modified) { _modified = modified; } void _change_withoutUpdatingModifiedField() { if (_concepts != null) _concepts.conceptChanged(this); } void _change() { change(); } String _programID() { return _concepts == null ? getDBProgramID() : _concepts.progID(); } // overridable void _addBackRef(Concept.Ref ref) { backRefs = addDyn_quickSync(backRefs, ref); _backRefsModified(); } void _backRefsModified() { if (_concepts != null && _concepts.modifyOnBackRef) change(); } void _removeBackRef(Concept.Ref ref) { backRefs = removeDyn_quickSync(backRefs, ref); _backRefsModified(); } void _removeRef(Concept.Ref ref) { refs = removeDyn_quickSync(refs, ref); } int _backRefCount() { return syncL(backRefs); } // convenience methods final void setField(String field, Object value) { _setField(field, value); } void _setField(String field, Object value) { cset(this, field, value); } boolean setField_trueIfChanged(String field, Object value) { return cset(this, field, value) != 0; } A setFieldAndReturn(String field, A value) { setField(field, value); return value; } final void setFields(Object... values) { _setFields(values); } void _setFields(Object... values) { cset(this, values); } Concepts concepts() { return _concepts; } boolean isDeleted() { return id == 0; } void _doneLoading2() { Map map = _fieldMigrations(); if (map != null) for (Map.Entry __0 : _entrySet( map)) { String oldField = __0.getKey(); FieldMigration m = __0.getValue(); crenameField_noOverwrite(this, oldField, m.newField); } } static class FieldMigration implements IFieldsToList{ String newField; FieldMigration() {} FieldMigration(String newField) { this.newField = newField;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + newField + ")"; } public boolean equals(Object o) { if (!(o instanceof FieldMigration)) return false; FieldMigration __7 = (FieldMigration) o; return eq(newField, __7.newField); } public int hashCode() { int h = 558692372; h = boostHashCombine(h, _hashCode(newField)); return h; } public Object[] _fieldsToList() { return new Object[] {newField}; } } // value is Map _fieldMigrations() { return null; } // new wrapper to get a copy of the refs list // so we can eventually drop the refs field Collection _refs() { return scanConceptForRefs(this); } // for debugging List _rawRefsField() { return refs; } Concepts _concepts() { return _concepts; } boolean _conceptsDefunct() { return _concepts != null && _concepts.defunct; } boolean _conceptsDefunctOrUnregistered() { return _concepts == null || _concepts.defunct; } // allow refs to do magic stuff? void _onRegistered() { /*for (Ref ref : _refs()) refs._onRegistered();*/ } boolean addAndChange(Collection cl, A a) { if (cl == null || !cl.add(a)) return false; change(); return true; } void clearAndChange(Collection cl) { if (cl == null) return; cl.clear(); change(); } } // end of Concept // remote reference (for inter-process communication or // external databases). Formerly "PassRef". // prepared for string ids if we do them later static class RC { transient Object owner; String id; RC() {} // make serialisation happy RC(long id) { this.id = str(id); } RC(Object owner, long id) { this.id = str(id); this.owner = owner; } RC(Concept c) { this(c.id); } long longID() { return parseLong(id); } public String toString() { return id; } transient RemoteDB db; String getString(String field) { return db.xS(this, field); } Object get(String field) { return db.xget(this, field); } void set(String field, Object value) { db.xset(this, field, value); } } // Reference to a concept in another program static class BaseXRef { String programID; long id; BaseXRef() {} BaseXRef(String programID, long id) { this.id = id; this.programID = programID;} public boolean equals(Object o) { if (!(o instanceof BaseXRef)) return false; BaseXRef r = (BaseXRef) o; return eq(programID, r.programID) && eq(id, r.id); } public int hashCode() { return programID.hashCode() + (int) id; } } // BaseXRef as a concept static class XRef extends Concept { BaseXRef ref; XRef() {} XRef(BaseXRef ref) { this.ref = ref; _doneLoading2(); } // after we have been added to concepts void _doneLoading2() { getIndex().put(ref, this); } HashMap getIndex() { return getXRefIndex(_concepts); } } static synchronized HashMap getXRefIndex(Concepts concepts) { HashMap cache = (HashMap) concepts.perClassData().get(XRef.class); if (cache == null) concepts.perClassData.put(XRef.class, cache = new HashMap()); return cache; } // uses mainConcepts static XRef lookupOrCreateXRef(BaseXRef ref) { XRef xref = getXRefIndex(db_mainConcepts()).get(ref); if (xref == null) xref = new XRef(ref); return xref; } // define standard concept functions to use main concepts // Now in db_mainConcepts() /*static void cleanMeUp_concepts() { if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp(); // mainConcepts = null; // TODO }*/ static void loadAndAutoSaveConcepts() { db_mainConcepts().persist(); } static void loadAndAutoSaveConcepts(int interval) { db_mainConcepts().persist(interval); } static RC toPassRef(Concept c) { return new RC(c); } // so we can instantiate the program to run as a bare DB bot static void concepts_setUnlistedByDefault(boolean b) { concepts_unlistedByDefault = b; } static interface IWebRequest { public String uri(); public Map params(); public Map headers(); public Map files(); default public String clientIP() { return unnull(getClientIPFromHeaders(headers())); } default public String getHeader(String name) { return mapGet(headers(), name); } default public String domain() { return getHeader("host"); } public String cookie(); public boolean isHttps(); public boolean isPost(); default String protocol() { return isHttps() ? "https://" : "http://"; } // mark this request as no-spam (don't count towards spam limit) default void noSpam() {} default String get(String param) { return mapGet(params(), param); } default String userAgent() { return getHeader("user-agent"); } // proxy another page default Object proxy(String url, boolean rewriteHost, boolean rewritePort) { throw unimplemented(); } } // could almost add IVar here - but void set() and bool set() are incompatible in some places static interface IRef extends IF0 { // called by the referencee to atomically replace itself // Passing oldValue to avoid race conditions public default void replaceValue(A oldValue, A newValue) {} } static abstract class F0 { abstract A get(); } static abstract class F1 { abstract B get(A a); } // you still need to implement hasNext() and next() static abstract class IterableIterator implements Iterator, Iterable { public Iterator iterator() { return this; } public void remove() { unsupportedOperation(); } } static abstract class F2 { abstract C get(A a, B b); } static interface IF0 { A get(); } static interface Hasher { int hashCode(A a); boolean equals(A a, A b); } static abstract class CloseableIterableIterator extends IterableIterator implements AutoCloseable { public void close() throws Exception {} } static interface IF2 { C get(A a, B b); } static class ConceptFieldIndexCI extends ConceptFieldIndexBase { ConceptFieldIndexCI(Class cc, String field) { super(cc, field); } ConceptFieldIndexCI(Concepts concepts, Class cc, String field) { super(concepts, cc, field); } void init() { valueToObject = generalizedCIMultiSetMap(); } void register() { concepts.addCIFieldIndex(cc, field, this); } } static interface Producer { public A next(); // null when end } // TODO - separate uploadedFiles etc between sites!! // a human who answers to clients static class Worker extends Concept { String loginName, displayName; //S password; boolean available = false; long lastOnline; // recent timestamp if online Ref image = new Ref(); static String _fieldOrder = "loginName displayName"; String renderAsHTML() { return htmlEncode2(loginName + " (display name: " + displayName + ")"); } } abstract static class DynNewBot2 extends DynPrintLogAndEnabled { // API for Eleu void setEleu(Object eleu) { mainBot = eleu; } int maxRefsToShow = 5; volatile long requestsServed; transient String templateID = "#1029809"; static String cssID = "#1029808"; transient String botName = "DynNewBot2"; transient String heading = "DynNewBot2"; transient String adminName = "DynNewBot2 Admin"; transient String botImageID = "#1102935"; transient String userImageID = "#1102803"; transient String chatHeaderImageID = "#1102802"; transient String timeZone = ukTimeZone_string(); transient String baseLink = ""; transient boolean newDesign = true; // use new chat bot design transient boolean ariaLiveTrick = false; transient boolean ariaLiveTrick2 = true; class WorkerChat { int workerLongPollTick = 200; int workerLongPollMaxWait = 1000*30; long lastWorkerRequested; // timestamp for notification sound String mainAdminLink = "/"; Object html(DynNewBot2.Req req) { Map params = req.params; String uri = req.uri, uri2 = appendSlash(uri); boolean requestAuthed = req.auth != null; Conversation conv = req.conv; if (startsWith(uri2, "/worker/")) { if (!req.webRequest.isHttps()) return subBot_serveRedirect("https://" + req.webRequest.domain() + req.uri + htmlQuery(req.params)); if (!requestAuthed) return serveAuthForm(params.get("uri")); if (nempty(params.get("turnBotOn"))) conv.turnBotOn(); return serveWorkerPage(req); } return null; } // auth is tested before we get here String serveWorkerPage(DynNewBot2.Req req) { AuthedDialogID auth = req.auth; Conversation conv = req.conv; String uri = req.uri; Map params = req.params; String cookie = conv.cookie; String uri2 = afterLastSlash(uri); if (eq(uri2, "availableWorkers")) return "Available workers: " + or2(joinWithComma(map(workersAvailable(), w -> w.renderAsHTML())), "-"); if (nempty(params.get("workerLogOut"))) cset(auth, "loggedIn" , null); if (auth.loggedIn != null && nempty(params.get("workerAvailableBox"))) if (cset_trueIfChanged(auth.loggedIn, "available" , nempty(params.get("workerAvailable")))) noteConversationChange(); // update list of available workers if (nempty(params.get("acceptConversation"))) { if (conv.worker == null) { // only if not accepted by anyone cset(conv, "worker" , auth.loggedIn); conv.turnBotOff(); } } String loginID = params.get("workerLogIn"); if (nempty(loginID)) cset(auth, "loggedIn" , getConcept(Worker.class, parseLong(loginID))); Map map = prependEmptyOptionForHSelect(mapToOrderedMap(conceptsSortedByFieldCI(Worker.class, "loginName"), w -> pair(w.id, w.loginName))); if (auth.loggedIn == null) return hsansserif() + p("You are not logged in as a worker") + hpostform( "Log in as: " + hselect("workerLogIn", map, conceptID(auth.loggedIn)) + " " + hsubmit("OK"), "action" , botMod().baseLink + "/worker"); // We are logged in if (eq(uri2, "conversation")) { if (conv == null) return "Conversation not found"; // Serve the checkbox & the JavaScript String onOffURL = botMod().baseLink + "/worker/botOnOff" + hquery("cookie", cookie) + "&on="; return hsansserif() + loadJQuery() + hhidden("cookie", cookie) // for other frame + hpostform( hhidden("cookie", cookie) + p(renderBotStatus(conv)) + p(conv.botOn ? hsubmit("Accept conversation", "name" , "acceptConversation") : hsubmit("Turn bot back on", "name" , "turnBotOn")) , "action" , botMod().baseLink + "/worker/innerFrameSet", "target" , "innerFrameSet") // include bot + hscriptsrc(botMod().baseLink + "/script" + hquery("workerMode" , 1, "cookie" , conv.cookie)); } if (eq(uri2, "conversations")) { cset(auth.loggedIn, "lastOnline" , now()); boolean poll = eq("1", params.get("poll")); // poll mode? String content = ""; if (poll) { long seenChange = parseLong(params.get("lastChange")); vmBus_send("chatBot_startingWorkerPoll", mc(), conv); long start = sysNow(); List msgs; boolean first = true; while (licensed() && sysNow() < start+workerLongPollMaxWait && lastConversationChange == seenChange) sleep(workerLongPollTick); printVars_str("lastWorkerRequested", lastWorkerRequested, "seenChange", seenChange); if (lastWorkerRequested > seenChange) content = hscript("\r\n window.parent.parent.frames[0].sendDesktopNotification(\"A worker is requested!\", { action: function() { window.focus(); } });\r\n window.parent.parent.frames[0].playWorkerRequestedSound();\r\n "); // if poll times out, send update anyway to update time calculations } long pingThreshold = now()-activeConversationTimeout(); List convos = sortByCalculatedFieldDesc(c -> c.lastMsgTime(), conceptsWithFieldGreaterThan(Conversation.class, "lastPing" , pingThreshold)); content += hhiddenWithID("lastConversationChange", lastConversationChange) + tag("table", hsimpletableheader("IP", "Country", "Bot/worker status", "Last change", "Last messages") + mapToLines(convos, c -> { List lastMsgs = lastTwo(c.msgs); String style = c == conv ? "background: #90EE90" : null; String convLink = botMod().baseLink + "/worker/innerFrameSet" + hquery("cookie" , c.cookie); return tag("tr", td(ahref(convLink, c.ip, "target" , "innerFrameSet")) + td(getCountry(c)) + td(renderBotStatus(c)) + td(renderHowLongAgo(c.lastMsgTime())) + td(ahref(convLink, hparagraphs(lambdaMap(__106 -> renderMsgForWorkerChat(__106), lastMsgs)), "target" , "innerFrameSet", "style" , "text-decoration: none")), "style", style); }), "class" , "responstable"); if (poll) return content; String incrementalURL = botMod().baseLink + "/worker/conversations?poll=1&lastChange="; return hhtml( hhead(hsansserif() + loadJQuery() + hscript_clickableRows()) + hbody(h3(botName) + hpostform( "Logged in as " + htmlEncode2(auth.loggedIn.loginName) + " (display name: " + htmlEncode2(auth.loggedIn.displayName) + ")" + hhidden("workerAvailableBox" , 1) + "   " + hcheckboxWithText("workerAvailable", "I am available", auth.loggedIn.available, "onclick" , "form.submit()") + "   " + hsubmit("Log out", "name" , "workerLogOut"), "target" , "innerFrameSet", "action" , botMod().baseLink + "/worker/innerFrameSet") + p("Available workers: " + b(or2(joinWithComma( map(workersAvailable(), w -> w.displayName)), "none"))) + h3("Active conversations") + hcss_responstable() + hdivWithID("contentArea", content) + hscript("\r\n function poll_start() {\r\n var lastChange = $(\"#lastConversationChange\").val();\r\n if (!lastChange)\r\n setTimeout(poll_start, 1000);\r\n else {\r\n var url = \"#INCREMENTALURL#\" + lastChange;\r\n console.log(\"Loading \" + url);\r\n $.get(url, function(src) {\r\n if (src.match(/^ERROR/)) console.log(src);\r\n else {\r\n console.log(\"Loaded \" + src.length + \" chars\");\r\n $(\"#contentArea\").html(src);\r\n }\r\n setTimeout(poll_start, 1000);\r\n }, 'text')\r\n .fail(function() {\r\n console.log(\"Rescheduling after fail\");\r\n setTimeout(poll_start, 1000);\r\n });\r\n }\r\n }\r\n poll_start();\r\n ".replace("#INCREMENTALURL#", incrementalURL) ))); } // end of worker/conversations part if (eq(uri2, "notificationArea")) return hhtml( hhead(hsansserif() + loadJQuery()) + hbody(hdesktopNotifications() + div(small( span(hbutton("CLICK HERE to enable notification sounds!"), "id" , "enableSoundsBtn") + " | " + span("", "id" , "notiStatus")), "style" , "float: right") + hscript("\r\n function enableSounds() {\r\n document.removeEventListener('click', enableSounds);\r\n $(\"#enableSoundsBtn\").html(\"Notification sounds enabled\");\r\n }\r\n document.addEventListener('click', enableSounds);\r\n \r\n if (window.workerRequestedSound == null) {\r\n console.log(\"Loading worker requested sound\");\r\n window.workerRequestedSound = new Audio(\"https://botcompany.de/files/1400404/worker-requested.mp3\");\r\n }\r\n \r\n function playWorkerRequestedSound() {\r\n console.log(\"Playing worker requested sound\");\r\n window.workerRequestedSound.play();\r\n }\r\n window.playWorkerRequestedSound = playWorkerRequestedSound;\r\n\r\n "))); if (eq(uri2, "innerFrameSet")) // serve frame set 2 return hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]") + hframeset_cols("*,*", tag("frame", "", "name" , "conversations", "src" , botMod().baseLink + "/worker/conversations" + hquery("cookie", cookie)) + tag("frame", "", "name" , "conversation", "src" , conv == null ? null : botMod().baseLink + "/worker/conversation" + hquery("cookie", cookie)))); // serve frame set 1 return hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]") + hframeset_rows("50,*", tag("frame", "", "name" , "notificationArea", "src" , botMod().baseLink + "/worker/notificationArea") + tag("frame", "", "name" , "innerFrameSet", "src" , conv == null ? null : botMod().baseLink + "/worker/innerFrameSet" + hquery("cookie", cookie)))); } String renderMsgForWorkerChat(Msg msg) { return (msg.fromWorker != null ? htmlEncode2(msg.fromWorker.displayName) : msg.fromUser ? "User" : "Bot") + ": " + b(htmlEncode2If(shouldHtmlEncodeMsg(msg), msg.text)); } Collection workersAvailable() { long timestamp = now()-workerLongPollMaxWait-10000; return filter(list(Worker.class), w -> w.available && w.lastOnline >= timestamp); } boolean anyWorkersAvailable() { return nempty(workersAvailable()); } String renderBotStatus(Conversation conv) { return "Bot is " + b(conv.botOn ? "on" : "off") + "
" + "Assigned worker: " + b(conv.worker == null ? "none" : conv.worker.displayName); } } transient WorkerChat workerChat = new WorkerChat(); transient ReliableSingleThread rstBotActions = dm_rst(this, new Runnable() { public void run() { try { botActions(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "botActions();"; }}); transient String defaultHeaderColorLeft = "#2a27da"; transient String defaultHeaderColorRight = "#00ccff"; transient boolean enableUsers = false; transient boolean useWebSockets = false; transient boolean showRegisterLink = false; transient boolean showTalkToBotLink = false; transient boolean alwaysRedirectToHttps = false; transient boolean redirectOnLogout = false; // remove that ugly logout=1 from url transient boolean showFullErrors = false; transient boolean lockWhileDoingBotActions = false; transient boolean standardButtonsAsSymbols = false; transient boolean enableVars = false; // $userName etc. in bot messages transient boolean enableAvatars = false; transient boolean enableRandomActions = false; transient boolean recordExecutionsAndInputs = true; transient boolean phoneNumberSpecialInputField = true; transient boolean enableRadioButtons = false; // allow radio buttons for single-choice selection in bot dialogs transient boolean newDialogAfterWindowClosed = false; // always restart conversation when user comes back transient boolean showJoiningConversation = false; // show "... joining conversation" transient boolean quickRadioButtons = false; // don't show an OK button, make radio buttons submit instantly transient boolean useDynamicComboBoxes = false; transient boolean prefixPrintsWithConvID = true; transient boolean enableUndoStates = false; //transient bool usePageRegexp; // show pageRegexp field for Domain transient boolean showCRUDToEveryone = false; transient boolean showCRUDToNonMasterUsers = true; transient boolean showOnlySelectedObject = false; transient boolean storeBaseClassesInStructure = false; // better schema evolution transient boolean botDisabled = false; // chat bot disabled, don't even make conversation objects transient boolean useBotNameAsModuleName = true; transient boolean showMailSenderInfo = true; transient boolean authedDialogIDForEveryCookie = false; transient boolean showDeliveredDomains = true; String mailSenderInfo; // empty for SMTP localhost, otherwise: "senderURL#pw" transient int typingTimeshift = 2000; // make typing bubble appear more reliably transient ThreadLocal currentReq = new ThreadLocal(); transient volatile Scorer consistencyCheckResults; transient Lock statsLock = lock(); transient Map specialPurposes = litcimap( "bad email address", "Please enter a valid e-mail address", "bad phone number", "Please enter a valid phone number"); transient String nameOfReferencesColumn = "Referenced by"; transient Set requestsInFlight = syncWeakSet(); // end of variables void start() { super.start(); if (useBotNameAsModuleName) dm_setModuleName(botName); dm_assertFirstSibling(); concepts_setUnlistedByDefault(true); standardTimeZone(); standardTimeZone_name = timeZone; print("DB program ID: " + dbProgramID()); realPW(); // make password pWebChatBot(); dm_doEvery(60.0, new Runnable() { public void run() { try { cleanConversations(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "cleanConversations();"; }}); rstBotActions.trigger(); } void indexAllLinkableClasses() { ensureConceptClassesAreIndexed(allLinkableClasses()); } public JComponent visualize() { JComponent _c = super.visualize(); addToControlArea(jPopDownButton_noText(popDownButtonEntries())); return _c; } Object[] popDownButtonEntries() { return litobjectarray( "Show/edit master password...", runnableThread(new Runnable() { public void run() { try { AutoCloseable __24 = enter(); try { editMasterPassword(); } finally { _close(__24); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); editMasterPassword();"; }}), !showMailSenderInfo ? null : "Show/edit mail sender info...", runnableThread(new Runnable() { public void run() { try { AutoCloseable __25 = enter(); try { editMailSenderInfo(); } finally { _close(__25); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); editMailSenderInfo();"; }})); } static class Req implements IWebRequest { IWebRequest webRequest; String uri; Map params; AuthedDialogID auth; Domain authDomainObj; String authDomain; HTMLFramer1 framer; boolean masterAuthed = false; Conversation conv; String subURI; // rest of URI for sub-object WeakReference handlingThread; public String uri() { return uri; } final String subUri() { return subURI(); } String subURI() { return subURI; } public Map params() { return params; } public Map headers() { return webRequest.headers(); } public Map files() { return webRequest.files(); } public boolean isHttps() { return webRequest.isHttps(); } public String cookie() { return webRequest.cookie(); } boolean requestAuthed() { return auth != null; } public String get(String param) { return mapGet(params, param); } public boolean isPost() { return webRequest.isPost(); } void markNoSpam() { webRequest.noSpam(); } public void noSpam() { markNoSpam(); } String uriWithParams() { return appendParamsToURL(uri, params); } HTMLFramer1 framer() { return framer; } } boolean calcMasterAuthed(Req req) { return req != null && req.auth != null && req.auth.master; } Object html(IWebRequest request) { AutoCloseable __19 = enter(); try { //printVars(func := "html", ihc := identityHashCode(dm_current_generic_tl()), cg := dm_current_generic()); vmBus_logMethodCall("html", "this", this, "request", request); try { return html2(request); } catch (Throwable e) { printStackTrace(e); return subBot_serve500(showFullErrors || calcMasterAuthed(currentReq()) ? getStackTrace(e) : "Error."); } finally { currentReq.set(null); } } finally { _close(__19); }} Req currentReq() { return currentReq.get(); } void requestServed() {} Object html2(IWebRequest request) { htmlencode_forParams_useV2(); { Lock __2 = statsLock; lock(__2); try { requestsServed++; change(); requestServed(); vmBus_logMethodCall("html", "this", this, "requestsServed", requestsServed); } finally { unlock(__2); } } //printVars_str html2(+uri); Req req = webRequestToReq(request); AutoCloseable __20 = tempAdd(requestsInFlight, req); try { if (alwaysRedirectToHttps) { Object __10= redirectToHttps(req); if (__10 != null) return __10; } vmBus_logMethodCall("html", "Calling html3"); return html3(req); } finally { _close(__20); }} Req newReq() { return new Req(); } Req webRequestToReq(IWebRequest request) { Req req = newReq(); req.webRequest = request; req.uri = request.uri(); req.params = request.params(); req.handlingThread = weakRef(currentThread()); return req; } Object html3(Req req) { IWebRequest request = req.webRequest; String uri = req.uri; Map params = req.params; //printVars_str html2_afterRedirectToHTTPS(+uri); if (eq(params.get("_newConvCookie"), "1")) return hrefresh(appendQueryToURL(req.uri, mapPlus(mapMinus(req.params, "_newConvCookie"), "cookie" , "test_" + aGlobalID()))); // cookie comes either from a URI parameter or from the request header // Note: this can be either a JS-generated (in dynamic chat bot part) // or server-generated cookie (when loading initial chat bot script or admin) // To distinguish: JS-generated cookies are shorter and can contain numbers String cookie = request.cookie(); //print("Request cookie: " + cookie); boolean workerMode = nempty(params.get("workerMode")) || startsWith(uri, "/worker"); // find out which domain we're on ("delivered domain") String domain = request.domain(), _domain = domain; saveDeliveredDomain(domain); Domain domainObj = findDomainObj(domain); //printVars_str html2_getConversation(+uri); // get conversation String convCookie = params.get("cookie"); //if (eq(params.get("_newConvCookie"), "1")) convCookie = "conv-" + aGlobalID(); Conversation conv = botDisabled ? null : isRequestFromBot(req) ? null : nempty(convCookie) ? getConv(convCookie) : nempty(cookie) ? getConv(cookie) : null; req.conv = conv; //printVars_str html2_tempStuff(+uri); AutoCloseable tempThing = conv == null || !prefixPrintsWithConvID ? null : temp_printPrefix("Conv " + conv.cookie + ": "); AutoCloseable __21 = tempThing; try { currentReq.set(req); // keep for error printing //printVars_str html2_botConfig(+uri); String botConfig = params.get("_botConfig"); Map botConfigParams = decodeURIParams(botConfig); String simulatedDomain = botConfigParams.get("domain"); Domain domainObj2 = nempty(simulatedDomain) ? findDomainObj(simulatedDomain) : domainObj; if (nempty(botConfigParams)) cset(conv, "botConfig" , botConfigParams); //printVars_str html2_workerMode(+uri); // save ip & domain in conversation if (conv != null && !workerMode) if (cset_trueIfChanged(conv, "ip" , request.clientIP(), "domain", domain, "domainObj" , domainObj2)) { calcCountry(conv); initAvatar(conv); } //print("URI: " + uri + ", cookie: " + cookie + ", msgs: " + l(conv.msgs)); //printVars_str html2_handleAuth(+uri); { Object __11= handleAuth(req, cookie); if (__11 != null) return __11; } // TODO: instead of maxCache, check file date & request header (If-Unmodified-Since?) Matches m = new Matches(); if (startsWith(uri, "/worker-image/", m)) { long id = parseLong(m.rest()); return subBot_serveFile_maxCache(workerImageFile(id), "image/jpeg"); } if (startsWith(uri, "/uploaded-image/", m)) { long id = parseLong(m.rest()); UploadedImage img = getConcept(UploadedImage.class, id); return img == null ? serve404() : subBot_serveFile_maxCache(img.imageFile(), "image/jpeg"); } if (startsWith(uri, "/uploaded-sound/", m)) { long id = parseLong(m.rest()); UploadedSound sound = getConcept(UploadedSound.class, id); return sound == null ? serve404() : subBot_serveFile_maxCache(sound.soundFile(), sound.mimeType); } if (startsWith(uri, "/uploaded-file/", m)) { long id = parseLong(dropAfterSlash(m.rest())); UploadedFile f = getConcept(UploadedFile.class, id); return f == null ? serve404() : subBot_serveFile_maxCache(f.theFile(), or2(params.get("ct"), or2(trim(f.mimeType), binaryMimeType()))); } AuthedDialogID auth = authObject(cookie); fillReqAuthFromCookie(req, cookie, auth); if (eq(params.get("logout"), "1")) { { Object __12= handleLogout(req); if (__12 != null) return __12; } auth = authObject(cookie); } boolean requestAuthed = req.auth != null; // any authentication boolean masterAuthed = req.masterAuthed; Domain authDomainObj = req.authDomainObj; String authDomain = req.authDomain; if (requestAuthed) req.markNoSpam(); //printVars_str html2_serve2(+uri); { Object __13= serve2(req); if (__13 != null) return __13; } makeFramer(req); HTMLFramer1 framer = req.framer; //printVars_str html2_beforeServeCRUD(uri := req.uri(), +requestAuthed, +showCRUDToEveryone); if (showCRUDToEveryone || showCRUDToNonMasterUsers && requestAuthed || req.masterAuthed) { Object __14= serveCRUD(req); if (__14 != null) return __14; } if (!requestAuthed && settings().talkToBotOnlyWithAuth) return serveAuthForm(params.get("uri")); if (eq(uri, "/emoji-picker/index.js")) //ret serveWithContentType(loadTextFile(loadLibrary(#1400436)), "text/javascript"); // TODO: optimize return withHeader(subBot_maxCacheHeaders(serveInputStream(bufferedFileInputStream(loadLibrary("#1400436")), "text/javascript"))); if (eq(uri, "/emoji-picker-test")) return loadSnippet("#1029870"); if (eq(uri, "/logs")) { if (!masterAuthed) return serveAuthForm(rawLink(uri)); return webChatBotLogsHTML2(rawLink(uri), params); } if (eq(uri, "/refchecker")) { if (!masterAuthed) return serveAuthForm(rawLink(uri)); ConceptsRefChecker refChecker = new ConceptsRefChecker (db_mainConcepts()); List errors = refChecker.run(); if (eq(params.get("fix"), "1")) return serveText(refChecker.fixAll()); else return serveText(jsonEncode_breakAtLevels(2, litorderedmap("errors" , allToString(errors)))); } if (eq(uri, "/backupDBNow")) { if (!masterAuthed) return serveAuthForm(rawLink(uri)); return serveText("Backed up DB as " + fileInfo(backupConceptsNow())); } if (eq(uri, "/auth-only")) { if (!requestAuthed) return serveAuthForm(params.get("uri")); return ""; } if (eq(uri, "/leads-api")) return serveLeadsAPI(request); if (workerChat != null) // TODO: don't permit when worker chat is disabled { Object __15= workerChat.html(req); if (__15 != null) return __15; } String message = trim(params.get("btn")); if (empty(message)) message = trim(params.get("message")); if (match("new dialog", message)) { Lock __3 = dbLock(); lock(__3); try { conv.newDialog(); message = null; } finally { unlock(__3); } } if (eqic(message, "!toggle notifications")) { cset(conv, "notificationsOn" , !conv.notificationsOn); message = null; } // debug cmd: list undo states if (eqic(message, "!undo states")) { conv.add(new Msg(false, n2(conv.undoStates, "undo state"))); message = null; } this.conv.set(conv); if (nempty(message) && !lastUserMessageWas(conv, message)) { Lock __4 = dbLock(); lock(__4); try { print("Adding message: " + message); possiblyTriggerNewDialog(conv); if (workerMode) { Msg msg = new Msg(false, message); msg.fromWorker = auth.loggedIn; conv.add(msg); } else { // add user message conv.add(new Msg(true, message)); //conv.botTyping = now(); addScheduledAction(new OnUserMessage(conv)); } } finally { unlock(__4); } } String testMode = params.get("testMode"); if (nempty(testMode)) { print("Setting testMode", testMode); cset(conv, "testMode" , eq("1", testMode)); } if (eq(uri, "/msg")) return withHeader("OK"); if (eq(uri, "/typing")) { if (workerMode) { conv.botTyping = now(); print(conv.botTyping + " Bot typing in: " + conv.cookie); } else { conv.userTyping = now(); print(conv.userTyping + " User typing in: " + conv.cookie); } return withHeader("OK"); } // render all of a conversation's messages for inspection if (eq(uri, "/renderMessages")) { long msgTime = parseLong(params.get("msgTime")); List msgs = conv.allMsgs(); if (msgTime != 0) msgs = filter(msgs, msg -> msg.time == msgTime); StringBuilder buf = new StringBuilder(); renderMessages(conv, buf, msgs, msg -> false); // don't show buttons return hhtml_title_body(nMessages(msgs), hstylesheetsrc(cssURL()) + p(nMessages(msgs) + " found") + buf); } if (eq(uri, "/incremental")) { if (newDialogAfterWindowClosed && !conv.isActive()) { print("Clearing conversation, timed out"); Lock __5 = dbLock(); lock(__5); try { conv.newDialog(); } finally { unlock(__5); } } long start = sysNow(), start2 = now(); print("start2", start2); cset(conv, "lastPing" , now()); possiblyTriggerNewDialog(conv); int a = parseInt(params.get("a")); int reloadCounter = conv.reloadCounter; List msgs; boolean first = true; int timeout = toInt(req.params.get("timeout")); long endTime = start+(timeout <= 0 || timeout > longPollMaxWait/1000 ? longPollMaxWait : timeout*1000L); while (licensed() && sysNow() < endTime) { int as = conv.archiveSize(); msgs = cloneSubList(conv.msgs, a-as); // just the new messages boolean reloadTriggered = conv.reloadCounter > reloadCounter; boolean actuallyNewDialog = a < as; boolean newDialog = actuallyNewDialog || reloadTriggered; if (newDialog) msgs = cloneList(conv.msgs); long typing = workerMode ? conv.userTyping : conv.botTyping; boolean otherPartyTyping = typing >= start2-typingTimeshift; boolean anyEvent = nempty(msgs) || newDialog || otherPartyTyping; if (!anyEvent) { if (first) { //print("Long poll starting on " + cookie + ", " + a + "/" + a); first = false; } sleep(longPollTick); } else { //if (!first) print("Long poll ended."); if (eq(req.params.get("json"), "1")) return serveJSON_breakAtLevels(2, litorderedmap( "n" , conv.allCount(), "newDialog" , trueOrNull(newDialog), "otherPartyTyping" , trueOrNull(otherPartyTyping), "msgs" , map(msgs, msg -> litorderedmap( "time" , msg.time, "fromUser" , msg.fromUser, "fromWorker" , msg.fromWorker == null ? null : msg.fromWorker.displayName, "text" , msg.text, // TODO: out "labels" , msg.labels)))); StringBuilder buf = new StringBuilder(); if (newDialog) { // send header colors & notification status String l = or2_trim(domainObj2.headerColorLeft, defaultDomain().headerColorLeft, defaultHeaderColorLeft), r = or2_trim(domainObj2.headerColorRight, defaultDomain().headerColorRight, defaultHeaderColorRight); buf.append(hcss(".chat_header { background: linear-gradient(135deg, " + hexColorToCSSRGB(l) + " 0%, " + hexColorToCSSRGB(r) + " 100%); }")); buf.append(hscript("$('#chatBot_notiToggleText').text(" + jsQuote("Turn " + (conv.notificationsOn ? "off" : "on") + " notifications") + ");")); if (showJoiningConversation) { buf.append(hscript("$('#otherSideTyping .joining').html(" + jsQuote(nameOfBotSide(conv) + " now joining conversation...") + ");" + "setTimeout(function() { $('#otherSideTyping .joining').html(''); }, 5000);")); } } if (otherPartyTyping) { //print("Noticed " + (workerMode ? "user" : "bot") + " typing in " + conv.cookie); buf.append(hscript("showTyping(" + jsQuote(conv.botImg()) + ");")); } renderMessages(conv, buf, msgs); if (ariaLiveTrick2 && !workerMode) { Msg msg = lastBotMsg(msgs); if (msg != null) { String author = msg.fromWorker != null ? htmlEncode2(msg.fromWorker.displayName) : botName; buf.append(hscript("$(\"#screenreadertrick\").html(" + jsQuote(author + " says: " + msg.text) + ");")); } } if (a != 0 && anyInterestingMessages(msgs, workerMode)) buf.append(hscript( stringIf(conv.notificationsOn, "window.playChatNotification();\n") + "window.setTitleStatus(" + jsQuote((workerMode ? "User" : botName) + " says…") + ");" )); String html = str(buf); // TODO: hack for notransition //if (newDialog && !actuallyNewDialog) html = html.replace([[class="chat_msg_item]], [[class="notransition chat_msg_item]]); return withHeader("\n" + html); } } return withHeader(""); } if (eqOneOf(uri, "/script", "/demo")) { Lock __6 = dbLock(); lock(__6); try { String myTemplateID = templateID; String templateIDParam = req.params.get("templateID"); if (nempty(templateIDParam) && allowedTemplateID(templateIDParam)) myTemplateID = templateIDParam; String html = loadSnippet_cached(myTemplateID); String botLayout = req.params.get("botLayout"); String layout = or2(botLayout, defaultBotLayout()); html = modifyTemplateBeforeDelivery(html, req); String heading = or2(headingForReq(req), or2(trim(domainObj2.botName), this.heading)); String botImg = botImageForDomain(domainObj); UploadedSound sound = settings().notificationSound.get(); String notificationSound = sound != null ? sound.soundURL() : defaultNotificationSound(); String miscParam = workerMode ? "workerMode=1&" : ""; if (nempty(botLayout)) miscParam += "botLayout=" + urlencode(botLayout) + "&"; String incrementalURL = baseLink + "/incremental?" + miscParam + "a="; String typingURL = baseLink + "/typing?" + miscParam; String msgURL = baseLink + "/msg?" + miscParam + "message="; if (eqic(layout, "sahil")) { html = replaceDollarVars(html, "incrementalURL", incrementalURL, "typingURL", typingURL, "msgURL", msgURL, "n" , 0, "notificationSound", notificationSound, "workerMode", workerMode, "heading", heading, "botImg", botImg); } else { String langlinks = ""; if (html.contains(langlinks)) html = html.replace(langlinks, ahref(rawLink("eng"), "English") + " | " + ahref(rawLink("deu"), "German")); //html = html.replace("#COUNTRYCODE_OPTIONS#", countryCodeOptions(conv)); html = html.replace("#COUNTRY#", lower(conv.country)); html = html.replace("#BOTIMG#", botImg); html = html.replace("#N#", "0"); html = html.replace("#INCREMENTALURL#", incrementalURL); html = html.replace("#MSGURL#", msgURL); html = html.replace("#TYPINGURL#", typingURL); html = html.replace("#CSS_ID#", psI_str(cssID)); if (ariaLiveTrick || ariaLiveTrick2) html = html.replace("aria-live=\"polite\">", ">"); html = html.replace("#OTHERSIDE#", workerMode ? "User" : "Representative"); if (nempty(params.get("debug"))) html = html.replace("var showActions = false;", "var showActions = true;"); html = html.replace("#AUTOOPEN#", jsBool(workerMode || eq(params.get("_autoOpenBot"), "1") || botAutoOpen(domainObj2))); html = html.replace("#BOT_ON#", jsBool(botOn() || eq(uri, "/demo"))); html = html.replace("$HEADING", heading); html = html.replace("#WORKERMODE", jsBool(workerMode)); html = html.replace("#NOTIFICATIONSOUND#", notificationSound); html = html.replace("", ""); html = hreplaceTitle(html, heading); } if (eq(uri, "/demo")) return serveBotDemo(req, html); else return withHeader(subBot_serveJavaScript(html)); } finally { unlock(__6); } } { Object __16= serveOtherPage(req); if (__16 != null) return __16; } if (eq(uri, "/")) { Object __17= serveHomePage(); if (__17 != null) return __17; } if (eq(uri, "/login")) return serveAuthForm(); // serve uploaded files String uri2 = dropSlashPrefix(uri); if (!startsWith(uri, "/crud/") && nempty(uri2)) { // TODO: more caching UploadedFile fileToServe = conceptWhere(UploadedFile.class, "liveURI" , urldecode(uri2)); if (fileToServe != null) return subBot_serveFile(fileToServe.theFile(), nempty(fileToServe.mimeType) ? fileToServe.mimeType : guessMimeTypeFromFileName(afterLastSlash(fileToServe.liveURI))); } // add more public uris here // serve admin // if (!requestAuthed) ret serveAuthForm(params.get('uri)); if (requestAuthed) { // authed from here (not necessarily master-authed though) if (masterAuthed && eq(uri, "/dialogTree")) { BotStep step = getConcept(BotStep.class, toLong(params.get("stepID"))); if (step == null) return serve404("Step not found"); return hhtml_head_title_body("Dialog Tree for " + step, hmobilefix() + hsansserif() + h2("Dialog Tree") + hcss_linkColorInherit() + hcss("\r\n .dialogTree li { margin-top: 0.8em; }\r\n ") + renderDialogTree(step)); } if (eq(uri, "/thoughts")) return serveThoughts(req); if (masterAuthed && eq(uri, "/search")) return serveSearch(req); if (eq(uri, "/leads-csv")) { String text = leadsToCSV(conceptsWhere(Lead.class, mapToParams(filtersForClass(Lead.class, req)))); String name = "bot-leads" + (authDomainObj == null ? "" : "-" + replace(str(authDomainObj), "/", "-")) + "-" + ymd_minus_hm() + ".csv"; return serveCSVWithFileName(name, text); } if (eq(uri, "/cleanConversations") && masterAuthed) { cleanConversations(); return hrefresh(baseLink + "/crud/Conversation"); } if (eq(uri, "/deleteDeliveredDomains") && masterAuthed) { deleteDeliveredDomains(); return hrefresh(baseLink + "/crud/DeliveredDomain"); } if (eq(uri, "/emojis")) { framer.title = "Emoji List"; framer.contents.add(htmlTable2(map(emojiShortNameMap(), (code, emoji) -> litorderedmap("Shortcode" , code, "Emoji" , emoji)))); } if (eq(uri, "/embedCode")) { String goDaddyEmbedCode = ""; framer.title = "Embed Code"; framer.contents.add(h2("Chat bot embed code") + (empty(goDaddyEmbedCode) ? "" : h3("GoDaddy Site Builder") + p("Add an HTML box with this code:") + pre(htmlEncode2(goDaddyEmbedCode)) + h3("Other")) + p("Add this code in front of your " + tt(htmlEncode2("")) + " tag:") + pre(htmlEncode2(hjavascript_src_withType("https://" + request.domain() + baseLink + "/script")))); } if (eq(uri, "/stats")) { framer.add(htableRaw2(nonNulls( renderStats() ), ll("class" , "responstable"), null, null)); return completeFrame(req); } if (eq(uri, "/unpackToWeb") & req.masterAuthed) { UploadedFile file = getConcept(UploadedFile.class, parseLong(req.params.get("fileID"))); if (file == null) return serve404("File not found"); String path = unnull(req.params.get("path")); File zipFile = file.theFile(); int changes = 0; for (String filePath : listZIP(zipFile)) { String liveURI = appendSlash(path) + filePath; UploadedFile entry = uniq(UploadedFile.class, "liveURI", liveURI); byte[] data = loadBinaryFromZip(zipFile, filePath); if (!fileContentsIs(entry.theFile(), data)) { ++changes; saveBinaryFile(entry.theFile(), data); touchConcept(entry); } } return "Unpacked, " + nChanges(changes); } // put more AUTHED uris here } //if (masterAuthed) framer.addNavItem(baseLink + "/search", "Search"); { Object __18= serveDefaultPage(req); if (__18 != null) return __18; } return completeFrame(req); } finally { _close(__21); }} // end of html2 Object serveDefaultPage(Req req) { return null; } Object completeFrame() { return completeFrame(currentReq()); } transient IF1 completeFrame; Object completeFrame(Req req) { return completeFrame != null ? completeFrame.get(req) : completeFrame_base(req); } final Object completeFrame_fallback(IF1 _f, Req req) { return _f != null ? _f.get(req) : completeFrame_base(req); } Object completeFrame_base(Req req) { HTMLFramer1 framer = req.framer; boolean authed = req.requestAuthed(); if (authed) { if (settings().enableWorkerChat) framer.addNavItem(baseLink + "/worker", "Worker chat"); framer.addNavItem(baseLink + "?logout=1", "Log out"); } if (!authed || alwaysShowLogInLink()) framer.addNavItem(logInLink(), "Log in"); return framer.render(); } boolean alwaysShowLogInLink() { return false; } String logInLink() { return baseLink + "/"; } // put early stuff here Object serve2(Req req) { vmBus_send("html_serve2", this, req); return null; } Object serveOtherPage(Req req) { return null; }
A getDomainValue(Domain domainObj, IF1 getter, A defaultValue) { if (domainObj != null) { A val = getter.get(domainObj); if (val != null) return val; } { A val = getter.get(defaultDomain()); if (val != null) return val; } return defaultValue; } String makeBaseTitle(Req req) { return ahref(baseLink + "/", adminName) + " " + squareBracket(loggedInUserDesc_html(req)); } HTMLFramer1 newFramerInstance() { return new HTMLFramer1(); } void makeFramer(Req req) { HTMLFramer1 framer = newFramerInstance(); req.framer = framer; framer.titleIsHTML = true; framer.title = makeBaseTitle(req); framer.addInHead(hsansserif() + hmobilefix() + hresponstable() + hcss_responstableForForms()); framer.addInHead(loadJQuery2()); framer.addInHead(hjs_selectize()); framer.addInHead(hjs_copyToClipboard()); framer.addInHead(hNotificationPopups()); framer.addInHead(hcss_linkColorInherit()); if (useWebSockets) framer.addInHead(webSocketHeadStuff(req)); makeNavItems(req, framer); List cmdClasses = botCmdClasses(req); if (nempty(cmdClasses)) framer.contents.add(p("Bot actions: " + joinWithVBar(map(cmdClasses, c -> makeClassNavItem(c, req))))); } void makeNavItems(Req req, HTMLFramer1 framer) { if (showTalkToBotLink) framer.addNavItem(simulateDomainLink(req.authDomain), "Talk to bot", "targetBlank" , true); for (Class c : navItemClasses(req)) framer.addNavItem(makeClassNavItem(c, req)); } List navItemClasses(Req req) { return crudClasses(req); } // make and adapt CRUD for class HCRUD makeCRUD(Class c, Req req) { return makeCRUD(c, req, req.framer); } HCRUD makeCRUD(Class c, Req req, HTMLFramer1 framer) { HCRUD_Concepts data = crudData(c, req); data.referencesBlockDeletion = true; String searchQuery = trim(req.get("search")); data.customFilter = crudSearchFilter(req, data, searchQuery); HCRUD crud = new HCRUD(crudLink(c), data); crud.searchQuery = searchQuery; crud.params = req.params; crud.buttonsOnTop = neqOneOf(c, UploadedFile.class, UploadedSound.class, UploadedImage.class); crud.haveJQuery = crud.haveSelectizeJS = true; crud.sortable = true; crud.paginate = true; crud.paginator.step = 25; crud.cmdsLeft = true; crud.showCheckBoxes = true; crud.tableClass = "responstable"; //framer.addInHead(hcss(".crudForm { width: 800px; }")); crud.formTableClass = "responstableForForms"; if (!req.requestAuthed()) crud.disableAllMutationRights(); crud.renderValue_inner = value -> { if (isConceptList(value)) { List l = (List) value; return joinMap(l, ref -> p(ahref(conceptLink(ref), htmlEncode_nlToBr_withIndents(str(ref))))); } String html = crud.renderValue_inner_base(value); if (value instanceof Concept) return ahref(conceptLink((Concept) value), html); return html; }; if (c == Conversation.class) { // Note: fields for list are created from scratch in massageItemMapForList crud.nav = () -> joinNemptiesWithVBar(crud.nav_base(), ahref(baseLink + "/cleanConversations", "Clean list")); crud.unshownFields = litset("oldDialogs", "worker", "botOn", "lastPing", "cookie", "form", "testMode", "userMessageProcessed", "newDialogTriggered"); } if (c == DeliveredDomain.class || c == Conversation.class || c == Lead.class || c == ConversationFeedback.class) crud.allowCreate = crud.allowEdit = false; if (c == Settings.class) { crud.singleton = countConcepts(Settings.class) == 1; if (!settings().multiLanguageMode) crud.unshownFields = litset("defaultLanguage"); { if (framer != null) framer.add(p(joinNemptiesWithVBar( ahref(baseLink + "/emojis", "Emojis"), ahref(baseLink + "/stats", "Stats"), ahref(baseLink + "/embedCode", "Embed Code")))); } } if (c == Lead.class) { if (framer != null) framer.add(p(ahref(baseLink + "/leads-csv", "Export as CSV"))); } if (c == BotOutgoingQuestion.class) { crud.unlistedFields = litset("multipleChoiceSeparator", "placeholder"); if (!enableRadioButtons) crud.unlistedFields.add("radioButtons"); crud.massageFormMatrix = (map, matrix) -> { int idx = indexOfPred(matrix, row -> cic(first(row), "Actions")); BotOutgoingQuestion item = getConcept(BotOutgoingQuestion.class, toLong(map.get(crud.idField()))); //printVars_str("massageFormMatrix", +item, +idx); if (item == null) return; if (idx < 0) return; List row = matrix.get(idx); row.set(1, hcrud_mergeTables(row.get(1), tag("table", map(s -> tr(td() + td(htmlEncode2(s))), item.buttons)), "for:")); }; } data.addFilters(filtersForClass(c, req)); if (c == Domain.class) { crud.renderCmds = map -> { Domain dom = getConcept(Domain.class, toLong(crud.itemID(map))); return joinNemptiesWithVBar(crud.renderCmds_base(map), targetBlank(simulateDomainLink(dom.domainAndPath), "Talk to bot")); }; /*if (!usePageRegexp) crud.unshownFields = litset("pageRegexp");*/ if (showDeliveredDomains) { if (framer != null) framer.add(p("See also the " + ahref(crudLink(DeliveredDomain.class), "list of domains the bot was delivered on"))); } } if (c == DeliveredDomain.class) { crud.nav = () -> joinNemptiesWithVBar(crud.nav_base(), ahref("/deleteDeliveredDomains", "Delete all")); crud.renderCmds = map -> { DeliveredDomain dom = getConcept(DeliveredDomain.class, toLong(crud.itemID(map))); return joinNemptiesWithVBar(crud.renderCmds_base(map), targetBlank(simulateDomainLink(dom.domain), "Talk to bot")); }; } if (c == UploadedImage.class) { crud.massageFormMatrix = (map, matrix) -> { UploadedImage item = getConcept(UploadedImage.class, toLong(crud.itemID(map))); addInFront(matrix, ll("Upload image", hjs_imgUploadBase64Encoder() + himageupload("id" , "imgUploader") + hhiddenWithIDAndName("f_img_base64"))); }; crud.formParameters = () -> paramsPlus(crud.formParameters_base(), "onsubmit" , "return submitWithImageConversion(this)"); } // TODO: set mime type! if (c == UploadedSound.class) { crud.massageFormMatrix = (map, matrix) -> { UploadedSound item = getConcept(UploadedSound.class, toLong(crud.itemID(map))); matrix.add(ll("Upload MP3/OGG/WAV/M4A", hjs_fileUploadBase64Encoder() + haudioUpload("id" , "fileUploader") + hhiddenWithIDAndName("f_file_base64"))); }; crud.formParameters = () -> litparams("onsubmit" , "return submitWithFileConversion(this)"); } if (c == UploadedFile.class) { crud.massageFormMatrix = (map, matrix) -> { UploadedFile item = getConcept(UploadedFile.class, toLong(crud.itemID(map))); matrix.add(ll("Upload File", hjs_fileUploadBase64Encoder() + hfileupload("id" , "fileUploader") + hhiddenWithIDAndName("f_file_base64") + hjs("\r\n $(\"input[name=thefile]\").change(function(e) {\r\n var name = e.target.files[0].name;\r\n if (name)\r\n $(\"input[name=f_name]\").val(name); \r\n });\r\n " ))); }; crud.formParameters = () -> litparams("onsubmit" , "return submitWithFileConversion(this)"); } if (isSubclassOf(c, BotStep.class)) { crud.renderCmds = map -> { BotStep step = getConcept(BotStep.class, toLong(crud.itemID(map))); return joinNemptiesWithVBar(crud.renderCmds_base(map), targetBlank(simulateScriptLink(step), "Test in bot"), hPopDownButton( targetBlank(baseLink + "/dialogTree?stepID=" + step.id, "Show Dialog Tree"))); }; if (showDeliveredDomains) { if (framer != null) framer.add(p("See also the " + ahref(crudLink(DeliveredDomain.class), "list of domains the bot was delivered on"))); } } if (c == Worker.class) { crud.unshownFields = litset("lastOnline"); crud.uneditableFields = litset("available", "lastOnline"); } // show references crud.postProcessTableRow = (item, rendered) -> { long id = crud.itemIDAsLong(item); Concept concept = getConcept(id); if (concept == null) return rendered; Collection refs = allBackRefs(concept); if (empty(refs)) return rendered; refs = sortedByConceptID(refs); String rts = req.get("refsToShow"); int refsToShow = empty(rts) ? maxRefsToShow : parseInt(rts); int more = l(refs)-refsToShow; return mapPlus(rendered, span_title("Where is this object used", nameOfReferencesColumn), joinMap(takeFirst(refsToShow, refs), ref -> p(ahref(conceptLink(ref), htmlEncode_nlToBr_withIndents(str(ref))))) + (more <= 0 ? "" : "
" + ahref(addParamsToURL(crudLink(concept), "refsToShow" , 10000), "+" + n2(more) + " more"))); }; return crud; } // returns base CRUD class Class isLinkableConcept(Concept c, Req req) { if (req == null || c == null) return null; Class theClass = _getClass(c); return firstThat(allLinkableClasses(req), base -> isSubclassOf(theClass, base)); } String crudLink(Concept c) { return conceptLink(c, currentReq()); } String conceptLink(Concept c) { return conceptLink(c, currentReq()); } String conceptLink(Concept c, Req req) { Class theClass = c.getClass(); if (req != null) { theClass = isLinkableConcept(c, req); if (theClass == null) return null; } return c == null ? null : appendQueryToURL(crudLink(theClass), "selectObj" , c.id, "showOnlySelected" , showOnlySelectedObject ? "1": null) + "#obj" + c.id; } String conceptEditLink(Concept c, Object... __) { return c == null ? null : appendQueryToURL(crudLink(c.getClass()), paramsPlus(__, "edit" , c.id)); } String conceptDuplicateLink(Concept c) { return c == null ? null : appendQueryToURL(crudLink(c.getClass()), "duplicate" , c.id); }
String makeClassNavItem(Class c, Req req) { HCRUD_Concepts data = crudData(c, req); HCRUD crud = makeCRUD(c, req, null); Map filters = filtersForClass(c, req); int count = countConcepts(c, mapToParams(filters)); //print("Count for " + c + " with " + filters + ": " + count); return (crud.singleton ? "" : span(n2(count), "data-isCountOf" , shortClassName(c)) + " ") + ahref(crudLink(c), count == 1 ? data.itemName() : data.itemNamePlural()) + (!crud.actuallyAllowCreate() ? "" : " " + targetBlankIf(newLinkTargetBlank(c), newLinkForCRUD(crud, c), "+", "title" , "Create New " + data.itemName())); } transient IF1 newLinkTargetBlank; boolean newLinkTargetBlank(Class c) { return newLinkTargetBlank != null ? newLinkTargetBlank.get(c) : newLinkTargetBlank_base(c); } final boolean newLinkTargetBlank_fallback(IF1 _f, Class c) { return _f != null ? _f.get(c) : newLinkTargetBlank_base(c); } boolean newLinkTargetBlank_base(Class c) { return false; } String newLinkForCRUD(HCRUD crud, Class c) { return crud.newLink(); } Collection allLinkableClasses() { Req req = newReq(); req.masterAuthed = true; return allLinkableClasses(req); } Collection allLinkableClasses(Req req) { return joinSets(crudClasses(req), botCmdClasses()); } List botCmdClasses() { return dynNewBot2_botCmdClasses(); } List dynNewBot2_botCmdClasses() { List l = ll(BotMessage.class, UploadedImage.class, BotImage.class, BotOutgoingQuestion.class, BotPause.class, Sequence.class); if (enableRandomActions) l.add(BotRandomAction.class); if (settings().multiLanguageMode) l.add(BotSwitchLanguage.class); return l; } List crudClasses(Req req) { return dynNewBot2_crudClasses(req); } List dynNewBot2_crudClasses(Req req) { if (req == null ? null : req.masterAuthed) { List l = ll(Conversation.class, Lead.class, ConversationFeedback.class, Domain.class, UserKeyword.class, UploadedSound.class, UploadedFile.class); if (enableAvatars) l.add(Avatar.class); l.add(Settings.class); if (settings().multiLanguageMode) l.add(Language.class); if (settings().enableWorkerChat) l.add(Worker.class); if (recordExecutionsAndInputs) addAll(l, ExecutedStep.class, InputHandled.class); return l; } else return ll(Conversation.class, Lead.class, ConversationFeedback.class); } Map filtersForClass(Class c, Req req) { if (c == Conversation.class && req.authDomainObj != null) return litmap("domainObj" , req.authDomainObj); if (eqOneOf(c, Lead.class, ConversationFeedback.class) && req.authDomainObj != null) return litmap("domain" , req.authDomainObj); return null; } String crudBase() { return baseLink + "/crud"; } String crudLink(Class c) { return crudBase() + "/" + shortName(c); } HCRUD_Concepts crudData(Class c, Req req) { HCRUD_Concepts < A > cc = new HCRUD_Concepts<>(c); if (useDynamicComboBoxes) cc.useDynamicComboBoxes = true; if (eq(req.get("dynamicComboBoxes"), "1")) cc.useDynamicComboBoxes = true; cc.trimAllSingleLineValues = true; cc.fieldHelp("comment", "Put any comment about this object here"); cc.itemName = () -> replaceIfEquals( dropPrefix("Bot ", cc.itemName_base()), "Jump Button", "Button"); cc.valueConverter = new DefaultValueConverterForField() { public OrError convertValue(Object object, Field field, Object value) { // e.g. for "buttons" field print("convertValue " + field + " " + className(value)); if (value instanceof String && eq(field.getGenericType(), type_LS())) { print("tlft"); return new OrError(tlft((String) value)); } return super.convertValue(object, field, value); } }; if (c == InputHandled.class) { cc.itemNamePlural = () -> "Inputs Handled"; } if (c == Domain.class) { cc.fieldHelp( "domainAndPath", "without http:// or https://", "botName", "Bot name for this domain (optional)", "headerColorLeft", "Hex color for left end of header gradient (optional)", "headerColorRight", "Hex color for right end of header gradient (optional)", "autoOpenBot" , "Open the bot when page is loaded", "password" , "A separate password for people who can see only this domain", "pageRegexp" , "A Java-style regular expression that filters which page the bot is shown on"); cc.massageItemMapForList = (item, map) -> { map.put("domainAndPath", new HTML(b(htmlEncode2(((Domain) item).domainAndPath)))); map.put("password", new SecretValue(map.get("password"))); }; } if (c == BotOutgoingQuestion.class) { cc.dropEmptyListValues = false; // for button actions cc.addRenderer("displayText", new HCRUD_Data.TextArea(80, 10)); cc.addRenderer("buttons", new HCRUD_Data.TextArea(80, 10, o -> lines_rtrim((List) o))); cc.fieldHelp( "displayText" , displayTextHelp(), "key" , "Internal key for question (any format, can be empty, put \"-\" to disable storing answer)", "defaultValue" , "What the input field is prefilled with", "placeholder" , "A text the empty input field shows as a hint", "buttons" , "Buttons to offer as standard answers (one per line, use | to add a shortened version submitted as user input)", "allowFreeText" , "Can user enter free text in addition to clicking a button?", "multipleChoice" , "Can user select multiple buttons?", "optional" , "Can user skip the question by entering nothing?", "multipleChoiceSeparator" , "Internal field, just leave as it is", "answerCheck" , "Select this to validate user's answer against a pattern", "buttonActions" , "Optional actions (one for each button in the list above)", "radioButtons" , "Show buttons as radio buttons"); cc.addRenderer("answerCheck", new HCRUD_Data.ComboBox("", "email address", "phone number")); cc.massageItemMapForList = (item, map) -> { map.put("buttons", new HTML(ol_htmlEncode(((BotOutgoingQuestion) item).buttons))); map.put("buttonActions", new HTML(ul( map(((BotOutgoingQuestion) item).buttonActions, action -> ahref(conceptLink(action), htmlEncode2_nlToBr(str(action))))))); }; } if (c == UserKeyword.class) { cc.fieldHelp( "language" , "Language that this keyword matches in (optional)", "pattern" , targetBlank("http://code.botcompany.de:8081/getraw.php?id=1030319", "'PhraseCache'") + " matching pattern (most important field)", "examples" , "Example inputs that should be matched (one per line, optional)", "counterexamples" , "Example inputs that should be NOT matched (one per line, optional)", "action" , "What bot does when input is matched", "enabled" , "Uncheck to disable this keyword", "priority" , "If set, this keyword overrides other input handlers (e.g. from outgoing questions)", "precedence" , "Precedence over other keywords (higher value = match first)"); for (String field : ll("examples", "counterexamples")) cc.addRenderer(field, new HCRUD_Data.TextArea(80, 5, o -> lines_rtrim((List) o))); cc.massageItemMapForList = (item, map) -> { map.put("examples", lines_rtrim(((UserKeyword) item).examples)); map.put("counterexamples", lines_rtrim(((UserKeyword) item).counterexamples)); }; } if (c == Settings.class) { cc.fieldHelp( "mainDomainName" , "Domain where bot is hosted (to correctly send image and sound links)", "botTypingDelay" , "Delay in seconds before bot sends message, base value", "botTypingDelayPerWord" , "Delay in seconds before bot sends message, per word", "botTypingMaxDelay" , "Delay in seconds before bot sends message, max value", "preferredCountryCodes" , "Country codes to display first in list (e.g. \"+1, +91\")", "notificationSound" , "Bot message notification sound (can leave empty, then we use the " + ahref(defaultNotificationSound(), "default sound"), "talkToBotOnlyWithAuth" , "Show bot only to authorized persons", "mailLeadsTo" , "Where to send a mail when a new lead occurs (addresses separated by comma)", "leadMailSubject" , "Subject for lead notification mails"); } if (c == Conversation.class) cc.massageItemMapForList = (item, map) -> { replaceMap(map, litorderedmap( "id" , map.get("id"), "IP" , map.get("ip"), "started" , formatLocalDateWithMinutes(((Conversation) item).created), "country" , map.get("country"), "msgs" , lines_rtrim(((Conversation) item).msgs), "avatar" , map.get("avatar"), "answers" , htmlEncode2(renderColonProperties(((Conversation) item).answers)), "stack" , new HTML(ol( cloneMap(((Conversation) item).stack, activeSeq -> htmlEncode2(str(activeSeq))))))); }; if (c == BotMessage.class) { cc.addRenderer("text", new HCRUD_Data.TextArea(80, 10)); cc.addRenderer("specialPurpose", new HCRUD_Data.ComboBox(itemPlus("", keys(specialPurposes)))); cc.fieldHelp( "text" , displayTextHelp(), "specialPurpose" , "Special occasion when to display this message", "disableInput" , "Check to disable user input while this message is showing"); } if (c == Form.class) cc.massageItemMapForList = (item, map) -> { map.put("steps", pnlToStringWithEmptyLines_rtrim(((Form) item).steps)); }; if (c == Sequence.class) cc.massageItemMapForList = (item, map) -> { map.put("steps", new HTML(ol( map(((Sequence) item).steps, step -> ahref(conceptLink(step), htmlEncode2_nlToBr(str(step))))))); }; if (c == BotImage.class) cc.massageItemMapForList = (item, map) -> { String url = ((BotImage) item).imageURL(); if (isURL(url)) map.put("Image Preview", new HTML(himg(url, "style" , hcrud_imagePreviewStyle()))); map.put(cc.fieldNameToHTML("imageURL"), new HTML( htmlEncode2(url) + " " + himgsnippet("#1101381", "onclick" , "copyToClipboard(" + jsQuote(url) + "); " + "window.createNotification({ theme: 'success', showDuration: 3000 })({ message: " + jsQuote("Image URL copied to clipboard") + "});"))); }; if (c == UploadedImage.class) { cc.massageItemMapForList = (item, map) -> { String url = ((UploadedImage) item).imageURL(); File f = ((UploadedImage) item).imageFile(); map.put("Image Size", toK_str(fileSize(f))); if (fileSize(f) > 0) map.put("Image Preview (click for full-scale)", new HTML(targetBlank(url, himg(url, "style" , hcrud_imagePreviewStyle())))); }; IVF2 massageItemMapForUpdate_old = cc.massageItemMapForUpdate; cc.massageItemMapForUpdate = (item, map) -> { cc.massageItemMapForUpdate_fallback(massageItemMapForUpdate_old, item, map); String base64 = (String) map.get("img_base64"); print("Got base64 data: " + l(base64)); if (nempty(base64)) { File f = ((UploadedImage) item).imageFile(); saveFileVerbose(f, base64decode(base64)); } map.remove("img_base64"); }; } if (c == UploadedSound.class) { cc.massageItemMapForList = (item, map) -> { String url = ((UploadedSound) item).soundURL(); File f = ((UploadedSound) item).soundFile(); map.put("Sound Size", toK_str(fileSize(f))); if (fileSize(f) > 0) map.put("Test Sound", new HTML(ahref(url, "Test Sound"))); }; IVF2 massageItemMapForUpdate_old = cc.massageItemMapForUpdate; cc.massageItemMapForUpdate = (item, map) -> { cc.massageItemMapForUpdate_fallback(massageItemMapForUpdate_old, item, map); String base64 = (String) map.get("file_base64"); print("Got base64 data: " + l(base64)); if (nempty(base64)) { File f = ((UploadedSound) item).soundFile(); saveFileVerbose(f, base64decode(base64)); } map.remove("file_base64"); }; } if (c == UploadedFile.class) { cc.fieldHelp( "name" , "Also used as file name when downloading", "mimeType" , "Content type (MIME type) for this file (optional)", "liveURI" , "URI to serve file under. No leading slash."); cc.massageItemMapForList = (item, map) -> { String url = ((UploadedFile) item).downloadURL(); File f = ((UploadedFile) item).theFile(); map.put("File Size", toK_str(fileSize(f))); if (fileSize(f) > 0) map.put("Download", new HTML(ahref(url, "Download"))); }; IVF2 massageItemMapForUpdate_old = cc.massageItemMapForUpdate; cc.massageItemMapForUpdate = (item, map) -> { cc.massageItemMapForUpdate_fallback(massageItemMapForUpdate_old, item, map); String base64 = (String) map.get("file_base64"); print("Got base64 data: " + l(base64)); if (nempty(base64)) { File f = ((UploadedFile) item).theFile(); saveFileVerbose(f, base64decode(base64)); } map.remove("file_base64"); }; } if (c == Worker.class) { cc.massageItemMapForList = (item, map) -> { AbstractBotImage image = ((Worker) item).image.get(); if (image != null) map.put("Image Preview", new HTML(himg(image.imageURL(), "style" , hcrud_imagePreviewStyle()))); }; } if (c == Avatar.class) { cc.fieldHelp("shift" , "hours when avatar is active, e.g. 8-18 (leave empty if always on shift, put 0-0 if never on shift)"); cc.massageItemMapForList = (item, map) -> { map.put("On Shift Now" , ((Avatar) item).onShift()); }; } return cc; } Map crudHelp() { return litmap(DeliveredDomain.class, "This list is filled by the bot with the domains it was delivered on. " + "Some may be bogus, we write down whatever the browser sends."); } void saveDeliveredDomain(String domain) { if (empty(domain)) return; uniqCI(DeliveredDomain.class, "domain" , beforeColonOrAll(domain)); } void cleanConversations() { withDBLock(new Runnable() { public void run() { try { cdelete(filter(list(Conversation.class), c -> l(c.msgs) <= 1 && elapsedSeconds_timestamp(c.created) >= 60 && c.lastPing == 0)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "cdelete(filter(list(Conversation), c -> l(c.msgs) <= 1\r\n && elapsedSec..."; }}); } void deleteDeliveredDomains() { cdelete(DeliveredDomain.class); } Object serveThoughts(Req req) { HTMLFramer1 framer = new HTMLFramer1(); framer.addInHead(hsansserif() + hmobilefix()); framer.add(div_floatRight(hbutton("Reload", "onclick" , "location.reload()"))); framer.add(h2("Bot Thoughts")); if (req.conv == null) framer.add("No conversation"); else { framer.add(p("Conversation cookie: " + req.conv.cookie)); Conversation conv = req.conv; framer.add(h3("Stack")); framer.add(empty(conv.stack) ? p("empty") : ol(lmap(__69 -> htmlEncode2_gen(__69), reversed(conv.stack)))); } return framer.render(); } Object serveSearch(Req req) { makeFramer(req); HTMLFramer1 framer = req.framer; framer.add("Search here"); return framer.render(); } transient boolean debug = false; // always returns an object Domain findDomainObj(String domain) { Domain domainObj = conceptWhereCI(Domain.class, "domainAndPath" , domain); if (domainObj == null) domainObj = defaultDomain(); return domainObj; } Domain defaultDomain() { return uniqCI(Domain.class, "domainAndPath" , ""); } // Web Chat Bot Include transient int longPollTick = 200; transient int longPollMaxWait = 1000*30; // lowered to 30 seconds transient int activeConversationSafetyMargin = 30000; // allow client 15 seconds to reload transient Set specialButtons = litciset("Cancel", "Back", "Abbrechen", "Zurück"); String dbStats() { Collection all = list(Conversation.class); int nRealConvos = countPred(all, c -> l(c.msgs) > 1); //int nTestConvos = countPred(all, c -> startsWith(c.cookie, "test_")); //ret n2(l(all)-nTestConvos, "real conversation") + ", " + n2(nTestConvos, "test conversation"); return nConversations(nRealConvos); } String realPW() { return loadTextFileOrCreateWithRandomID(realPWFile()); } File realPWFile() { return secretProgramFile("password.txt"); } String defaultRedirectAfterLogin() { return baseLink + "/"; } String serveAuthForm() { return serveAuthForm(null); } String serveAuthForm(String redirect) { redirect = dropParamFromURL(redirect, "logout"); // don't log us out right again if (empty(redirect)) redirect = defaultRedirectAfterLogin(); return hhtml(hhead(htitle(botName + ": Log-In") + hsansserif() + hmobilefix() + (useWebSockets ? webSocketHeadStuff() : "") ) + hbody(hfullcenter( authFormHeading() + hpostform( hhidden("redirect", redirect) + tag("table", (enableUsers ? tr(td("User:") + td(hinputfield("user")) + td()) : "") + tr(td("Password:") + td(hpassword("pw")) + td(hsubmit("Log in")))), "action" , baseLink + "/login") + (!showTalkToBotLink? "" : p(ahref(baseLink + "/demo", "Talk to bot"))) + pUnlessEmpty(joinNemptiesWithSpace( !showRegisterLink ? "" : hbuttonLink(baseLink + "/register", "Register as new user"), !requestAuthed() ? "" : hbuttonLink(baseLink + "?logout=1", "Log out"))) + authFormMoreContent() ))); } String authFormHeading() { return h3_htmlEncode(adminName); } String authFormMoreContent() { return ""; } String leadsToCSV(Collection leads) { List fields = ciContentsIndexedList(); List rows = new ArrayList(); for (Lead lead : unnullForIteration(leads)) { List row = new ArrayList(); for (Map.Entry __22 : _entrySet( unnullForIteration(mapPlus_inFront((Map) (Map) lead.answers, "Date" , formatLocalDateWithMinutes(lead.date.date), "Domain" , lead.domain.get(), "Conversation ID" , conceptID(lead.conversation.get()))))) { String key = __22.getKey(); Object val = __22.getValue(); int idx = fields.indexOf(key); if (idx < 0) idx = addAndReturnIndex(fields, key); listSet(row, idx, val, ""); } rows.add(row); } return formatCSVFileForExcel2(itemPlusList(fields, rows)); } String countryCodeOptions(Conversation conv) { Collection countryCodes = putSetElementsFirst(keys(countryDialCodesMultiMap()), splitAtComma_trim(settings().preferredCountryCodes)); String selected = dialCodeStringForCountryCode(conv.country); return mapToLines(c -> { // c == dial code List cdc = countryDialCodesMultiMap().get(c); String text = nempty(cdc) ? c + " [" + joinWithComma(collectSorted("countryCode", cdc)) + "]" : c; return tag("option", text, "value" , c, "selected" , eq(c, selected) ? html_valueLessParam() : null); }, itemPlus("", countryCodes)); } List addTypingDelays(List steps) { //printStackTrace(); return concatMap(steps, step -> { double delay = step.preTypingDelay(); if (delay <= 0) return ll(step); return ll(new BotSendTyping(), new BotPause(delay), step); }); } BotMessage messageForPurpose(String specialPurpose) { return conceptWhere(BotMessage.class, "specialPurpose", specialPurpose); } Object serveLeadsAPI(Object req) { Map headers = (Map) (rcall("headers", req)); Map params = (Map) (get("params", req)); String tenantID = params.get("tenantID"); if (empty(tenantID)) return serveJSONError("Empty tenantID"); String pw = params.get("pw"); if (empty(pw)) pw = dropPrefix_trim("Bearer ", headers.get("Authorization")); if (empty(pw)) return serveJSONError("Empty password"); Domain domain = conceptWhere(Domain.class, "tenantID", tenantID); if (domain == null) return serveJSONError("Tenant ID not found"); if (neq(domain.password, pw)) return serveJSONError("Bad passsword"); Collection leads = conceptsWhere(Lead.class, "domain", domain); return serveJSON(map(leads, lead -> { Map map = litorderedmap("tenantID", tenantID, "leadID" , lead.id, "domain" , str(lead.domain), "date" , formatLocalDateWithSeconds(lead.created), "data" , lead.answers ); return map; })); } Object serveJSONError(String error) { return serveJSON(litorderedmap("error", error)); } String botImageForDomain(Domain domainObj) { String botImg = imageSnippetURLOrEmptyGIF(chatHeaderImageID); if (domainObj != null && domainObj.botImage.has()) botImg = domainObj.botImage.get().imageURL(); else if (defaultDomain().botImage.has()) botImg = defaultDomain().botImage.get().imageURL(); return botImg; } String simulateDomainLink(String domain) { return appendQueryToURL(baseLink + "/demo", "_botConfig" , makePostData("domain", domain), "_newConvCookie" , 1, "_autoOpenBot" , 1); } String simulateScriptLink(BotStep script) { return appendQueryToURL(baseLink + "/demo", "_botConfig" , makePostData("mainScript" , script.id), "_newConvCookie" , 1, "_autoOpenBot" , 1); } Scorer consistencyCheckResults() { if (consistencyCheckResults == null) consistencyCheckResults = doConsistencyChecks(); return consistencyCheckResults; } Scorer doConsistencyChecks() { Scorer scorer = scorerWithSuccessesAndErrors(); for (UserKeyword uk : list(UserKeyword.class)) try { localConsistencyCheck(uk, scorer); } catch (Throwable e) { scorer.addError(htmlEncode2(str(e))); } try { globalConsistencyCheck(scorer); } catch (Throwable e) { scorer.addError(htmlEncode2(str(e))); } return scorer; } void localConsistencyCheck(UserKeyword uk, Scorer scorer) { String patHTML = span_title(str(uk.parsedPattern()), htmlEncode2(uk.pattern)); String item = "[Entry " + ahref(conceptLink(uk), uk.id) + "] "; for (String input : unnullForIteration(uk.examples)) { boolean ok = uk.matchWithTypos(input); scorer.add(ok, ok ? item + "Example OK: " + patHTML + " => " + htmlEncode2(quote(input)) : item + "Example doesn't match pattern: " + patHTML + " => " + htmlEncode2(quote(input))); } for (String input : unnullForIteration(uk.counterexamples)) { boolean ok = !uk.matchWithTypos(input); scorer.add(ok, ok ? item + "Counterexample OK: " + patHTML + " => " + htmlEncode2(quote(input)) : item + "Counterexample matches pattern: " + patHTML + " => " + htmlEncode2(quote(input))); } } void globalConsistencyCheck(Scorer scorer) { List list = concatLists(enabledUserKeywordsForPriority(true), enabledUserKeywordsForPriority(false)); for (UserKeyword uk : list) for (String example : unnullForIteration(uk.examples)) { String item1 = ahref(conceptLink(uk), uk.id); UserKeyword found = firstThat(list, uk2 -> uk2.matchWithTypos(example)); if (found == null) continue; // Error should be found in local check if (found == uk) scorer.addOK("[Entry " + item1 + "] Global check OK for input " + htmlEncode2(quote(example))); else { String item2 = ahref(conceptLink(found), found.id); String items = "[Entries " + item1 + " and " + item2 + "] "; scorer.addError(items + "Input " + htmlEncode2(quote(example)) + " shadowed by pattern: " + htmlEncode2(quote(found.pattern)) + ". Try reducing precedence value of entry " + item2 + " or increasing precedence value of entry " + item1); } } } // sorted by precedence static Collection enabledUserKeywordsForPriority(boolean priority) { return sortByFieldDesc("precedence", conceptsWhere(UserKeyword.class, onlyNonNullParams("priority", priority))); } boolean handleGeneralUserInput(Conversation conv, Msg msg, Boolean priority) { for (UserKeyword qa : enabledUserKeywordsForPriority(priority)) { if (qa.enabled && qa.matchWithTypos(msg.text)) { print("Matched pattern: " + qa.pattern + " / " + msg.text); conv.noteEvent(new EvtMatchedUserKeyword(qa, msg)); conv.callSubroutine(qa.action.get()); conv.scheduleNextStep(); return true; } } return false; } void didntUnderstandUserInput(Conversation conv, Msg msg) { // TODO } transient Settings settings_cache; Settings settings() { if (settings_cache == null) settings_cache = settings_load(); return settings_cache; } Settings settings_load() { Settings settings = conceptWhere(Settings.class); print("Got settings: " + settings); return settings; } String displayTextHelp() { return "Text to show to user (can include HTML and " + targetBlank(baseLink + "/emojis", "emojis") + ")"; } void botActions() { Lock __7 = lockWhileDoingBotActions ? dbLock() : null; lock(__7); try { while (licensed()) { ScheduledAction action = lowestConceptByField(ScheduledAction.class, "time"); if (action == null) break; if (action.time > now()) { //print("Postponing next bot action - " + (action.time-now())); doAfter(100, rstBotActions); break; } cdelete(action); print("Executing action " + action); try { action.run(); } catch (Throwable __e) { _handleException(__e); } } } finally { unlock(__7); } } // action must be unlisted void addScheduledAction(ScheduledAction action) { addScheduledAction(action, 0); } void addScheduledAction(ScheduledAction action, long delay) { action.time = now()+delay; registerConcept(action); rstBotActions.trigger(); } transient ThreadLocal out = new ThreadLocal(); transient ThreadLocal conv = new ThreadLocal(); transient long lastConversationChange = now(); void makeIndices() { indexConceptFieldDesc(ScheduledAction.class, "time"); indexConceptFieldCI(Domain.class, "domainAndPath"); indexConceptFieldCI(DeliveredDomain.class, "domain"); indexConceptFieldCI(CannedAnswer.class, "hashTag"); indexConceptFieldCI(Language.class, "languageName"); indexConceptField(Lead.class, "domain"); indexConceptField(ConversationFeedback.class, "domain"); indexConceptField(UploadedFile.class, "liveURI"); } void startDB() { db(); } void pWebChatBot() { db_mainConcepts().storeBaseClassesInStructure = storeBaseClassesInStructure || storeBaseClassesInStructure; startDB(); indexConceptFields(Conversation.class, "cookie", Conversation.class, "worker", Conversation.class, "lastPing", Worker.class, "loginName", AuthedDialogID.class, "cookie"); db_mainConcepts().miscMapPut(DynNewBot2.class, this); assertNotNull("settings 1", uniq(Settings.class)); indexSingletonConcept(Settings.class); assertNotNull("settings 2", uniq(Settings.class)); makeIndices(); indexAllLinkableClasses(); assertNotNull("settings 3", uniq(Settings.class)); // legacy clean-up // Make other singleton concepts uniq(BotSaveLead.class); uniq(BotSaveFeedback.class); uniq(BotClearStack.class); uniq(BotEndConversation.class); uniq(BotDoNothing.class); uniq(BotIdleMode.class); uniq(BotNewDialog.class); if (enableUndoStates) uniq(BotGoBack.class); if (enableAvatars) uniq(RandomAvatar.class); } void addReplyToConvo(Conversation conv, IF0 think) { out.set(new Out()); String reply = ""; try { reply = think.get(); } catch (Throwable __e) { _handleException(__e); } Msg msg = new Msg(false, reply); msg.out = out.get(); conv.add(msg); } Msg msgFromThinkFunction(IF0 think) { out.set(new Out()); String reply = ""; try { reply = think.get(); } catch (Throwable __e) { _handleException(__e); } Msg msg = new Msg(false, reply); msg.out = out.get(); return msg; } Object withHeader(String html) { return withHeader(subBot_noCacheHeaders(subBot_serveHTML(html))); } Object withHeader(Object response) { call(response, "addHeader", "Access-Control-Allow-Origin", "*"); return response; } String renderMessageText(String text, boolean htmlEncode) { text = trim(text); if (eqic(text, "!rate conversation")) text = "Rate this conversation"; if (htmlEncode) text = htmlEncode2(text); text = nlToBr(text); return html_emojisToUnicode(text); } void renderMessages(Conversation conv, StringBuilder buf, List msgs) { renderMessages(conv, buf, msgs, msg -> msg == last(msgs)); } void renderMessages(Conversation conv, StringBuilder buf, List msgs, IF1 showButtons) { if (empty(msgs)) return; ChatRenderer renderer = makeChatRenderer(currentReq()); for (Msg m : msgs) { boolean showTheButtons = showButtons.get(m); // render message if from user, not empty or having buttons if (m.fromUser || neqOneOf(m.text, "-", "") || showTheButtons && m.out != null && nempty(m.out.buttons)) renderer.appendMsg(conv, buf, m, showTheButtons); if (showTheButtons) { // yeah we're doing it too much buf.append(hscript( botMod().phoneNumberSpecialInputField ? "$(\"#chat_telephone, .iti\").hide(); $(\"#chat_message\").show().prop('disabled', false).focus();" : "$(\"#chat_message\").prop('disabled', false).focus();")); if (m.out != null && nempty(m.out.javaScript)) buf.append(hscript(m.out.javaScript)); } } } abstract class ChatRenderer { abstract void appendMsg(Conversation conv, StringBuilder buf, Msg m, boolean showButtons); Conversation conv; String id; Msg m; String html; String name; String time; String text; boolean bot = false; Worker fromWorker; Out out; List labels; boolean useTrick = false; String author; String imgURL; void prepare(Conversation conv, Msg m) { this.conv = conv; this.m = m; html = renderMessageText(m.text, shouldHtmlEncodeMsg(m)); name = m.fromUser ? defaultUserName() : botName; time = formatTime(m.time); text = html; modifyTextForRendering(); bot = !m.fromUser; fromWorker = m.fromWorker; out = m.out; labels = m.labels; useTrick = ariaLiveTrick; id = randomID(); author = fromWorker != null ? htmlEncode2(fromWorker.displayName) : botName; if (bot) { if (fromWorker != null && fileExists(workerImageFile(fromWorker.id))) imgURL = fullRawLink("worker-image/" + fromWorker.id); else imgURL = conv.botImg(); } } void modifyTextForRendering() { if (eqic(text, "!back")) text = unicode_undoArrow(); } abstract String renderMultipleChoice(List buttons, Collection selections, String multipleChoiceSeparator); // render single-choice buttons String renderSingleChoice(List buttons) { List out = new ArrayList(); for (int i = 0; i < l(buttons); i++) { String code = buttons.get(i); String text = replaceButtonText(code); String userInput = htmldecode_dropAllTags(text); List split = splitAtVerticalBar(text); if (l(split) == 2) { userInput = second(split); text = code = first(split); } out.add(hbuttonOnClick_returnFalse(text, "submitAMsg(" + jsQuote(userInput) + ")", "class" , "chatbot-choice-button automated-message", "title" , eq(code, text) ? null : code)); if (!specialButtons.contains(code) && i+1 < l(buttons) && specialButtons.contains(buttons.get(i+1))) out.add("  "); } return lines(out); } } class ChatRendererSahilStyle extends ChatRenderer { void appendMsg(Conversation conv, StringBuilder buf, Msg m, boolean showButtons) { prepare(conv, m); List div = new ArrayList(); List inMessage = new ArrayList(); List afterMessage = new ArrayList(); if (!bot) imgURL = "/pays5/imgs/icons/avatar.svg"; if (nempty(imgURL)) { //div.add(himg(imgURL, alt := "Avatar")); div.add(div("", "class" , "bg-img", "role" , "img", "aria-labelledby" , m.fromUser ? "Avatar" : "Bot", "style" , "background-image: url('" + imgURL + "')")); } if (showButtons) appendButtons(inMessage, afterMessage, m.out); div.add(span(text + lines(inMessage), "class" , "message")); div.add(span(time, "class" , "message-timestamp")); buf.append(div(lines(div) + lines(afterMessage), "class" , "message-wrapper" + stringUnless(bot, " user"))).append("\n"); } void appendButtons(List inMessage, List afterMessage, Out out) { String placeholder = out == null ? "" : unnull(out.placeholder); String defaultInput = out == null ? "" : unnull(out.defaultInput); inMessage.add(hscript("chatBot_setInput(" + jsQuote(defaultInput) + ", " + jsQuote(placeholder) + ");")); if (out == null) return; List buttons = out.buttons; if (empty(buttons)) return; String buttonsHtml; if (out.multipleChoice) inMessage.add(span(renderMultipleChoice(buttons, text_multipleChoiceSplit(out.defaultInput, out.multipleChoiceSeparator), out.multipleChoiceSeparator), "class" , "select-options")); else if (out.radioButtons) inMessage.add(span(renderRadioButtons(buttons, out.defaultInput), "class" , "select-options")); else afterMessage.add(div(renderSingleChoice(buttons), "class" , "automated-messages")); } String renderMultipleChoice(List buttons, Collection selections, String multipleChoiceSeparator) { Set selectionSet = asCISet(selections); String rand = randomID(); String className = "chat_multiplechoice_" + rand; String allCheckboxes = "$(\"." + className + "\")"; if (eq(multipleChoiceSeparator, ",")) multipleChoiceSeparator += " "; return joinWithBR(map(buttons, name -> span( span("", "class" , "square-box") + " " + name , "class" , className + " option" + stringIf(contains(selectionSet, name), " selected"), "data-value" , name) )) + "
" + hbuttonOnClick_returnFalse("OK", "submitMsg()", "class" , "btn btn-ok") + hscript(replaceDollarVars("\r\n $(\".$className *, .$className\").click(function(e) {\r\n e.stopImmediatePropagation(); // prevent double firing\r\n\r\n // toggle check box\r\n console.log(\"Target: \" + e.target);\r\n $(e.target).closest(\".option\").toggleClass(\"selected\");\r\n\r\n // update message\r\n var theList = $('.$className.selected').map(function() { return this.dataset.value; }).get();\r\n console.log('theList: ' + theList);\r\n $('#chat_message').val(theList.join($sep));\r\n });\r\n //$(\".$className *\").click(function(e) { e.stopPropagation(); });\r\n ", "className", className, "sep" , jsQuote(multipleChoiceSeparator))); } String renderRadioButtons(List buttons, String selection) { String rand = randomID(); String className = "chat_radio_" + rand; String allRadioButtons = "$(\"." + className + "\")"; return joinWithBR(map(buttons, name -> span( span("", "class" , "square-box round") + " " + name , "class" , className + " option" + stringIf(eqic(selection, name), " selected"), "data-value" , name) )) + (quickRadioButtons ? "" : "
" + hbuttonOnClick_returnFalse("OK", "submitMsg()", "class" , "btn btn-ok")) + hscript(replaceDollarVars("\r\n $(\".$className *, .$className\").click(function(e) {\r\n e.stopImmediatePropagation(); // prevent double firing\r\n var option = $(e.target).closest(\".option\");\r\n \r\n // clear other radio buttons\r\n $(\".$className\").removeClass(\"selected\");\r\n\r\n // activate radio button\r\n option.addClass(\"selected\");\r\n\r\n // update message\r\n $('#chat_message').val(option[0].dataset.value);\r\n\r\n if ($quickRadioButtons)\r\n submitMsg();\r\n });\r\n //$(\".$className *\").click(function(e) { e.stopPropagation(); });\r\n ", "className", className, "quickRadioButtons", quickRadioButtons)); } } // end of ChatRendererSahilStyle class ChatRendererHusainStyle extends ChatRenderer { void appendMsg(Conversation conv, StringBuilder buf, Msg m, boolean showButtons) { prepare(conv, m); String tag = useTrick ? "div" : "span"; if (bot) { // msg from bot (show avatar) if (nempty(m.text)) { if (fromWorker != null) buf.append("

" + author + "

"); buf.append("<" + tag + " class=\"chat_msg_item chat_msg_item_admin\"" + (useTrick ? " id=\"" + id + "\" aria-live=\"polite\" tabindex=\"-1\"" : "") + ">"); if (nempty(imgURL)) buf.append("\r\n
\r\n \r\n
" .replace("$IMG", imgURL)); buf.append("" + (fromWorker != null ? "" : botName + " ") + "says"); buf.append(text); buf.append(""); if (fromWorker != null) buf.append("
"); if (useTrick) buf.append(hscript("$('#" + id + "').focus();")); } } else // msg from user (no avatar) buf.append(("\r\n You say\r\n <" + tag + " class=\"chat_msg_item chat_msg_item_user\"" + (useTrick ? " aria-live=\"polite\"" : "") + ">$TEXT\r\n ").replace("$TEXT", text)); if (nempty(labels)) buf.append(span( join("   ", map(labels, lbl -> span("  " + lbl + "  "))), "class" , "labels chat_msg_item chat_msg_item_user")).append("\n"); if (showButtons) appendButtons(buf, out, null); } void appendButtons(StringBuilder buf, Out out, Set buttonsToSkip) { String placeholder = out == null ? "" : unnull(out.placeholder); String defaultInput = out == null ? "" : unnull(out.defaultInput); if (out != null && out.disableInput) buf.append(hscript(jsDisableInputField())); else buf.append(hscript("chatBot_setInput(" + jsQuote(defaultInput) + ", " + jsQuote(placeholder) + ");")); if (out == null) return; List buttons = listMinusSet(out.buttons, buttonsToSkip); if (empty(buttons)) return; //printVars_str(+buttons, +buttonsToSkip); String buttonsHtml; if (out.multipleChoice) buttonsHtml = renderMultipleChoice(buttons, text_multipleChoiceSplit(out.defaultInput, out.multipleChoiceSeparator), out.multipleChoiceSeparator); else buttonsHtml = renderSingleChoice(buttons); buf.append(span(buttonsHtml, "class" , "chat_msg_item chat_msg_item_admin chat_buttons" + stringIf(!out.multipleChoice, " single-choice"))); } String renderMultipleChoice(List buttons, Collection selections, String multipleChoiceSeparator) { Set selectionSet = asCISet(selections); String rand = randomID(); String className = "chat_multiplechoice_" + rand; String allCheckboxes = "$(\"." + className + "\")"; return joinWithBR(map(buttons, name -> hcheckbox("", contains(selectionSet, name), "value" , name, "class" , className) + " " + name)) + "
" + hbuttonOnClick_returnFalse("OK", "submitMsg()", "class" , "btn btn-ok") + hscript(allCheckboxes + ".change(function() {" //+ " console.log('multiple choice change');" + " var theList = $('." + className + ":checkbox:checked').map(function() { return this.value; }).get();" + " console.log('theList: ' + theList);" + " $('#chat_message').val(theList.join(" + jsQuote(multipleChoiceSeparator) + "));" + "});"); } } // end of ChatRendererHusainStyle String replaceButtonText(String s) { if (standardButtonsAsSymbols) { if (eqicOneOf(s, "back", "zurück")) return unicode_undoArrow(); if (eqicOneOf(s, "cancel", "Abbrechen")) return unicode_crossProduct(); } return s; } void appendDate(StringBuilder buf, String date) { buf.append("\r\n
\r\n DATE\r\n
".replace("DATE", date)); } boolean lastUserMessageWas(Conversation conv, String message) { Msg m = last(conv.msgs); return m != null && m.fromUser && eq(m.text, message); } String formatTime(long time) { return timeInTimeZoneWithOptionalDate_24(timeZone, time); } String formatDialog(String id, List msgs) { List lc = new ArrayList(); for (Msg m : msgs) lc.add(htmlencode((m.fromUser ? "> " : "< ") + m.text)); return id + ul(lc); } Conversation getConv(final String cookie) { return uniq(Conversation.class, "cookie", cookie); } String errorMsg(String msg) { return hhtml(hhead_title("Error") + hbody(hfullcenter(msg + "

" + ahref(jsBackLink(), "Back")))); } String defaultUserName() { return "You"; } boolean botOn() { return true; } boolean botAutoOpen(Domain domain) { return getDomainValue(domain, d -> d.autoOpenBot, false); } File workerImageFile(long id) { return id == 0 ? null : javaxDataDir("adaptive-bot/images/" + id + ".jpg"); } long activeConversationTimeout() { return longPollMaxWait+activeConversationSafetyMargin; } AuthedDialogID authObject(String cookie) { AuthedDialogID auth = null; if (nempty(cookie)) auth = authedDialogIDForEveryCookie ? uniq(AuthedDialogID.class, "cookie", cookie) : conceptWhere(AuthedDialogID.class, "cookie", cookie); //printVars_str(+cookie, +auth); return auth; } boolean anyInterestingMessages(List msgs, boolean workerMode) { return any(msgs, m -> m.fromUser == workerMode); } boolean shouldHtmlEncodeMsg(Msg msg) { return msg.fromUser; } void calcCountry(Conversation c) { cset(c, "country" , ""); getCountry(c); } String getCountry(Conversation c) { if (empty(c.country) && nempty(c.ip)) cset(c, "country" , ipToCountry2020_safe(c.ip)); return or2(c.country, "?"); } void initAvatar(Conversation c) { //printVars_str("initAvatar", +c, +enableAvatars, domain := c.domainObj, avatar := c.avatar); if (enableAvatars && !c.avatar.has() && c.domainObj.has()) { AbstractAvatar a = c.domainObj.get().avatar.get(); Avatar avatar = a == null ? null : a.getActualAvatar(); //print("initAvatar " + c + " => " + avatar); cset(c, "avatar", avatar); } } void noteConversationChange() { lastConversationChange = now(); } String template(String hashtag, Object... params) { return replaceSquareBracketVars(getCannedAnswer(hashtag), params); } // TODO: redefine this String getCannedAnswer(String hashTag) { return getCannedAnswer(hashTag, null); } String getCannedAnswer(String hashTag, Conversation conv) { return hashTag; //if (!startsWith(hashTag, "#")) ret hashTag; //ret or2(trim(text(conceptWhereCI CannedAnswer(+hashTag))), hashTag); } List text_multipleChoiceSplit(String input, String multipleChoiceSeparator) { return trimAll(splitAt(input, dropSpaces(multipleChoiceSeparator))); } Msg lastBotMsg(List l) { return lastThat(l, msg -> !msg.fromUser); } File uploadedImagesDir() { return programDir("uploadedImages"); } File uploadsBaseDir() { return programDir(); } File uploadedSoundsDir() { return newFile(uploadsBaseDir(), "uploadedSounds"); } File uploadedFilesDir() { return programDir("uploadedFiles"); } String defaultNotificationSound() { return "https://botcompany.de/files/1400403/notification.mp3"; } String convertToAbsoluteURL(String url) { if (isURL(url)) return url; String domain = settings().mainDomainName; return empty(domain) ? url : "https://" + domain + addSlashPrefix(url); } // only an absolute URL if settings().mainDomainName is set String absoluteAdminURL() { return convertToAbsoluteURL(baseLink); } void editMasterPassword() { if (!confirmOKCancel("Are you sure? This will reveal the password.")) return; JTextField tf = jtextfield(realPW()); showFormTitled2(botName + " master password", "Password", tf, new Runnable() { public void run() { try { String pw = gtt(tf); if (empty(pw)) infoBox("Empty password, won't save"); else { saveTextFile(realPWFile(), pw); infoBox("Saved new password for " + adminName); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "String pw = gtt(tf);\r\n if (empty(pw)) infoBox(\"Empty password, won't s..."; }}); } void editMailSenderInfo() { JTextField tf = jtextfield(realPW()); inputText("Mail sender info", mailSenderInfo, new VF1() { public void get(String mailSenderInfo) { try { setField("mailSenderInfo", mailSenderInfo); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setField(+mailSenderInfo);"; }}); } // handle user log-in (create/update AuthedDialogID concept) Object handleAuth(Req req, String cookie) { String pw = trim(req.params.get("pw")); if (nempty(pw) && nempty(cookie)) { Domain authDomain; if (eq(pw, realPW())) cset(uniq(AuthedDialogID.class, "cookie", cookie), "restrictedToDomain" , null, "master" , true); else if ((authDomain = conceptWhere(Domain.class, "password" , pw)) != null) cset(uniq(AuthedDialogID.class, "cookie", cookie), "restrictedToDomain" , authDomain, "master" , false); else return errorMsg("Bad password, please try again"); String redirect = req.params.get("redirect"); if (nempty(redirect)) return hrefresh(redirect); } return null; } String loggedInUserDesc_html(Req req) { return htmlEncode2(loggedInUserDesc(req)); } String loggedInUserDesc(Req req) { return req.masterAuthed ? "super user" : req.authDomain; } Object redirectToHttps(Req req) { if (!req.webRequest.isHttps()) return subBot_serveRedirect("https://" + req.webRequest.domain() + req.uri + htmlQuery(req.params)); return null; } void startMainScript(Conversation conv) { initAvatar(conv); Domain domain = conv.domainObj.get(); if (domain == null) { botMod().addReplyToConvo(conv, () -> "Error: No domain set"); return; } BotStep seq = getConcept(BotStep.class, parseLong(mapGet(conv.botConfig, "mainScript"))); if (seq == null) seq = domain.mainScript.get(); if (seq == null) { botMod().addReplyToConvo(conv, () -> "Error: No main script set for " + htmlEncode2(str(domain))); return; } conv.callSubroutine(seq); if (enableUndoStates) conv.addUndoState(); conv.scheduleNextStep(); /* old way: if (executeStep(seq, conv)) nextStep(conv); */ } boolean isRequestFromBot(Req req) { return false; } String modifyTemplateBeforeDelivery(String html, Req req) { return html; } String headingForReq(Req req) { return null; } Object serveHomePage() { return null; } void possiblyTriggerNewDialog(Conversation conv) { if (empty(conv.msgs) && conv.newDialogTriggered < conv.archiveSize()) { cset(conv, "newDialogTriggered" , conv.archiveSize()); addScheduledAction(new OnNewDialog(conv)); } } String rewriteBotMessage(Conversation conv, String text) { if (!enableVars || conv == null) return text; // replace tags text = html_evaluateIfTags(text, var -> eqic("true", print("if " + var + ": ", calcVar(conv, dropDollarPrefix(var))))); // replace dollar vars print("text", text); text = replaceDollarVars_dyn(text, var -> print("calcVar " + var, calcVar(conv, var))); return text; } String calcVar(Conversation conv, String var) { if (conv == null) return null; { String val = syncGet(conv.answers, var); if (val != null) return val; } if (eqic(var, "botName") && conv.avatar.has()) return conv.avatar.get().name; return null; } // must match what template does with CSS_ID String cssURL() { return serveSnippetURL(cssID); } class RenderDialogTree { Set seen = new HashSet(); String render(BotStep step) { if (!seen.add(step)) return "[see above] " + htmlEncode2(str(step)); List children = new ArrayList(); if (step instanceof Sequence) for (Pair __0 : iterateWithIndex1(((Sequence) step).steps)) { int i = pairA(__0); BotStep step2 = pairB(__0); children.add("Step " + i + ": " + renderDialogTree(step2)); } if (step instanceof BotOutgoingQuestion) for (Pair __1 : ((BotOutgoingQuestion) step).buttonsWithActions()) { String input = pairA(__1); BotStep step2 = pairB(__1); children.add("If user says " + b(htmlEncode2(quote(last(splitAtVerticalBar(input))))) + "

=> " + (step2 == null ? "no action defined" : renderDialogTree(step2))); } return stepToHTMLFull(step) + ulIfNempty(children, "class" , "dialogTree"); } } String stepToHTMLFull(BotStep step) { if (step == null) return "-"; String link = conceptLink(step, currentReq()); return ahref_unstyled(link, prependSquareBracketed(step.id)) + ahref(link, htmlEncode2_nlToBr(step.fullToString())); } String renderDialogTree(BotStep step) { return new RenderDialogTree().render(step); } ChatRenderer makeChatRenderer(Req req) { String botLayout = req.params.get("botLayout"); String layout = or2(botLayout, defaultBotLayout()); return eqic(layout, "sahil") ? new ChatRendererSahilStyle() : new ChatRendererHusainStyle(); } String defaultBotLayout() { return "husain"; } boolean allowedTemplateID(String id) { return false; } double typingDelayForText(String text) { double perWord = settings().botTypingDelayPerWord; double x = settings().botTypingDelay; if (perWord != 0) x += countWords(dropHTMLTags(text)) * settings().botTypingDelayPerWord; double max = settings().botTypingMaxDelay; if (max != 0) x = min(x, max); return x; } String nameOfBotSide(Conversation conv) { String __9 = calcVar(conv, "botName"); if (!empty(__9)) return __9; return "Representative"; } void addThingsAboveCRUDTable(Req req, Class c, List aboveTable) { } boolean checkPhoneNumber(String s) { return isValidInternationalPhoneNumber(s); } void onNewLead(Lead lead) { // TODO: notify someone in case of mail error for (String address : splitAtComma_trim(settings().mailLeadsTo)) try { String text = formatColonProperties(lead.answers); sendMailThroughScript(mailSenderInfo, "auto@botcompany.de", address, settings().leadMailSubject, text); } catch (Throwable e) { printStackTrace(e); appendToFile(programFile("mail-errors"), getStackTrace(e)); } } void doUndo(Conversation conv) { UndoState undoState = syncNextToLast(conv.undoStates); print("Undo state: " + undoState); if (undoState != null) { syncRemoveLast(2, conv.undoStates); // now drop anything we were about to do & replace stack with undo state conv.dropScheduledActions(); syncReplaceCollection(conv.stack, undoState.stack); print("Stack after undo: " + conv.stack); // finally schedule next step conv.scheduleNextStep(); } } // all classes that you can open as /crud/{Name} Collection cruddableClasses(Req req) { List classes = crudClasses(req); List cmdClasses = botCmdClasses(req); return flattenList2(classes, DeliveredDomain.class, cmdClasses); } List botCmdClasses(Req req) { return req.masterAuthed ? botCmdClasses() : null; } Object serveCRUD(Req req) { String uri = req.uri(); if (!uri.startsWith(crudBase())) return null; //printVars_str serveCRUD(+uri, +classes, +baseLink); for (Class c : (Set) asSet(cruddableClasses(req))) if (eq(uri, dropUriPrefix(baseLink, crudLink(c)))) { HCRUD crud = makeCRUD(c, req); return serveCRUD(req, c, crud); } return null; } Object serveCRUD(Req req, Class c, HCRUD crud) { HTMLFramer1 framer = req.framer; Map params = req.params(); String help = mapGet(crudHelp(), c); if (nempty(help)) framer.add(p(help)); String json = crud.handleComboSearch(params); if (nempty(json)) return serveText(json); // display things above CRUD List aboveTable = new ArrayList(); if (c == UserKeyword.class) { Scorer scorer = consistencyCheckResults(); framer.add(p("Consistency check results: " + nTests(scorer.successes) + " OK, " + nErrors(scorer.errors))); if (nempty(scorer.errors)) framer.add(ul(map(scorer.errors, e -> "Error: " + e))); } addThingsAboveCRUDTable(req, c, aboveTable); // render CRUD String baseTitle = framer.title; crud.makeFrame = (title, contents) -> { framer.title = joinNemptiesWithVBar(title, baseTitle); return h2(title) + lines(aboveTable) + contents; }; crud.processSortParameter(params); String crudHTML = crud.renderPage(params); if (containsHTMLRedirect(crudHTML)) return crudHTML; framer.add(crudHTML); // javascript magic to highlight table row according to anchor in URL framer.add(hjs_markRowMagic()); return completeFrame(req); } IF1, Collection> crudSearchFilter(Req req, HCRUD_Concepts data, String searchQuery) { if (empty(searchQuery)) return null; boolean debug = eq(req.get("searchDebug"), "1"); HTMLFramer1 framer = req.framer; return objects -> { ScoredSearcher < A > searcher = new ScoredSearcher<>(searchQuery); if (debug) { if (framer != null) framer.add(hcomment(renderVars_struct("searcher", searcher))); } for (A a : unnullForIteration(objects)) { Map map = allConceptFieldsAsMap(a); List fields = allToString(values(map)); fields.add(str(a.id)); if (debug) { if (framer != null) framer.add(hcomment(renderVars_str("a", a, "fields", fields))); } double score = searcher.scoreFields(fields) + searcher.scoreFields(keys(map))*0.2; // so we can search for field names too searcher.put(a, score); } return searcher.get(); }; } boolean requestAuthed() { return currentReq() != null && currentReq().requestAuthed(); } String webSocketHeadStuff() { return webSocketHeadStuff(currentReq()); } transient IF1 webSocketHeadStuff; String webSocketHeadStuff(Req req) { return webSocketHeadStuff != null ? webSocketHeadStuff.get(req) : webSocketHeadStuff_base(req); } final String webSocketHeadStuff_fallback(IF1 _f, Req req) { return _f != null ? _f.get(req) : webSocketHeadStuff_base(req); } String webSocketHeadStuff_base(Req req) { return hreconnectingWebSockets() + hjs("\r\n var webSocketQuery = window.location.search;\r\n var ws;\r\n var wsVerbose = true;\r\n var wsReady = false;\r\n \r\n function wsOnOpen(onOpen) {\r\n if (wsReady) onOpen();\r\n else {\r\n var old = ws.onopen;\r\n ws.onopen = function() {\r\n if (old) old();\r\n onOpen();\r\n };\r\n };\r\n }\r\n \r\n //$(document).ready(function() {\r\n var url = ((window.location.protocol === \"https:\") ? \"wss://\" : \"ws://\") + window.location.host + " + jsQuote(baseLink + req.uri()) + " + webSocketQuery;\r\n console.log(\"Opening WebSocket: \" + url);\r\n ws = new ReconnectingWebSocket(url);\r\n ws.onopen = function(event) {\r\n wsReady = true;\r\n console.log(\"WebSocket ready!\");\r\n ws.onmessage = " + js_evalOnWebSocketMessage() + ";\r\n };\r\n //});\r\n "); } void fillReqAuthFromCookie(Req req, String cookie, AuthedDialogID auth) { req.auth = auth; boolean requestAuthed = auth != null; // any authentication if (requestAuthed) cset(auth, "lastSeen" , now()); req.masterAuthed = calcMasterAuthed(req); req.authDomainObj = !requestAuthed ? null : auth.restrictedToDomain.get(); req.authDomain = !requestAuthed ? null : auth.domain(); } // each item is a row of the stats table // first element = description (HTML) // second element = value (HTML) transient IF0>> renderStats; List> renderStats() { return renderStats != null ? renderStats.get() : renderStats_base(); } final List> renderStats_fallback(IF0>> _f) { return _f != null ? _f.get() : renderStats_base(); } List> renderStats_base() { long us = db_mainConcepts().uncompressedSize; return ll( ll("Concepts", n2(conceptCount())), ll("DB size on disk", htmlEncode2(toK_str(fileSize(conceptsFile())))), us == 0 ? null : ll("DB size uncompressed", htmlEncode2(toK_str(us))), ll("DB load time", htmlEncode2(renderDBLoadTime())), ll("DB save time", htmlEncode2(renderDBSaveTime())), ll("Last saved", htmlEncode2(renderHowLongAgo(db_mainConcepts().lastSaveWas))) ); } Object serveBotDemo(Req req, String js) { return hhtml(hhead( htitle(heading) + loadJQuery2() + botDemoHeadStuff() ) + hbody(hjavascript(js))); } String botDemoHeadStuff() { return ""; } Object handleLogout(Req req) { cdelete(req.auth); if (redirectOnLogout) return hrefresh(req.uri); return null; } } // end of module // start of concepts static class ExecutedStep extends Concept { static final String _fieldOrder = "step conversation msg"; Ref step = new Ref(); Ref conversation = new Ref(); Msg msg; // msg generated by step (if any) } static class InputHandled extends Concept { // what was the input and where/when did it occur? static final String _fieldOrder = "input conversation msg inputHandler handled"; String input; // store again here in case msg is edited later or something Ref conversation = new Ref(); Msg msg; // what was the input handler? Ref inputHandler = new Ref(); boolean handled = false; // did inputHandler return true? } abstract static class BotStep extends Concept { String comment; // returns true iff execution should continue immediately boolean run(Conversation conv) { return false; } ScheduledAction nextStepAction(Conversation conv) { return new Action_NextStep(conv, conv.executedSteps); } String fullToString() { return toString(); } double preTypingDelay() { return 0; } // should an undo state be generated before executing this step boolean isUndoBoundary() { return false; } } static class Sequence extends BotStep { RefL steps = new RefL(); public String toString() { return "Sequence " + quoteOr(comment, "(unnamed)") + spaceRoundBracketed(nSteps(steps)) /*+ ": " + joinWithComma(steps)*/; } boolean run(Conversation conv) { // add me to stack syncAdd(conv.stack, new ActiveSequence(this)); conv.change(); return true; } } // select one action at random static class BotRandomAction extends BotStep { RefL actions = new RefL(); public String toString() { return "Random Action " + appendBracketed(comment) + (empty(actions) ? " [empty]" : ": " + first(actions) + (l(actions) == 1 ? "" : " + " + n2(l(actions)-1) + " more")); } boolean run(Conversation conv) { BotStep step = random(actions); if (step == null) return true; return step.run(conv); } } static class BotMessage extends BotStep { String text; String specialPurpose; boolean disableInput = false; //S hashtag; // optional static String _fieldOrder = "text specialPurpose"; public String toString() { return toString(40); } public String toString(int shorten) { return "Message" + spacePlusRoundBracketedIfNempty(comment) + ": " + newLinesToSpaces2(shorten(text, shorten)); } String fullToString() { return toString(Integer.MAX_VALUE); } boolean run(Conversation conv) { Msg msg = new Msg(false, text); msg.out = new Out(); msg.out.disableInput = disableInput; conv.add(msg); return true; } double preTypingDelay() { return botMod().typingDelayForText(text); } } abstract static class AbstractBotImage extends BotStep { String altText; boolean run(Conversation conv) { botMod().addReplyToConvo(conv, () -> himgsrc(imageURL(), "title" , altText, "alt" , altText, "class" , "chat_contentImage")); return true; } abstract String imageURL(); double preTypingDelay() { return botMod().settings().botTypingDelay; } } // external image static class BotImage extends AbstractBotImage { String imageURL, altText; static String _fieldOrder = "imageURL"; String imageURL() { return imageURL; } public String toString() { return "Image" + spacePlusRoundBracketedIfNempty(comment) + ": " + imageURL; } } static class UploadedImage extends AbstractBotImage { String imageURL() { return botMod().convertToAbsoluteURL(botMod().baseLink + "/uploaded-image/" + id); } File imageFile() { return newFile(botMod().uploadedImagesDir(), id + ".png"); } public String toString() { return "Image" + spacePlusRoundBracketedIfNempty(altText) + spacePlusRoundBracketedIfNempty(comment); } void delete() { if (concepts() == db_mainConcepts()) deleteFile(imageFile()); super.delete(); } BufferedImage getImage() { return loadImage2(imageFile()); } } static class UploadedSound extends Concept { String comment; String mimeType = mp3mimeType(); String soundURL() { return botMod().convertToAbsoluteURL(botMod().baseLink + "/uploaded-sound/" + id); } // .mp3 for hysterical reasons File soundFile() { return newFile(botMod().uploadedSoundsDir(), id + ".mp3"); } public String toString() { return "Sound" + spacePlusRoundBracketedIfNempty(comment); } void delete() { if (concepts() == db_mainConcepts()) deleteFile(soundFile()); super.delete(); } } static class UploadedFile extends Concept { static final String _fieldOrder = "name comment mimeType liveURI"; String name, comment, mimeType, liveURI; String downloadURL() { return downloadURL(null); } String downloadURL(String contentType) { return botMod().convertToAbsoluteURL(appendQueryToURL(botMod().baseLink + "/uploaded-file/" + id + (empty(name) ? "" : "/" + urlencode(name)), "ct" , contentType)); } final File getFile() { return theFile(); } File theFile() { return newFile(botMod().uploadedFilesDir(), id + ".png"); } public String toString() { return "File " + spacePlusRoundBracketedIfNempty(name) + spacePlusRoundBracketedIfNempty(comment); } void delete() { if (concepts() == db_mainConcepts()) deleteFile(theFile()); super.delete(); } } static class BotDoNothing extends BotStep { boolean run(Conversation conv) { return false; } public String toString() { return "Do nothing"; } } static class BotSendTyping extends BotStep { boolean run(Conversation conv) { conv.botTyping = print("Bot typing", now()); return true; } } static class BotGoBack extends BotStep { boolean run(Conversation conv) { botMod().doUndo(conv); return false; } public String toString() { return "Go back one step"; } } static class BotNewDialog extends BotStep { boolean run(Conversation conv) { conv.newDialog(); return false; } public String toString() { return "New dialog"; } } static class BotPause extends BotStep { double seconds; BotPause() {} BotPause(double seconds) { this.seconds = seconds;} boolean run(Conversation conv) { botMod().addScheduledAction(nextStepAction(conv), toMS(seconds)); return false; } public String toString() { return "Pause for " + (seconds == 1 ? "1 second" : formatDouble(seconds, 1) + " seconds"); } } static class BotOutgoingQuestion extends BotStep implements IInputHandler { String displayText; String key; String defaultValue; String placeholder; // null for same as displayText, "" for none List buttons; boolean allowFreeText = true; // only matters when there are buttons boolean multipleChoice = false; // buttons are checkboxes boolean radioButtons = false; // show radio buttons instead of normal buttons String multipleChoiceSeparator = ", "; // how to join choices into value boolean optional = false; // can be empty String answerCheck; // e.g. "email address" RefL buttonActions = new RefL(); // what to do on each button static String _fieldOrder = "displayText key defaultValue placeholder buttons buttonActions allowFreeText multipleChoice optional answerCheck"; public String toString() { return "Question: " + orEmptyQuotes(displayText); } List> buttonsWithActions() { return zipTwoListsToPairs_longer(buttons, buttonActions); } boolean run(Conversation conv) { String text = botMod().rewriteBotMessage(conv, displayText); Msg msg = new Msg(false, text); msg.out = makeMsgOut(); conv.add(msg); cset(conv, "inputHandler" , this); return false; } Out makeMsgOut() { Out out = new Out(); out.placeholder = or(placeholder, displayText); out.defaultInput = defaultValue; out.buttons = cloneList(buttons); out.multipleChoice = multipleChoice; out.multipleChoiceSeparator = multipleChoiceSeparator; out.radioButtons = radioButtons; if (eqic(answerCheck, "phone number") && botMod().phoneNumberSpecialInputField) //out.javaScript = [[$("#chat_countrycode").addClass("visible");]]; out.javaScript = "$(\"#chat_message\").hide(); $(\"#chat_telephone, .iti\").show(); $(\"#chat_telephone\").val(\"\").focus();"; else if (!allowFreeText) { out.javaScript = jsDisableInputField(); if (empty(out.placeholder)) out.placeholder = " "; } return out; } public boolean handleInput(String s, Conversation conv) { print("BotOutgoingQuestion handleInput " + s); // Store answer s = trim(s); String theKey = or2(key, htmldecode_dropAllTags(trim(displayText))); if (nempty(theKey) && !eq(theKey, "-")) syncPut(conv.answers, theKey, s); conv.change(); // Validate if (eqic(answerCheck, "email address") && !isValidEmailAddress_simple(s)) { handleValidationFail(conv, "bad email address"); return true; } if (eqic(answerCheck, "phone number") && !botMod().checkPhoneNumber(s)) { handleValidationFail(conv, "bad phone number"); return true; } conv.removeInputHandler(this); int idx = indexOfIC(trimAll(lmap(__70 -> dropStuffBeforeVerticalBar(__70), buttons)), s); print("Button index of " + quote(s) + " in " + sfu(buttons) + " => " + idx); BotStep target = get(buttonActions, idx); print("Button action: " + target); if (target != null) //conv.jumpTo(target); conv.callSubroutine(target); // We now interpret the target as a subroutine call // Go to next step print("Scheduling next step in " + conv); conv.scheduleNextStep(); return true; // acknowledge that we handled the input } double preTypingDelay() { return empty(displayText) ? 0 : botMod().typingDelayForText(displayText); } // show error msg and reschedule input handling void handleValidationFail(Conversation conv, String purpose) { BotMessage msg = botMod().messageForPurpose(purpose); String text = or2(or2(msg == null ? null : msg.text, getOrKeep(botMod().specialPurposes, purpose)), "Invalid input, please try again"); printVars_str("handleValidationFail", "purpose", purpose, "msg", msg, "text", text); Msg m = new Msg(); m.text = text; m.out = makeMsgOut(); conv.add(m); cset(conv, "inputHandler" , this); return; } boolean isUndoBoundary() { return true; } } static class BotIdleMode extends BotStep { public String toString() { return "Idle Mode (wait indefinitely)"; } boolean run(Conversation conv) { return false; } } static class BotEndConversation extends BotStep { public String toString() { return "End Conversation (disable user input)"; } boolean run(Conversation conv) { Msg msg = new Msg(false, ""); msg.out = new Out(); msg.out.javaScript = jsDisableInputField(); msg.out.placeholder = " "; conv.add(msg); return true; } } static String jsDisableInputField() { return "$(\"#chat_message\").prop('disabled', true).attr('placeholder', '');"; } static class BotSaveLead extends BotStep { public String toString() { return "Save Lead"; } boolean run(Conversation conv) { Lead lead = cnew(Lead.class, "conversation" , conv, "domain" , conv.domainObj, "date" , new Timestamp(now()), "answers" , cloneMap(conv.answers)); botMod().onNewLead(lead); return true; } } static class BotSaveFeedback extends BotStep { public String toString() { return "Save Conversation Feedback"; } boolean run(Conversation conv) { cnew(ConversationFeedback.class, "conversation" , conv, "domain" , conv.domainObj, "date" , new Timestamp(now()), "answers" , filterKeys(conv.answers, swic$("Conversation Feedback:"))); return true; } } static class BotClearStack extends BotStep { boolean run(Conversation conv) { syncRemoveAllExceptLast(conv.stack); conv.change(); return true; } public String toString() { return "Clear Stack [forget all running procedures in conversation]"; } } static class BotSwitchLanguage extends BotStep { Ref language = new Ref(); boolean run(Conversation conv) { cset(conv, "language", language); return true; } public String toString() { return "Switch to " + language; } } static class UserKeyword extends Concept { static final String _fieldOrder = "language pattern examples counterexamples action enabled priority precedence parsedPattern"; Ref language = new Ref(); String pattern; List examples, counterexamples; Ref action = new Ref(); boolean enabled = true; boolean priority = false; int precedence; transient MMOPattern parsedPattern; void change() { parsedPattern = null; botMod().consistencyCheckResults = null; super.change(); } MMOPattern parsedPattern() { if (parsedPattern == null) return parsedPattern = mmo2_parsePattern(pattern); return parsedPattern; } public String toString() { return "User Keyword: " + pattern; } boolean matchWithTypos(String s) { return mmo2_matchWithTypos(parsedPattern(), s); } } static class Language extends Concept { static final String _fieldOrder = "languageName comment"; String languageName; String comment; public String toString() { return languageName + spaceRoundBracketed(comment); } } // event concepts are stored as part of the Conversation object, so they don't have IDs // (might change this) static class Evt extends Concept {} static class EvtMatchedUserKeyword extends Evt { static final String _fieldOrder = "userKeyword msg"; Ref userKeyword = new Ref(); Msg msg; EvtMatchedUserKeyword() {} EvtMatchedUserKeyword(UserKeyword userKeyword, Msg msg) { this.userKeyword.set(userKeyword); this.msg = msg; } } static class EvtJumpTo extends Evt { Ref target = new Ref(); EvtJumpTo() {} EvtJumpTo(BotStep target) { this.target.set(target); } } static class EvtCallSubroutine extends Evt { Ref target = new Ref(); EvtCallSubroutine() {} EvtCallSubroutine(BotStep target) { this.target.set(target); } } abstract static class AbstractAvatar extends Concept { abstract Avatar getActualAvatar(); } static class RandomAvatar extends AbstractAvatar { Avatar getActualAvatar() { return random(filter(list(Avatar.class), a -> a.onShift())); } public String toString() { return "Choose a random avatar that is on shift"; } } static class Avatar extends AbstractAvatar { static final String _fieldOrder = "name comment shift image"; String name, comment; String shift; // hours (e.g. "8-18", optional) Ref image = new Ref(); Avatar getActualAvatar() { return this; } boolean onShift() { List shifts = parseBusinessHours_pcall(shift); if (empty(shifts)) return true; shifts = splitBusinessHoursAtMidnight(shifts); int minute = minuteInDay(timeZone(botMod().timeZone)); return anyIntRangeContains(shifts, minute); } public String toString() { return empty(name) ? super.toString() : "Avatar " + name + appendBracketed(comment); } } /*concept ActorInConversation { new Ref conv; new Ref avatar; new L stack; }*/ // end of concepts // special options for a message static class Out extends DynamicObject { List buttons; boolean multipleChoice, radioButtons; String multipleChoiceSeparator; String placeholder; String defaultInput; String javaScript; boolean disableInput = false; } static class Msg extends DynamicObject { long time; boolean fromUser = false; //Avatar avatar; Worker fromWorker; String text; Out out; List labels; Msg() {} Msg(boolean fromUser, String text) { this.text = text; this.fromUser = fromUser; time = now(); } Msg(String text, boolean fromUser) { this.fromUser = fromUser; this.text = text; time = now(); } public String toString() { return (fromUser ? "User" : "Bot") + ": " + text; } } static class AuthedDialogID extends Concept { static final String _fieldOrder = "cookie master restrictedToDomain loggedIn lastSeen"; String cookie; boolean master = false; Ref restrictedToDomain = new Ref(); Worker loggedIn; // who is logged in with this cookie long lastSeen; String domain() { return !restrictedToDomain.has() ? null : restrictedToDomain.get().domainAndPath; } Ref user = new Ref(); // who is logged in boolean userMode = false; // show things as if user was not master User user() { return user.get(); } } static class ActiveSequence { Sequence originalSequence; List steps; int stepIndex; ActiveSequence() {} ActiveSequence(BotStep step) { if (step instanceof Sequence) { steps = cloneList(((Sequence) step).steps); originalSequence = (Sequence) step; } else steps = ll(step); steps = botMod().addTypingDelays(steps); } boolean done() { return stepIndex >= l(steps); } BotStep currentStep() { return get(steps, stepIndex); } void nextStep() { ++stepIndex; } public String toString() { return (stepIndex >= l(steps) ? "DONE " : "Step " + (stepIndex+1) + "/" + l(steps) + " of ") + (originalSequence != null ? str(originalSequence) : squareBracket(joinWithComma(steps))); } } static class UndoState { List stack; UndoState() {} UndoState(List stack) { this.stack = stack;} public String toString() { return "UndoState " + stack; } } // our base concept - a conversation between a user and a bot or sales representative static class Conversation extends Concept { static final String _fieldOrder = "cookie ip country domain domainObj oldDialogs msgs lastPing botOn notificationsOn worker userTyping botTyping testMode nextActionTime userMessageProcessed newDialogTriggered dryRun avatar stack undoStates executedSteps inputHandler language answers botConfig events reloadCounter"; String cookie, ip, country, domain; Ref domainObj = new Ref(); List> oldDialogs = new ArrayList(); List msgs = new ArrayList(); long lastPing; boolean botOn = true; boolean notificationsOn = true; Worker worker; // who are we talking to? transient volatile long userTyping, botTyping; // timestamps boolean testMode = false; long nextActionTime = Long.MAX_VALUE; long userMessageProcessed; // timestamp int newDialogTriggered = -1; transient boolean dryRun = false; Ref avatar = new Ref(); List stack = new ArrayList(); List undoStates; int executedSteps; IInputHandler inputHandler; Ref language = new Ref(); //Long lastProposedDate; Map answers = litcimap(); Map botConfig; RefL events = new RefL(); // just logging stuff int reloadCounter; // increment this to have whole conversation reloaded in browser void add(Msg m) { m.text = trim(m.text); //if (!m.fromUser && empty(m.text) && (m.out == null || empty(m.out.buttons))) ret; // don't store empty msgs from bot syncAdd(msgs, m); botMod().noteConversationChange(); change(); vmBus_send("chatBot_messageAdded", mc(), this, m); } int allCount() { return archiveSize() + syncL(msgs); } int archiveSize() { return syncLengthLevel2(oldDialogs) + syncL(oldDialogs); } List allMsgs() { return concatLists_syncIndividual(syncListPlus(oldDialogs, msgs)); } long lastMsgTime() { Msg m = last(msgs); return m == null ? 0 : m.time; } void incReloadCounter() { cset(this, "reloadCounter" , reloadCounter+1); } void turnBotOff() { cset(this, "botOn" , false); botMod().noteConversationChange(); } void turnBotOn() { cset(this, "botOn" , true, "worker" , null); String backMsg = botMod().getCannedAnswer("#botBack", this); if (empty(msgs) || lastMessageIsFromUser() || !eq(last(msgs).text, backMsg)) add(new Msg(backMsg, false)); botMod().noteConversationChange(); } boolean lastMessageIsFromUser() { return nempty(msgs) && last(msgs).fromUser; } void newDialog() { if (syncNempty(msgs)) syncAdd(oldDialogs, msgs); cset(this, "msgs" , new ArrayList()); syncClear(answers); syncClear(stack); syncClear(undoStates); dropScheduledActions(); change(); print("newDialog " + archiveSize() + "/" + allCount()); vmBus_send("chatBot_clearedSession", mc(), this); //botMod().addScheduledAction(new OnNewDialog(this)); } void dropScheduledActions() { deleteConcepts(ScheduledActionOnConversation.class, "conv" , this); } void jumpTo(BotStep step) { syncPopLast(stack); // terminate inner script print("Jumping to " + step); noteEvent(new EvtJumpTo(step)); if (step == null) return; syncAdd(stack, new ActiveSequence(step)); change(); } void callSubroutine(BotStep step) { if (step == null) return; noteEvent(new EvtCallSubroutine(step)); print("Calling subroutine " + step); syncAdd(stack, new ActiveSequence(step)); change(); } void removeInputHandler(IInputHandler h) { if (inputHandler == h) cset(this, "inputHandler" , null); } void scheduleNextStep() { botMod().addScheduledAction(new Action_NextStep(this)); } void noteEvent(Concept event) { events.add(event); } boolean isActive() { return lastPing + botMod().activeConversationTimeout() >= now(); } String botImg() { if (avatar.has()) return avatar.get().image.get().imageURL(); else return botMod().botImageForDomain(domainObj.get()); } void addUndoState() { // undo state = cloned current stack UndoState state = new UndoState(syncShallowCloneElements(stack)); print("Made undo state: " + state); undoStates = syncAddOrCreate(undoStates, state); change(); } Ref mirrorPost = new Ref(); Ref detailedMirrorPost = new Ref(); void change() { super.change(); ((DynGazelleRocks) botMod()).rstUpdateMirrorPosts.add(this); } void updateMirrorPost() { if (isDeleted()) return; // Simple mirror post with just texts and "User:" or "Bot:" if (!mirrorPost.has() && syncNempty(msgs)) cset(Conversation.this, "mirrorPost" , cnew(UserPost.class, "title" , "Conversation " + id, "type" , "Conversation Mirror", "creator" , ((DynGazelleRocks) botMod()).internalUser(), "botInfo" , "Conversation Mirror Bot")); cset(mirrorPost.get(), "text" , lines_rtrim(msgs)); // Detailed mirror post with a lot of info if (!detailedMirrorPost.has() && syncNempty(msgs)) cset(Conversation.this, "detailedMirrorPost" , cnew(UserPost.class, "title" , "Conversation " + id + " with details", "type" , "Detailed Conversation Mirror", "creator" , ((DynGazelleRocks) botMod()).internalUser(), "botInfo" , "Conversation Mirror Bot")); cset(detailedMirrorPost.get(), "text" , indentedStructure(msgs)); } void delete() { cdelete(mirrorPost.get()); super.delete(); } } // end of Conversation abstract static class ScheduledAction extends Concept implements Runnable { long time; } abstract static class ScheduledActionOnConversation extends ScheduledAction { Ref conv = new Ref(); } static class Action_NextStep extends ScheduledActionOnConversation { int executedSteps = -1; // if >= 0, we verify against conv.executedSteps Action_NextStep() {} Action_NextStep(Conversation conv) { this.conv.set(conv); } Action_NextStep(Conversation conv, int executedSteps) { this.conv.set(conv); this.executedSteps = executedSteps; } public void run() { nextStep(conv.get(), executedSteps); } } static class OnNewDialog extends ScheduledActionOnConversation { OnNewDialog() {} OnNewDialog(Conversation conv) { this.conv.set(conv); } public void run() { botMod().startMainScript(conv.get()); } } static void nextStep(Conversation conv) { nextStep(conv, -1); } static void nextStep(Conversation conv, int expectedExecutedSteps) { if (expectedExecutedSteps >= 0 && conv.executedSteps != expectedExecutedSteps) return; while (licensed()) { if (empty(conv.stack)) return; ActiveSequence seq = syncLast(conv.stack); boolean done = seq.done(); printVars_str("Active sequence", "conv", conv, "seq", seq, "done", done, "stackSize" , syncL(conv.stack)); if (done) { syncPopLast(conv.stack); continue; } BotStep step = seq.currentStep(); if (botMod().enableUndoStates && step.isUndoBoundary()) conv.addUndoState(); seq.nextStep(); ++conv.executedSteps; conv.change(); if (!executeStep(step, conv)) { print("Step returned false: " + step); break; } } } static boolean executeStep(BotStep step, Conversation conv) { if (step == null || conv == null) return false; print("Executing step " + step + " in " + conv); ExecutedStep executed = null; if (botMod().recordExecutionsAndInputs) executed = cnew(ExecutedStep.class, "step", step, "conversation" , conv); Msg lastMsg = syncLast(conv.msgs); boolean result = step.run(conv); Msg newMsg = syncLast(conv.msgs); if (newMsg != null && !newMsg.fromUser && newMsg != lastMsg) cset(executed, "msg" , newMsg); return result; } static class OnUserMessage extends ScheduledActionOnConversation { OnUserMessage() {} OnUserMessage(Conversation conv) { this.conv.set(conv); } public void run() { Conversation conv = this.conv.get(); // TODO: handle multiple messages in case handling was delayed Msg msg = syncLast(conv.msgs); if (msg == null || !msg.fromUser) return; print("OnUserMessage: " + msg); // process undo command (dev.) if (eqic(msg.text, "!back")) { botMod().doUndo(conv); return; } if (botMod().handleGeneralUserInput(conv, msg, true)) return; IInputHandler inputHandler = conv.inputHandler; if (inputHandler != null) { InputHandled rec = null; String input = msg.text; if (botMod().recordExecutionsAndInputs) rec = cnew(InputHandled.class, "inputHandler", inputHandler, "conversation" , conv, "msg", msg, "input", input); boolean result = conv.inputHandler.handleInput(input, conv); cset(rec, "handled" , result); if (result) return; } botMod().handleGeneralUserInput(conv, msg, false); } } // application-specific concepts // a server we're running - either a domain name or domain.bla/path static class Domain extends Concept { String domainAndPath; String tenantID; // for external software // bot config - av Ref botImage = new Ref(); String botName; // if empty, keep default Ref avatar = new Ref(); String headerColorLeft, headerColorRight; // ditto Boolean autoOpenBot; Ref mainScript = new Ref(); String password = aGlobalID(); //S pageRegexp; static String _fieldOrder = "domainAndPath botImage mainScript"; public String toString() { return domainAndPath; } } static class Form extends Concept { String name; RefL steps = new RefL(); public String toString() { return name; } } static class DeliveredDomain extends Concept { String domain; } static class CannedAnswer extends Concept { String hashTag, text; } static String text(CannedAnswer a) { return a == null ? null : a.text; } static class Lead extends Concept { static final String _fieldOrder = "domain conversation date answers"; Ref domain = new Ref(); Ref conversation = new Ref(); Timestamp date; Map answers; } static class ConversationFeedback extends Concept { static final String _fieldOrder = "domain conversation date answers"; Ref domain = new Ref(); Ref conversation = new Ref(); Timestamp date; Map answers; } static class Settings extends Concept { static final String _fieldOrder = "mainDomainName botTypingDelay botTypingDelayPerWord botTypingMaxDelay preferredCountryCodes multiLanguageMode notificationSound talkToBotOnlyWithAuth enableWorkerChat defaultLanguage mailLeadsTo leadMailSubject"; String mainDomainName; double botTypingDelay; double botTypingDelayPerWord; double botTypingMaxDelay; String preferredCountryCodes; // dial codes, comma-separated boolean multiLanguageMode = false; Ref notificationSound = new Ref(); boolean talkToBotOnlyWithAuth = false; boolean enableWorkerChat = false; Ref defaultLanguage = new Ref(); String mailLeadsTo; // where to mail leads String leadMailSubject = "A new lead occurred in your chat bot"; } static interface IInputHandler { // true if handled boolean handleInput(String s, Conversation conv); } static DynNewBot2 botMod() { return (DynNewBot2) dm_current_mandatory(); //ret (DynNewBot2) db_mainConcepts().miscMapGet(DynNewBot2); } static interface IF1 { B get(A a); } static class ShowComboBoxForm { String desc; String itemDesc; List items; String defaultValue; IVF1 action; JComboBox cb; String selected() { return selectedItem(cb); } final void show() { run(); } void run() { cb = jComboBox(items, defaultValue); showFormTitled(desc, "", jlabel(desc), itemDesc, cb, runnableThread(new Runnable() { public void run() { try { String item = selected(); printVars("item", item, "action", action); if (nempty(item)) action.get(item); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "String item = selected();\r\n printVars(+item, +action);\r\n if (ne..."; }})); } } static interface IVF1 { void get(A a); } static interface IVF2 { void get(A a, B b); } // immutable, has strong refs // Do not run in a synchronized block - it goes wrong in the presence // of elaborate classloaders (like in Gazelle BEA) // see #1102990 and #1102991 final static class _MethodCache { final Class c; final HashMap> cache = new HashMap(); _MethodCache(Class c) { this.c = c; _init(); } void _init() { Class _c = c; while (_c != null) { for (Method m : _c.getDeclaredMethods()) if (!isAbstract(m) && !reflection_isForbiddenMethod(m)) multiMapPut(cache, m.getName(), makeAccessible(m)); _c = _c.getSuperclass(); } // add default methods - this might lead to a duplication // because the overridden method is also added, but it's not // a problem except for minimal performance loss. for (Class intf : allInterfacesImplementedBy(c)) for (Method m : intf.getDeclaredMethods()) if (m.isDefault() && !reflection_isForbiddenMethod(m)) multiMapPut(cache, m.getName(), makeAccessible(m)); } // Returns only matching methods Method findMethod(String method, Object[] args) { try { List m = cache.get(method); if (m == null) return null; int n = m.size(); for (int i = 0; i < n; i++) { Method me = m.get(i); if (call_checkArgs(me, args, false)) return me; } return null; } catch (Exception __e) { throw rethrow(__e); } } Method findStaticMethod(String method, Object[] args) { try { List m = cache.get(method); if (m == null) return null; int n = m.size(); for (int i = 0; i < n; i++) { Method me = m.get(i); if (isStaticMethod(me) && call_checkArgs(me, args, false)) return me; } return null; } catch (Exception __e) { throw rethrow(__e); } } } static class Matches { String[] m; Matches() {} Matches(String... m) { this.m = m;} String get(int i) { return i < m.length ? m[i] : null; } String unq(int i) { return unquote(get(i)); } String tlc(int i) { return unq(i).toLowerCase(); } 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)); } public String toString() { return "Matches(" + joinWithComma(quoteAll(asList(m))) + ")"; } public int hashCode() { return _hashCode(toList(m)); } public boolean equals(Object o) { return o instanceof Matches && arraysEqual(m, ((Matches) o).m); } } // for the version with MasterSymbol (used WAY back in "Smart Bot"!) see #1010608 static class Symbol implements CharSequence { String text; Symbol() {} Symbol(String text, boolean dummy) { this.text = text;} // weird signature to prevent accidental calling public int hashCode() { return _hashCode(text); } public String toString() { return text; } public boolean equals(Object o) { return this == o; } // implementation of CharSequence methods public int length() { return text.length(); } public char charAt(int index) { return text.charAt(index); } public CharSequence subSequence(int start, int end) { return text.substring(start, end); } } static interface IMeta { // see class "Meta" for the bla bla public void _setMeta(Object meta); public Object _getMeta(); default public IAutoCloseableF0 _tempMetaMutex() { return new IAutoCloseableF0() { public Object get() { return IMeta.this; } public void close() {} }; } } static class HCRUD_Concepts extends HCRUD_Data { Concepts cc = db_mainConcepts(); Class cClass; List> onCreateOrUpdate = new ArrayList(); List> onCreate = new ArrayList(); IVF2> afterUpdate; // parameter 2: old values Map filters; // fields to filter by/add to new objects Map ciFilters; // case-insensitive filters IF1, Collection> customFilter; ValueConverterForField valueConverter; boolean referencesBlockDeletion = false; boolean trimAllSingleLineValues = false; Set fieldsToHideInCreationForm; boolean lockDB = false; // lock DB while updating object boolean verbose = false; boolean dropEmptyListValues = true; boolean lsMagic = false; boolean convertConceptValuesToRefs = false; A currentConcept; // while editing boolean useDynamicComboBoxes = false; // dynamically load concepts combo box entries for all fields IF1 useDynamicComboBoxesForField; // activate dynamic combo boxes for selected fields int dynamicComboBoxesThreshold = 1000; // if there are this many entries or more, use a dynamic combo box HCRUD_Concepts(Class cClass) { this.cClass = cClass;} HCRUD_Concepts(Concepts cc, Class cClass) { this.cClass = cClass; this.cc = cc;} // XXX - breaking change from just shortName() transient IF0 itemName; String itemName() { return itemName != null ? itemName.get() : itemName_base(); } final String itemName_fallback(IF0 _f) { return _f != null ? _f.get() : itemName_base(); } String itemName_base() { return humanizeShortName(cClass); } transient IF0 itemNamePlural; String itemNamePlural() { return itemNamePlural != null ? itemNamePlural.get() : itemNamePlural_base(); } final String itemNamePlural_fallback(IF0 _f) { return _f != null ? _f.get() : itemNamePlural_base(); } String itemNamePlural_base() { return super.itemNamePlural(); } //LS fields() { ret conceptFields(cClass); } List itemsForListing() { return defaultSort(asList(listConcepts())); } @Override List> list() { //ret lazyMap itemToMapForList(itemsForListing()); return lazyMap(itemsForListing(), a -> new Item(str(a.id)) { public Map calcFullMap() { return itemToMapForList(a); } }); } // more efficient version - convert items only after taking subList // TODO: this is never called by HCRUD; make lazy list instead? @Override List> list(IntRange range) { return lambdaMap(__71 -> itemToMapForList(__71), subListOrFull(itemsForListing(), range)); } Collection listConcepts() { Collection l = listConcepts_firstStep(); return postProcess(customFilter, l); } transient IF0> listConcepts_firstStep; Collection listConcepts_firstStep() { return listConcepts_firstStep != null ? listConcepts_firstStep.get() : listConcepts_firstStep_base(); } final Collection listConcepts_firstStep_fallback(IF0> _f) { return _f != null ? _f.get() : listConcepts_firstStep_base(); } Collection listConcepts_firstStep_base() { if (empty(ciFilters)) return conceptsWhere(cc, cClass, mapToParams(filters)); else if (empty(filters)) return conceptsWhereCI(cc, cClass, mapToParams(ciFilters)); else { // TODO: choose best index Collection l = conceptsWhere(cc, cClass, mapToParams(filters)); return filterConceptsIC(l, mapToParams(ciFilters)); } } transient IF0> defaultSortField; Pair defaultSortField() { return defaultSortField != null ? defaultSortField.get() : defaultSortField_base(); } final Pair defaultSortField_fallback(IF0> _f) { return _f != null ? _f.get() : defaultSortField_base(); } Pair defaultSortField_base() { return pair("id", true); } transient IF1, List> defaultSort; List defaultSort(List l) { return defaultSort != null ? defaultSort.get(l) : defaultSort_base(l); } final List defaultSort_fallback(IF1, List> _f, List l) { return _f != null ? _f.get(l) : defaultSort_base(l); } List defaultSort_base(List l) { return sortedByConceptIDDesc(l); } transient IF0 emptyConcept; A emptyConcept() { return emptyConcept != null ? emptyConcept.get() : emptyConcept_base(); } final A emptyConcept_fallback(IF0 _f) { return _f != null ? _f.get() : emptyConcept_base(); } A emptyConcept_base() { return unlisted(cClass); } transient IF0> emptyObject; Map emptyObject() { return emptyObject != null ? emptyObject.get() : emptyObject_base(); } final Map emptyObject_fallback(IF0> _f) { return _f != null ? _f.get() : emptyObject_base(); } Map emptyObject_base() { A c = emptyConcept(); // not actually necessary here, we do it on create /*cset(c, mapToParams(filters)); cset(c, mapToParams(ciFilters));*/ Map map = itemToMap(c); //printVars_str emptyObject(+cClass, +filters, +ciFilters, +map); return mapMinusKeys(fieldsToHideInCreationForm, map); } Map itemToMap(A c) { if (c == null) return null; return putKeysFirst(getFieldOrder(c), conceptToMap_gen_withNullValues(c)); } Map itemToMapForList(A c) { if (c == null) return null; Map map = itemToMap(c); massageItemMapForList(c, map); return map; } transient IVF2> massageItemMapForList; void massageItemMapForList(A c, Map map) { if (massageItemMapForList != null) massageItemMapForList.get(c, map); else massageItemMapForList_base(c, map); } final void massageItemMapForList_fallback(IVF2> _f, A c, Map map) { if (_f != null) _f.get(c, map); else massageItemMapForList_base(c, map); } void massageItemMapForList_base(A c, Map map) { } transient IVF2> massageItemMapForUpdate; void massageItemMapForUpdate(A c, Map map) { if (massageItemMapForUpdate != null) massageItemMapForUpdate.get(c, map); else massageItemMapForUpdate_base(c, map); } final void massageItemMapForUpdate_fallback(IVF2> _f, A c, Map map) { if (_f != null) _f.get(c, map); else massageItemMapForUpdate_base(c, map); } void massageItemMapForUpdate_base(A c, Map map) { // Concepts.RefL magic Collection refLFields = nonStaticNonTransientFieldObjectsOfType(Concept.RefL.class, c); printVars_str("refLFields", refLFields, "c", c); for (Field f : refLFields) { TreeMap values = new TreeMap(); Matches m = new Matches(); for (Map.Entry __2 : _entrySet( cloneMap(map))) { String key = __2.getKey(); Object value = __2.getValue(); if (startsWith(key, f.getName() + "_", m) && isInteger(m.rest())) { Concept concept = getConceptFromString((String) value); //printVars_str("RefL magic", +key, +conceptID, +concept); if (concept != null || !dropEmptyListValues) values.put(parseInt(m.rest()), concept); map.remove(key); } } if (!dropEmptyListValues) while (nempty(values) && lastValue(values) == null) { if (verbose) print("Dropping value " + lastEntry(values)); removeLastKey(values); } map.put(f.getName(), valuesAsList(values)); } // process dynamic bool, concept fields for (String name : cloneKeys(map)) { String metaInfo = metaInfoFromForm(name); print("metaInfo for " + name + ": " + metaInfo); if (eqic(metaInfo, "concept")) replaceStringValueWithConcept(map, name); else if (eqic(metaInfo, "bool")) replaceStringValueWithBool(map, name); } // Concepts.Ref magic (look up concept) for (Field f : nonStaticNonTransientFieldObjectsOfType(Concept.Ref.class, c)) replaceStringValueWithConcept(map, f.getName()); // trim values if (trimAllSingleLineValues) for (Map.Entry e : map.entrySet()) { String val = optCastString(e.getValue()); if (val != null && isSingleLine(val) && isUntrimmed(val)) e.setValue(trim(val)); } // LS magic if (lsMagic) for (Field f : nonStaticNonTransientFieldObjectsOfType(List.class, c)) { if (eqOneOf(f.getName(), "refs", "backRefs")) continue; TreeMap values = new TreeMap(); Matches m = new Matches(); for (Map.Entry __1 : _entrySet( cloneMap(map))) { String key = __1.getKey(); Object value = __1.getValue(); if (startsWith(key, f.getName() + "_", m) && isInteger(m.rest())) { if (!dropEmptyListValues || nempty((String) value)) { if (verbose) print("Adding value " + m.rest() + " / " + value); mapPut(values, parseInt(m.rest()), (String) value); } map.remove(key); } } if (!dropEmptyListValues) while (nempty(values) && empty(lastValue(values))) { if (verbose) print("Dropping value " + lastEntry(values)); removeLastKey(values); } map.put(f.getName(), valuesAsList(values)); } // don't set SecretValue fields for (Field f : nonStaticNonTransientFieldObjectsOfType(SecretValue.class, c)) map.remove(f.getName()); } void replaceStringValueWithConcept(Map map, String key) { Object value = map.get(key); if (value instanceof String) { Concept concept = getConceptFromString((String) value); map.put(key, concept); } } void replaceStringValueWithBool(Map map, String key) { Object value = map.get(key); if (value instanceof String) { map.put(key, englishStringToBool((String) value)); } } transient IF1> getObject; Map getObject(Object id) { return getObject != null ? getObject.get(id) : getObject_base(id); } final Map getObject_fallback(IF1> _f, Object id) { return _f != null ? _f.get(id) : getObject_base(id); } Map getObject_base(Object id) { return itemToMap(conceptForID(id)); } Map getObjectForEdit(Object id) { currentConcept = conceptForID(id); return getObject(id); } Object createObject(Map fullMap, String fieldPrefix) { rawFormValues = fullMap; try { Map map = extractFieldValues(fullMap, fieldPrefix); A c = cnew(cc, cClass); // make sure filters override setValues(c, mapMinusKeys(map, filteredFields()), true); cset(c, mapToParams(filters)); cset(c, mapToParams(ciFilters)); pcallFAll(onCreate, c); pcallFAll(onCreateOrUpdate, c); callOpt(c, "_onCreated"); // TODO: synchronize? return c.id; } finally { rawFormValues = null; } } void setValues(A c, Map map, boolean creating) { Lock __3 = lockDB && !creating ? dbLock(cc) : null; lock(__3); try { Map map2 = (Map) cloneMap(map); massageItemMapForUpdate(c, map2); if (verbose) { print("setValues " + map); print("backRefs: " + c.backRefs); } Map oldValues = !creating && afterUpdate != null ? cgetAll_cloneLists(c, keys(map2)) : null; if (convertConceptValuesToRefs) convertAllConceptValuesToRefs(c, map2); if (valueConverter == null) cSmartSet(c, mapToParams(map2)); else cSmartSet_withConverter_pcall(verbose, valueConverter, c, mapToParams(map2)); if (oldValues != null) callF(afterUpdate, c, oldValues); if (verbose) print("backRefs: " + c.backRefs); } finally { unlock(__3); } } // for dynamic fields void convertAllConceptValuesToRefs(A c, Map map) { for (Map.Entry __0 : _entrySet( cloneMap(map))) { String key = __0.getKey(); Object value = __0.getValue(); if (value instanceof Concept) if (!hasField(c, key)) { print("Converting value to ref: " + key); map.put(key, c.new Ref((Concept) value)); } } } A conceptForID(Object id) { return _getConcept(cc, cClass, toLong(id)); } String updateObject(Object id, Map fullMap, String fieldPrefix) { rawFormValues = fullMap; try { Map map = extractFieldValues(fullMap, fieldPrefix); A c = conceptForID(id); if (c == null) return "Object " + id + " not found"; { String __5 = checkFilters(c); if (!empty(__5)) return __5; } setValues(c, map, false); pcallFAll(onCreateOrUpdate, c); return "Object " + id + " updated"; } finally { rawFormValues = null; } } String deleteObject(Object id) { A c = conceptForID(id); if (c == null) return "Object " + id + " not found"; { String __6 = checkFilters(c); if (!empty(__6)) return __6; } actuallyDeleteConcept(c); return "Object " + id + " deleted"; } transient IVF1 actuallyDeleteConcept; void actuallyDeleteConcept(A c) { if (actuallyDeleteConcept != null) actuallyDeleteConcept.get(c); else actuallyDeleteConcept_base(c); } final void actuallyDeleteConcept_fallback(IVF1 _f, A c) { if (_f != null) _f.get(c); else actuallyDeleteConcept_base(c); } void actuallyDeleteConcept_base(A c) { deleteConcept(c); } String checkFilters(A c) { if (!checkConceptFields(c, mapToParams(filters)) || !checkConceptFieldsIC(c, mapToParams(ciFilters))) return "Object " + c.id + " not in view"; return ""; } HCRUD_Concepts addFilters(Map map) { for (Map.Entry __7 : _entrySet( unnullForIteration(map))) { String field = __7.getKey(); Object value = __7.getValue(); addFilter(field, value); } return this; } HCRUD_Concepts addFilter(String field, Object value) { filters = orderedMapPutOrCreate(filters, field, value); return this; } // TODO: do this the other way around (check for editable types) transient IF1 isEditableValue; boolean isEditableValue(Object value) { return isEditableValue != null ? isEditableValue.get(value) : isEditableValue_base(value); } final boolean isEditableValue_fallback(IF1 _f, Object value) { return _f != null ? _f.get(value) : isEditableValue_base(value); } boolean isEditableValue_base(Object value) { //if (value instanceof Concept.RefL) true; // subsumed in next line if (value instanceof List) return true; if (value instanceof Collection) return false; return true; } Renderer getRenderer(String field) { if (!isEditableValue(currentValue)) return new NotEditable(); Class type = fieldType(or(currentConcept, cClass), field); String metaInfo = metaInfoFromForm(field); //printVars_str("getRenderer", +cClass, +field, +type, +rawFormValues); if (eq(type, boolean.class)) return new CheckBox(); if (eq(type, Boolean.class)) return new ComboBox(ll("", "yes", "no"), b -> trueFalseNull((Boolean) b, "yes", "no", "")); // show a Ref<> field as a combo box if (eq(type, Concept.Ref.class)) { Class c = fieldTypeArg(field); AbstractComboBox cb = makeConceptsComboBox(field, c); cb.metaInfo = "concept"; return cb; } // show dynamic field with concept/ref value as combo box Object val = deref(currentValue); if (val instanceof Concept) { Class c = ((Concept) val).getClass(); AbstractComboBox cb = makeConceptsComboBox(field, c); cb.metaInfo = "concept"; return cb; } // show dynamic ref field from URL parameters if (eqic(metaInfo, "concept")) { printVars_str("metaInfo value", "field", field, "val", val); AbstractComboBox cb = makeConceptsComboBox(field, Concept.class); // TODO cb.metaInfo = "concept"; return cb; } // show a RefL<> field as a list of combo boxes if (eq(type, Concept.RefL.class)) { Class c = fieldTypeArg(field); return new FlexibleLengthList(makeConceptsComboBox(field, c)); } if (eq(type, List.class)) { //Class c = fieldTypeArg(field); return new FlexibleLengthList(new TextField(80)); } if (val instanceof Boolean) return new CheckBox(); return super.getRenderer(field); } DynamicComboBox makeDynamicComboBox(String field, Class c) { DynamicComboBox cb = new DynamicComboBox(field); cb.valueToEntry = value -> { value = deref(value); //print("ComboBox: value type=" + _getClass(value); long id = 0; if (value instanceof Concept) id = conceptID((Concept) value); else if (value instanceof String && isInteger((String) value)) id = parseLong(value); if (id != 0) return comboBoxItem(_getConcept(cc, id)); return null; }; return cb; } AbstractComboBox makeConceptsComboBox(String field, Class c) { if (c == null) throw fail(("Null type for field " + field + ". currentConcept: ") + currentConcept); if (useDynamicComboBoxes || useDynamicComboBoxesForField != null && useDynamicComboBoxesForField.get(field)) return makeDynamicComboBox(field, c); Collection concepts = listConceptClass(c); if (l(concepts) >= dynamicComboBoxesThreshold) return makeDynamicComboBox(field, c); List entries = comboBoxItems(concepts); ComboBox cb = new ComboBox(entries); cb.valueToEntry = value -> { value = deref(value); //print("ComboBox: value type=" + _getClass(value); long id = 0; if (value instanceof Concept) id = conceptID((Concept) value); else if (value instanceof String && isInteger((String) value)) id = parseLong(value); if (id != 0) { String entry = firstWhereFirstLongIs(entries, id); //print("combobox selected: " + id + " / " + entry); return entry; } return null; }; return cb; } // TODO: filters? Collection listConceptClass(Class c) { return cc.list(c); } List comboBoxItemsForConceptClass(Class c) { return comboBoxItems(listConceptClass(c)); } List comboBoxItems(Collection l) { return comboBoxItems_static(l); } static List comboBoxItems_static(Collection l) { return itemPlus("", lmap(__72 -> comboBoxItem_static(__72), l)); } String comboBoxItem(Concept val) { return comboBoxItem_static(val); } static String comboBoxItem_static(Concept val) { return val == null ? null : shorten(val.id + ": " + val); } transient IF1 objectCanBeDeleted; boolean objectCanBeDeleted(Object id) { return objectCanBeDeleted != null ? objectCanBeDeleted.get(id) : objectCanBeDeleted_base(id); } final boolean objectCanBeDeleted_fallback(IF1 _f, Object id) { return _f != null ? _f.get(id) : objectCanBeDeleted_base(id); } boolean objectCanBeDeleted_base(Object id) { return !referencesBlockDeletion || !hasBackRefs(conceptForID(id)); } Set filteredFields() { return joinSets(keys(filters), keys(ciFilters)); } Class fieldTypeArg(String field) { return getTypeArgumentAsClass(genericFieldType(or(currentConcept, cClass), field)); } transient IF2> conceptClassForComboBoxSearch; Class conceptClassForComboBoxSearch(String info, String query) { return conceptClassForComboBoxSearch != null ? conceptClassForComboBoxSearch.get(info, query) : conceptClassForComboBoxSearch_base(info, query); } final Class conceptClassForComboBoxSearch_fallback(IF2> _f, String info, String query) { return _f != null ? _f.get(info, query) : conceptClassForComboBoxSearch_base(info, query); } Class conceptClassForComboBoxSearch_base(String info, String query) { // info is the field name (Ref or RefL). get concept class if (!isIdentifier(info)) return cClass; String field = info; Class c = fieldTypeArg(field); // simple hack to show list for dynamic/new fields // assuming it's the same as the main class // clients can improve on this if (c == null) c = cClass; return c; } transient IF2> comboBoxSearchBaseItems; List comboBoxSearchBaseItems(String info, String query) { return comboBoxSearchBaseItems != null ? comboBoxSearchBaseItems.get(info, query) : comboBoxSearchBaseItems_base(info, query); } final List comboBoxSearchBaseItems_fallback(IF2> _f, String info, String query) { return _f != null ? _f.get(info, query) : comboBoxSearchBaseItems_base(info, query); } List comboBoxSearchBaseItems_base(String info, String query) { var c = conceptClassForComboBoxSearch(info, query); if (c == null) return emptyList(); return comboBoxItemsForConceptClass(c); } List comboBoxSearch(String info, String query) { List items = comboBoxSearchBaseItems(info, query); return takeFirst(10, scoredSearch(query, items)); } // to find the concept e.g. within massageFormMatrix A conceptForMap(Map map) { if (map == null) return null; long id = toLong(map.get(idField())); return id == 0 ? null : (A) _getConcept(cc, id); } A getConcept(Map map) { return conceptForMap(map); } Map extractFieldValues(Map fullMap, String fieldPrefix) { return subMapStartingWith_dropPrefix(fullMap, fieldPrefix); } Concept getConceptFromString(String s) { long conceptID = parseFirstLong(s); return _getConcept(cc, conceptID); } String metaInfoFromForm(String field) { return mapGet(rawFormValues, "metaInfo_" + field); } void addCIFilter(String field, String value) { ciFilters = putOrCreate(ciFilters, field, value); } transient IF1 titleForObjectID; String titleForObjectID(Object id) { return titleForObjectID != null ? titleForObjectID.get(id) : titleForObjectID_base(id); } final String titleForObjectID_fallback(IF1 _f, Object id) { return _f != null ? _f.get(id) : titleForObjectID_base(id); } String titleForObjectID_base(Object id) { return htmlEncode2(strOrNull(conceptForID(id))); } } static class JS implements IF0 { JS() {} String code; Set requiredLibraries; Set requiredLibraries() { return requiredLibraries; } JS(String code, Object... dollarVars) { this.code = jsDollarVars(code, dollarVars); } public String get() { return code; } public String toString() { return unnull(code); } void requireJQuery() { require("jquery"); } void require(Object library) { requiredLibraries = addToSet_create(requiredLibraries, library); } } // a variant of thread where you can get the Runnable target later. // Also notes its existence on the VM bus. // We should use this exclusively instead of Thread. static class BetterThread extends Thread { Runnable target; BetterThread(Runnable target) { this.target = target; _created(); } BetterThread(Runnable target, String name) { super(name); this.target = target; _created(); } void _created() { vmBus_send("threadCreated", this); } public void run() { try { vmBus_send("threadStarted", this); if (target != null) target.run(); } finally { vmBus_send("threadEnded", this); } } Runnable getTarget() { return target; } } static class Timestamp implements Comparable , IFieldsToList{ long date; Timestamp(long date) { this.date = date;} public boolean equals(Object o) { if (!(o instanceof Timestamp)) return false; Timestamp __1 = (Timestamp) o; return date == __1.date; } public int hashCode() { int h = 2059094262; h = boostHashCombine(h, _hashCode(date)); return h; } public Object[] _fieldsToList() { return new Object[] {date}; } Timestamp() { date = now(); } public String toString() { return formatLocalDateWithSeconds(date); } // Hmm. Should Timestamp(0) be equal to null? Question, questions... public int compareTo(Timestamp t) { return t == null ? 1 : cmp(date, t.date); } } static class MultiMap { Map> data = new HashMap>(); int fullSize; MultiMap() {} MultiMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); } MultiMap(MultiMap map) { putAll(map); } MultiMap(Map> data) { this.data = data;} void put(A key, B value) { synchronized(data) { List list = data.get(key); if (list == null) data.put(key, list = _makeEmptyList()); list.add(value); ++fullSize; }} void add(A key, B value) { put(key, value); } void addAll(A key, Collection values) { putAll(key, values); } void addAllIfNotThere(A key, Collection values) { synchronized(data) { for (B value : values) setPut(key, value); }} void setPut(A key, B value) { synchronized(data) { if (!containsPair(key, value)) put(key, value); }} boolean containsPair(A key, B value) { synchronized(data) { return get(key).contains(value); }} void putAll(Collection keys, B value) { synchronized(data) { for (A key : unnullForIteration(keys)) put(key, value); }} void putAll(A key, Collection values) { synchronized(data) { if (nempty(values)) getActual(key).addAll(values); }} void putAll(Iterable> pairs) { synchronized(data) { for (Pair p : unnullForIteration(pairs)) put(p.a, p.b); }} void removeAll(A key, Collection values) { synchronized(data) { for (B value : values) remove(key, value); }} List get(A key) { synchronized(data) { List list = data.get(key); return list == null ? Collections. emptyList() : list; }} List getOpt(A key) { synchronized(data) { return data.get(key); }} // returns actual mutable live list // creates the list if not there List getActual(A key) { synchronized(data) { List list = data.get(key); if (list == null) data.put(key, list = _makeEmptyList()); return list; }} void clean(A key) { synchronized(data) { List list = data.get(key); if (list != null && list.isEmpty()) { fullSize -= l(list); data.remove(key); } }} Set keySet() { synchronized(data) { return data.keySet(); }} Set keys() { synchronized(data) { return data.keySet(); }} void remove(A key) { synchronized(data) { fullSize -= l(getOpt(key)); data.remove(key); }} void remove(A key, B value) { synchronized(data) { List list = data.get(key); if (list != null) { if (list.remove(value)) fullSize--; if (list.isEmpty()) data.remove(key); } }} void clear() { synchronized(data) { data.clear(); }} boolean containsKey(A key) { synchronized(data) { return data.containsKey(key); }} B getFirst(A key) { synchronized(data) { List list = get(key); return list.isEmpty() ? null : list.get(0); }} void addAll(MultiMap map) { putAll(map); } void putAll(MultiMap map) { synchronized(data) { for (A key : map.keySet()) putAll(key, map.get(key)); }} void putAll(Map map) { synchronized(data) { if (map != null) for (Map.Entry e : map.entrySet()) put(e.getKey(), e.getValue()); }} final int keyCount() { return keysSize(); } int keysSize() { synchronized(data) { return l(data); }} // full size - note: expensive operation final int fullSize() { return size(); } int size() { synchronized(data) { return fullSize; }} // expensive operation List reverseGet(B b) { synchronized(data) { List l = new ArrayList(); for (A key : data.keySet()) if (data.get(key).contains(b)) l.add(key); return l; }} Map> asMap() { synchronized(data) { return cloneMap(data); }} boolean isEmpty() { synchronized(data) { return data.isEmpty(); }} // override in subclasses List _makeEmptyList() { return new ArrayList(); } // returns live lists Collection> allLists() { synchronized(data) { return new ArrayList(data.values()); } } Collection> values() { return allLists(); } List allValues() { return concatLists(data.values()); } Object mutex() { return data; } public String toString() { return "mm" + str(data); } } static class Lowest { A best; double score; transient Object onChange; synchronized boolean isNewBest(double score) { return best == null || score < this.score; } synchronized double bestScore() { return best == null ? Double.NaN : score; } double score() { return bestScore(); } synchronized float floatScore() { return best == null ? Float.NaN : (float) score; } synchronized float floatScoreOr(float defaultValue) { return best == null ? defaultValue : (float) score; } boolean put(A a, double score) { boolean change = false; synchronized(this) { if (a != null && isNewBest(score)) { best = a; this.score = score; change = true; } } if (change) pcallF(onChange); return change; } synchronized void clear() { best = null; score = 0; } synchronized A get() { return best; } synchronized boolean has() { return best != null; } synchronized Pair pair() { return best == null ? null : new Pair(best, bestScore()); } public String toString() { return "Score " + formatDouble_significant2(score, 4) + ": " + best; } } static interface IResourceLoader { String loadSnippet(String snippetID); String getTranspiled(String snippetID); // with libs int getSnippetType(String snippetID); String getSnippetTitle(String snippetID); File loadLibrary(String snippetID); default File pathToJavaXJar() { return pathToJavaxJar_noResourceLoader(); } // may return null, then caller compiles themselves default File getSnippetJar(String snippetID, String transpiledSrc) { return null; } } /* * @(#)WeakHashMap.java 1.5 98/09/30 * * Copyright 1998 by Sun Microsystems, Inc., * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information * of Sun Microsystems, Inc. ("Confidential Information"). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with Sun. */ // From https://github.com/mernst/plume-lib/blob/df0bfafc3c16848d88f4ea0ef3c8bf3367ae085e/java/src/plume/WeakHasherMap.java static final class WeakHasherMap extends AbstractMap implements Map { private Hasher hasher = null; /*@Pure*/ private boolean keyEquals(Object k1, Object k2) { return (hasher==null ? k1.equals(k2) : hasher.equals(k1, k2)); } /*@Pure*/ private int keyHashCode(Object k1) { return (hasher==null ? k1.hashCode() : hasher.hashCode(k1)); } // The WeakKey class can't be static because it depends on the hasher. // That in turn means that its methods can't be static. // However, I need to be able to call the methods such as create() that // were static in the original version of this code. // This finesses that. private /*@Nullable*/ WeakKey WeakKeyCreate(K k) { if (k == null) return null; else return new WeakKey(k); } private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue q) { if (k == null) return null; else return new WeakKey(k, q); } // Cannot be a static class: uses keyHashCode() and keyEquals() private final class WeakKey extends WeakReference { private int hash; /* Hashcode of key, stored here since the key may be tossed by the GC */ private WeakKey(K k) { super(k); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k) { if (k == null) return null; else return new WeakKey(k); } private WeakKey(K k, ReferenceQueue q) { super(k, q); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k, ReferenceQueue q) { if (k == null) return null; else return new WeakKey(k, q); } /* A WeakKey is equal to another WeakKey iff they both refer to objects that are, in turn, equal according to their own equals methods */ /*@Pure*/ @Override public boolean equals(/*@Nullable*/ Object o) { if (o == null) return false; // never happens if (this == o) return true; // This test is illegal because WeakKey is a generic type, // so use the getClass hack below instead. // if (!(o instanceof WeakKey)) return false; if (!(o.getClass().equals(WeakKey.class))) return false; Object t = this.get(); @SuppressWarnings("unchecked") Object u = ((WeakKey)o).get(); if ((t == null) || (u == null)) return false; if (t == u) return true; return keyEquals(t, u); } /*@Pure*/ @Override public int hashCode() { return hash; } } /* Hash table mapping WeakKeys to values */ private HashMap hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue queue = new ReferenceQueue(); /* Remove all invalidated entries from the map, that is, remove all entries whose keys have been discarded. This method should be invoked once by each public mutator in this class. We don't invoke this method in public accessors because that can lead to surprising ConcurrentModificationExceptions. */ @SuppressWarnings("unchecked") private void processQueue() { WeakKey wk; while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast hash.remove(wk); } } /* -- Constructors -- */ /** * Constructs a new, empty WeakHashMap with the given * initial capacity and the given load factor. * * @param initialCapacity the initial capacity of the * WeakHashMap * * @param loadFactor the load factor of the WeakHashMap * * @throws IllegalArgumentException If the initial capacity is less than * zero, or if the load factor is * nonpositive */ public WeakHasherMap(int initialCapacity, float loadFactor) { hash = new HashMap(initialCapacity, loadFactor); } /** * Constructs a new, empty WeakHashMap with the given * initial capacity and the default load factor, which is * 0.75. * * @param initialCapacity the initial capacity of the * WeakHashMap * * @throws IllegalArgumentException If the initial capacity is less than * zero */ public WeakHasherMap(int initialCapacity) { hash = new HashMap(initialCapacity); } /** * Constructs a new, empty WeakHashMap with the default * capacity and the default load factor, which is 0.75. */ public WeakHasherMap() { hash = new HashMap(); } /** * Constructs a new, empty WeakHashMap with the default * capacity and the default load factor, which is 0.75. * The WeakHashMap uses the specified hasher for hashing * keys and comparing them for equality. * @param h the Hasher to use when hashing values for this map */ public WeakHasherMap(Hasher h) { hash = new HashMap(); hasher = h; } /* -- Simple queries -- */ /** * Returns the number of key-value mappings in this map. * Note: In contrast to most implementations of the * Map interface, the time required by this operation is * linear in the size of the map. */ /*@Pure*/ @Override public int size() { return entrySet().size(); } /** * Returns true if this map contains no key-value mappings. */ /*@Pure*/ @Override public boolean isEmpty() { return entrySet().isEmpty(); } /** * Returns true if this map contains a mapping for the * specified key. * * @param key the key whose presence in this map is to be tested */ /*@Pure*/ @Override public boolean containsKey(Object key) { @SuppressWarnings("unchecked") K kkey = (K) key; return hash.containsKey(WeakKeyCreate(kkey)); } /* -- Lookup and modification operations -- */ /** * Returns the value to which this map maps the specified key. * If this map does not contain a value for this key, then return * null. * * @param key the key whose associated value, if any, is to be returned */ /*@Pure*/ @Override public /*@Nullable*/ V get(Object key) { // type of argument is Object, not K @SuppressWarnings("unchecked") K kkey = (K) key; return hash.get(WeakKeyCreate(kkey)); } /** * Updates this map so that the given key maps to the given * value. If the map previously contained a mapping for * key then that mapping is replaced and the previous value is * returned. * * @param key the key that is to be mapped to the given * value * @param value the value to which the given key is to be * mapped * * @return the previous value to which this key was mapped, or * null if if there was no mapping for the key */ @Override public V put(K key, V value) { processQueue(); return hash.put(WeakKeyCreate(key, queue), value); } /** * Removes the mapping for the given key from this map, if * present. * * @param key the key whose mapping is to be removed * * @return the value to which this key was mapped, or null if * there was no mapping for the key */ @Override public V remove(Object key) { // type of argument is Object, not K processQueue(); @SuppressWarnings("unchecked") K kkey = (K) key; return hash.remove(WeakKeyCreate(kkey)); } /** * Removes all mappings from this map. */ @Override public void clear() { processQueue(); hash.clear(); } /* -- Views -- */ /* Internal class for entries */ // This can't be static, again because of dependence on hasher. @SuppressWarnings("TypeParameterShadowing") private final class Entry implements Map.Entry { private Map.Entry ent; private K key; /* Strong reference to key, so that the GC will leave it alone as long as this Entry exists */ Entry(Map.Entry ent, K key) { this.ent = ent; this.key = key; } /*@Pure*/ @Override public K getKey() { return key; } /*@Pure*/ @Override public V getValue() { return ent.getValue(); } @Override public V setValue(V value) { return ent.setValue(value); } /*@Pure*/ private boolean keyvalEquals(K o1, K o2) { return (o1 == null) ? (o2 == null) : keyEquals(o1, o2); } /*@Pure*/ private boolean valEquals(V o1, V o2) { return (o1 == null) ? (o2 == null) : o1.equals(o2); } /*@Pure*/ @SuppressWarnings("NonOverridingEquals") public boolean equals(Map.Entry e /* Object o*/) { // if (! (o instanceof Map.Entry)) return false; // Map.Entry e = (Map.Entry)o; return (keyvalEquals(key, e.getKey()) && valEquals(getValue(), e.getValue())); } /*@Pure*/ @Override public int hashCode() { V v; return (((key == null) ? 0 : keyHashCode(key)) ^ (((v = getValue()) == null) ? 0 : v.hashCode())); } } /* Internal class for entry sets */ private final class EntrySet extends AbstractSet> { Set> hashEntrySet = hash.entrySet(); @Override public Iterator> iterator() { return new Iterator>() { Iterator> hashIterator = hashEntrySet.iterator(); Map.Entry next = null; @Override public boolean hasNext() { while (hashIterator.hasNext()) { Map.Entry ent = hashIterator.next(); WeakKey wk = ent.getKey(); K k = null; if ((wk != null) && ((k = wk.get()) == null)) { /* Weak key has been cleared by GC */ continue; } next = new Entry(ent, k); return true; } return false; } @Override public Map.Entry next() { if ((next == null) && !hasNext()) throw new NoSuchElementException(); Map.Entry e = next; next = null; return e; } @Override public void remove() { hashIterator.remove(); } }; } /*@Pure*/ @Override public boolean isEmpty() { return !(iterator().hasNext()); } /*@Pure*/ @Override public int size() { int j = 0; for (Iterator> i = iterator(); i.hasNext(); i.next()) j++; return j; } @Override public boolean remove(Object o) { processQueue(); if (!(o instanceof Map.Entry)) return false; @SuppressWarnings("unchecked") Map.Entry e = (Map.Entry)o; // unchecked cast Object ev = e.getValue(); WeakKey wk = WeakKeyCreate(e.getKey()); Object hv = hash.get(wk); if ((hv == null) ? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) { hash.remove(wk); return true; } return false; } /*@Pure*/ @Override public int hashCode() { int h = 0; for (Iterator> i = hashEntrySet.iterator(); i.hasNext(); ) { Map.Entry ent = i.next(); WeakKey wk = ent.getKey(); Object v; if (wk == null) continue; h += (wk.hashCode() ^ (((v = ent.getValue()) == null) ? 0 : v.hashCode())); } return h; } } private /*@Nullable*/ Set> entrySet = null; /** * Returns a Set view of the mappings in this map. */ /*@SideEffectFree*/ @Override public Set> entrySet() { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } // find matching key K findKey(Object key) { processQueue(); K kkey = (K) key; // TODO: use replacement for HashMap to avoid reflection WeakKey wkey = WeakKeyCreate(kkey); WeakKey found = hashMap_findKey(hash, wkey); return found == null ? null : found.get(); } } static class Either { byte which; Object value; Either() {} Either(int which, Object value) { this.which = (byte) which; this.value = value; } boolean isA() { return which == 1; } boolean isB() { return which == 2; } A a() { if (which != 1) _failMe(); return (A) value; } B b() { if (which != 2) _failMe(); return (B) value; } A aOpt() { return which != 1 ? null : (A) value; } B bOpt() { return which != 2 ? null : (B) value; } void _failMe() { throw fail("Either object is of wrong type: " + shortClassName(value)); } public String toString() { return "Either" + (isA() ? "A" : "B") + "(" + value + ")"; } } static class ReliableSingleThread_Multi { IVF1 processObject; int delay; // delay before changes are processed ReliableSingleThread_Multi(IVF1 processObject) { this.processObject = processObject;} ReliableSingleThread_Multi(int delay, IVF1 processObject) { this.processObject = processObject; this.delay = delay;} ReliableSingleThread_Multi(int delay, IVF1 processObject, F0 enter) { this.processObject = processObject; this.delay = delay; setEnter(enter); } // internal Set changedObjects = synchroSet(); ReliableSingleThread rst = new ReliableSingleThread(new Runnable() { public void run() { try { process(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "process();"; }}); // notify of change in object void add(A a) { if (changedObjects.add(a)) rst.trigger(); } void addAll(Iterable l) { for (A a : unnullForIteration(l)) { ping(); add(a); } } private void process() { sleep(delay); for (A object : syncCloneAndClearList(changedObjects)) { ping(); pcallF(processObject, object); } } void setEnter(F0 enter) { rst.enter = enter; } } static class proxy_InvocationHandler implements InvocationHandler { Object target; proxy_InvocationHandler() {} proxy_InvocationHandler(Object target) { this.target = target;} public Object invoke(Object proxy, Method method, Object[] args) { return call(target, method.getName(), unnull(args)); } } // uses HashMap by default static class MultiSet implements IMultiSet { Map map = new HashMap(); int size; // now maintaining a size counter MultiSet(boolean useTreeMap) { if (useTreeMap) map = new TreeMap(); } MultiSet(TreeMap map) { this.map = map;} MultiSet() {} MultiSet(Iterable c) { addAll(c); } MultiSet(MultiSet ms) { synchronized(ms) { for (A a : ms.keySet()) add(a, ms.get(a)); }} // returns new count public synchronized int add(A key) { return add(key, 1); } synchronized void addAll(Iterable c) { if (c != null) for (A a : c) add(a); } synchronized void addAll(MultiSet ms) { for (A a : ms.keySet()) add(a, ms.get(a)); } synchronized int add(A key, int count) { if (count <= 0) return 0; // don't calculate return value in this case size += count; Integer i = map.get(key); map.put(key, i != null ? (count += i) : count); return count; } synchronized void put(A key, int count) { int oldCount = get(key); if (count == oldCount) return; size += count-oldCount; if (count != 0) map.put(key, count); else map.remove(key); } public synchronized int get(A key) { Integer i = map.get(key); return i != null ? i : 0; } synchronized boolean contains(A key) { return map.containsKey(key); } synchronized void remove(A key) { Integer i = map.get(key); if (i != null) { --size; if (i > 1) map.put(key, i - 1); else map.remove(key); } } synchronized List topTen() { return getTopTen(); } synchronized List getTopTen() { return getTopTen(10); } synchronized List getTopTen(int maxSize) { List list = getSortedListDescending(); return list.size() > maxSize ? list.subList(0, maxSize) : list; } synchronized List highestFirst() { return getSortedListDescending(); } synchronized List lowestFirst() { return reversedList(getSortedListDescending()); } synchronized List getSortedListDescending() { List list = new ArrayList(map.keySet()); Collections.sort(list, new Comparator() { public int compare(A a, A b) { return map.get(b).compareTo(map.get(a)); } }); return list; } synchronized int getNumberOfUniqueElements() { return map.size(); } synchronized int uniqueSize() { return map.size(); } synchronized Set asSet() { return map.keySet(); } synchronized NavigableSet navigableSet() { return navigableKeys((NavigableMap) map); } synchronized Set keySet() { return map.keySet(); } synchronized A getMostPopularEntry() { int max = 0; A a = null; for (Map.Entry entry : map.entrySet()) { if (entry.getValue() > max) { max = entry.getValue(); a = entry.getKey(); } } return a; } synchronized void removeAll(A key) { size -= get(key); map.remove(key); } synchronized int size() { return size; } synchronized MultiSet mergeWith(MultiSet set) { MultiSet result = new MultiSet(); for (A a : set.asSet()) { result.add(a, set.get(a)); } return result; } synchronized boolean isEmpty() { return map.isEmpty(); } synchronized public String toString() { // hmm. sync this? return str(map); } synchronized void clear() { map.clear(); size = 0; } final synchronized Map toMap() { return asMap(); } synchronized Map asMap() { return cloneMap(map); } } static class MetaTransformer { static interface StructureHandler { default Object transform(Object o, IF1 recurse) { return null; } void visit(Object o, IVF1 recurse); } List structureHandlers; //bool keepUnknown; // assume true for now Set seen; // initialize to identityHashSet() if you want to check for cycles MetaTransformer() {} MetaTransformer(StructureHandler... handlers) { structureHandlers = asList(handlers); } final void add(StructureHandler sh) { addStructureHandler(sh); } void addStructureHandler(StructureHandler sh) { structureHandlers.add(sh); } // if f returns null, go through structure // if f returns an object, do not recurse into it // TODO: honor seen Object transform(IF1 f, Object o) { ping(); Object x = f.get(o); if (x != null) return x; IF1 recurse = liftFunction(f); for (StructureHandler h : unnullForIteration(structureHandlers)) { ping(); { Object __2= h.transform(o, recurse); if (__2 != null) return __2; } } //ret keepUnknown ? o : null; return o; } // transform without result void visit(IVF1 f, Object o) { ping(); if (o == null) return; if (seen != null && !seen.add(o)) return; f.get(o); for (StructureHandler h : unnullForIteration(structureHandlers)) { ping(); IVF1 recurse = x -> { markPointer(o, x); visit(f, x); }; h.visit(o, recurse); } } void visit_vstack(IVF1 f, Object o) { vstackCompute(new visit_vstackComputable(f, o)); } class visit_vstackComputable extends VStackComputableWithStep implements IFieldsToList{ IVF1 f; Object o; visit_vstackComputable() {} visit_vstackComputable(IVF1 f, Object o) { this.o = o; this.f = f;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + f + ", " + o + ")"; } public boolean equals(Object _o) { if (!(_o instanceof visit_vstackComputable)) return false; visit_vstackComputable __0 = (visit_vstackComputable) _o; return eq(f, __0.f) && eq(o, __0.o); } public int hashCode() { int h = 1091269166; h = boostHashCombine(h, _hashCode(f)); h = boostHashCombine(h, _hashCode(o)); return h; } public Object[] _fieldsToList() { return new Object[] {f, o}; } void step(VStack stack) { if (step == 0) { ping(); if (o == null) { stack._return(); return; } step++; } if (step == 1) { if (seen != null && !seen.add(o)) { stack._return(); return; } ++step; } if (step == 2) { f.get(o); ++step; } stack.replace(new ForEach_vstack<>(structureHandlers, h -> { IVF1 recurse = x -> { markPointer(o, x); stack.call(new visit_vstackComputable(f, x)); }; h.visit(o, recurse); })); } } // lift transformer function to handle structures IF1 liftFunction(IF1 f) { return o -> transform(f, o); } // check if any element satisfies a predicate. // Note: might even be faster without the cancel point logic boolean any(IF1 pred, Object o) { Flag flag = new Flag(); withCancelPoint(cp -> visit(x -> { if (pred.get(x)) { flag.raise(); cancelTo(cp); } }, o) ); return flag.isUp(); } void addVisitor(IVF1 visitor) { if (visitor == null) return; addStructureHandler(new StructureHandler() { public void visit(Object o, IVF1 recurse) { visitor.get(o); } }); } void avoidCycles() { seen = identityHashSet(); } transient IVF2 markPointer; void markPointer(Object a, Object b) { if (markPointer != null) markPointer.get(a, b); else markPointer_base(a, b); } final void markPointer_fallback(IVF2 _f, Object a, Object b) { if (_f != null) _f.get(a, b); else markPointer_base(a, b); } void markPointer_base(Object a, Object b) {} } // A is the type of object being listed by the search static class ScoredSearcher { int maxResults = 1000; List preparedTerms; Map scores = new HashMap(); ScoredSearcher() {} ScoredSearcher(String query, Object... __) { maxResults = optPar("maxResults", __, maxResults); setQuery(query); } void setQuery(String query) { preparedTerms = scoredSearch_prepare(query); } void put(A object, String s) { putUnlessZero(scores, object, scoredSearch_score(s, preparedTerms)); } void put(A object, Collection fields) { add(object, scoreFields(fields)); } // process fields with weights void putWithWeights(A object, Collection> fields) { scoredSearch_scoreWeighted2(fields, preparedTerms); } int scoreFields(Collection fields) { return scoredSearch_score(fields, preparedTerms); } int score(String text) { return scoredSearch_score(text, preparedTerms); } void add(A object) { put(object, str(object)); } void add(A object, Collection fields) { put(object, fields); } void put(A object, double score) { add(object, score); } void add(A object, double score) { putUnlessZero(scores, object, score); } List get() { return takeFirst(maxResults, keysSortedByValuesDesc(scores)); } List> withScores() { return map(get(), a -> scoredWithMapValue(scores, a)); } A best() { return keyWithHighestValue(scores); } } static class Pair implements Comparable> { A a; B b; Pair() {} Pair(A a, B b) { this.b = b; this.a = a;} public int hashCode() { return hashCodeFor(a) + 2*hashCodeFor(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); } public String toString() { return "<" + a + ", " + b + ">"; } public int compareTo(Pair p) { if (p == null) return 1; int i = ((Comparable) a).compareTo(p.a); if (i != 0) return i; return ((Comparable) b).compareTo(p.b); } } static interface IResourceHolder { A add(A a); } static class HTMLFramer1 { String title; boolean titleIsHTML = false; List navItems = new ArrayList(); List contents = new ArrayList(); // items will be evaluated to strings upon rendering LinkedHashSet headElements = new LinkedHashSet(); // automatically avoid duplication boolean navBeforeTitle = false; List willRender = new ArrayList(); static class NavItem implements IFieldsToList{ static final String _fieldOrder = "link html targetBlank"; String link; String html; NavItem() {} NavItem(String link, String html) { this.html = html; this.link = link;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + link + ", " + html + ")"; } public boolean equals(Object o) { if (!(o instanceof NavItem)) return false; NavItem __1 = (NavItem) o; return eq(link, __1.link) && eq(html, __1.html); } public int hashCode() { int h = -900864330; h = boostHashCombine(h, _hashCode(link)); h = boostHashCombine(h, _hashCode(html)); return h; } public Object[] _fieldsToList() { return new Object[] {link, html}; } boolean targetBlank = false; } public String get() { return render(); } transient IF0 render; String render() { return render != null ? render.get() : render_base(); } final String render_fallback(IF0 _f) { return _f != null ? _f.get() : render_base(); } String render_base() { callFAll(willRender); return hhtml(hhead(htitle(titleIsHTML ? hTitleClean(title) : title) + lines(headElements)) + hbody( (navBeforeTitle ? renderNav() + renderTitle() : renderTitle() + renderNav()) + nemptyLines(contents))); } transient IF0 renderTitle; String renderTitle() { return renderTitle != null ? renderTitle.get() : renderTitle_base(); } final String renderTitle_fallback(IF0 _f) { return _f != null ? _f.get() : renderTitle_base(); } String renderTitle_base() { return h1(encodedTitle()); } String encodedTitle() { //printVars_str encodedTitle(+title, +titleIsHTML); return titleIsHTML ? title : htmlEncode2(title); } transient IF0 renderNav; String renderNav() { return renderNav != null ? renderNav.get() : renderNav_base(); } final String renderNav_fallback(IF0 _f) { return _f != null ? _f.get() : renderNav_base(); } String renderNav_base() { if (empty(navItems)) return ""; return div_vbar(map(__73 -> renderNavItem(__73), navItems), "style" , "margin-bottom: 0.5em"); } String renderNavItem(NavItem ni) { return ahref_possiblyTargetBlank(ni.link, ni.html, ni.targetBlank); } void addNavItem(String html) { navItems.add(new NavItem(null, html)); } void addNavItems(Iterable htmls) { forEach(__107 -> addNavItem(__107), htmls); } void addNavItem(String link, String html, Object... __) { navItems.add(setAll(new NavItem(link, html), filterParams(__, "targetBlank"))); } final void addInHEAD(Object html) { addInHead(html); } void addInHead(Object html) { headElements.add(str(html)); } // legacy signature for binary compatibility void add(String html) { add((Object) html); } // convenience signature for lambdas void add(IF0 html) { add(lambdaToToString(html)); } // add contents: TODO move things to head automatically void add(Object html) { if (notNullOrEmptyString(html)) contents.add(html); } void addAll(Collection l) { forEach(__108 -> add(__108), l); } void clearNavItems() { navItems.clear(); } HTMLFramer1 setTitle(String title) { this.title = title; return this; } String htmlTitle(String title) { this.title = title; titleIsHTML = true; return title; } Collection headElements() { return headElements; } } static class HCRUD_Data { Map renderers = new HashMap(); Map fieldHelp = new HashMap(); Object currentValue; // current field value held temporarily boolean humanizeFieldNames = true; Map rawFormValues; // temporary map with raw form values abstract static class Renderer { String metaInfo; transient IF1 preprocessValue; Object preprocessValue(Object value) { return preprocessValue != null ? preprocessValue.get(value) : preprocessValue_base(value); } final Object preprocessValue_fallback(IF1 _f, Object value) { return _f != null ? _f.get(value) : preprocessValue_base(value); } Object preprocessValue_base(Object value) { return value; } } static class NotEditable extends Renderer {} static class TextArea extends Renderer implements IFieldsToList{ int cols; int rows; TextArea() {} TextArea(int cols, int rows) { this.rows = rows; this.cols = cols;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + cols + ", " + rows + ")"; } public boolean equals(Object o) { if (!(o instanceof TextArea)) return false; TextArea __1 = (TextArea) o; return cols == __1.cols && rows == __1.rows; } public int hashCode() { int h = -939552902; h = boostHashCombine(h, _hashCode(cols)); h = boostHashCombine(h, _hashCode(rows)); return h; } public Object[] _fieldsToList() { return new Object[] {cols, rows}; } // star constructor syntax not working here (transpiler bug) TextArea(int cols, int rows, IF1 preprocessValue) { this.cols = cols; this.rows = rows; this.preprocessValue = preprocessValue; } } static class AceEditor extends TextArea { AceEditor() {} AceEditor(int cols, int rows) { this.rows = rows; this.cols = cols;} } static class TextField extends Renderer implements IFieldsToList{ int cols; TextField() {} TextField(int cols) { this.cols = cols;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + cols + ")"; } public boolean equals(Object o) { if (!(o instanceof TextField)) return false; TextField __2 = (TextField) o; return cols == __2.cols; } public int hashCode() { int h = 942981037; h = boostHashCombine(h, _hashCode(cols)); return h; } public Object[] _fieldsToList() { return new Object[] {cols}; } } static class AbstractComboBox extends Renderer implements IFieldsToList{ AbstractComboBox() {} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ")"; } public boolean equals(Object o) { return o instanceof AbstractComboBox; } public int hashCode() { int h = -1802496065; return h; } public Object[] _fieldsToList() { return null; } boolean editable = false; // help find item to select transient IF1 valueToEntry; String valueToEntry(Object value) { return valueToEntry != null ? valueToEntry.get(value) : valueToEntry_base(value); } final String valueToEntry_fallback(IF1 _f, Object value) { return _f != null ? _f.get(value) : valueToEntry_base(value); } String valueToEntry_base(Object value) { return strOrNull(value); } } static class ComboBox extends AbstractComboBox implements IFieldsToList{ List entries; ComboBox() {} ComboBox(List entries) { this.entries = entries;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + entries + ")"; } public boolean equals(Object o) { if (!(o instanceof ComboBox)) return false; ComboBox __3 = (ComboBox) o; return eq(entries, __3.entries); } public int hashCode() { int h = -547674755; h = boostHashCombine(h, _hashCode(entries)); return h; } public Object[] _fieldsToList() { return new Object[] {entries}; } ComboBox(String... entries) { this(asList(entries)); } ComboBox(boolean editable, String... entries) { this(entries); this.editable = editable; } ComboBox(boolean editable, List entries) { this(entries); this.editable = editable; } // star constructor syntax not working here (transpiler bug) ComboBox(List entries, IF1 valueToEntry) { this.entries = entries; this.valueToEntry = valueToEntry; } } static class DynamicComboBox extends AbstractComboBox implements IFieldsToList{ String info; DynamicComboBox() {} DynamicComboBox(String info) { this.info = info;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + info + ")"; } public boolean equals(Object o) { if (!(o instanceof DynamicComboBox)) return false; DynamicComboBox __4 = (DynamicComboBox) o; return eq(info, __4.info); } public int hashCode() { int h = -737505636; h = boostHashCombine(h, _hashCode(info)); return h; } public Object[] _fieldsToList() { return new Object[] {info}; } String url; } static class CheckBox extends Renderer implements IFieldsToList{ CheckBox() {} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ")"; } public boolean equals(Object o) { return o instanceof CheckBox; } public int hashCode() { int h = 1601505219; return h; } public Object[] _fieldsToList() { return null; } { metaInfo = "Bool"; } } static class FlexibleLengthList extends Renderer implements IFieldsToList{ Renderer itemRenderer; FlexibleLengthList() {} FlexibleLengthList(Renderer itemRenderer) { this.itemRenderer = itemRenderer;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + itemRenderer + ")"; } public boolean equals(Object o) { if (!(o instanceof FlexibleLengthList)) return false; FlexibleLengthList __5 = (FlexibleLengthList) o; return eq(itemRenderer, __5.itemRenderer); } public int hashCode() { int h = -1874811153; h = boostHashCombine(h, _hashCode(itemRenderer)); return h; } public Object[] _fieldsToList() { return new Object[] {itemRenderer}; } } String itemName() { return "object"; } String itemNamePlural() { return plural(itemName()); } //LS fields() { null; } List> list() { return null; } List> list(IntRange range) { return subListOrFull(list(), range); } String idField() { return "id"; } Map emptyObject() { return null; } Map getObject(Object id) { return null; } Map getObjectForEdit(Object id) { return getObject(id); } transient IF1> getObjectForDuplication; Map getObjectForDuplication(Object id) { return getObjectForDuplication != null ? getObjectForDuplication.get(id) : getObjectForDuplication_base(id); } final Map getObjectForDuplication_fallback(IF1> _f, Object id) { return _f != null ? _f.get(id) : getObjectForDuplication_base(id); } Map getObjectForDuplication_base(Object id) { return getObject(id); } // return ID Object createObject(Map fullMap, String fieldPrefix) { throw unimplemented(); } // return text msg String deleteObject(Object id) { throw unimplemented(); } boolean objectCanBeDeleted(Object id) { return true; } transient IF1 objectCanBeEdited; boolean objectCanBeEdited(Object id) { return objectCanBeEdited != null ? objectCanBeEdited.get(id) : objectCanBeEdited_base(id); } final boolean objectCanBeEdited_fallback(IF1 _f, Object id) { return _f != null ? _f.get(id) : objectCanBeEdited_base(id); } boolean objectCanBeEdited_base(Object id) { return true; } // return text msg String updateObject(Object id, Map fullMap, String fieldPrefix) { throw unimplemented(); } // return null for standard input field Renderer getRenderer(String field) { return renderers.get(field); } final Renderer getRenderer(String field, Object value) { this.currentValue = value; try { return getRenderer(field); } finally { this.currentValue = null; } } // returns HTML String fieldHelp(String field) { return fieldHelp.get(field); } HCRUD_Data addRenderer(String field, Renderer renderer) { renderers.put(field, renderer); return this; } HCRUD_Data fieldHelp(String field, String help, String... more) { fieldHelp.put(field, help); for (int i = 0; i+1 < l(more); i += 2) fieldHelp.put(more[i], more[i+1]); return this; } String fieldNameToHTML(String name) { String help = fieldHelp.get(name); return spanTitle(help, htmlencode2( humanizeFieldNames ? humanizeLabel(name) : name)); } Set filteredFields() { return null; } List comboBoxSearch(String info, String query) { return null; } // optional page title when showing a single object String titleForObjectID(Object id) { return null; } // second parameter indicates descending Pair defaultSortField() { return null; } abstract class Item extends AbstractMap { Object id; Map fullMap; Item(Object id) { this.id = id;} abstract Map calcFullMap(); Map fullMap() { if (fullMap == null) fullMap = calcFullMap(); return fullMap; } public int size() { return l(fullMap()); } public Set> entrySet() { return fullMap().entrySet(); } public boolean containsKey(Object o) { return fullMap().containsKey(o); } public Object get(Object o) { if (fullMap == null && eq(o, idField())) return id; return fullMap().get(o); } public Object put(String key, Object value) { return fullMap().put(key, value); } } } static interface IAutoCloseableF0 extends IF0, AutoCloseable {} static interface Transformable { Object transformUsing(IF1 f); } static class BetterLabel extends JLabel { boolean autoToolTip = true; BetterLabel() { // Listeners given out to componentPopupMenu must not directly // reference the outer object (-> weak map problem). final WeakReference < BetterLabel > me = new WeakReference<>(this); componentPopupMenu(this, BetterLabel_menuItems(me)); } BetterLabel(String text) { this(); this.setText(text); } public void setText(String text) { super.setText(text); if (autoToolTip) if (!swic(text, "")) // HTML labels make super-huge, confusing tool tips setToolTipText(nullIfEmpty(text)); } } // moved outside of class for GC reasons (see above) static VF1 BetterLabel_menuItems(final WeakReference me) { return new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, "Copy text to clipboard", new Runnable() { public void run() { try { copyTextToClipboard(me.get().getText()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyTextToClipboard(me.get().getText());"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, \"Copy text to clipboard\", r {\r\n copyTextToClipboard(me..."; }}; } // elements are put to front when added (not when accessed) static class MRUCache extends LinkedHashMap { int maxSize = 10; MRUCache() {} MRUCache(int maxSize) { this.maxSize = maxSize;} protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxSize; } Object _serialize() { return ll(maxSize, cloneLinkedHashMap(this)); } static MRUCache _deserialize(List l) { MRUCache m = new MRUCache(); m.maxSize = (int) first(l); m.putAll((LinkedHashMap) second(l)); return m; } } /* * The Alphanum Algorithm is an improved sorting algorithm for strings * containing numbers. Instead of sorting numbers in ASCII order like * a standard sort, this algorithm sorts numbers in numeric order. * * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com * * Released under the MIT License - https://opensource.org/licenses/MIT * * Copyright 2007-2017 David Koelle * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * This is an updated version with enhancements made by Daniel Migowski, * Andre Bogus, and David Koelle. Updated by David Koelle in 2017. */ static class AlphanumComparator implements Comparator { boolean ignoreCase = false; private final boolean isDigit(char ch) { return ((ch >= 48) && (ch <= 57)); } /** Length of string is passed in for improved efficiency (only need to calculate it once) **/ private final String getChunk(String s, int slength, int marker) { StringBuilder chunk = new StringBuilder(); char c = s.charAt(marker); chunk.append(c); marker++; if (isDigit(c)) while (marker < slength) { c = s.charAt(marker); if (!isDigit(c)) break; chunk.append(c); marker++; } else while (marker < slength) { c = s.charAt(marker); if (isDigit(c)) break; chunk.append(c); marker++; } return chunk.toString(); } public int compare(String s1, String s2) { if (s1 == null) return s2 == null ? 0 : -1; if (s2 == null) return 1; int thisMarker = 0; int thatMarker = 0; int s1Length = s1.length(); int s2Length = s2.length(); while (thisMarker < s1Length && thatMarker < s2Length) { String thisChunk = getChunk(s1, s1Length, thisMarker); thisMarker += thisChunk.length(); String thatChunk = getChunk(s2, s2Length, thatMarker); thatMarker += thatChunk.length(); // If both chunks contain numeric characters, sort them numerically int result = 0; if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { // Simple chunk comparison by length. int thisChunkLength = thisChunk.length(); result = thisChunkLength - thatChunk.length(); // If equal, the first different number counts if (result == 0) { for (int i = 0; i < thisChunkLength; i++) { result = thisChunk.charAt(i) - thatChunk.charAt(i); if (result != 0) { return result; } } } } else { result = thisChunk.compareTo(thatChunk); } if (result != 0) return result; } return s1Length - s2Length; } } static class PersistableThrowable extends DynamicObject { String className; String msg; String stacktrace; PersistableThrowable() {} PersistableThrowable(Throwable e) { if (e == null) className = "Crazy Null Error"; else { className = getClassName(e).replace('/', '.'); msg = e.getMessage(); stacktrace = getStackTrace_noRecord(e); } } public String toString() { return nempty(msg) ? className + ": " + msg : className; } } // e.g. ("Germany", "DE", "+49") static class CountryDialCode implements IFieldsToList{ String country; String countryCode; String dialCode; CountryDialCode() {} CountryDialCode(String country, String countryCode, String dialCode) { this.dialCode = dialCode; this.countryCode = countryCode; this.country = country;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + country + ", " + countryCode + ", " + dialCode + ")"; } public boolean equals(Object o) { if (!(o instanceof CountryDialCode)) return false; CountryDialCode __1 = (CountryDialCode) o; return eq(country, __1.country) && eq(countryCode, __1.countryCode) && eq(dialCode, __1.dialCode); } public int hashCode() { int h = 240284435; h = boostHashCombine(h, _hashCode(country)); h = boostHashCombine(h, _hashCode(countryCode)); h = boostHashCombine(h, _hashCode(dialCode)); return h; } public Object[] _fieldsToList() { return new Object[] {country, countryCode, dialCode}; } } static interface IMultiSet { // returns new count int add(A key); /*void addAll(Iterable c) { if (c != null) for (A a : c) add(a); } void addAll(MultiSet ms) { for (A a : ms.keySet()) add(a, ms.get(a)); }*/ //int add(A key, int count); //void put(A key, int count); int get(A key); //bool contains(A key); //void remove(A key); /*List topTen(); synchronized List getTopTen() { ret getTopTen(10); } synchronized List getTopTen(int maxSize) { List list = getSortedListDescending(); return list.size() > maxSize ? list.subList(0, maxSize) : list; } synchronized L highestFirst() { ret getSortedListDescending(); } synchronized L lowestFirst() { ret reversedList(getSortedListDescending()); } synchronized L getSortedListDescending() { List list = new ArrayList(map.keySet()); Collections.sort(list, new Comparator() { public int compare(A a, A b) { return map.get(b).compareTo(map.get(a)); } }); ret list; } synchronized int getNumberOfUniqueElements() { return map.size(); } synchronized int uniqueSize() { ret map.size(); } synchronized Set asSet() { return map.keySet(); } synchronized NavigableSet navigableSet() { return navigableKeys((NavigableMap) map); } synchronized Set keySet() { return map.keySet(); } synchronized A getMostPopularEntry() { int max = 0; A a = null; for (Map.Entry entry : map.entrySet()) { if (entry.getValue() > max) { max = entry.getValue(); a = entry.getKey(); } } return a; } synchronized void removeAll(A key) { size -= get(key); map.remove(key); } synchronized int size() { ret size; } synchronized MultiSet mergeWith(MultiSet set) { MultiSet result = new MultiSet(); for (A a : set.asSet()) { result.add(a, set.get(a)); } return result; } synchronized boolean isEmpty() { return map.isEmpty(); } synchronized public String toString() { // hmm. sync this? return str(map); } synchronized void clear() { map.clear(); size = 0; } synchronized Map asMap() { ret cloneMap(map); }*/ } // the lockFile must be a file separate from any data files. // It is created & deleted by this class, and will always have // size 0. static class FileBasedLock implements AutoCloseable { File lockFile; double timeout = 60.0; // in seconds. refresh happens twice as often boolean verbose = false; boolean haveLock = false; java.util.Timer touchTimer; FileBasedLock() {} FileBasedLock(File lockFile) { this.lockFile = lockFile;} FileBasedLock(File lockFile, double timeout) { this.timeout = timeout; this.lockFile = lockFile;} // returns true iff lock was acquired (or kept) synchronized boolean tryToLock() { if (haveLock) return true; if (fileExists(lockFile)) { double age = fileAgeInSeconds(lockFile); if (verbose) print("Lock file age: " + lockFile + " = " + iround(age) + " s"); if (age >= timeout) { print("Deleting old lock file (program crashed?): " + lockFile + " (age: " + iround(age) + " seconds)"); deleteFile(lockFile); } } try { mkdirsForFile(lockFile); java.nio.file.Files.createFile(toPath(lockFile)); acquired(); return true; } catch (Throwable e) { printExceptionShort("Can't lock", e); return false; } } private void acquired() { haveLock = true; startTouchTimer(); } void forceLock() { try { print("Force-locking " + lockFile); touchFile(lockFile); // make or touch file acquired(); } catch (Exception __e) { throw rethrow(__e); } } String lockError() { return "Couldn't aquire lock file: " + lockFile; } void lockOrFail() { if (!tryToLock()) throw fail(lockError()); } synchronized void startTouchTimer() { if (touchTimer != null) return; double interval = timeout/2; touchTimer = doEvery(interval, new Runnable() { public void run() { try { doTouch(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "doTouch();"; }}); if (verbose) print("Touch timer started for " + lockFile + " (" + interval + "s)"); } synchronized void doTouch() { try { if (haveLock) { if (verbose) print("Touching lock file: " + lockFile); touchExistingFile(lockFile); } } catch (Throwable __e) { _handleException(__e); }} public synchronized void close() { try { { cleanUp(touchTimer); touchTimer = null; } if (haveLock) { haveLock = false; if (verbose) print("Deleting lock file: " + lockFile); deleteFile(lockFile); } } catch (Throwable __e) { _handleException(__e); }} synchronized void _simulateCrash() { { cleanUp(touchTimer); touchTimer = null; } } } static interface ValueConverterForField { // convert value "value" for storage in "field" of "object" // can return null in case of unknown conversion OrError convertValue(Object object, Field field, Object value); } static class Scorer { double score, total; List successes, errors; // set to non-null if you want them filled boolean verboseFailures, verboseAll; final void add(double score) { addZeroToOne(score); } void addZeroToOne(double score) { ++total; this.score += clamp(score, 0, 1); } void addZeroToOneError(double error) { addZeroToOne(1-error); } void addError() { add(false); } void addError(A info) { add(false, info); } void error(A info) { addError(info); } void addOK() { add(true); } void addOK(A info) { add(true, info); } void ok() { addOK(); } void ok(A info) { addOK(info); } boolean add(boolean correct) { ++total; if (correct) ++score; return correct; } boolean add(boolean correct, A info) { main.add(correct ? successes : errors, info); if (verboseAll || verboseFailures && !correct) _print((correct ? "[GOOD] " : "[BAD] ") + info); return add(correct); } // works if you use Scorer or Scorer void eq(Object a, Object b) { if (_eq(a, b)) add(true); else add(false, (A) (a + " != " + b)); } void print() { main.print(toString()); } public String toString() { return formatDouble(ratioToPercent(score, total), 1) + "% correct (n=" + formatDouble(total, 1) + ")"; } double get() { return ratioToPercent(score, total); } double percentScore() { return get(); } double score() { return get(); } boolean allCorrect() { return score == total; } void add(Scorer scorer) { if (scorer == null) return; total += scorer.total; score += scorer.score; addAll(successes, scorer.successes); addAll(errors, scorer.errors); } void collectErrors() { errors = new ArrayList(); } void collectSuccesses() { successes = new ArrayList(); } } static class ConceptsRefChecker { Concepts cc; List errors = new ArrayList(); boolean veryVerbose = false; static class Err {} static class ErrNoBackRef extends Err implements IFieldsToList{ static final String _fieldOrder = "ref dest"; Concept.Ref ref; Concept dest; ErrNoBackRef() {} ErrNoBackRef(Concept.Ref ref, Concept dest) { this.dest = dest; this.ref = ref;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ref + ", " + dest + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrNoBackRef)) return false; ErrNoBackRef __1 = (ErrNoBackRef) o; return eq(ref, __1.ref) && eq(dest, __1.dest); } public int hashCode() { int h = -1351603866; h = boostHashCombine(h, _hashCode(ref)); h = boostHashCombine(h, _hashCode(dest)); return h; } public Object[] _fieldsToList() { return new Object[] {ref, dest}; } } static class ErrSuperfluousBackRef extends Err implements IFieldsToList{ static final String _fieldOrder = "ref dest"; Concept.Ref ref; Concept dest; ErrSuperfluousBackRef() {} ErrSuperfluousBackRef(Concept.Ref ref, Concept dest) { this.dest = dest; this.ref = ref;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ref + ", " + dest + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrSuperfluousBackRef)) return false; ErrSuperfluousBackRef __2 = (ErrSuperfluousBackRef) o; return eq(ref, __2.ref) && eq(dest, __2.dest); } public int hashCode() { int h = 1840777816; h = boostHashCombine(h, _hashCode(ref)); h = boostHashCombine(h, _hashCode(dest)); return h; } public Object[] _fieldsToList() { return new Object[] {ref, dest}; } } static class ErrDuplicateRef extends Err implements IFieldsToList{ Concept c; ErrDuplicateRef() {} ErrDuplicateRef(Concept c) { this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrDuplicateRef)) return false; ErrDuplicateRef __3 = (ErrDuplicateRef) o; return eq(c, __3.c); } public int hashCode() { int h = 1148352877; h = boostHashCombine(h, _hashCode(c)); return h; } public Object[] _fieldsToList() { return new Object[] {c}; } } static class ErrNullRef extends Err implements IFieldsToList{ Concept c; ErrNullRef() {} ErrNullRef(Concept c) { this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrNullRef)) return false; ErrNullRef __4 = (ErrNullRef) o; return eq(c, __4.c); } public int hashCode() { int h = 209467527; h = boostHashCombine(h, _hashCode(c)); return h; } public Object[] _fieldsToList() { return new Object[] {c}; } } static class ErrMissingRef extends Err implements IFieldsToList{ Concept c; Concept.Ref ref; ErrMissingRef() {} ErrMissingRef(Concept c, Concept.Ref ref) { this.ref = ref; this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + ref + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrMissingRef)) return false; ErrMissingRef __5 = (ErrMissingRef) o; return eq(c, __5.c) && eq(ref, __5.ref); } public int hashCode() { int h = 1969969266; h = boostHashCombine(h, _hashCode(c)); h = boostHashCombine(h, _hashCode(ref)); return h; } public Object[] _fieldsToList() { return new Object[] {c, ref}; } } static class ErrSuperfluousRef extends Err implements IFieldsToList{ Concept c; Concept.Ref ref; ErrSuperfluousRef() {} ErrSuperfluousRef(Concept c, Concept.Ref ref) { this.ref = ref; this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + ref + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrSuperfluousRef)) return false; ErrSuperfluousRef __6 = (ErrSuperfluousRef) o; return eq(c, __6.c) && eq(ref, __6.ref); } public int hashCode() { int h = 762378943; h = boostHashCombine(h, _hashCode(c)); h = boostHashCombine(h, _hashCode(ref)); return h; } public Object[] _fieldsToList() { return new Object[] {c, ref}; } } // a reference from a concept that is not in the concepts list static class ErrDanglingSource extends Err implements IFieldsToList{ static final String _fieldOrder = "ref dest"; Concept.Ref ref; Concept dest; ErrDanglingSource() {} ErrDanglingSource(Concept.Ref ref, Concept dest) { this.dest = dest; this.ref = ref;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ref + ", " + dest + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrDanglingSource)) return false; ErrDanglingSource __7 = (ErrDanglingSource) o; return eq(ref, __7.ref) && eq(dest, __7.dest); } public int hashCode() { int h = 666937292; h = boostHashCombine(h, _hashCode(ref)); h = boostHashCombine(h, _hashCode(dest)); return h; } public Object[] _fieldsToList() { return new Object[] {ref, dest}; } } // a reference to a concept that is not in the concepts list static class ErrDanglingDestination extends Err implements IFieldsToList{ static final String _fieldOrder = "ref dest"; Concept.Ref ref; Concept dest; ErrDanglingDestination() {} ErrDanglingDestination(Concept.Ref ref, Concept dest) { this.dest = dest; this.ref = ref;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + ref + ", " + dest + ")"; } public boolean equals(Object o) { if (!(o instanceof ErrDanglingDestination)) return false; ErrDanglingDestination __8 = (ErrDanglingDestination) o; return eq(ref, __8.ref) && eq(dest, __8.dest); } public int hashCode() { int h = -1158731011; h = boostHashCombine(h, _hashCode(ref)); h = boostHashCombine(h, _hashCode(dest)); return h; } public Object[] _fieldsToList() { return new Object[] {ref, dest}; } } ConceptsRefChecker() {} ConceptsRefChecker(Concepts cc) { this.cc = cc;} List run() { errors.clear(); if (cc == null) return errors; Collection concepts = cc.allConcepts(); if (veryVerbose) print("Checking " + nConcepts(concepts)); for (Concept c : concepts) try { Object badElement = firstElementNotSubclassing(c.backRefs, Concept.Ref.class); if (badElement != null) { print("Bad element in backRefs of " + c + ": " + badElement); continue; } for (Concept.Ref ref : cloneList(c.backRefs)) { if (ref.get() != c) errors.add(new ErrSuperfluousBackRef(ref, c)); if (ref.concept()._concepts != cc) errors.add(new ErrDanglingSource(ref, c)); } Collection refs = c._refs(); badElement = firstElementNotSubclassing(refs, Concept.Ref.class); if (badElement != null) { print("Bad element in backRefs of " + c + ": " + badElement); continue; } if (!allUnique(refs)) errors.add(new ErrDuplicateRef(c)); if (contains(refs, null)) errors.add(new ErrNullRef(c)); refs = nonNulls(refs); Collection actualRefs = scanConceptForRefs(c); for (Concept.Ref ref : listMinusSet(refs, actualRefs)) errors.add(new ErrSuperfluousRef(c, ref)); for (Concept.Ref ref : listMinusSet(actualRefs, refs)) errors.add(new ErrMissingRef(c, ref)); for (Concept.Ref ref : refs) if (ref.has()) { if (!contains(ref.get().backRefs, ref)) errors.add(new ErrNoBackRef(ref, c)); if (ref.get()._concepts != cc) errors.add(new ErrDanglingDestination(ref, c)); } } catch (Throwable e) { _handleException(e); print("Error processing concept " + c); } return errors; } List errors() { return errors; } String fixAll() { List out = new ArrayList(); // start from end because list removes should be faster like this for (Err err : reversed(cloneAndClear(errors))) { try { out.add("Processing " + err); if (err instanceof ErrNoBackRef) { ((ErrNoBackRef) err).ref.index(); out.add(" reference added"); } else if (err instanceof ErrSuperfluousRef) { ((ErrSuperfluousRef) err).ref.unindexAndDrop(); out.add(" reference dropped"); } else if (err instanceof ErrDanglingSource) { ((ErrDanglingSource) err).ref.get()._removeBackRef(((ErrDanglingSource) err).ref); out.add(" reference removed from destination"); } else if (err instanceof ErrDanglingDestination) { ((ErrDanglingDestination) err).ref.get()._removeBackRef(((ErrDanglingDestination) err).ref); out.add(" reference removed from destination"); } else if (err instanceof ErrMissingRef) { ((ErrMissingRef) err).ref.registerRef(); out.add(" reference registered"); } else if (err instanceof ErrSuperfluousBackRef) { ((ErrSuperfluousBackRef) err).dest._removeBackRef(((ErrSuperfluousBackRef) err).ref); out.add(" backreference dropped"); } else out.add(" TODO - fix not implemented"); } catch (Throwable __e) { _handleException(__e); }} return lines(out); } String runAndFixAll() { run(); return fixAll(); } } static class Q implements AutoCloseable { String name = "Unnamed Queue"; List q = synchroLinkedList(); ReliableSingleThread rst = new ReliableSingleThread(new Runnable() { public void run() { try { _run() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_run()"; }}); volatile boolean retired = false; Q() {} Q(String name) { this.name = name;} void add(Runnable r) { q.add(r); _trigger(); } void addInFront(Runnable r) { q.add(0, r); _trigger(); } void _trigger() { rst.name = name; rst.go(); } void add(Object r) { add(toRunnable(r)); } void _run() { Runnable r; while (licensed() && !retired && (r = syncPopFirst(q)) != null) { try { r.run(); } catch (Throwable __e) { _handleException(__e); } } onIdle(); } public void close() { retired = true; } // TODO: interrupt thread void done() {} // legacy function boolean isEmpty() { return q.isEmpty(); } int size() { return q.size(); } Object mutex() { return q; } // clients can synchronize on this List snapshot() { return cloneList(q); } // override this void onIdle() {} } abstract static class ConceptFieldIndexBase implements IConceptIndex, IFieldIndex, IConceptCounter, AutoCloseable { Concepts concepts; Class cc; String field; Map objectToValue = syncHashMap(); MultiSetMap valueToObject; // initialized in subclass // bool indexNulls = true; // set this only when there are few null values in this field in the db. or when the point is to index a non-existing field (for singletons) ConceptFieldIndexBase() { init(); } ConceptFieldIndexBase(Class cc, String field) { this(db_mainConcepts(), cc, field); } ConceptFieldIndexBase(Concepts concepts, Class cc, String field) { this(); this.field = field; this.cc = cc; this.concepts = concepts; concepts.addConceptIndex(this); updateAll(); register(); updateAll(); } void updateAll() { for (A c : setToIndex()) updateImpl(c); } Collection setToIndex() { return list(concepts, cc); } abstract void init(); abstract void register(); public void update(Concept c) { if (!isInstance(cc, c)) return; updateImpl((A) c); } synchronized void updateImpl(A c) { Val newValue = (Val) (cget(c, field)); Val oldValue = objectToValue.get(c); if (newValue == oldValue && (oldValue != null || objectToValue.containsKey(c))) return; valueToObject.remove(oldValue, c); valueToObject.put(newValue, c); put(objectToValue, c, newValue); } public synchronized void remove(Concept c) { if (!isInstance(cc, c)) return; if (!objectToValue.containsKey(c)) return; Val value = objectToValue.get(c); objectToValue.remove(c); valueToObject.remove(value, (A) c); } synchronized A get(Val value) { return valueToObject.getFirst(value); } // older version - may return empty list as null // should be phased out everywhere! public synchronized Collection getAll(Val value) { return valueToObject.get(value); } public synchronized List allValues() { return cloneKeys_noSync(valueToObject.data); } public IterableIterator objectIterator() { return navigableMultiSetMapValuesIterator_concurrent(valueToObject, this); } public synchronized MultiSet allValues_multiSet() { return multiSetMapToMultiSet(valueToObject); } public Class conceptClass() { return cc; } public int countConcepts() { return l(objectToValue); } public Collection allConcepts() { return (Collection) keys(objectToValue); } Object mutex() { return this; } public void close() { concepts.removeConceptIndex(this); } } // requires JQuery static class HTMLAceEditor implements Htmlable { String text; String name = "text"; String id; Map divParams = litmap("style" , "width: 80ch; height: 20em"); String onKeyDown; HTMLAceEditor() {} HTMLAceEditor(String text) { this.text = text;} // only needed once (call on any instance) public String headStuff() { return hscriptsrc("https://botcompany.de/ace-builds/src-noconflict/ace.js") + hscriptsrc("https://botcompany.de/ace-builds/src-noconflict/ext-language_tools.js"); } // for each editor public String html() { id = "ace_" + name; return div(htmlEncode2(text), params_stylePlus("display: none", paramsPlus(mapToParams(divParams), "id", id))) + hhiddenWithIDAndName(name) + hscript(replaceDollarVars(" (function() {\r\n ace.require(\"ace/ext/language_tools\");\r\n var editor = ace.edit($id);\r\n editor.setTheme(\"ace/theme/ambience\");\r\n editor.getSession().setTabSize(2);\r\n editor.getSession().setUseSoftTabs(true);\r\n editor.getSession().setUseWrapMode(true);\r\n document.getElementById($id).style.fontSize='15px';\r\n editor.setOptions({\r\n enableBasicAutocompletion: true\r\n });\r\n \r\n var div = $(\"#\" + $id);\r\n var hiddenVal = document.getElementById($name);\r\n function updateHidden() {\r\n div.trigger('input'); // for auto-expand\r\n //hiddenVal.value = editor.getValue();\r\n var newVal = editor.getValue();\r\n if (hiddenVal.value != newVal)\r\n $(hiddenVal).val(newVal).trigger('change');\r\n }\r\n updateHidden();\r\n \r\n editor.session.on('change', updateHidden);\r\n $onKeyDown\r\n \r\n div.show();\r\n //editor.focus();\r\n })() ", "id" , jsQuote(id), "name" , jsQuote(name), "onKeyDown" , empty(onKeyDown) ? "" : "editor.textInput.getElement().onkeydown = " + onKeyDown + ";")); } String complete() { return linesLL(headStuff(), html()); } } static class OrError extends Var { Object error; // usually a Throwable OrError() {} OrError(A value) { super(value); } OrError(boolean dummy, Object error) { this.error = error; assertNotNull(error); } boolean ok() { return error == null; } Object error() { return error; } static OrError ok(B a) { return new OrError(a); } static OrError error(Object error) { return new OrError(true, error); } public String toString() { return ok() ? super.toString() : str(error); } } static class ReliableSingleThread implements Runnable { boolean _isTransient() { return true; } Object runnable; // usually a Runnable. is allowed to call trigger() itself String name = "Single Thread"; boolean cancelBeforeTrigger = false; // always cancel running thread and wait for it to end before starting new operation boolean waitBetweenCancelAndTrigger = false; // make sure the old thread is actually ended F0 enter; // optional ownership marker, e.g. for DynModules int cancelTimeOut = 10000; boolean trigger = false; Thread thread; WeakReference threadBeingCancelled; List inserts = syncL(); ReliableSingleThread(Object runnable) { this.runnable = runnable;} void trigger() { go(); } synchronized void go() { if (cancelBeforeTrigger) cancelAndPossiblyWait(); trigger = true; if (!running()) { AutoCloseable __1 = callF(enter); try { thread = startThread(name, new Runnable() { public void run() { try { AutoCloseable __2 = callF(enter); try { _run(); } finally { _close(__2); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp callF(enter);\r\n _run();"; }}); } finally { _close(__1); }} } public void run() { go(); } void get() { go(); } // so you can use the ! syntax synchronized boolean running() { return thread != null; } // use only if this is the last time you trigger this void triggerAndWait() { trigger(); waitUntilDone(); } void waitUntilDone() { while (running()) sleep(1); } void _run() { try { while (licensed()) { Thread oldThread; synchronized(this) { var currentInserts = syncGetAndClear(inserts); pcallFAll(currentInserts); if (!trigger) { thread = null; break; } oldThread = getWeakRef(threadBeingCancelled); trigger = false; } if (oldThread != null && oldThread != currentThread()) oldThread.join(cancelTimeOut); pcallF(runnable); } } catch (Exception __e) { throw rethrow(__e); } } synchronized void cancel() { if (thread == null) return; threadBeingCancelled = new WeakReference(thread); cancelAndInterruptThread(thread); thread = null; } void cancelAndWait() { Thread _thread; synchronized(this) { if (thread == null) return; _thread = thread; threadBeingCancelled = new WeakReference(thread); thread = null; } cancelAndInterruptThread(_thread); } void cancelAndTrigger() { cancelAndPossiblyWait(); trigger(); } synchronized boolean triggered() { return trigger; } void cleanMeUp() { cancel(); } ReliableSingleThread cancelBeforeTrigger() { cancelBeforeTrigger = true; return this; } void cancelAndPossiblyWait() { if (waitBetweenCancelAndTrigger) cancel(); } // TODO: trigger technically not necessary, just notify (I guess) void insert(Runnable r) { inserts.add(r); trigger(); } } static class VStack implements Steppable { List stack = new ArrayList(); Object latestResult; transient Object newResult; // the null sentinel replaces a null function result // when stored in latestResult static class NullSentinel {} static NullSentinel nullSentinel = new NullSentinel(); VStack() {} VStack(Computable computation) { push(computation); } VStack(Iterable l) { pushAll(l); } // A is what the function returns interface Computable { public void step(VStack stack, Object subComputationResult); } private Object deSentinel(Object o) { return o instanceof NullSentinel ? null : o; } private Object sentinel(Object o) { return o == null ? nullSentinel : o; } // called by computations or users to start a subroutine final void call(Computable computation) { push(computation); } void push(Computable computation) { stack.add(computation); } // perform a computation step. returns false iff done public boolean step() { if (empty(stack)) return false; newResult = null; last(stack).step(this, result()); latestResult = newResult; newResult = null; return true; } // called from a computation to return a value void _return() { _return(null); } void _return(Object value) { newResult = sentinel(value); removeLast(stack); } // called from a computation to tail-call another routine final void replace(Computable computation) { tailCall(computation); } void tailCall(Computable computation) { removeLast(stack); stack.add(computation); } // all-in-one evaluation function - call on an empty stack A compute(Computable computation) { if (computation == null) return null; push(computation); stepAll(this); return (A) latestResult; } // return result of just completed computation or sub-computation final Object subResult() { return result(); } Object result() { return deSentinel(latestResult); } boolean hasSubResult() { return latestResult != null; } void pushAll(Iterable l) { for (Computable c : unnullForIteration(l)) push(c); } void add(Runnable r) { if (r == null) return; push((stack, subComputationResult) -> r.run()); } } static class MMOPattern { static class Phrase extends MMOPattern implements IFieldsToList{ String phrase; boolean quoted = false; Phrase() {} Phrase(String phrase, boolean quoted) { this.quoted = quoted; this.phrase = phrase;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + phrase + ", " + quoted + ")"; } public boolean equals(Object o) { if (!(o instanceof Phrase)) return false; Phrase __1 = (Phrase) o; return eq(phrase, __1.phrase) && eq(quoted, __1.quoted); } public int hashCode() { int h = -1905095975; h = boostHashCombine(h, _hashCode(phrase)); h = boostHashCombine(h, _hashCode(quoted)); return h; } public Object[] _fieldsToList() { return new Object[] {phrase, quoted}; } } static class And extends MMOPattern implements IFieldsToList{ List l; And() {} And(List l) { this.l = l;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + l + ")"; } public boolean equals(Object o) { if (!(o instanceof And)) return false; And __2 = (And) o; return eq(l, __2.l); } public int hashCode() { int h = 65975; h = boostHashCombine(h, _hashCode(l)); return h; } public Object[] _fieldsToList() { return new Object[] {l}; } } static class Or extends MMOPattern implements IFieldsToList{ List l; Or() {} Or(List l) { this.l = l;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + l + ")"; } public boolean equals(Object o) { if (!(o instanceof Or)) return false; Or __3 = (Or) o; return eq(l, __3.l); } public int hashCode() { int h = 2563; h = boostHashCombine(h, _hashCode(l)); return h; } public Object[] _fieldsToList() { return new Object[] {l}; } } static class Not extends MMOPattern implements IFieldsToList{ MMOPattern p; Not() {} Not(MMOPattern p) { this.p = p;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + p + ")"; } public boolean equals(Object o) { if (!(o instanceof Not)) return false; Not __4 = (Not) o; return eq(p, __4.p); } public int hashCode() { int h = 78515; h = boostHashCombine(h, _hashCode(p)); return h; } public Object[] _fieldsToList() { return new Object[] {p}; } } // v2 patterns static class Weighted extends MMOPattern implements IFieldsToList{ static final String _fieldOrder = "weight p"; double weight; MMOPattern p; Weighted() {} Weighted(double weight, MMOPattern p) { this.p = p; this.weight = weight;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + weight + ", " + p + ")"; } public boolean equals(Object o) { if (!(o instanceof Weighted)) return false; Weighted __5 = (Weighted) o; return weight == __5.weight && eq(p, __5.p); } public int hashCode() { int h = -446368457; h = boostHashCombine(h, _hashCode(weight)); h = boostHashCombine(h, _hashCode(p)); return h; } public Object[] _fieldsToList() { return new Object[] {weight, p}; } } static class StartOfLine extends MMOPattern implements IFieldsToList{ MMOPattern p; StartOfLine() {} StartOfLine(MMOPattern p) { this.p = p;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + p + ")"; } public boolean equals(Object o) { if (!(o instanceof StartOfLine)) return false; StartOfLine __6 = (StartOfLine) o; return eq(p, __6.p); } public int hashCode() { int h = -326863539; h = boostHashCombine(h, _hashCode(p)); return h; } public Object[] _fieldsToList() { return new Object[] {p}; } } static class EndOfLine extends MMOPattern implements IFieldsToList{ MMOPattern p; EndOfLine() {} EndOfLine(MMOPattern p) { this.p = p;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + p + ")"; } public boolean equals(Object o) { if (!(o instanceof EndOfLine)) return false; EndOfLine __7 = (EndOfLine) o; return eq(p, __7.p); } public int hashCode() { int h = -810372346; h = boostHashCombine(h, _hashCode(p)); return h; } public Object[] _fieldsToList() { return new Object[] {p}; } } } static class HTMLPaginator { String startParam = "start"; String baseLink; int start, step = 50; int max; // number of items in list void processParams(Map params) { start = parseInt(mapGet(params, startParam)); } String renderNav(Object... __) { return pageNav2(baseLink, max, start, step, startParam, __); } IntRange visibleRange() { return intRange(start, min(max, start+step)); } } static class StringTree2 { A leafValue; HashMap> children = new HashMap(); A get() { return leafValue; } StringTree2 get(String s) { return children.get(s); } StringTree2 getOrAdd(String s) { StringTree2 t = children.get(s); if (t == null) children.put(s, t = new StringTree2()); return t; } } static interface ISetter { void set(A a); } static interface IConceptCounter { Class conceptClass(); int countConcepts(); Collection allConcepts(); } // it's unclear whether the end is inclusive or exclusive // (usually exclusive I guess) static class IntRange { int start, end; IntRange() {} IntRange(int start, int end) { this.end = end; this.start = start;} IntRange(IntRange r) { start = r.start; end = r.end; } public boolean equals(Object o) { return stdEq2(this, o); } public int hashCode() { return stdHash2(this); } final int length() { return end-start; } final boolean empty() { return start >= end; } final boolean isEmpty() { return start >= end; } static String _fieldOrder = "start end"; public String toString() { return "[" + start + ";" + end + "]"; } } abstract static class HAbstractRenderable { String baseLink = ""; MakeFrame makeFrame = (title, contents) -> h1_title(title) + contents; static interface MakeFrame { String makeFrame(String title, String contents); } HAbstractRenderable() {} HAbstractRenderable(String baseLink) { this.baseLink = baseLink;} HAbstractRenderable makeFrame(MakeFrame makeFrame) { this.makeFrame = makeFrame; return this; } String baseLinkPlus(String uri) { return nempty(uri) ? appendSlash(baseLink) + uri : baseLink; } // title is in HTML String frame(String title, String contents) { return makeFrame.makeFrame(title, contents); } String refreshWithMsgs(String... msgs) { return refreshWithMsgs(asList(msgs)); } String refreshWithMsgs(List msgs, Object... __) { String anchor = (String) (optPar("anchor", __)); Map params = (Map) (optPar("params", __)); return hrefresh(addAnchorToURL(appendQueryToURL(baseLink, mapPlus(params, "msg" , htmlEncode_nlToBr(lines_rtrim(msgs)))), anchor)); } String renderMsgs(Map params) { return pUnlessEmpty(params.get("msg")); } } /* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ // modified by Stefan Reich // Implements the Set interface more compactly than // java.util.HashSet by using a closed hashtable. // Note: equals is always called on the _stored_ object, not the one // passed as an argument to find(), contains() etc. // (In case you want to put special magic in your equals() function) static class UnsynchronizedCompactHashSet extends java.util.AbstractSet { protected final static int INITIAL_SIZE = 3; public final static double LOAD_FACTOR = 0.75; protected final static Object nullObject = new Object(); protected final static Object deletedObject = new Object(); protected int elements; protected int freecells; protected A[] objects; protected int modCount; UnsynchronizedCompactHashSet() { this(INITIAL_SIZE); } UnsynchronizedCompactHashSet(int size) { // NOTE: If array size is 0, we get a // "java.lang.ArithmeticException: / by zero" in add(Object). objects = (A[]) new Object[(size==0 ? 1 : size)]; elements = 0; freecells = objects.length; modCount = 0; } UnsynchronizedCompactHashSet(Collection c) { this(c.size()); addAll(c); } @Override public Iterator iterator() { return new CompactHashIterator(); } @Override public int size() { return elements; } @Override public boolean isEmpty() { return elements == 0; } @Override public boolean contains(Object o) { return find(o) != null; } A find(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } return objects[index]; } boolean removeIfSame(Object o) { A value = find(o); if (value == o) { remove(value); return true; } return false; } @Override public boolean add(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; int deletedix = -1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { // if there's a deleted object here we can put this object here, // provided it's not in here somewhere else already if (objects[index] == deletedObject) deletedix = index; index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } if (objects[index] == null) { // wasn't present already if (deletedix != -1) // reusing a deleted cell index = deletedix; else freecells--; modCount++; elements++; // here we face a problem regarding generics: // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' // so adding an empty object is a problem here // If (! o instanceof A) : This will cause a class cast exception // If (o instanceof A) : This will work fine objects[index] = (A) o; // do we need to rehash? if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) rehash(); return true; } else // was there already return false; } @Override public boolean remove(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } // we found the right position, now do the removal if (objects[index] != null) { // we found the object // same problem here as with add objects[index] = (A) deletedObject; modCount++; elements--; return true; } else // we did not find the object return false; } @Override public void clear() { elements = 0; for (int ix = 0; ix < objects.length; ix++) objects[ix] = null; freecells = objects.length; modCount++; } @Override public Object[] toArray() { Object[] result = new Object[elements]; Object[] objects = this.objects; int pos = 0; for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) { if (objects[i] == nullObject) result[pos++] = null; else result[pos++] = objects[i]; } // unchecked because it should only contain A return result; } // not sure if this needs to have generics @Override public T[] toArray(T[] a) { int size = elements; if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); A[] objects = this.objects; int pos = 0; for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) { if (objects[i] == nullObject) a[pos++] = null; else a[pos++] = (T) objects[i]; } return a; } protected void rehash() { int garbagecells = objects.length - (elements + freecells); if (garbagecells / (double) objects.length > 0.05) // rehash with same size rehash(objects.length); else // rehash with increased capacity rehash(objects.length*2 + 1); } protected void rehash(int newCapacity) { int oldCapacity = objects.length; @SuppressWarnings("unchecked") A[] newObjects = (A[]) new Object[newCapacity]; for (int ix = 0; ix < oldCapacity; ix++) { Object o = objects[ix]; if (o == null || o == deletedObject) continue; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % newCapacity; int offset = 1; // search for the object while(newObjects[index] != null) { // no need to test for duplicates index = ((index + offset) & 0x7FFFFFFF) % newCapacity; offset = offset*2 + 1; if (offset == -1) offset = 2; } newObjects[index] = (A) o; } objects = newObjects; freecells = objects.length - elements; } private class CompactHashIterator implements Iterator { private int index; private int lastReturned = -1; private int expectedModCount; @SuppressWarnings("empty-statement") public CompactHashIterator() { for (index = 0; index < objects.length && (objects[index] == null || objects[index] == deletedObject); index++) ; expectedModCount = modCount; } @Override public boolean hasNext() { return index < objects.length; } @SuppressWarnings("empty-statement") @Override public T next() { /*if (modCount != expectedModCount) throw new ConcurrentModificationException();*/ int length = objects.length; if (index >= length) { lastReturned = -2; throw new NoSuchElementException(); } lastReturned = index; for (index += 1; index < length && (objects[index] == null || objects[index] == deletedObject); index++) ; if (objects[lastReturned] == nullObject) return null; else return (T) objects[lastReturned]; } @Override public void remove() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (lastReturned == -1 || lastReturned == -2) throw new IllegalStateException(); // delete object if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) { objects[lastReturned] = (A) deletedObject; elements--; modCount++; expectedModCount = modCount; // this is expected; we made the change } } } int capacity() { return objects.length; } // returns true if there was a shrink boolean shrinkToFactor(double factor) { if (factor > LOAD_FACTOR) throw fail("Shrink factor must be equal to or smaller than load factor: " + factor + " / " + LOAD_FACTOR); int newCapacity = max(INITIAL_SIZE, iround(size()/factor)); if (newCapacity >= capacity()) return false; rehash(newCapacity); return true; } } static class RemoteDB implements AutoCloseable { DialogIO db; String name; // s = bot name or snippet ID RemoteDB(String s) { this(s, false); } RemoteDB(String s, boolean autoStart) { name = s; if (isSnippetID(s)) name = dbBotName(s); db = findBot(name); if (db == null) if (autoStart) { nohupJavax(fsI(s)); waitForBotStartUp(name); assertNotNull("Weird problem", db = findBot(s)); } else throw fail("DB " + s + " not running"); } boolean functional() { return db != null; } // now always true List list() { return adopt((List) rpc(db, "xlist")); } List list(String className) { return adopt((List) rpc(db, "xlist", className)); } List xlist() { return list(); } List xlist(String className) { return list(className); } // adopt is an internal method List adopt(List l) { if (l != null) for (RC rc : l) adopt(rc); return l; } RC adopt(RC rc) { if (rc != null) rc.db = this; return rc; } Object adopt(Object o) { if (o instanceof RC) return adopt((RC) o); return o; } String xclass(RC o) { return (String) rpc(db, "xclass", o); } Object xget(RC o, String field) { return adopt(rpc(db, "xget", o, field)); } String xS(RC o, String field) { return (String) xget(o, field); } RC xgetref(RC o, String field) { return adopt((RC) xget(o, field)); } void xset(RC o, String field, Object value) { rpc(db, "xset", o, field, value); } RC uniq(String className) { RC ref = first(list(className)); if (ref == null) ref = xnew(className); return ref; } RC xuniq(String className) { return uniq(className); } RC xnew(String className, Object... values) { return adopt((RC) rpc(db, "xnew", className, values)); } void xdelete(RC o) { rpc(db, "xdelete", o); } void xdelete(List l) { rpc(db, "xdelete", l); } public void close() { _close(db); } String fullgrab() { return (String) rpc(db, "xfullgrab"); } String xfullgrab() { return fullgrab(); } void xshutdown() { rpc(db, "xshutdown"); } long xchangeCount() { return (long) rpc(db, "xchangeCount"); } int xcount() { return (int) rpc(db, "xcount"); } void reconnect() { close(); db = findBot(name); } RC rc(long id) { return new RC(this, id); } } /** this class is fully thread-safe */ static class Flag implements Runnable { private boolean up = false; /** returns true if flag was down before (i.e. flag was actually raised right now) */ public synchronized boolean raise() { if (!up) { up = true; notifyAll(); return true; } else return false; } public synchronized void waitUntilUp() { while (!up) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public boolean waitUntilUp(double timeout) { return waitUntilUp(toMS(timeout)); } public synchronized boolean waitUntilUp(long timeout) { if (!up) { try { wait(timeout); } catch (InterruptedException e) { e.printStackTrace(); } } return isUp(); } public synchronized boolean isUp() { return up; } boolean get() { return isUp(); } public String toString() { return isUp() ? "up" : "down"; } // currently does a semi-active wait with latency = 50 ms public void waitForThisOr(Flag otherFlag) { try { while (!isUp() && !otherFlag.isUp()) Thread.sleep(50); } catch (Exception __e) { throw rethrow(__e); } } public void run() { raise(); } } abstract static class VStackComputableWithStep implements VStack.Computable { int step; // you can implement either of these public void step(VStack stack, Object subComputationResult) { step(stack); } void step(VStack stack) {} } static class DefaultValueConverterForField implements ValueConverterForField { public OrError convertValue(Object object, Field field, Object value) { Class actualType = field.getType(); Class type = primitiveToBoxedTypeOpt(actualType); // nothing to do if (isInstance(type, value)) return new OrError(value); // int magic if (type == Integer.class && !(value instanceof Integer)) return new OrError(toInt(trimIfString(value))); // double magic if (type == Double.class && !(value instanceof Double)) return new OrError(toDouble(trimIfString(value))); // bool magic if (type == Boolean.class && value instanceof String) if (actualType == Boolean.class && eq(value, "")) return new OrError(null); else return new OrError(eqicOneOf((String) value, "1", "true", "yes")); // GlobalID magic return new OrError(value); } } static interface IFieldsToList { Object[] _fieldsToList(); } static class ForEach_vstack extends VStackComputableWithStep implements IFieldsToList{ Iterable l; IVF1 body; ForEach_vstack() {} ForEach_vstack(Iterable l, IVF1 body) { this.body = body; this.l = l;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + l + ", " + body + ")"; }public Object[] _fieldsToList() { return new Object[] {l, body}; } Iterator it; void step(VStack stack) { if (step == 0) { if (l == null) { stack._return(); return; } it = iterator(l); ++step; } if (!it.hasNext()) { stack._return(); return; } body.get(it.next()); } } static class Scored extends Var { float score; Scored() {} Scored(A a, float score) { super(a); this.score = score; } Scored(A a, double score) { super(a); this.score = (float) score; } float score() { return score; } public String toString() { return toIntPercent(score) + "%: " + str(get()); } } static interface IVar extends IF0 { void set(A a); A get(); default boolean has() { return get() != null; } default void clear() { set(null); } } abstract static class DynPrintLogAndEnabled extends DynPrintLog { public JComponent visualize() { return dm_visualizeWithEnabled(super.visualize()); } volatile boolean enabled = true; transient JPanel buttons; transient SingleComponentPanel scpStretcherControl; JComponent visualizeWithoutEnabled() { return super.visualize(); } JComponent dm_visualizeWithEnabled(JComponent main) { return centerAndSouthWithMargins(main, wrapControlArea()); } JComponent wrapControlArea() { return makeControlArea(); } JPanel makeControlArea() { if (scpStretcherControl == null) scpStretcherControl = singleComponentPanel(); buttons = jrightalignedline(makeEnabledCheckBox()); return centerAndEastWithMargin(scpStretcherControl, buttons); } static JCheckBox makeEnabledCheckBox() { return dm_fieldCheckBox("enabled"); } void setEnabled(boolean b) { setField("enabled" , b); } final void addControls(Component... components) { addToControlArea(components); } void addToControlArea(Component... components) { addComponentsFirst(buttons, components); } final void addControl(Component component) { addButton(component); } void addButton(Component component) { addComponentsFirst(buttons, component); } void setMainControl(JComponent control) { scpSet(scpStretcherControl, control); } } static interface Htmlable { default String headStuff() { return ""; } String html(); } static class Not implements IFieldsToList, Transformable, Visitable{ A a; Not() {} Not(A a) { this.a = a;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + a + ")"; } public boolean equals(Object o) { if (!(o instanceof Not)) return false; Not __0 = (Not) o; return eq(a, __0.a); } public int hashCode() { int h = 78515; h = boostHashCombine(h, _hashCode(a)); return h; } public Object[] _fieldsToList() { return new Object[] {a}; } public Object transformUsing(IF1 f) { return new Not((A) f.get(a)); } public void visitUsing(IVF1 f) { f.get(a); } Boolean get(Object o) { return not((Boolean) callF(a, o)); } } abstract static class DynPrintLog extends DynModule { transient JFastLogView_noWrap printLogView; transient Lock updatePrintLog_lock = lock(); transient StringBuffer actualPrintLog; transient int printLogUpdateInterval = 500; transient JComponent printLogPanel; public JComponent visualize() { return makePrintLogPanel(); } JComponent makePrintLogPanel() { JComponent section = printLogPanel = jSection("Log", jscroll_copyBackground(printLogView = jFastLogView_noWrap(getPrintLog()))); componentPopupMenuItem(section, "Clear", new Runnable() { public void run() { try { AutoCloseable __3 = enter(); try { dm_clearModulePrintLog(); } finally { _close(__3); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); dm_clearModulePrintLog();"; }}); return awtEvery(section, printLogUpdateInterval, new Runnable() { public void run() { try { updatePrintLog(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "updatePrintLog();"; }}); } void updatePrintLog() { swing(new Runnable() { public void run() { try { // TODO: optimize Lock __0 = updatePrintLog_lock; lock(__0); try { JFastLogView_noWrap _view = printLogView; if (_view == null) return; if (_view.setText(getPrintLog())) { // Still trying to reliably scroll down scrollAllTheWayDown(_view); awtLater(100, new Runnable() { public void run() { try { scrollAllTheWayDown(_view) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "scrollAllTheWayDown(_view)"; }}); } } finally { unlock(__0); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "// TODO: optimize\r\n Lock __0 = updatePrintLog_lock; lock(__0); try {\r\n ..."; }}); } JFastLogView_noWrap myPrintLogComponent() { return printLogView; } void enableWordWrap() { // TODO } String getPrintLog() { if (actualPrintLog == null) { if (printToModule()) actualPrintLog = _printLog; else actualPrintLog = liveLocalPrintLog_realMC(this); } return str(actualPrintLog); } } // uses hash sets as inner sets unless subclassed // uses a hash map as the outer map by default static class MultiSetMap { Map> data = new HashMap>(); int size; // number of values MultiSetMap() {} MultiSetMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); } MultiSetMap(MultiSetMap map) { putAll(map); } MultiSetMap(Map> data) { this.data = data;} boolean put(A key, B value) { synchronized(data) { Set set = data.get(key); if (set == null) data.put(key, set = _makeEmptySet()); if (!set.add(value)) return false; { ++size; return true; } }} boolean add(A key, B value) { return put(key, value); } void addAll(A key, Collection values) { synchronized(data) { putAll(key, values); }} void addAllIfNotThere(A key, Collection values) { synchronized(data) { for (B value : values) setPut(key, value); }} void setPut(A key, B value) { synchronized(data) { if (!containsPair(key, value)) put(key, value); }} final boolean contains(A key, B value) { return containsPair(key, value); } boolean containsPair(A key, B value) { synchronized(data) { return get(key).contains(value); }} void putAll(A key, Collection values) { synchronized(data) { for (B value : values) put(key, value); }} void removeAll(A key, Collection values) { synchronized(data) { for (B value : values) remove(key, value); }} Set get(A key) { synchronized(data) { Set set = data.get(key); return set == null ? Collections. emptySet() : set; }} // return null if empty Set getOpt(A key) { synchronized(data) { return data.get(key); }} // returns actual mutable live set // creates the set if not there Set getActual(A key) { synchronized(data) { Set set = data.get(key); if (set == null) data.put(key, set = _makeEmptySet()); return set; }} // TODO: this looks unnecessary void clean(A key) { synchronized(data) { Set list = data.get(key); if (list != null && list.isEmpty()) data.remove(key); }} Set keySet() { synchronized(data) { return data.keySet(); }} Set keys() { synchronized(data) { return data.keySet(); }} void remove(A key) { synchronized(data) { size -= l(data.get(key)); data.remove(key); }} void remove(A key, B value) { synchronized(data) { Set set = data.get(key); if (set != null) { if (set.remove(value)) { --size; if (set.isEmpty()) data.remove(key); } } }} void clear() { synchronized(data) { data.clear(); size = 0; }} boolean containsKey(A key) { synchronized(data) { return data.containsKey(key); }} B getFirst(A key) { synchronized(data) { return first(get(key)); }} void addAll(MultiSetMap map) { putAll(map); } void putAll(MultiSetMap map) { synchronized(data) { for (A key : map.keySet()) putAll(key, map.get(key)); }} void putAll(Map map) { synchronized(data) { if (map != null) for (Map.Entry e : map.entrySet()) put(e.getKey(), e.getValue()); }} int keysSize() { synchronized(data) { return l(data); }} // full size int size() { synchronized(data) { return size; }} // count values for key int getSize(A key) { return l(data.get(key)); } int count(A key) { return getSize(key); } // expensive operation Set reverseGet(B b) { synchronized(data) { Set l = new HashSet(); for (A key : data.keySet()) if (data.get(key).contains(b)) l.add(key); return l; }} // expensive operation A keyForValue(B b) { synchronized(data) { for (A key : data.keySet()) if (data.get(key).contains(b)) return key; return null; }} Map> asMap() { synchronized(data) { return cloneMap(data); }} boolean isEmpty() { synchronized(data) { return data.isEmpty(); }} // override in subclasses Set _makeEmptySet() { return new HashSet(); } Collection> allLists() { synchronized(data) { return new HashSet(data.values()); } } List allValues() { return concatLists(values(data)); } List> allEntries() { synchronized(data) { List> l = emptyList(size); for (Map.Entry> __0 : _entrySet( data)) { A a = __0.getKey(); Set set = __0.getValue(); for (B b : set) l.add(pair(a, b)); } return l; }} Object mutex() { return data; } public String toString() { return "mm" + str(data); } Pair firstEntry() { synchronized(data) { if (empty(data)) return null; Map.Entry> entry = data.entrySet().iterator().next(); return pair(entry.getKey(), first(entry.getValue())); }} A firstKey() { synchronized(data) { return main.firstKey(data); }} A lastKey() { synchronized(data) { return (A) ((NavigableMap) data).lastKey(); }} A higherKey(Object a) { synchronized(data) { return (A) ((NavigableMap) data).higherKey(a); }} } static abstract class DialogIO implements AutoCloseable { String line; boolean eos, loud, noClose; Lock lock = lock(); abstract String readLineImpl(); abstract boolean isStillConnected(); abstract void sendLine(String line); abstract boolean isLocalConnection(); abstract Socket getSocket(); int getPort() { Socket s = getSocket(); return s == null ? 0 : s.getPort(); } boolean helloRead = false; int shortenOutputTo = 500; String readLineNoBlock() { String l = line; line = null; return l; } boolean waitForLine() { try { ping(); if (line != null) return true; //print("Readline"); line = readLineImpl(); //print("Readline done: " + line); if (line == null) eos = true; return line != null; } catch (Exception __e) { throw rethrow(__e); } } String readLine() { waitForLine(); helloRead = true; return readLineNoBlock(); } String ask(String s, Object... args) { if (loud) return askLoudly(s, 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("> " + shorten(s, shortenOutputTo)); sendLine(s); String answer = readLine(); print("< " + shorten(answer, shortenOutputTo)); return answer; } void pushback(String l) { if (line != null) throw fail(); line = l; helloRead = false; } } static abstract class DialogHandler { abstract void run(DialogIO io); } static class And implements IFieldsToList, Transformable, Visitable{ A a; A b; And() {} And(A a, A b) { this.b = b; this.a = a;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + a + ", " + b + ")"; } public boolean equals(Object o) { if (!(o instanceof And)) return false; And __0 = (And) o; return eq(a, __0.a) && eq(b, __0.b); } public int hashCode() { int h = 65975; h = boostHashCombine(h, _hashCode(a)); h = boostHashCombine(h, _hashCode(b)); return h; } public Object[] _fieldsToList() { return new Object[] {a, b}; } public Object transformUsing(IF1 f) { return new And((A) f.get(a), (A) f.get(b)); } public void visitUsing(IVF1 f) { f.get(a); f.get(b); } } static interface Steppable { public boolean step(); // return false if done } static class JFastLogView_noWrap extends JComponent implements Scrollable { List lines = syncList(); boolean endsWithNewLine, verbose; public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 20; } public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return (direction == SwingConstants.HORIZONTAL ? visibleRect.width : visibleRect.height)*5/6; } public boolean getScrollableTracksViewportWidth() { return false; } public boolean getScrollableTracksViewportHeight() { return false; } public void paint(Graphics g) { //if (verbose) _print("JFastLogView_noWrap painting"); int w = getWidth(), h = getHeight(); g.setColor(getBackground()); g.fillRect(0, 0, w, h); g.setColor(getForeground()); FontMetrics fm = componentFontMetrics(this); int fh = fm.getHeight(); Rectangle clip = g.getClipBounds(); int start, end; if (clip == null) { start = 0; end = l(lines); } else { start = max(0, clip.y/fh); end = min(l(lines), idiv_ceil(clip.y+clip.height, fh)); } //printVars(+start, +end); int y = fm.getAscent()+start*fh; for (int i = start; i < end; i++) { String s = get(lines, i); if (s != null) g.drawString(s, 0, y); y += fh; } //if (verbose) _print("JFastLogView_noWrap painted"); } public Dimension getPreferredSize() { FontMetrics fm = componentFontMetrics(this); if (fm == null) return new Dimension(50, 50); // not renderable yet int fh = fm.getHeight(); int w = 0; for (int i = 0; i < l(lines); i++) { String s = get(lines, i); w = max(w, fm.stringWidth(unnull(s))); // TODO: just count chars } return new Dimension(w, fh*l(lines)); } JFastLogView_noWrap() {} JFastLogView_noWrap(String text) { setText(text); } { componentPopupMenuItem(this, "Copy full text", new Runnable() { public void run() { try { copyFullText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyFullText();"; }}); } // API // returns true if changed boolean setLines(Collection lines) { List newList = asSyncList(lines); if (eq(this.lines, newList)) return false; this.lines = newList; _revalidate(this); return true; } // returns true if changed boolean setText(String text) { endsWithNewLine = endsWithNewLine(text); return setLines(lines(text)); } void append(String text) { if (nempty(text)) setText(getText() + text); } String getText() { return lines_rtrimIf(!endsWithNewLine, cloneList(lines)); } void copyFullText() { copyTextToClipboard(getText()); } } static List myInnerClasses_list=litlist( null, "Meta", "RandomAvatar", "DefunctClassLoader", "ConceptsRefChecker", "IFieldIndex", "talkToThisVM_IO", "Str", "BotRandomAction", "SimpleLiveValue", "ConceptFieldIndexDesc", "SingleComponentPanel", "DynModule", "OnNewDialog", "IWebRequest", "Rect", "Pt", "AbstractLayoutManager", "IterableIterator", "betterCIComparator_C", "RemoteDB", "MakesBufferedImage", "Concepts", "RC", "Hasher", "PostReferenceable", "CloseableIterableIterator", "ExpiringMap2", "getOpt_Map", "IFieldsToList", "IInputHandler", "ForEach_vstack", "CustomBotStep", "structure_Data", "jsonDecode_Y", "IVar", "DynPrintLogAndEnabled", "Matches", "HCRUD_Concepts", "ParameterizedTypeImpl", "Dyn_FieldWatcher", "Timestamp", "Q", "ConceptFieldIndexBase", "OrError", "LongRange", "EvtCallSubroutine", "SnippetTitles", "RestartableCountdown", "ReliableSingleThread_Multi", "VStack", "MultiSet", "BotStep", "T3", "IResourceHolder", "Htmlable", "HCRUD_Data", "StringTree2", "IConceptCounter", "IntRange", "ActiveSequence", "UnsynchronizedCompactHashSet", "BetterLabel", "componentPopupMenu_Adapter", "InternationalPhoneValidator", "UndoState", "ScheduledAction", "SmartTimerTask", "JSection", "Visitable", "ValueConverterForField", "FullChange", "HyperCompactTreeSet", "ConceptDelete", "BotSwitchLanguage", "ReliableSingleThread", "Eval", "DeliveredDomain", "ProgramScan", "ConceptFieldIndex", "BotMessage", "BotClearStack", "F0", "BotIdleMode", "F1", "Snippet", "F2", "RandomAccessAbstractList", "RGB", "unstructure_Receiver", "IF0", "Not", "BaseXRef", "AbstractAvatar", "DefaultValueConverterForField", "IF2", "ConceptFieldIndexCI", "IF1", "ShowComboBoxForm", "IVF1", "IVF2", "Domain", "Cache", "_MethodCache", "structure_ClassInfo", "Form", "BotOutgoingQuestion", "LiveValue", "BotSaveFeedback", "QuickException", "BotNewDialog", "Language", "DialogHandler", "JavaScript", "_PrintIndent", "Steppable", "proxy_InvocationHandler", "MMOPattern", "UserPost", "MetaTransformer", "ExecutedStep", "Pair", "HTMLFramer1", "BotPause", "Avatar", "UploadedFile", "ConversationFeedback", "IConceptIndex", "ConceptCreate", "WebPushSubscription", "MultiSetMap", "BotSendTyping", "Var", "Scorer", "ITokCondition", "SecretValue", "CountingOutputStream", "HTMLAceEditor", "CancelToCancelPoint", "AppendableChain", "ConceptChange", "CompactLinkedHashSet", "FixedRateTimer", "Settings", "Fail", "ISetter", "OnUserMessage", "Out", "ScannedBot", "XRef", "CountryDialCode", "JS", "Conversation", "MultiMap", "CriticalAction", "Lead", "IResourceLoader", "WeakHasherMap", "Worker", "ScheduledActionOnConversation", "Either", "BotSaveLead", "ScoredSearcher", "Msg", "JMenuScroller", "DialogIO", "IAutoCloseableF0", "MRUCache", "AbstractBotImage", "AlphanumComparator", "PersistableThrowable", "talkToSubBot_IO", "Scored", "WidthAndHeight", "HasIndex", "Responder", "VF1", "VF2", "IMultiSet", "JavaXHyperlinker", "ConceptsChange", "HTML", "TokCondition", "BotGoBack", "RightAlignedLine", "HCRUD", "findCodeTokens_Matcher", "EvtJumpTo", "UploadedImage", "JFastLogView_noWrap", "UserKeyword", "Concept", "IRef", "HAbstractRenderable", "Sequence", "Flag", "generalizedCIComparator_C", "ContentsIndexedList", "BotEndConversation", "VStackComputableWithStep", "IContentsIndexedList2", "Producer", "DynNewBot2", "EvtMatchedUserKeyword", "And", "DynGazelleRocks", "talkTo_IO", "Evt", "User", "FileBasedLock", "Symbol", "IMeta", "BetterThread", "IContentsIndexedList", "LetterLayout", "AuthedDialogID", "InputHandled", "Lowest", "Action_NextStep", "HTMLPaginator", "UploadedSound", "DynPrintLog", "MinimalChain", "Transformable", "CannedAnswer", "BotDoNothing", "jLiveValueSection_class", "CancelPoint", "UserCreatedObject", "Android3", "BotImage"); static List myInnerClasses() { return myInnerClasses_list; } static void cancelTimerOrInterruptThread(Object timer) { if (timer instanceof Thread && !(timer instanceof AutoCloseable)) // AutoCloseable takes precedence interruptThread((Thread) timer); else cancelTimer(timer); } static boolean bareDBMode_on = false; static void bareDBMode() { bareDBMode(null); // default autoSaveInterval } static void bareDBMode(Integer autoSaveInterval) { bareDBMode_on = true; conceptsAndBot(autoSaveInterval); } static Object metaMapGet(IMeta o, Object key) { if (o == null || key == null) return null; Object meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta).get(key); return null; } static StringTree2 stringTree2_javaTok(Map entries) { StringTree2 tree = new StringTree2(); for (String key : keys(entries)) addToStringTree(tree, javaTokC(key), entries.get(key)); return tree; } static void putMultipleKeys(Map map, Collection keys, B value) { if (map == null || keys == null) return; for (A key : keys) map.put(key, value); } static List allPlus(final int x, List l) { return map(l, new F1() { public Integer get(Integer i) { try { return i+x; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "i+x"; }}); } static List jfindAll(List tok, String pat) { return jfindAll(tok, pat, null); } static List jfindAll(List tok, String pat, ITokCondition condition) { return jfindAll(tok, jfind_preprocess(javaTok(pat)), condition); } // tokPat must be jfind_preprocess'd static List jfindAll(List tok, List tokPat) { return jfindAll(tok, tokPat, null); } static List jfindAll(List tok, List tokPat, ITokCondition condition) { TokCondition cond = toTokCondition(condition); String[] toks = toStringArray(codeTokensOnly(tokPat)); int i = -1; List l = new ArrayList(); while ((i = findCodeTokens(tok, i+1, false, toks, cond)) >= 0) l.add(i); return l; } static List tok_subListWithoutBorderNTokens(List tok, int i, int j) { List tok2 = cloneSubList(tok, i & (~1), j | 1); tok2.set(0, ""); tok2.set(l(tok2)-1, ""); return tok2; } static String tok_expandIfQuoted(String s) { return applyTranspilationFunction(__109 -> tok_expandIfQuoted(__109), s); } static void tok_expandIfQuoted(List tok) { jreplace(tok, "if || ", "if (matchOneOf(s, m, $2, $5))"); // "bla * bla | blubb * blubb" jreplace_dyn(tok, "if ", new F2, Integer, String>() { public String get(List tok, Integer cIdx) { try { String s = unquote(tok.get(cIdx+2)); //print("multimatch: " + quote(s)); List l = new ArrayList(); for (String pat : splitAtJavaToken(s, "|")) { //print("multimatch part: " + quote(pat)); if (pat.contains("...")) l.add("matchX(" + quote(trim(pat)) + ", s, m)"); else if (javaTok(pat).contains("*")) l.add("match(" + quote(trim(pat)) + ", s, m)"); else l.add("match(" + quote(trim(pat)) + ", s)"); } return "if (" + join(" || ", l) + ")"; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "S s = unquote(tok.get(cIdx+2));\r\n //print(\"multimatch: \" + quote(s));\r\n ..."; }}, new TokCondition() { public boolean get(final List tok, final int i) { return javaTokC(unquote(tok.get(i+3))).contains("|"); }}); tok_transpileIfQuoted_dollarVars(tok); // "...bla..." jreplace(tok, "if ", "if (find3plusRestsX($2, s, m))", new TokCondition() { public boolean get(final List tok, final int i) { return startsAndEndsWith(unquote(tok.get(i+3)), "..."); }}); // "bla..." jreplace(tok, "if ", "if (matchStartX($2, s, m))", new TokCondition() { public boolean get(final List tok, final int i) { return unquote(tok.get(i+3)).endsWith("..."); }}); // "bla" jreplace(tok, "if ", "if (match($2, s))", new TokCondition() { public boolean get(final List tok, final int i) { return !javaTokC(unquote(tok.get(i+3))).contains("*"); }}); // "bla * bla" jreplace(tok, "if ", "if (match($2, s, m))"); jreplace(tok, "if match ", "if (match($3, s, m))"); } static void doublePut(Map map, A key1, A key2, B value) { map.put(key1, value); map.put(key2, value); } static boolean isSingleQuoteIdentifier(String s) { if (l(s) < 2 || s.charAt(0) != '\'' || !Character.isJavaIdentifierStart(s.charAt(1))) return false; for (int i = 2; i < l(s); i++) if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; return true; } static String fromSingleQuoteIdentifier(String s) { return isSingleQuoteIdentifier(s) ? substring(s, 1) : s; } static boolean tok_tokenBeforeLonelyReturnValue(List tok, int i) { String t = get(tok, i); if (l(t) == 1 && "{};".contains(t) || eq(t, "else")) return true; if (!eq(t, ")")) return false; int level = 0; while (i > 0) { if (eq(tok.get(i), ")")) ++level; if (eq(tok.get(i), "(")) --level; if (level == 0) return eq(get(tok, i-2), "if"); i -= 2; } return false; } static boolean eqGet(List l, int i, Object o) { return eq(get(l, i), o); } static boolean eqGet(Map map, A key, Object o) { return eq(mapGet(map, key), o); } static Pair stringTreeLeafValue2(StringTree2 tree, List items) { int i = 0; while (tree != null && i < l(items)) { if (tree.leafValue != null) return pair(tree.leafValue, i); tree = tree.children.get(items.get(i)); ++i; } return null; } static void mapPutInRange(Map map, int start, int end, A value) { if (map != null && value != null) for (int i = start; i < end; i++) map.put(i, value); } static String neatMechListURL(String listName) { return "http://botcompany.de/mech/raw/list/" + urlencode(listName); } static boolean getSnippetTitles_verbose = false; static Map getSnippetTitles(Collection ids) { try { if (empty(ids)) return new HashMap(); String url = tb_mainServer() + "/tb-int/get-titles.php"; String ids2 = join(" ", parseSnippetIDs(ids)); return trimValues(transformKeys(jsonDecodeMap( doPost(mapPlus(standardCredentialsMap(), "ids" , ids2), url)), new F1() { public Object get(String id) { try { return fsI(id); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "fsI(id)"; }})); } catch (Exception __e) { throw rethrow(__e); } } static String dottedSpan(String contents, String title) { return span(contents, "title", title, "style" , "border-bottom: dotted 1px"); } static ThreadLocal dynamize_linkParams = new ThreadLocal(); static String dynamize_noEncode(String html) { List tok = htmlTok(html); Pattern p = Pattern.compile(regexp_findSnippetIDs()); for (int i = 0; i < l(tok); i += 2) { Matcher m = p.matcher(tok.get(i).replace("&#", "&~~~")); StringBuffer buf = new StringBuffer(); while (m.find()) { String id = m.group(1); String link = m.group(); try { link = ahref(snippetLink(id), link, concatArrays(new Object[] {"title" , getSnippetTitle_cached(id)}, dynamize_linkParams.get())); } catch (Throwable __e) { _handleException(__e); } mreplace(m, buf, link); } m.appendTail(buf); String s = str(buf).replace("&~~~", "&#"); tok.set(i, s); } dynamize_linkParams.set(null); return join(tok); } static String longSnippetLink(String id) { return tb_mainServer() + "/tb/show-snippet.php?id=" + parseSnippetID(id); } static Map stdFunctions_cached_map; // name -> snippet ID static Lock stdFunctions_cached_lock = lock(); static Map stdFunctions_cached() { Lock __0 = stdFunctions_cached_lock; lock(__0); try { if (stdFunctions_cached_map == null) stdFunctions_cached_map = stdFunctions_uncached(); return stdFunctions_cached_map; } finally { unlock(__0); } } static synchronized void stdFunctions_clearCache() { stdFunctions_cached_map = null; } // name -> id static Map standardClassesMap() { String sc = loadSnippet("#1003674"); Map map = new HashMap(); for (String line : tlft_j(sc)) { int idx = line.indexOf('/'); map.put(line.substring(0, idx), line.substring(idx+1)); } return map; } static boolean tok_whitespaceContainsJavaComments(String s) { int l = l(s)-1; for (int j = 0; j < l; j++) if (s.charAt(j) == '/') { char d = s.charAt(j+1); if (d == '*' || d == '/') return true; } return false; } static String shortClassName_dropNumberPrefix(Object o) { return dropNumberPrefix(shortClassName(o)); } static int boostHashCombine(int a, int b) { return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2)); } static int _hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static String hInlineSearchForm(String query, String action) { return hInlineSearchForm("q", query, action); } static String hInlineSearchForm(String queryField, String query, String action) { return hform(hinputfield(queryField, query, "style" , "width: 75px") + " " + hsubmit("Search"), "style" , "display: inline", "action", action); } static String joinWithVBar(Iterable l) { return join(" | ", l); } static String joinWithVBar(String... l) { return joinWithVBar(asList(l)); } static List keysDeprefixNemptyValue(Map map, String prefix) { List l = new ArrayList(); for (String s : keys(map)) if (startsWith(s, prefix) && nempty(map.get(s))) l.add(s.substring(l(prefix))); return l; } static String hhiddenStuff(String secretHTML) { String id = aGlobalID(); String js = "document.getElementById(" + jsQuote(id) + ").innerHTML = " + jsQuote(secretHTML) + "; return false;"; return span(ahref("#", "show", "onclick" , js), "id", id); } static String yesNo_short(boolean b) { return b ? "yes" : "no"; } static String htmlEncode_nlToBr_withIndents(String s) { return nlToBr_withIndents(htmlEncode(s)); } static String nEntries(long n) { return n2(n, "entry", "entries"); } static String nEntries(Collection l) { return nEntries(l(l)); } static String nEntries(Map map) { return nEntries(l(map)); } static > List sortByTransformedMapKey_alphaNum(IF1 transform, Iterable c, B key) { List l = cloneList(c); sort(l, (a, b) -> cmpAlphaNum(transform.get(mapGet(a, key)), transform.get(mapGet(b, key)))); return l; } static List lazyMap(final Object f, final List l) { return lazilyMap(f, l); } static List lazyMap(List l, IF1 f) { return lazilyMap(f, l); } static List lazyMap(IF1 f, List l) { return lazilyMap(f, l); } static Map mapMinusKeys(Map map, A... keys) { return mapMinusKeys(map, asList(keys)); } static Map mapMinusKeys(Map map, Collection keys) { if (empty(keys) || empty(map)) return map; keys = asSet(keys); Map m2 = similarEmptyMap(map); for (Map.Entry __0 : _entrySet( map)) { A key = __0.getKey(); B val = __0.getValue(); if (!keys.contains(key)) m2.put(key, val); } return m2; } static Map mapMinusKeys(Collection keys, Map map) { return mapMinusKeys(map, keys); } static Map mapToMap(IF1> f, Iterable l) { Map map = new HashMap(); for (A o : unnullForIteration(l)) { Pair p = callF(f, o); map.put(p.a, p.b); } return map; } static Map mapToMap(Iterable l, IF1> f) { return mapToMap(f, l); } static Map mapToMap(Map m, IF2> f) { return mapMapToMap(f, m); } static Map mapToMap(IF2> f, Map m) { return mapMapToMap(f, m); } static B mapPut_returnValue(Map map, A key, B value) { mapPut(map, key, value); return value; } static A firstKey(Map map) { return first(keys(map)); } static A firstKey(MultiSetMap map) { return map == null ? null : firstKey(map.data); } static String aname(String anchor, Object contents, Object... params) { return tag("a", contents, concatArrays(new Object[] {"name", anchor}, params)); } static B firstValue(Map map) { return first(values(map)); } static B firstValue(MultiSetMap map) { return map == null ? null : first(firstValue(map.data)); } static B firstValue(MultiMap map) { return map == null ? null : first(firstValue(map.data)); } static String addParamsToURL(String url, Map params) { return appendQueryToURL(url, params); } static String addParamsToURL(String url, Object... data) { return appendQueryToURL(url, data); } static List subListOrFull(List l, IntRange r) { return r == null ? l : subList(l, r.start, r.end); } static String unicode_downOrUpPointingTriangle(boolean down) { return down ? unicode_downPointingTriangle() : unicode_upPointingTriangle(); } static Object[] litobjectarray(Object... l) { return litObjectArray(l); } static String htmldecode_dropTagsAndComments(String html) { return htmldecode(dropTagsAndHTMLComments(html)); } static String htmlTable2_noHtmlEncode(Object data, Object... __) { return htmlTable2(data, paramsPlus(__, "htmlEncode" , false)); } static String hCheckBoxMultiSelect_v2(Object... __) { boolean verbose = boolPar("verbose", __); return hscript(replaceDollarVars("\r\n var lastChecked;\r\n\r\n $(document).ready(function() {\r\n console.log(\"document ready\");\r\n $(\"input[type=checkbox]\").click(function(e) {\r\n var verbose = $verbose;\r\n var cls = this.className;\r\n if (verbose) console.log(\"cls=\" + cls);\r\n if (!cls) return;\r\n \r\n if (!lastChecked || lastChecked.className != cls) {\r\n lastChecked = this;\r\n return;\r\n }\r\n \r\n var $chkboxes = $('input[type=checkbox].' + cls);\r\n \r\n if (e.shiftKey) {\r\n var start = $chkboxes.index(this);\r\n var end = $chkboxes.index(lastChecked);\r\n\r\n // change checkboxes, trigger their change event in case there are handlers installed\r\n $chkboxes.slice(Math.min(start, end), Math.max(start,end)+1).prop('checked', lastChecked.checked)\r\n .each(function(idx, chkbox) {\r\n chkbox.dispatchEvent(new Event('change'));\r\n });\r\n }\r\n \r\n lastChecked = this;\r\n });\r\n });\r\n ", "verbose", verbose)); } // tok should be the output of htmlcoarsetok static List dropAllTags(List tok) { List list = new ArrayList(); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && t.startsWith("<")) { list.set(list.size()-1, list.get(list.size()-1) + tok.get(i+1)); ++i; } else list.add(t); } return list; } // alternatively, call this convenient function static String dropAllTags(String html) { if (!contains(html, '<')) return html; return join(dropAllTags(htmlcoarsetok(html))); } static String hselect(String name, Map map, Object... params) { return hselect(map, paramsPlus_skipFirst(params, "name", name)); } static String hselect(Map map, Object... params) { StringBuilder buf = new StringBuilder(); String selected = null; if (odd(l(params))) { selected = str(first(params)); params = dropFirst(params); } int i = indexOf(params, "allowEmpty"); if (even(i)) { buf.append("\n"); params[i] = params[i+1] = null; } if (nempty(map)) for (Object key : keys(map)) { Object value = map.get(key); String k = str(key); buf.append(tag("option", htmlencode(str(or(value, ""))), "value" , k, "selected" , eq(selected, k) ? "selected" : null)).append("\n"); } return tag("select", buf, params) + "\n"; } static String hcheckbox(String name, boolean checked, Object... params) { return tag("input", "", paramsPlus(params, "type", "checkbox", "name", name, checked ? "checked" : null, "1")); } static String hcheckbox(String name) { return hcheckbox(name, false); } static String hcheckbox(String name, String text) { return hcheckboxWithText(name, text); } static Map putKeysFirst(Map map, Object... keys) { Map m2 = litorderedmap(); Map remaining = cloneMap(map); for (Object key : keys) { if (remaining.containsKey(key)) { m2.put(key, remaining.get(key)); remaining.remove(key); } } m2.putAll(remaining); return m2; } static Map putKeysFirst(List keys, Map map) { return putKeysFirst(map, toObjectArray(keys)); } static String htableRaw_valignTop(List data, Object... params) { return htableRaw2(data, asList(params), ll(), ll("valign", "top")); } static String htextarea(String text, Object... params) { params = html_massageAutofocusParam(params); return hopeningTag("textarea", params) + htmlencode2(text) + ""; } static String htrickcheckboxWithText(String name, String text, boolean checked, Object... params) { String id = randomID(); return hhidden(name, checked ? "1" : "0") + hcheckbox(null, checked, paramsPlus(params, "id", id, "onclick" , "this.previousElementSibling.value=this.checked ? 1 : 0")) + " " + hlabelFor(id, htmlEncode2(text)); } static String htrickcheckboxWithText(String name, String text) { return htrickcheckboxWithText(name, text, false); } static String jquery_submitFormOnCtrlEnter() { return "if ((event.keyCode == 10 || event.keyCode == 13) && event.ctrlKey) $(this).closest('form').submit();"; } static String htextfield(String name, Object... params) { return htextinput(name, params); } static Map subMapStartingWith_dropPrefix(Map map, String prefix) { if (map == null) return null; Map map2 = new HashMap(); for (Map.Entry __0 : _entrySet( map)) { String key = __0.getKey(); T value = __0.getValue(); if (startsWith(key, prefix)) map2.put(substring(key, l(prefix)), value); } return map2; } // creates map similar to map1 (if both are non-empty) static Map joinMaps(Map map1, Map map2) { if (empty(map2)) return map1; if (empty(map1)) return map2; Map map3 = cloneMap(map1); putAll(map3, map2); return map3; } // TODO: returns empty first, but not empty last static List splitAt(String s, String splitter) { if (empty(splitter)) return null; // avoid endless loop List parts = new ArrayList(); int i = 0; if (s != null) while (i < l(s)) { int j = indexOf(s, splitter, i); if (j < 0) j = l(s); parts.add(substring(s, i, j)); i = j+l(splitter); } return parts; } static void listPut(List l, int i, A a, A emptyElement) { listSet(l, i, a, emptyElement); } static void listPut(List l, int i, A a) { listSet(l, i, a); } static String stringIf(boolean b, String s) { return stringIfTrue(b, s); } static String stringIf(String s, boolean b) { return stringIf(b, s); } static List tok_identifiersOnly(String s) { return tok_identifiersInOrder(s); } static String hbuttonOnClick_noSubmit(String text, String onClick, Object... params) { return hfulltag("button", text, paramsPlus(params, "onclick" , onClick, "type" , "button")); } static String firstToUpper(String s) { if (empty(s)) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } static String makeFrame_defaultIcon; static boolean makeFrame_hideConsole = false; static ThreadLocal> makeFrame_post = new ThreadLocal(); static JFrame makeFrame() { return makeFrame((Component) null); } static JFrame makeFrame(Object content) { return makeFrame(programTitle(), content); } static JFrame makeFrame(String title) { return makeFrame(title, null); } static JFrame makeFrame(String title, Object content) { return makeFrame(title, content, true); } static JFrame makeFrame(final String title, final Object content, final boolean showIt) { final VF1 post = optParam(makeFrame_post); return swing(new F0() { public JFrame get() { try { if (getFrame(content) != null) return getFrame(setFrameTitle((Component) content, title)); final JFrame frame = new JFrame(title); if (makeFrame_defaultIcon != null) setFrameIconLater(frame, makeFrame_defaultIcon); _initFrame(frame); Component wrapped = wrap(content); if (wrapped != null) frame.getContentPane().add(wrapped); frame.setBounds(defaultNewFrameBounds()); callF(post, frame); if (showIt) frame.setVisible(true); //callOpt(content, "requestFocus"); //exitOnFrameClose(frame); if (showIt && makeFrame_hideConsole) { hideConsole(); makeFrame_hideConsole = false; } return frame; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (getFrame(content) != null)\r\n ret getFrame(setFrameTitle((Component) ..."; }}); } static String htmlDecode_dropTags(String html) { return htmldecode_dropAllTags(html); } static Object getVarOpt(Object o) { return o instanceof IF0 ? ((IF0) o).get() : o; } static String targetBlankIf(String link, Object contents, boolean targetBlank, Object... params) { return ahref_possiblyTargetBlank(link, contents, targetBlank, params); } static String targetBlankIf(boolean targetBlank, String link, Object contents, Object... params) { return ahref_possiblyTargetBlank(targetBlank, link, contents, params); } static String hselect_list(String name, Collection entries, Object... params) { return hselect_list(entries, paramsPlus_skipFirst(params, "name", name)); } static String hselect_list(Collection entries, Object... params) { StringBuilder buf = new StringBuilder(); String selected = null; if (odd(l(params))) { selected = str(first(params)); params = dropFirst(params); } int i = indexOf(params, "allowEmpty"); if (even(i)) { buf.append("\n"); params[i] = params[i+1] = null; } if (nempty(entries)) for (String k : entries) { String value = k; boolean isSelected = eq(selected, k); buf.append(hfulltag("option", htmlencode(str(or(value, ""))), "value" , k, "selected" , isSelected ? html_valueLessParam() : null)).append("\n"); } return hfulltag("select", buf, params) + "\n"; } static String jsBool(boolean b) { return b ? "true" : "false"; } static String hoption(String text) { return tag("option", htmlEncode2(text)); } static List llNempties(String... a) { ArrayList l = new ArrayList(a.length); if (a != null) for (String x : a) if (nempty(x)) l.add(x); return l; } static String unicode_DEL() { return unicodeFromCodePoint(0x2421); } static String ahrefWithConfirm(String msg, String url, Object contents, Object... params) { return ahref(url, contents, paramsPlus(params, "onClick" , "return confirm(" + jsQuote(msg) + ");")); } static String dropPrefixOrNull(String prefix, String s) { return s != null && s.startsWith(prefix) ? s.substring(l(prefix)) : null; } static String regexpQuote(String s) { return s.length() == 0 ? "" : Pattern.quote(s); } static List regexpGroups(String pat, String s) { return regexpFirstGroups(pat, s); } static MultiSetMap treeMultiSetMap() { return new MultiSetMap(true); } static MultiSetMap treeMultiSetMap(Comparator comparator) { return new MultiSetMap(new TreeMap>(comparator)); } static Comparator reverseOrder() { return new Comparator() { public int compare(Object a, Object b) { return cmp(b, a); } }; } static String find(String pattern, String text) { Matcher matcher = Pattern.compile(pattern).matcher(text); if (matcher.find()) return matcher.group(1); return null; } static A find(Collection c, Object... data) { for (A x : c) if (checkFields(x, data)) return x; return null; } static Flag dm_initErrorHandling_flag = new Flag(); static void dm_initErrorHandling() { raiseFlagAndDo(dm_initErrorHandling_flag, new Runnable() { public void run() { try { _handleException_addHandler(__1 -> dm_initErrorHandling_handler(__1)); assertNull(_onRegisterThread); _onRegisterThread = new VF1() { public void get(Thread t) { try { Object m = dm_current_generic(); if (m == null) printStackTrace("New thread made outside of a module"); else if (m instanceof DynModule) ((DynModule) m).ownTimer(t); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O m = dm_current_generic();\r\n if (m == null) printStackTrace(\"New thread..."; }}; _threadInfo_makers.add(new VF1() { public void get(Map map) { try { mapPut(map, "currentModule", dm_currentModule()) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "mapPut(map, \"currentModule\", dm_currentModule())"; }}); _threadInheritInfo_retrievers.add(new VF1() { public void get(Map map) { try { Object mod = map.get("currentModule"); if (mod instanceof DynModule) dm_current_generic_tl().set(new WeakReference((DynModule) mod)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O mod = map.get(\"currentModule\");\r\n if (mod cast DynModule)\r\n dm_..."; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_handleException_addHandler(lambda1 dm_initErrorHandling_handler);\r\n \r\n ..."; }}); } static void dm_initErrorHandling_handler(Throwable e) { DynModule m = dm_currentModule(); if (m == null) print("Weird: Error outside of module"); else m.setField("_error" , persistableThrowable(e)); } static void setMainDesktopPane(JDesktopPane desktop) { mainDesktopPane_value = desktop; } static A getCreatorOpt(String field) { return (A) getOpt(creator(), field); } static A possiblyInternalFrameTitle(A c, String title) { JInternalFrame f = getInternalFrame(c); if (f != null) internalFrameTitle(f, title); else frameTitle(getFrame(c), title); return c; } static String possiblyInternalFrameTitle(Component c) { JInternalFrame f = getInternalFrame(c); return f != null ? internalFrameTitle(f) : frameTitle(getFrame(c)); } static void ownResource(AutoCloseable c) { _registerAutoCloseable(c); } static List getAndClearList(Collection l) { if (l == null) return emptyList(); synchronized(collectionMutex(l)) { List out = cloneList(l); l.clear(); return out; } } static Map getAndClearMap(Map map) { if (map == null) return emptyMap(); synchronized(collectionMutex(map)) { Map out = cloneMap(map); map.clear(); return out; } } static Object pcallFInRealOrMyMC(final Object realm, final Object f, final Object... args) { try { return callFInRealOrMyMC(realm, f, args); } catch (Throwable __e) { _handleException(__e); } return null; } static A pcallFInRealOrMyMC(Object realm, F0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { return null; } } static B pcallFInRealOrMyMC(Object realm, F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { return null; } } static Rect dm_getBounds(Object module) { return shallowCloneToClass(Rect.class, call(dm_getStem(module), "getFrameRect")); } static Rect dm_getBounds() { return dm_getBounds(dm_current_generic()); } static boolean hasMethod_onTypes(Object o, String method, Class... argTypes) { return findMethod_precise_onTypes(o, method, argTypes) != null; } static void dm_onTopInput_q(final VF1 r) { final DynModule m = dm_current_mandatory(); dm_onTopInput(new VF1() { public void get(final String s) { try { m.q().add(new Runnable() { public void run() { try { callF(r, s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(r, s);"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "switch to m.q();\r\n callF(r, s);"; }}); } static AutoCloseable tempSetBetterThreadLocalIfNecessary_weakRef(x30_pkg.x30_util.BetterThreadLocal tl, A a) { if (tl == null) return null; WeakReference prev = tl.get(); if (eq(getWeakRef(prev), a)) return null; tl.set(weakRef(a)); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static AutoCloseable combineAutoCloseables(final AutoCloseable a, final AutoCloseable b) { return a == null ? b : b == null ? a : new AutoCloseable() { public String toString() { return "pClose(a); pClose(b);"; } public void close() throws Exception { pClose(a); pClose(b); }}; } static AutoCloseable combineAutoCloseables(AutoCloseable a, AutoCloseable b, AutoCloseable c, AutoCloseable... more) { return combineAutoCloseables(concatLists(ll(a, b, c), asList(more))); } static AutoCloseable combineAutoCloseables(Iterable l) { return foldl(new F2() { public AutoCloseable get(AutoCloseable a, AutoCloseable b) { try { return combineAutoCloseables(a,b); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "combineAutoCloseables(a,b)"; }}, null, l); } static boolean addIfNotThere(Collection c, A a) { return setAdd(c, a); } static A onChange(A spinner, Object r) { { swing(new Runnable() { public void run() { try { spinner.addChangeListener(changeListener(r)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "spinner.addChangeListener(changeListener(r));"; }}); } return spinner; } static A onChange(A b, Object r) { { swing(new Runnable() { public void run() { try { b.addItemListener(itemListener(r)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "b.addItemListener(itemListener(r));"; }}); } return b; } static void onChange(JTextComponent tc, Object r) { onUpdate(tc, r); } static A onChange(A slider, final Object r) { { swing(new Runnable() { public void run() { try { slider.addChangeListener(changeListener(r)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "slider.addChangeListener(changeListener(r));"; }}); } return slider; } static JComboBox onChange(Object r, JComboBox cb) { return onChange(cb, r); } static JComboBox onChange(JComboBox cb, final Object r) { if (isEditableComboBox(cb)) onChange(textFieldFromComboBox(cb), r); else onSelectedItem(cb, new VF1() { public void get(String s) { try { callF(r) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(r)"; }}); return cb; } // action = runnable or method name static void onUpdateAndNow(JComponent c, final Object r) { onUpdate(c, r); callF(r); } static void onUpdateAndNow(List l, Object r) { for (JComponent c : l) onUpdate(c, r); callF(r); } // action = runnable or method name static void onUpdate(JComponent c, final Object r) { if (c instanceof JTextComponent) ((JTextComponent) c).getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { call(r); } public void removeUpdate(DocumentEvent e) { call(r); } public void changedUpdate(DocumentEvent e) { call(r); } }); else if (c instanceof ItemSelectable) // JCheckBox and others ((ItemSelectable) c).addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { call(r); } }); else print("Warning: onUpdate doesn't know " + getClassName(c)); } static void onUpdate(List l, Object r) { for (JComponent c : l) onUpdate(c, r); } static void zeroAllFieldsOfTypeExcept(Object o, Class type, String... exceptions) { zeroAllFieldsOfTypeExcept(o, type, asSet(exceptions)); } static void zeroAllFieldsOfTypeExcept(Object o, Class type, Collection exceptions) { Set set = asSet(exceptions); for (String field : allFields(o)) if (!set.contains(field) && instanceOf(getOpt(o, field), type)) set(o, field, null); } static List paramsToAbstractActions(Object... params) { List l = new ArrayList(); for (int i = 0; i+1 < l(params); i += 2) { String text = (String) (params[i]); Runnable action = (Runnable) (params[i+1]); if (nempty(text)) l.add(abstractAction(text, action)); } return l; } static void internalFramePopupMenuFromActions_threaded(Container f, List actions) { if (!(f instanceof JInternalFrame)) return; for (final AbstractAction a : unnull(actions)) if (a != null) internalFramePopupMenuItem(((JInternalFrame) f), str(a.getValue(Action.NAME)), runnableThread(new Runnable() { public void run() { try { callAction(a) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callAction(a)"; }})); } static LinkedHashSet asLinkedHashSet(A[] array) { LinkedHashSet set = new LinkedHashSet(); for (A o : array) if (o != null) set.add(o); return set; } static LinkedHashSet asLinkedHashSet(Collection l) { if (l instanceof LinkedHashSet) return (LinkedHashSet) l; LinkedHashSet set = new LinkedHashSet(); for (A o : l) if (o != null) set.add(o); return set; } static List splitAtSpace(String s) { return empty(s) ? emptyList() : asList(s.split("\\s+")); } static Map> allFields_cache = weakHashMap(); static Set allFields(Object o) { if (o == null) return emptySet(); Class _c = _getClass(o); Set fields = allFields_cache.get(_c); if (fields == null) allFields_cache.put(_c, fields = asTreeSet(keys(getOpt_getFieldMap(o)))); return fields; } static void dm_boolFieldMenuItem(Container frame, final String field, final Object... __) { final DynModule m = dm_current_mandatory(); final String humanized = humanizeFormLabel(field); internalFrameTitlePopupMenu(((JInternalFrame) frame), new VF1() { public void get(JPopupMenu menu) { try { menu.add(jCheckBoxMenuItem(humanized, (boolean) _get(m, field), new VF1() { public void get(Boolean b) { try { m.setField(field, b); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, b);\r\n callF(optPar(_, 'onSet));"; }})); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "menu.add(jCheckBoxMenuItem(humanized, (bool) _get(m, field), voidfunc(bool b)..."; }}); } static void dm_intFieldMenuItem(Container frame, final String field, final Object... __) { final DynModule m = dm_current_mandatory(); final String humanized = humanizeFormLabel(field); internalFramePopupMenuItem(((JInternalFrame) frame), humanizeFormLabel(field) + "...", new Runnable() { public void run() { try { final JTextField tf = jtextfield(get(m, field)); showFormTitled(or2(stringOptPar(__, "formTitle"), "Set " + humanized), humanized + ":", tf, new Runnable() { public void run() { try { m.setField(field, parseInt(gtt(tf))); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, parseInt(gtt(tf)));\r\n callF(optPar(__, \"onSet\"));"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final JTextField tf = jtextfield(get(m, field));\r\n showFormTitled(or2(stri..."; }}); } static void dm_longFieldMenuItem(Container frame, final String field, final Object... __) { final DynModule m = dm_current_mandatory(); final String humanized = humanizeFormLabel(field); internalFramePopupMenuItem(((JInternalFrame) frame), humanizeFormLabel(field) + "...", new Runnable() { public void run() { try { final JTextField tf = jtextfield(get(m, field)); showFormTitled(or2(stringOptPar(__, "formTitle"), "Set " + humanized), humanized + ":", tf, new Runnable() { public void run() { try { m.setField(field, parseLong(gtt(tf))); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, parseLong(gtt(tf)));\r\n callF(optPar(__, \"onSet\"));"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final JTextField tf = jtextfield(get(m, field));\r\n showFormTitled(or2(stri..."; }}); } static void dm_doubleFieldMenuItem(Container frame, final String field, final Object... __) { final DynModule m = dm_current_mandatory(); internalFramePopupMenuItem(((JInternalFrame) frame), humanizeFormLabel(field) + "...", new Runnable() { public void run() { try { AutoCloseable __1 = m.enter(); try { dm_doubleFieldSetterDialog(field, __); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp m.enter();\r\n dm_doubleFieldSetterDialog(field, __);"; }}); } static void dm_floatFieldMenuItem(Container frame, String field, Object... __) { DynModule m = dm_current_mandatory(); internalFramePopupMenuItem(((JInternalFrame) frame), humanizeFormLabel(field) + "...", new Runnable() { public void run() { try { AutoCloseable __1 = m.enter(); try { dm_floatFieldSetterDialog(field, __); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp m.enter();\r\n dm_floatFieldSetterDialog(field, __);"; }}); } static void dm_stringFieldMenuItem(Container frame, final String field, final Object... __) { final DynModule m = dm_current_mandatory(); internalFramePopupMenuItem(((JInternalFrame) frame), humanizeFormLabel(field) + "...", new Runnable() { public void run() { try { AutoCloseable __1 = m.enter(); try { dm_stringFieldDialog(field, __); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp m.enter();\r\n dm_stringFieldDialog(field, __);"; }}); } static void dm_fileFieldMenuItem(Container frame, String field, Object... __) { DynModule m = dm_current_mandatory(); internalFramePopupMenuItem(((JInternalFrame) frame), humanizeFormLabel(field) + "...", new Runnable() { public void run() { try { AutoCloseable __1 = m.enter(); try { dm_fileFieldDialog(field, __); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp m.enter();\r\n dm_fileFieldDialog(field, __);"; }}); } static ReliableSingleThread dm_rst(Runnable r) { return dm_rst(dm_current_mandatory(), r); } static ReliableSingleThread dm_rst(DynModule mod, Runnable r) { return dm_rst(mod, new ReliableSingleThread(r)); } static ReliableSingleThread dm_rst(DynModule mod, ReliableSingleThread rst) { rst.enter = dm_rEnter(mod); return rst; } static Q dm_startQ() { return dm_ownQ(startQ()); } static Q dm_startQ(String name) { return dm_ownQ(startQ(name)); } static JComponent dm_noVisualisation() { return setToolTip("Module has no visualization", jimage("#1101480")); } static String snippetTitle(String id) { return getSnippetTitle(id); } static String snippetTitle(long id) { return getSnippetTitle(id); } static Class mainClass() { return getMainClass(); } static Class mainClass(Object o) { return getMainClass(o); } static Set _registerTimer_list = newWeakHashSet(); static void _registerTimer_original(java.util.Timer timer) { _registerTimer_list.add(timer); } static void cleanMeUp__registerTimer() { cancelTimers(getAndClearList(_registerTimer_list)); } static List getAll(Map map, Collection l) { return lookupAllOpt(map, l); } static List getAll(Collection l, Map map) { return lookupAllOpt(map, l); } static > List getAll(Iterable l) { return getVars(l); } static NavigableMap synchroTreeMap() { return Collections.synchronizedNavigableMap(new TreeMap()); } static boolean hasConcept(Class c, Object... params) { return findConceptWhere(c, params) != null; } static Object load(String varName) { readLocally(varName); return get(mc(), varName); } static Object load(String progID, String varName) { readLocally(progID, varName); return get(mc(), varName); } static boolean exceptionMessageContains(Throwable e, String s) { return cic(getInnerMessage(e), s); } static void printShortException(Throwable e) { print(exceptionToStringShort(e)); } static void printShortException(String s, Throwable e) { print(s, exceptionToStringShort(e)); } static void clearConcepts() { db_mainConcepts().clearConcepts(); } static void clearConcepts(Concepts concepts) { concepts.clearConcepts(); } static void restoreLatestBackupIfConceptsFileEmpty(String dbID, Object... __) { boolean doIt = boolPar("doIt", __); File file = conceptsFile(dbID); if (fileExists(file) && fileSize(file) == 0) { print(file + " corrupted, trying to restore"); File backup = lastThat(__110 -> fileNotEmpty(__110), sortByFileName(conceptBackupFiles(dbID))); if (backup == null) { print("No usable backup found :("); return; } String msg = "RESTORING: " + backup; File log = javaxDataDir("db-restores.log"); if (doIt) logQuotedWithTime(log, msg); print(stringIf(!doIt, "[would be] ") + msg); if (doIt) { clearConceptsOf(dbID); copyFile(backup, file); print(msg = "DB RESTORED!"); if (doIt) logQuotedWithTime(log, msg); } } } 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); } static int readLocally_stringLength; static ThreadLocal readLocally2_allDynamic = new ThreadLocal(); static ThreadLocal readLocally2_classFinder = new ThreadLocal(); // read a string variable from standard storage // does not overwrite variable contents if there is no file static void readLocally2(Object obj, String progID, String varNames) { try { boolean allDynamic = isTrue(getAndClearThreadLocal(readLocally2_allDynamic)); for (String variableName : javaTokC(varNames)) { File textFile = new File(programDir(progID), variableName + ".text"); String value = loadTextFile(textFile); if (value != null) set(main.class, variableName, value); else { File structureFile = new File(programDir(progID), variableName + ".structure"); value = loadTextFile(structureFile); if (value == null) { File structureGZFile = new File(programDir(progID), variableName + ".structure.gz"); if (!structureGZFile.isFile()) return; //value = loadGZTextFile(structureGZFile); InputStream fis = new FileInputStream(structureGZFile); try { GZIPInputStream gis = newGZIPInputStream(fis); InputStreamReader reader = new InputStreamReader(gis, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(reader); //O o = unstructure_reader(bufferedReader); Object o = unstructure_tok(javaTokC_noMLS_onReader(bufferedReader), allDynamic, readLocally2_classFinder.get()); readLocally_set(obj, variableName, o); return; } finally { _close(fis); }} readLocally_stringLength = l(value); if (nempty(value)) readLocally_set(obj, variableName, unstructure(value, allDynamic, readLocally2_classFinder.get())); } } } catch (Exception __e) { throw rethrow(__e); } } 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 int done_minPrint = 10; static long done(long startTime, String desc) { long time = now()-startTime; if (time >= done_minPrint) print(desc + " [" + time + " ms]"); return time; } static long done(String desc, long startTime) { return done(startTime, desc); } static long done(long startTime) { return done(startTime, ""); } static RemoteDB connectToDBOpt(String dbNameOrID) { try { return new RemoteDB(dbNameOrID); } catch (Throwable __e) { return null; } } static String dynShortName(Object o) { return shortDynamicClassName(o); } 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 File saveLocally2(Object obj, String progID, String variableName) { Lock __0 = saveLock(); lock(__0); try { 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(); return null; } else if (x instanceof String) { saveTextFile(textFile, (String) x); structureFile.delete(); return textFile; } else { saveTextFile(structureFile, javaTokWordWrap(structure(x))); textFile.delete(); return structureFile; } } finally { unlock(__0); } } static File conceptsFile(String progID) { return getProgramFile(progID, conceptsFileName()); } static File conceptsFile() { return conceptsFile(dbProgramID()); } // wrapper: VF1 or null static void callRunnableWithWrapper(Object wrapper, Runnable r) { if (wrapper == null) callF(r); else callF(wrapper, r); } static List callFAll(Collection l, Object... args) { return callF_all(l, args); } static long saveGZStructureToFile(String file, Object o) { return saveGZStructureToFile(getProgramFile(file), o); } // returns number of uncompressed bytes written static long saveGZStructureToFile(File file, Object o) { return saveGZStructureToFile(file, o, new structure_Data()); } static long saveGZStructureToFile(File file, Object o, structure_Data data) { try { File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); File tempFile = tempFileFor(file); if (tempFile.exists()) try { String saveName = tempFile.getPath() + ".saved." + now(); copyFile(tempFile, new File(saveName)); } catch (Throwable e) { printStackTrace(e); } FileOutputStream fileOutputStream = newFileOutputStream(tempFile.getPath()); CountingOutputStream cos; try { GZIPOutputStream gos = new GZIPOutputStream(fileOutputStream); cos = new CountingOutputStream(gos); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(cos, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); structureToPrintWriter(o, printWriter, data); printWriter.close(); gos.close(); fileOutputStream.close(); } catch (Throwable e) { fileOutputStream.close(); tempFile.delete(); throw rethrow(e); } if (file.exists() && !file.delete()) throw new IOException("Can't delete " + file.getPath()); if (!tempFile.renameTo(file)) throw new IOException("Can't rename " + tempFile + " to " + file); return cos.getFilePointer(); } catch (Exception __e) { throw rethrow(__e); } } static String javaTokWordWrap(String s) { return javaTokWordWrap(120, s); } // TODO: complete trimming static String javaTokWordWrap(int cols, String s) { int col = 0; List tok = javaTok(s); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && col >= cols && !containsNewLine(t)) tok.set(i, t = rtrimSpaces(t) + "\n"); int idx = t.lastIndexOf('\n'); if (idx >= 0) col = l(t)-(idx+1); else col += l(t); } return join(tok); } static String ymd() { return ymd(now()); } static String ymd(long now) { return year(now) + formatInt(month(now), 2) + formatInt(dayOfMonth(now), 2); } static String ymd(long now, TimeZone tz) { return year(now, tz) + formatInt(month(now, tz), 2) + formatInt(dayOfMonth(now, tz), 2); } static String formatInt(int i, int digits) { return padLeft(str(i), '0', digits); } static String formatInt(long l, int digits) { return padLeft(str(l), '0', digits); } static int hours() { return hours(java.util.Calendar.getInstance()); } static int hours(java.util.Calendar c) { return c.get(java.util.Calendar.HOUR_OF_DAY); } static int hours(long time) { return hours(calendarFromTime(time)); } static int hours(long time, TimeZone tz) { return hours(calendarFromTime(time, tz)); } static int roundDownTo_rev(int x, int n) { return roundDownTo(n, x); } static long roundDownTo_rev(long x, long n) { return roundDownTo(n, x); } static int minutes() { return minutes(Calendar.getInstance()); } static int minutes(Calendar c) { return c.get(Calendar.MINUTE); } static long toK(long l) { return (l+1023)/1024; } static Collection allConcepts() { return db_mainConcepts().allConcepts(); } static Collection allConcepts(Concepts concepts) { return concepts.allConcepts(); } // firstDelay = delay static FixedRateTimer doEvery_daemon(long delay, final Object r) { return doEvery_daemon(defaultTimerName(), delay, r); } static FixedRateTimer doEvery_daemon(String timerName, long delay, final Object r) { return doEvery_daemon(timerName, delay, delay, r); } static FixedRateTimer doEvery_daemon(long delay, long firstDelay, final Object r) { return doEvery_daemon(defaultTimerName(), delay, firstDelay, r); } static FixedRateTimer doEvery_daemon(String timerName, long delay, long firstDelay, final Object r) { FixedRateTimer timer = new FixedRateTimer(true); timer.scheduleAtFixedRate(smartTimerTask(r, timer, delay), firstDelay, delay); return timer; } static FixedRateTimer doEvery_daemon(double delaySeconds, final Object r) { return doEvery_daemon(toMS(delaySeconds), r); } static float abs(float f) { return Math.abs(f); } static int abs(int i) { return Math.abs(i); } static double abs(double d) { return Math.abs(d); } static A firstOfType(Collection c, Class type) { for (Object x : c) if (isInstanceX(type, x)) return (A) x; return null; } static List conceptsOfType(String type) { return db_mainConcepts().conceptsOfType(type); } static List concatLists_conservative(List a, List b) { if (empty(a)) return b; if (empty(b)) return a; return concatLists(a, b); } static List concatLists_conservative(Collection a, Collection b) { if (empty(a) && b instanceof List) return ((List) b); if (empty(b) && a instanceof List) return ((List) a); return concatLists(a, b); } static List filterByType(Iterable c, Class type) { List l = new ArrayList(); if (c != null) for (Object x : c) if (isInstanceX(type, x)) l.add((A) x); return l; } static List filterByType(Object[] c, Class type) { return filterByType(asList(c), type); } static List filterByType(Class type, Iterable c) { return filterByType(c, type); } static List filterByDynamicType(Collection c, String type) { List l = new ArrayList(); for (A x : c) if (eq(dynamicClassName(x), type)) l.add(x); return l; } static boolean hasType(Collection c, Class type) { for (Object x : c) if (isInstanceX(type, x)) return true; return false; } static A findBackRef(Concept c, Class type) { for (Concept.Ref r : c.backRefs) if (instanceOf(r.concept(), type)) return (A) r.concept(); return null; } static A findBackRef(Class type, Concept c) { return findBackRef(c, type); } static void mapRemove(Map map, A key) { if (map != null && key != null) map.remove(key); } static String loadConceptsStructure(String progID) { return loadTextFilePossiblyGZipped(getProgramFile(progID, "concepts.structure")); } static String loadConceptsStructure() { return loadConceptsStructure(dbProgramID()); } static void cleanKillVM() { try { ping(); assertNotOnAWTThread(); cleanKillVM_noSleep(); Object o = new Object(); synchronized(o) { o.wait(); } } catch (Exception __e) { throw rethrow(__e); } } static void cleanKillVM_noSleep() { call(getJavaX(), "cleanKill"); } static boolean syncRemove(Collection c, A b) { if (c == null) return false; synchronized(collectionMutex(c)) { return c.remove(b); } } static A syncRemove(List l, int idx) { if (l == null) return null; synchronized(collectionMutex(l)) { return l.remove(idx); } } static B syncRemove(Map map, A key) { return map == null ? null : map.remove(key); } static List syncAddOrCreate(List l, A a) { if (l == null) l = syncList(); l.add(a); return l; } static String nConcepts(long n) { return n2(n, "concept"); } static String nConcepts(Collection l) { return nConcepts(l(l)); } static String nConcepts(Map map) { return nConcepts(l(map)); } static int identityHashCode(Object o) { return System.identityHashCode(o); } static String shortDynamicClassName(Object o) { if (o instanceof DynamicObject && ((DynamicObject) o).className != null) return ((DynamicObject) o).className; return shortClassName(o); } static boolean dynamicObjectIsLoading() { return isTrue(getThreadLocal(DynamicObject_loading)); } static void assertIsInstance(Class type, Object o) { if (!isInstance(type, o)) throw fail(_getClass(o) + " is not a subclass of " + type); } static void assertIsInstance(Object o, Class type) { assertIsInstance(type, o); } static Str concept(String name) { for (Str s : list(Str.class)) if (eqic(s.name, name) || containsIgnoreCase(s.otherNames, name)) return s; return new Str(name); } static void change() { //mainConcepts.allChanged(); // safe version for now cause function is sometimes included unnecessarily (e.g. by EGDiff) callOpt(getOptMC("mainConcepts"), "allChanged"); } static boolean has(String a, String b, String c) { return false; } static boolean has(T3 t) { return false; } static void removeLast(List l) { if (!l.isEmpty()) l.remove(l(l)-1); } static void removeLast(List l, int n) { removeSubList(l, l(l)-n); } static void removeLast(int n, List l) { removeLast(l, n); } static void add(BitSet bs, int i) { bs.set(i); } static boolean add(Collection c, A a) { return c != null && c.add(a); } static void add(Container c, Component x) { addToContainer(c, x); } static long add(AtomicLong l, long b) { return l.addAndGet(b); } static A syncGet(List l, int idx) { if (l == null || idx < 0) return null; synchronized(l) { return idx < l(l) ? l.get(idx) : null; } } static B syncGet(Map map, A a) { if (map == null) return null; synchronized(map) { return map.get(a); } } static int syncL(Collection l) { if (l == null) return 0; synchronized(collectionMutex(l)) { return l.size(); } } static int syncL(Map map) { if (map == null) return 0; synchronized(collectionMutex(map)) { return map.size(); } } static List syncL() { return syncList(); } static List addDyn_quickSync(List l, A a) { if (l == null) l = new ArrayList(); syncAdd(l, a); return l; } static List removeDyn_quickSync(List l, A a) { if (l == null) return null; synchronized(collectionMutex(l)) { l.remove(a); return empty(l) ? null : l; } } static void crenameField_noOverwrite(Concept c, String oldField, String newField) { if (c == null || eq(oldField, newField)) return; Object value = cget(c, oldField); if (newField != null && cget(c, newField) == null) cset(c, newField, value); cset(c, oldField, null); } static Collection scanConceptForRefs(Concept c) { Set refs = new HashSet(); if (c != null) for (Object o : values(objectToMap(c))) { if (o instanceof Concept.Ref) refs.add((Concept.Ref) o); else if (o instanceof Concept.RefL) addAll(refs, ((Concept.RefL) o).l); } return refs; } static String getClientIPFromHeaders(Map headers) { //print("All headers: " + sfu(headers)); if (headers == null) return null; String remoteAddr = (String) (headers.get("remote-addr")); String client = (String) (headers.get("x-forwarded-for")); if (nempty(client)) remoteAddr += "," + client; return remoteAddr; } static RuntimeException unimplemented() { throw fail("TODO"); } static RuntimeException unimplemented(String msg) { throw fail("TODO: " + msg); } static UnsupportedOperationException unsupportedOperation() { throw new UnsupportedOperationException(); } static MultiSetMap generalizedCIMultiSetMap() { MultiSetMap mm = new MultiSetMap(); mm.data = generalizedCIMap(); return mm; } static String ukTimeZone_string() { return "Europe/London"; } static Set syncWeakSet() { return synchroWeakHashSet(); } static void dm_setModuleName(Object module, String name) { dm_callModule(module, "setModuleName", name); } static void dm_setModuleName(String name) { dm_setModuleName(dm_current_mandatory_generic(), name); } static void dm_assertFirstSibling() { if (!dm_isFirstSiblingModule(dm_current_mandatory_generic())) throw fail("Don't start this module (" + dm_moduleLibID() + ") multiple times. This instance will exit."); } static String dbProgramID() { return getDBProgramID(); } // firstDelay = delay static FixedRateTimer dm_doEvery(long delay, Runnable r) { return dm_ownTimer(doEvery(delay, delay, dm_rEnter(r))); } static FixedRateTimer dm_doEvery(long delay, long firstDelay, Runnable r) { return dm_ownTimer(doEvery(delay, firstDelay, dm_rEnter(r))); } // reversed argument order for fun static FixedRateTimer dm_doEvery(double initialSeconds, double delaySeconds, Runnable r) { return dm_ownTimer(doEvery(initialSeconds, delaySeconds, dm_rEnter(r))); } static FixedRateTimer dm_doEvery(double delaySeconds, Runnable r) { return dm_ownTimer(doEvery(delaySeconds, dm_rEnter(r))); } // classes must be concept classes static void ensureConceptClassesAreIndexed(Collection classes) { for (Class c : unnullForIteration(classes)) ensureConceptClassIsIndexed(c); } // menuParams: text, Runnable, ... // also: position := 'left/'center/'right static JButton jPopDownButton_noText(final Object... menuParams) { return toolTip("Additional commands", jPopDownButton("", menuParams)); } static void vmBus_logMethodCall(String methodName, Object... argsOrVars) { vmBus_send("method_" + methodName, objectArrayPlus_inFront(argsOrVars, "thread" , currentThread())); } // TODO: extract stuff to head etc. static String html(Object contents) { return fixHTML(contents); } static void htmlencode_forParams_useV2() { htmlencode_forParams_useV2.set(true); } static AutoCloseable temp_printPrefix (String prefix) { Object oldValue = print_byThread(); print_setPrefixForThread(prefix); return new AutoCloseable() { public String toString() { return "print_byThread().set(oldValue);"; } public void close() throws Exception { print_byThread().set(oldValue); }}; } static Map decodeURIParams(String query) { return decodeHQuery(query); } static boolean cset_trueIfChanged(Concept c, Object... values) { try { return cset(c, values) != 0; } catch (Exception __e) { throw rethrow(__e); } } static Object subBot_serveFile_maxCache(File file) { return subBot_maxCacheHeaders(subBot_serveFile(file)); } static Object subBot_serveFile_maxCache(File file, String mimeType) { return subBot_maxCacheHeaders(subBot_serveFile(file, mimeType)); } static String dropAfterSlash(String s) { if (s == null) return null; int i = s.indexOf('/'); return i < 0 ? s : rtrim(substring(s, 0, i)); } static String binaryMimeType() { return "application/octet-stream"; } static Object subBot_maxCacheHeaders(Object response) { return callMainBot("maxCacheHeaders", response); } static Object serveInputStream(InputStream in, String mimeType) { return subBot_serveInputStream(in, mimeType); } static BufferedInputStream bufferedFileInputStream(File f) { return bufferedInputStream(f); } static BufferedInputStream bufferedFileInputStream(File f, int bufSize) { try { return new BufferedInputStream(new FileInputStream(f), bufSize); } catch (Exception __e) { throw rethrow(__e); } } static boolean preferCached = false; static boolean loadSnippet_debug = false; static ThreadLocal loadSnippet_silent = new ThreadLocal(); static ThreadLocal loadSnippet_publicOnly = new ThreadLocal(); static int loadSnippet_timeout = 30000; static String loadSnippet(Snippet s) { return loadSnippet(s.id); } static String loadSnippet(String snippetID) { try { if (snippetID == null) return null; return loadSnippet(parseSnippetID(snippetID), preferCached); } catch (Exception __e) { throw rethrow(__e); } } static String loadSnippet(String snippetID, boolean preferCached) throws IOException { return loadSnippet(parseSnippetID(snippetID), preferCached); } static IF1 loadSnippet; static String loadSnippet(long snippetID) { return loadSnippet != null ? loadSnippet.get(snippetID) : loadSnippet_base(snippetID); } final static String loadSnippet_fallback(IF1 _f, long snippetID) { return _f != null ? _f.get(snippetID) : loadSnippet_base(snippetID); } static String loadSnippet_base(long snippetID) { try { return loadSnippet(snippetID, preferCached); } catch (Exception __e) { throw rethrow(__e); } } static String loadSnippet(long snippetID, boolean preferCached) throws IOException { if (isLocalSnippetID(snippetID)) return loadLocalSnippet(snippetID); IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadSnippet(fsI(snippetID)); return loadSnippet_noResourceLoader(snippetID, preferCached); } static String loadSnippet_noResourceLoader(long snippetID, boolean preferCached) throws IOException { String text; // boss bot (old concept) /*text = getSnippetFromBossBot(snippetID); if (text != null) return text;*/ initSnippetCache(); text = DiskSnippetCache_get(snippetID); if (preferCached && text != null) return text; try { if (loadSnippet_debug && text != null) System.err.println("md5: " + md5(text)); String url = tb_mainServer() + "/getraw.php?id=" + snippetID + "&utf8=1"; if (nempty(text)) url += "&md5=" + md5(text); if (!isTrue(loadSnippet_publicOnly.get())) url += standardCredentials(); String text2 = loadSnippet_loadFromServer(url); boolean same = eq(text2, "==*#*=="); if (loadSnippet_debug) print("loadSnippet: same=" + same); if (!same) text = text2; } catch (RuntimeException e) { e.printStackTrace(); throw new IOException("Snippet #" + snippetID + " not found or not public"); } try { initSnippetCache(); DiskSnippetCache_put(snippetID, text); } catch (IOException e) { System.err.println("Minor warning: Couldn't save snippet to cache (" + DiskSnippetCache_getDir() + ")"); } return text; } static File DiskSnippetCache_dir; public static void initDiskSnippetCache(File dir) { DiskSnippetCache_dir = dir; dir.mkdirs(); } public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException { return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null); } private static File DiskSnippetCache_getFile(long snippetID) { return new File(DiskSnippetCache_dir, "" + snippetID); } public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException { saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet); } public static File DiskSnippetCache_getDir() { return DiskSnippetCache_dir; } public static void initSnippetCache() { if (DiskSnippetCache_dir == null) initDiskSnippetCache(getGlobalCache()); } static String loadSnippet_loadFromServer(String url) { Integer oldTimeout = setThreadLocal(loadPage_forcedTimeout_byThread, loadSnippet_timeout); try { return isTrue(loadSnippet_silent.get()) ? loadPageSilently(url) : loadPage(url); } finally { loadPage_forcedTimeout_byThread.set(oldTimeout); } } static String webChatBotLogsHTML2(final String baseLink, final Map params) { return withDBLock(new F0() { public String get() { try { List l = new ArrayList(); for (Conversation conv : sortByCalculatedFieldDesc(list(Conversation.class), new F1() { public Object get(Conversation c) { try { return empty(c.msgs) ? c.created : last(c.msgs).time; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "empty(c.msgs) ? c.created : last(c.msgs).time"; }})) { List> dialogs = reversed(unnull(conv.oldDialogs)); if (l(conv.msgs) > 1) l.add(webChatBotLogsHTML_formatDialog(str(conv.id), conv.msgs)); int i = 2; for (List msgs : dialogs) if (l(msgs) > 1) l.add(webChatBotLogsHTML_formatDialog(conv.id + "/" + (i++), msgs)); } int perPage = 50, n = parseIntOpt(params.get("n")); return h3_htitle("Chat Logs") + pageNav2(baseLink, l(l), n, perPage, "n") + ul(subList(l, n, n+perPage), null, "style" , "margin-top: 1em"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "new L l;\r\n for (Conversation conv : sortByCalculatedFieldDesc(list Conv..."; }}); } static String fileInfo(File f) { return renderFileInfo(f); } static String fileInfo(String f) { return renderFileInfo(newFile(f)); } // assumes concepts have been written out static File backupConceptsNow() { return backupConceptsNow(db_mainConcepts()); } static File backupConceptsNow(Concepts cc) { if (cc == null) return null; File f1 = cc.conceptsFile(); File f2 = fileInSameDir(f1, "backups/concepts.structure" + (cc.useGZIP ? ".gz" : "") + ".backup" + ymd_minus_hms()); return copyFile(f1, f2); } 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 boolean match(String pat, List toks, Matches matches) { return match3(pat, toks, matches); } static String hhtml_title_body(String title, Object bodyContents, Object... bodyParams) { return hhtml_head_title_body(title, bodyContents, bodyParams); } static String nMessages(long n) { return n2(n, "message"); } static String nMessages(Collection l) { return nMessages(l(l)); } static Boolean trueOrNull(Boolean b) { return eq(b, false) ? null : b; } static String or2_trim(String a, String b) { a = trim(a); if (nempty(a)) return a; return trim(b); } static String or2_trim(String a, String b, String c) { return or2_trim(or2_trim(a, b), c); } static String hexColorToCSSRGB(String hex) { return empty(hex) ? null : "rgb(" + joinWithComma(rgbToIntList(new RGB(hex))) + ")"; } static String loadSnippet_cached(String id) { return loadSnippet_simpleCache(id); } static String lower(String s) { return s == null ? null : s.toLowerCase(); } static char lower(char c) { return Character.toLowerCase(c); } static String psI_str(String snippetID) { return str(psI(snippetID)); } static String hreplaceTitle(String html, String newTitle) { return hreplacetag(html, "title", htitle(newTitle)); } static Object subBot_serveJavaScript(String html) { return subBot_serveWithContentType(html, "text/javascript"); } static A conceptWhere(Class c, Object... params) { return findConceptWhere(c, params); } static A conceptWhere(Concepts cc, Class c, Object... params) { return findConceptWhere(cc, c, params); } static Map guessMimeTypeFromFileName_map = litmap( ".css", "text/css", ".js", "text/javascript"); static String guessMimeTypeFromFileName(String name) { return guessMimeTypeFromFileName(name, binaryMimeType()); } static String guessMimeTypeFromFileName(String name, String defaultMimeType) { // We do some mime types ourselves, leave the rest to the JDK function String __1 = guessMimeTypeFromFileName_map.get(fileExtension(name)); if (!empty(__1)) return __1; return or2(URLConnection.guessContentTypeFromName(name), defaultMimeType); } static String afterLastSlash(String s) { if (s == null) return null; int i = s.lastIndexOf('/'); return i < 0 ? s : substring(s, i+1); } static String hhtml_head_title_body(String title, Object bodyContents, Object... bodyParams) { return hhtml(hhead(htitle(title)) + "\n" + hbody(bodyContents, bodyParams)); } static String h2(String s, Object... params) { return tag("h2", s, params); } static String hcss_linkColorInherit() { return hcss("a { color: inherit; }"); } static Collection conceptsWhere(Class c, Object... params) { return findConceptsWhere(c, params); } static Collection conceptsWhere(String c, Object... params) { return findConceptsWhere(c, params); } static Collection conceptsWhere(Concepts concepts, Class c, Object... params) { return findConceptsWhere(concepts, c, params); } static String ymd_minus_hm() { return ymd() + "-" + hhmm(); } static Object serveCSVWithFileName(String fileName, String text) { Object response = serveWithContentType(text, "text/csv"); call(response, "addHeader", "Content-Disposition", "inline; filename=\"" + fileName + "\""); return response; } // func(S) -> S static ThreadLocal htmlTable2_cellEncoder = new ThreadLocal(); // htmlEncode = true static String htmlTable2(Object data, Object... __) { boolean htmlEncode = optPar("htmlEncode", __, true); boolean useBr = boolPar("useBr", __); Map paramsByColName = (Map) (optPar("paramsByColName", __)); Object[] tableParams = (Object[]) (optPar("tableParams", __)); Object[] trParams = (Object[]) (optPar("trParams", __)); Object[] tdParams = (Object[]) (optPar("tdParams", __)); Map replaceHeaders = (Map) (optPar("replaceHeaders", __)); // optionally replace HTML contents for header row boolean noHeader = boolPar("noHeader", __); // prepare table List> rows = new ArrayList(); List cols = new ContentsIndexedList(); if (data instanceof List) { for (Object x : (List) data) { try { rows.add(dataToTable_makeRow(x, cols)); } catch (Throwable __e) { _handleException(__e); }} } else if (data instanceof Map) { Map map = (Map) data; for (Object key : map.keySet()) { Object value = map.get(key); rows.add(litlist(structureOrText(key), structureOrText(value))); } } else print("Unknown data type: " + data); // get table width int w = 0; for (List row : rows) w = max(w, l(row)); // construct HTML for table StringBuilder buf = new StringBuilder(); buf.append(hopeningtag("table", paramsPlus(tableParams, "border" , html_valueLessParam())) + "\n"); // title if (!noHeader) { buf.append("\n"); for (String cell : padList(cols, w, "")) buf.append(" " + htmlTable2_encodeCell( getOrKeep(replaceHeaders, cell), htmlEncode, useBr) + "\n"); buf.append("\n"); } // data for (List row : rows) { buf.append(hopeningtag("tr", trParams)); int i = 0; for (String cell : padList(row, w, "")) { String col = get(cols, i++); Object[] params = paramsPlus(tdParams, mapGet(paramsByColName, col)); buf.append(" " + tag("td", htmlTable2_encodeCell(cell, htmlEncode, useBr), params) + "\n"); } buf.append("\n"); } buf.append("\n"); return buf.toString(); } static String htmlTable2_encodeCell(String cell, boolean useHtmlEncode, boolean useBr) { if (htmlTable2_cellEncoder.get() != null) return (String) callF(htmlTable2_cellEncoder.get(), cell); if (useHtmlEncode) cell = htmlEncode2(cell); if (useBr) cell = nlToBr(cell); return cell; } static NavigableMap emojiShortNameMap_cache; static NavigableMap emojiShortNameMap() { if (emojiShortNameMap_cache == null) emojiShortNameMap_cache = emojiShortNameMap_load(); return emojiShortNameMap_cache; } static NavigableMap emojiShortNameMap_load() { NavigableMap map = ciMap(); for (Map m : rawEmojisList()) { String shortName = (String) (m.get("shortname")); String emoji = (String) (m.get("emoji")); if (startsWith(shortName, ":")) map.put(shortName, emoji); } return map; } static String pre(Object contents, Object... params) { return tag("pre", contents, params); } static void tt() { typeWriterConsole(); } static String tt(Object contents, Object... params) { return tag("tt", contents, params); } static String hjavascript_src_withType(String src, Object... __) { return hfulltag("script", "", paramsPlus_inFront(__, "src", src, "type" , "text/javascript")); } // params arrays can also be null static String htableRaw2(List data, List paramsTable, List paramsTR, List paramsTD) { StringBuilder buf = new StringBuilder(); for (List row : data) { buf.append(hopeningTag("tr", toObjectArray(paramsTR))); for (Object cell : row) buf.append(htd(cell, toObjectArray(paramsTD))).append("\n"); buf.append("\n"); } return htag("table", buf, toObjectArray(paramsTable)); } static List nonNulls(Iterable l) { return withoutNulls(l); } static List nonNulls(A[] l) { return withoutNulls(l); } static Map nonNulls(Map map) { return withoutNulls(map); } // lists files only - who needs to list directories?! // lists in typical .zip fashion - dir1/dir2/bla.txt static List listZIP(File inZip) { return listZip(inZip); } static String appendSlash(String s) { return addSlash(s); } static byte[] loadBinaryFromZip(File inZip, String fileName) { try { ZipFile zip = new ZipFile(inZip); try { ZipEntry entry = zip.getEntry(fileName); if (entry == null) //fail("Entry " + fileName + " not found in zip file: " + inZip.getAbsolutePath()); return null; InputStream fin = zip.getInputStream(entry); ByteArrayOutputStream baos = new ByteArrayOutputStream(); copyStream(fin, baos); fin.close(); return baos.toByteArray(); } finally { zip.close(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean fileContentsIs(File f, byte[] data) { try { if (isFile(f) != (data != null)) return false; int l = l(data); if (fileSize(f) != l) return false; BufferedInputStream in = bufferedFileInputStream(f); try { for (int ofs = 0; ofs < l; ofs++) if (data[ofs] != (byte) in.read()) return false; return true; } finally { _close(in); }} catch (Exception __e) { throw rethrow(__e); } } static String nChanges(long n) { return n2(n, "change"); } static String nChanges(Collection l) { return nChanges(l(l)); } static String hcss_responstableForForms() { return hcss("\r\n .responstableForForms {\r\n margin: 1em 0;\r\n /*width: 100%;*/\r\n overflow: hidden;\r\n background: #FFF;\r\n color: #024457;\r\n border-radius: 10px;\r\n border: 1px solid #167F92;\r\n }\r\n \r\n .responstableForForms tr {\r\n border: 1px solid #D9E4E6;\r\n }\r\n .responstableForForms tr:nth-child(odd) {\r\n background-color: #EAF3F3;\r\n }\r\n .responstableForForms th {\r\n display: none;\r\n border: 1px solid #FFF;\r\n background-color: #167F92;\r\n color: #FFF;\r\n padding: 1em;\r\n }\r\n .responstableForForms th:first-child {\r\n display: table-cell;\r\n text-align: center;\r\n }\r\n .responstableForForms th:nth-child(2) {\r\n display: table-cell;\r\n }\r\n .responstableForForms th:nth-child(2) span {\r\n display: none;\r\n }\r\n .responstableForForms th:nth-child(2):after {\r\n content: attr(data-th);\r\n }\r\n @media (min-width: 480px) {\r\n .responstableForForms th:nth-child(2) span {\r\n display: block;\r\n }\r\n .responstableForForms th:nth-child(2):after {\r\n display: none;\r\n }\r\n }\r\n .responstableForForms td {\r\n display: block;\r\n word-wrap: break-word;\r\n /*max-width: 7em;*/\r\n }\r\n .responstableForForms td:first-child {\r\n display: table-cell;\r\n text-align: center;\r\n border-right: 1px solid #D9E4E6;\r\n }\r\n @media (min-width: 480px) {\r\n .responstableForForms td {\r\n border: 1px solid #D9E4E6;\r\n }\r\n }\r\n .responstableForForms th, .responstableForForms td {\r\n text-align: left;\r\n margin: .5em 1em;\r\n }\r\n @media (min-width: 480px) {\r\n .responstableForForms th, .responstableForForms td {\r\n display: table-cell;\r\n padding: 1em;\r\n }\r\n }\r\n "); } static String loadJQuery2() { return ""; } static String hjs_selectize() { return "\r\n \r\n \r\n "; } // by Angelos Chalaris, https://www.30secondsofcode.org/blog/s/copy-text-to-clipboard-with-javascript static JavaScript hjs_copyToClipboard() { return new JavaScript("\r\n const copyToClipboard = str => {\r\n const el = document.createElement('textarea');\r\n el.value = str;\r\n el.setAttribute('readonly', '');\r\n el.style.position = 'absolute';\r\n el.style.left = '-9999px';\r\n document.body.appendChild(el);\r\n const selected =\r\n document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;\r\n el.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(el);\r\n if (selected) {\r\n document.getSelection().removeAllRanges();\r\n document.getSelection().addRange(selected);\r\n }\r\n };\r\n "); } // see https://github.com/JamieLivingstone/styled-notifications static String hNotificationPopups() { return hStylesheetSnippet_maxCache("#1029723") + hjssnippet_maxCache("#1029726") + hjs("\r\n function successNotification(text) {\r\n window.createNotification({ theme: 'success', showDuration: 3000 })({ message: text });\r\n }\r\n \r\n function errorNotification(text) {\r\n window.createNotification({ theme: 'error', showDuration: 3000 })({ message: text });\r\n }\r\n "); } static boolean neqOneOf(Object o, Object... l) { for (Object x : l) if (eq(o, x)) return false; return true; } // f must return a string static String joinMap(Object f, Iterable l) { return join(map(f, l)); } static String joinMap(Iterable l, Object f) { return joinMap(f, l); } static String joinMap(Iterable l, IF1 f) { return joinMap(f, l); } static String joinMap(IF1 f, Iterable l) { return join(map(f, l)); } static String joinMap(String separator, Map map, IF2 f) { return join(separator, map(map, f)); } static int indexOfPred(List l, Object pred) { for (int i = 0; i < l(l); i++) if (checkCondition(pred, l.get(i))) return i; return -1; } static int indexOfPred(List l, IF1 pred) { return indexOfPred(l, (Object) pred); } static String hcrud_mergeTables(String table1, String table2, String connector) { List tok1 = htmlTok(table1), tok2 = htmlTok(table2); List tbl1 = first(findContainerTag(tok1, "table")); List tbl2 = first(findContainerTag(tok2, "table")); if (tbl1 == null) return table2; if (tbl2 == null) return table1; List> rows1 = findContainerTag(tbl1, "tr"); List> rows2 = findContainerTag(tbl2, "tr"); print("rows1: " + l(rows1) + ", rows2: " + l(rows2)); int nRows = max(l(rows1), l(rows2)); List rows = new ArrayList(); for (int i = 0; i < nRows; i++) { List row1 = dropFirstTwoAndLastTwo(get(rows1, i)); List row2 = dropFirstTwoAndLastTwo(get(rows2, i)); row2 = dropFirst(row2, indexOfIC(row2, "")+1); // drop the 1., 2., 3. rows.add(tr(join(concatLists(row1, ll(td(connector)), row2)))); } replaceTokens(tbl1, 2, l(tbl1)-2, lines(rows)); return join(tok1); } static void addInFront(List c, A a) { addFirst(c, a); } static String hjs_imgUploadBase64Encoder() { return hscript("\r\n var imgConverted = false;\r\n \r\n function submitWithImageConversion(form) {\r\n console.log(\"imgConverted: \" + imgConverted + \", form: \" + form);\r\n if (imgConverted) return true;\r\n imgConverted = true;\r\n var file = $('#imgUploader')[0].files[0];\r\n if (file == null) return true;\r\n var reader = new FileReader();\r\n\r\n // delay form submit until reader is done\r\n reader.onloadend = function () {\r\n var b64 = reader.result.replace(/^data:.+;base64,/, '');\r\n $(\"#f_img_base64\").val(b64);\r\n console.log(\"Got base64 data: \" + b64.length);\r\n setTimeout(function() {\r\n $(form).submit();\r\n }, 1000);\r\n };\r\n\r\n reader.readAsDataURL(file);\r\n return false;\r\n }\r\n "); } static String himageupload(Object... params) { return hfileupload(paramsPlus(params, "accept" , "image/png,image/jpeg,image/gif")); } static String hhiddenWithIDAndName(String idAndName) { return hhiddenWithIDAndName(idAndName, null); } static String hhiddenWithIDAndName(String idAndName, Object value, Object... params) { return tag("input", "", paramsPlus(params, "type" , "hidden", "id" , idAndName, "name" , idAndName, "value", value)); } static String hjs_fileUploadBase64Encoder() { return hscript("\r\n var fileConverted = false;\r\n \r\n function submitWithFileConversion(form) {\r\n if (fileConverted) return true;\r\n fileConverted = true;\r\n var file = $('#fileUploader')[0].files[0];\r\n if (file == null) return true;\r\n var reader = new FileReader();\r\n\r\n // delay form submit until reader is done\r\n reader.onloadend = function () {\r\n var b64 = reader.result.replace(/^data:.+;base64,/, '');\r\n $(\"#f_file_base64\").val(b64);\r\n console.log(\"Got base64 data: \" + b64.length);\r\n setTimeout(function() {\r\n $(form).submit();\r\n }, 1000);\r\n };\r\n\r\n reader.readAsDataURL(file);\r\n return false;\r\n }\r\n "); } static String haudioUpload(Object... params) { return hfileupload(paramsPlus(params, "accept" , joinWithComma(mp3mimeType(), oggMimeType(), wavMimeType(), m4aMimeType()))); } // e. g. static String hfileupload(Object... params) { return hinputtag("", paramsPlus_noOverwrite(params, "type" , "file", "name" , "thefile")); } static boolean isSubclassOf(Class a, Class b) { return isSubclass(a, b); } static String shortName(Object o) { return shortClassName(o); } static A replaceIfEquals(A a, A b, A c) { return replaceIfEqual(a, b, c); } static Type type_LS() { return new ParameterizedTypeImpl(null, List.class, String.class); } static String ol_htmlEncode(String... list) { return ol_htmlEncode(asList(list)); } // legacy static String ol_htmlEncode(List list, Object... params) { return ol_htmlEncode((Collection) list, params); } static String ol_htmlEncode(Collection list, Object... params) { return ol(lmap(__111 -> htmlEncode2(__111), list), params); } static void replaceMap(Map dest, Map src) { if (dest == src) return; dest.clear(); dest.putAll(src); } static String formatLocalDateWithMinutes(long time) { return localDateWithMinutes(time); } static String formatLocalDateWithMinutes() { return localDateWithMinutes(); } static String renderColonProperties(Map map) { return formatColonProperties(map); } static String renderColonProperties(List> l) { return formatColonProperties(l); } static String ol(String... list) { return ol(asList(list)); } static String ol(List list, Object... params) { StringBuilder buf = new StringBuilder(); int i = indexOf(params, null); // null separates params for ul from params for li if (i == -1) i = l(params); for (String s : withoutNulls(list)) buf.append(tag("li", s, subArray(params, i+1))).append("\n"); return containerTag("ol", buf, subArray(params, 0, i)) + "\n"; } static String pnlToStringWithEmptyLines_rtrim(Iterable l) { return rtrim(pnlToStringWithEmptyLines(l)); } static String hcrud_imagePreviewStyle() { return "max-width: 200px; max-height: 100px; width: auto; height: auto"; } static String toK_str(long l) { return str_toK(l); } static void saveFileVerbose(File f, byte[] data) { saveBinaryFileVerbose(f, data); } static byte[] base64decode(String s) { byte[] alphaToInt = base64decode_base64toint; int sLen = s.length(); int numGroups = sLen/4; if (4*numGroups != sLen) throw new IllegalArgumentException( "String length must be a multiple of four."); int missingBytesInLastGroup = 0; int numFullGroups = numGroups; if (sLen != 0) { if (s.charAt(sLen-1) == '=') { missingBytesInLastGroup++; numFullGroups--; } if (s.charAt(sLen-2) == '=') missingBytesInLastGroup++; } byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; // Translate all full groups from base64 to byte array elements int inCursor = 0, outCursor = 0; for (int i=0; i> 4)); result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); result[outCursor++] = (byte) ((ch2 << 6) | ch3); } // Translate partial group, if present if (missingBytesInLastGroup != 0) { int ch0 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); int ch1 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); if (missingBytesInLastGroup == 1) { int ch2 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); } } // assert inCursor == s.length()-missingBytesInLastGroup; // assert outCursor == result.length; return result; } static int base64decode_base64toint(char c, byte[] alphaToInt) { int result = alphaToInt[c]; if (result < 0) throw new IllegalArgumentException("Illegal character " + c); return result; } static final byte base64decode_base64toint[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; static String beforeColonOrAll(String s) { return takeFirst(smartIndexOf(s, ':'), s); } static Object withDBLock(Object r) { Lock __0 = db_mainConcepts().lock; lock(__0); try { return callF(r); } finally { unlock(__0); } } static A withDBLock(F0 r) { return (A) withDBLock((Object) r); } static Object withDBLock(Concepts concepts, Object r) { Lock __1 = concepts.lock; lock(__1); try { return callF(r); } finally { unlock(__1); } } static A withDBLock(Concepts concepts, F0 r) { return (A) withDBLock(concepts, (Object) r); } static A withDBLock(Concept concept, IF0 r) { return (A) withDBLock(concept._concepts, r); } static double elapsedSeconds_timestamp(long time) { return toSeconds(now()-time); } static String div_floatRight(Object contents, Object... params) { return div(contents, params_stylePlus("float: right", params)); } static String hbutton(String text, Object... params) { return hfulltag("button", text, params); } static String htmlEncode2_gen(Object o) { return htmlEncode2(strOrEmpty(o)); } static int countPred(Iterable c, Object pred) { return nfilter(c, pred); } static int countPred(Object pred, Iterable c) { return nfilter(pred, c); } static int countPred(Iterable c, IF1 pred) { return nfilter(c, pred); } static String nConversations(long n) { return n2(n, "conversation"); } static String nConversations(Collection l) { return nConversations(l(l)); } static String nConversations(Map map) { return nConversations(l(map)); } static ContentsIndexedList ciContentsIndexedList() { return new ContentsIndexedList(ciMap()); } static Map mapPlus_inFront(Map m, Object... data) { LinkedHashMap m2 = new LinkedHashMap(); litmap_impl(m2, data); putAll(m2, m); return m2; } static int addAndReturnIndex(List l, A a) { if (l == null) return -1; int idx = l.size(); l.add(a); return idx; } static String formatCSVFileForExcel2(List rows) { String sep = "|"; // multi-character separator doesn't work with Excel return windowsLineBreaks("sep=" + sep + "\n" + mapToLines_rtrim(rows, row -> join(sep, map(row, col -> replace(newLinesToSpaces(str(col)), sep, " "))))); } static Set putSetElementsFirst(Collection cl, Collection elements) { Set inputSet = asSet(cl); LinkedHashSet set = new LinkedHashSet(); for (A a : unnullForIteration(elements)) if (contains(inputSet, a)) set.add(a); addAll(set, cl); // keep order return set; } static Set putSetElementsFirst(Collection cl, A... elements) { return putSetElementsFirst(cl, asList(elements)); } static Cache> countryDialCodesMultiMap_cache = new Cache<>(() -> countryDialCodesMultiMap_load()); static MultiMap countryDialCodesMultiMap() { return countryDialCodesMultiMap_cache.get(); } static MultiMap countryDialCodesMultiMap_load() { return treeMultiMapIndexByField("dialCode", countryDialCodes()); } static List splitAtComma_trim(String s) { return nempties(trimAll(splitAtComma(s))); } // countryCode = GB, US, DE, IN, ... // returns "+1" etc. (or null) static String dialCodeStringForCountryCode(String countryCode) { CountryDialCode cdc = countryDialCodeByCountryCode(countryCode); return cdc == null ? null : cdc.dialCode; } static List collectSorted(Iterable c, String field) { return sorted(collect(c, field)); } static List collectSorted(String field, Iterable c) { return collectSorted(c, field); } // f must return a list static List concatMap(Object f, Iterable l) { return concatLists(map(f, l)); } static List concatMap(Iterable l, Object f) { return concatMap(f, l); } static List concatMap(Object f, Object[] l) { return concatLists(map(f, l)); } static List concatMap(Object[] l, Object f) { return concatMap(f, l); } static > List concatMap(Iterable l, IF1 f) { return concatMap(l, (Object) f); } static > List concatMap(IF1 f, Iterable l) { return concatMap(l, f); } static String dropPrefix_trim(String prefix, String s) { return dropPrefixTrim(prefix, s); } static String imageSnippetURLOrEmptyGIF(String snippetID) { return empty(snippetID) ? smallestTransparentGIFDataURI() : snippetImageURL(snippetID); } static Scorer scorerWithSuccessesAndErrors() { Scorer scorer = new Scorer(); scorer.successes = new ArrayList(); scorer.errors = new ArrayList(); return scorer; } static List sortByFieldDesc(Collection c, final String field) { return sortedByFieldDesc(c, field); } static List sortByFieldDesc(String field, Collection c) { return sortedByFieldDesc(field, c); } static Object[] onlyNonNullParams(Object... params) { List out = new ArrayList(); for (int i = 0; i+1 < l(params); i += 2) if (params[i] != null && params[i+1] != null) { out.add(params[i]); out.add(params[i+1]); } return toObjectArray(out); } static A lowestConceptByField(Class c, String field) { return lowestConceptByField(db_mainConcepts(), c, field); } static A lowestConceptByField(Concepts concepts, Class c, String field) { // indexed IFieldIndex index = concepts.getFieldIndex(c, field); if (index instanceof ConceptFieldIndexDesc) { Map.Entry e = ((NavigableMap) ((ConceptFieldIndexDesc) index).valueToObject.data).lastEntry(); return e == null ? null : (A) first((Collection) e.getValue()); } return lowestByField(field, concepts.list(c)); } static java.util.Timer doAfter(long delay, Object r) { return doLater(delay, r); } static java.util.Timer doAfter(double delaySeconds, Object r) { return doLater(delaySeconds, r); } static A registerConcept(A c) { return registerConcept(db_mainConcepts(), c); } static A registerConcept(Concepts cc, A c) { { if (cc != null) cc.register(c); } return c; } static void indexConceptField(Class c, String field) { indexConceptField(db_mainConcepts(), c, field); } static void indexConceptField(Concepts concepts, Class c, String field) { if (!isConceptFieldIndexed(concepts, c, field)) new ConceptFieldIndex(concepts, c, field); } static void db() { conceptsAndBot(); } // use -10000 for 10 seconds plus slowdown logic static void db(Integer autoSaveInterval) { conceptsAndBot(autoSaveInterval); } // first parameter can be Concepts static void indexConceptFields(Object... params) { int i = 0; Concepts concepts; if (first(params) instanceof Concepts) { concepts = (Concepts) first(params); ++i; } else concepts = db_mainConcepts(); for (; i < l(params); i += 2) indexConceptField(concepts, (Class) params[i], (String) params[i+1]); } static void indexSingletonConcept(Concepts cc, Class c) { indexConceptField(cc, c, "_dummy"); } static void indexSingletonConcept(Class c) { indexSingletonConcept(db_mainConcepts(), c); } static Object subBot_noCacheHeaders(Object r) { call(r, "addHeader", "Cache-Control", "no-cache, must-revalidate, max-age=0"); return r; } static Object subBot_serveHTML(Object html) { return subBot_serveWithContentType(str(html), "text/html"); } static String html_emojisToUnicode(String s) { return join(mapNonCodeTokens(__112 -> emojisToUnicode(__112), htmlTok(s))); } static String fullRawLink(String pageName) { // TODO: port return (subBot_isHttps() ? "https" : "http") + "://" + domain() + rawLink(pageName); } static String unicode_undoArrow() { return unicodeFromCodePoint(0x21B6); } static String htmldecode_dropAllTags(String html) { return htmldecode(dropAllTags(html)); } static List splitAtVerticalBar(String s) { return s == null ? new ArrayList() : trimAll(asList(s.split("\\|"))); } static String hbuttonOnClick_returnFalse(String text, String onClick, Object... params) { return hfulltag("button", text, paramsPlus(params, "onclick" , addSuffix(trim(onClick), ";") + " return false;")); } static String stringUnless(boolean b, String s) { return b ? "" : s; } static List text_multipleChoiceSplit(String input, String multipleChoiceSeparator) { return trimAll(splitAt(input, dropSpaces(multipleChoiceSeparator))); } static TreeSet asCISet(Iterable c) { return toCaseInsensitiveSet(c); } static TreeSet asCISet(String... x) { return toCaseInsensitiveSet(x); } static String unicode_crossProduct() { return unicodeFromCodePoint(0x2A2F); } static String timeInTimeZoneWithOptionalDate_24(String timezone, long time) { return timeInTimeZoneWithOptionalDate_24(timeZone(timezone), time); } static String timeInTimeZoneWithOptionalDate_24(TimeZone timezone, long time) { SimpleDateFormat format = simpleDateFormat("yyyy/MM/dd", timezone); String date = format.format(time); boolean needDate = neq(date, format.format(now())); return (needDate ? date + " " : "") + timeInTimeZone(timezone, time); } static String hhead_title(String title) { return hhead(htitle(title)); } static String jsBackLink() { return "javascript:history.go(-1)"; } static String ipToCountry2020_safe(String ip) { try { return or2(ipToCountry2020(ip), "?"); } catch (Throwable __e) { _handleException(__e); } return "?"; } static String replaceSquareBracketVars(String s, Object... params) { if (empty(params)) return s; Map vars = mapKeys(__113 -> deSquareBracket(__113), (Map) litcimap(params)); return regexpReplaceIC(s, "\\[(.+?)\\]", matcher -> { String var = matcher.group(1); Object val = vars.get(var); return val == null ? matcher.group() : str(val); }); } static List trimAll(Collection l) { List l2 = new ArrayList(); if (l != null) for (String s : l) l2.add(trim(s)); return l2; } static String dropSpaces(String s) { return unnull(s).replace(" ", ""); } static A lastThat(List l, Object pred) { for (int i = l(l)-1; i >= 0; i--) { A a = l.get(i); if (checkCondition(pred, a)) return a; } return null; } static A lastThat(Object pred, List l) { return lastThat(l, pred); } static A lastThat(IF1 pred, List l) { return lastThat((Object) pred, l); } static A lastThat(List l, IF1 pred) { return lastThat(pred, l); } static String addSlashPrefix(String s) { return addPrefix("/", s); } static JTextField jtextfield() { return jTextField(); } static JTextField jtextfield(String text) { return jTextField(text); } static JTextField jtextfield(Object o) { return jTextField(o); } static JPanel showFormTitled2(String title, Object... parts) { return showFormTitled_customLayout(formLayouter1(), title, parts); } static String gtt(JTextComponent c) { return getTextTrim(c); } static String gtt(JComboBox cb) { return getTextTrim(cb); } static JWindow infoBox(String text) { return infoMessage(text); } static JWindow infoBox(String text, double seconds) { return infoMessage(text, seconds); } static JWindow infoBox(Throwable e) { return infoMessage(e); } static void inputText(final String msg, final Object action) { inputText(msg, "", action); } // action: voidfunc(S) static void inputText(final String msg, final String defaultText, final Object action) { final Object threadInfo = _threadInfo(); swingLater(new Runnable() { public void run() { try { final JTextField tf = jtextfield(defaultText); String title = joinStrings(" | ", msg, programName()); JComponent form = showFormTitled(title, unnull(msg), tf, new Runnable() { public void run() { try { vmBus_send("inputtingText_OK", threadInfo, msg, tf); callF_thread(action, getTextTrim(tf)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "vmBus_send(\"inputtingText_OK\", threadInfo, msg, tf);\r\n callF_thread(ac..."; }}); renameSubmitButton(form, "OK"); vmBus_send("inputtingText", threadInfo, msg, tf); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final JTextField tf = jtextfield(defaultText);\r\n String title = joinString..."; }}); } static void inputText(String msg, String defaultText, IVF1 action) { inputText(msg, defaultText, (Object) action); } static void inputText(String msg, IVF1 action) { inputText(msg, (Object) action); } static Object subBot_serveRedirect(String url) { return call(getMainBot(), "serveRedirect", url); } // pred receives the params of opening tag as string (e.g. => "$bla") static String html_evaluateIfTags(String html, IF1 pred) { List tok = htmlTok(html); boolean anyChange = false; while (true) { ping(); List ifTag = first(findContainerTag(tok, "if")); if (ifTag == null) break; anyChange = true; int iElse = indexOfIC(ifTag, ""); boolean val = pred.get(htmlTagFullParamString(second(ifTag))); if (val) { // value is true, drop else branch (if there) if (iElse >= 0) clearTokens(ifTag, iElse, l(ifTag)-2); } else { // value is false, drop everything up until (or whole thing) clearTokens(ifTag, 2, iElse < 0 ? l(ifTag) - 2 : iElse+1); } // clear opening and closing tag clearToken(ifTag, 1); clearToken(ifTag, l(ifTag)-2); } return anyChange ? join(tok) : html; } static String serveSnippetURL(String snippetID) { return "https://botcompany.de/serve/" + psI(snippetID); } /*static S serveSnippetURL(S snippetID, S contentType) { ret "https://botcompany.de/serve/" + psI(snippetID) + "?ct=" + contentType; }*/ static IterableIterator> iterateWithIndex1(Collection l) { return iterateListWithIndex(l, 1); } static IterableIterator> iterateWithIndex1(A[] l) { return iterateListWithIndex(asVirtualList(l), 1); } static String ulIfNempty(Collection list, Object... params) { return empty(list) ? "" : ul(list, params); } static String ahref_unstyled(String link, Object contents, Object... params) { if (link == null) return str(contents); return ahref(link, contents, paramsPlus(params, "style" , unparseCSSParameter(mapPlus_noOverwrite(parseCSSParameter(stringPar("style", params)), "text-decoration" , "none", "color" , "inherit")))); } static String prependSquareBracketed(Object o) { String s = str(o); return empty(s) ? "" : squareBracketed(s) + " "; } static int countWords(String s) { return numberOfWords2(s); } // tok should be the output of htmlcoarsetok static List dropHTMLTags(List tok) { return dropAllTags(tok); } // alternatively, call this convenient function static String dropHTMLTags(String html) { return dropAllTags(html); } static boolean isValidInternationalPhoneNumber(String s) { InternationalPhoneValidator v = new InternationalPhoneValidator(s); v.run(); if (v.error != null) print(v.error); return v.valid; } static String formatColonProperties(Map map) { return mapToLines(__114 -> joinPairWithColon(__114), mapToPairs(map)); } static String formatColonProperties(List> l) { return mapToLines(__115 -> joinPairWithColon(__115), l); } static void sendMailThroughScript(String senderInfo, String from, String to, String subject, String text) { List l = splitAt(senderInfo, "#"); if (l(l) != 2) throw fail("Bad mail sender info"); String url = first(l), pw = second(l); print("Sending mail through " + url); print("From: " + from); print("To: " + to); print("Subject: " + subject); print("Text:"); printIndent("> ", text); assertStartsWith("OK", postPage(url, "pw", pw, "from", from, "to", to, "subject", subject, "text", text)); } static A syncNextToLast(List l) { if (l == null) return null; synchronized(l) { return nextToLast(l); } } static void syncRemoveLast(List l) { if (l != null) synchronized(l) { removeLast(l); } } static void syncRemoveLast(List l, int n) { if (l != null) synchronized(l) { removeLast(l, n); } } static void syncRemoveLast(int n, List l) { syncRemoveLast(l, n); } static void syncReplaceCollection(Collection dest, Collection src) { if (dest == src) return; synchronized(collectionMutex(dest)) { dest.clear(); if (src != null) dest.addAll(src); } } static List flattenList2(Object... a) { List l = new ArrayList(); if (a != null) for (Object x : a) if (x instanceof Collection) for (Object sub : (Collection) x) l.addAll(flattenList2(sub)); else if (x != null) l.add(x); return l; } static String dropUriPrefix(String prefix, String uri) { prefix = dropTrailingSlash(prefix); if (empty(prefix)) return uri; if (eq(uri, prefix) || startsWith(uri, prefix + "/")) return substring(uri, l(prefix)); return uri; } static String nTests(long n) { return n2(n, "test"); } static String nTests(Collection l) { return nTests(l(l)); } static String nErrors(long n) { return n2(n, "error"); } static String nErrors(Collection l) { return nErrors(l(l)); } static String nErrors(Map map) { return nErrors(l(map)); } static boolean containsHTMLRedirect(String html) { return cic(html, "http-equiv") && any(__116 -> isHTMLRefreshTag(__116), findAllTagsNamed("meta", html)); } // marks row containing URL anchor. needs JQuery static String hjs_markRowMagic(Object... __) { String color = optPar("color", __, "#99f"); return hjs(replaceDollarVars("\r\n var rowMagic_hash = window.location.hash.substr(1);\r\n if (rowMagic_hash)\r\n $(\"a[name=\" + rowMagic_hash + \"]\").closest('tr').css({ 'background-color' : '$color'});\r\n ", "color", color)); } // Use like this: renderVars(+x, +y) static String renderVars_struct(Object... params) { List l = new ArrayList(); for (int i = 0; i+1 < l(params); i += 2) l.add(params[i] + "=" + struct(params[i+1]) + ". "); return trim(join(l)); } static Map allConceptFieldsAsMap(Concept c) { Map map = new HashMap(); for (String field : unnullForIteration(conceptFields(c))) map.put(field, cget(c, field)); return map; } static String js_evalOnWebSocketMessage() { return "\r\n function(event) {\r\n var data = JSON.parse(event.data);\r\n if (data.eval) {\r\n if (wsVerbose) console.log(\"Evaluating: \" + data.eval);\r\n var geval = eval; // use global scope\r\n geval(data.eval);\r\n if (wsVerbose) console.log(\"Evaluated.\");\r\n } else\r\n if (wsVerbose) console.log(\"WebSocket incoming: \" + event.data);\r\n }\r\n "; } static int conceptCount(Concepts concepts, Class c, Object... params) { return countConcepts(concepts, c, params); } static int conceptCount(Class c, Object... params) { return countConcepts(c, params); } static int conceptCount() { return countConcepts(); } static int conceptCount(String className) { return countConcepts(className); } static int conceptCount(Concepts concepts, String className) { return countConcepts(concepts, className); } static int conceptCount(Concepts concepts) { return countConcepts(concepts); } static String renderDBLoadTime() { return renderDBLoadTime(db_mainConcepts()); } static String renderDBLoadTime(Concepts cc) { if (cc == null) return null; return cc.loadTook < 0 ? "unknown" : n2(cc.loadTook) + " ms"; } static String renderDBSaveTime() { return renderDBSaveTime(db_mainConcepts()); } static String renderDBSaveTime(Concepts cc) { if (cc == null) return null; return cc.lastSaveTook < 0 ? "unknown" : n2(cc.lastSaveTook) + " ms"; } static String toString(Object o) { return strOrNull(o); } static String quoteOr(String s, String whenEmpty) { return empty(s) ? whenEmpty : quote(s); } static String spaceRoundBracketed(String s) { return spacePlusRoundBracketedIfNempty(s); } static String nSteps(long n) { return n2(n, "step"); } static String nSteps(Collection l) { return nSteps(l(l)); } static String appendBracketed(Object o) { String b = strOrNull(o); return empty(b) ? "" : "" + " (" + b + ")"; } static String appendBracketed(String a, String b) { return a + appendBracketed(b); } static Random random_random = new Random(); static int random(int n) { return random(random_random, n); } static int random(int n, Random r) { return random(r, n); } static int random(Random r, int n) { return n <= 0 ? 0 : r.nextInt(n); } static double random(double max) { return random()*max; } static double random() { return random_random.nextInt(100001)/100000.0; } static double random(double min, double max) { return min+random()*(max-min); } // min <= value < max static int random(int min, int max) { return min+random(max-min); } static int random(int min, int max, Random r) { return random(r, min, max); } static int random(Random r, int min, int max) { return min+random(r, max-min); } static A random(List l) { return oneOf(l); } static A random(Collection c) { if (c instanceof List) return random((List) c); int i = random(l(c)); return collectionGet(c, i); } static int random(IntRange r) { return random(r.start, r.end); } static Pair random(Map map) { return entryToPair(random(entries(map))); } static String spacePlusRoundBracketedIfNempty(String s) { return nempty(s) ? " " + roundBracket(s) : ""; } static String newLinesToSpaces2(String s) { return newLinesToSpaces_trim(s); } static String himgsrc(String src, Object... params) { return tag("img", "", arrayPlus(params, "src", src)); } static boolean deleteFile(File file) { return file != null && file.delete(); } static String mp3mimeType() { return "audio/mpeg"; } static long toMS(double seconds) { return (long) (seconds*1000); } static String orEmptyQuotes(String s) { return or2(s, "\"\""); } static List> zipTwoListsToPairs_longer(List l1, List l2) { int n = max(l(l1), l(l2)); List> out = emptyList(n); for (int i = 0; i < n; i++) addPair(out, _get(l1, i), _get(l2, i)); return out; } static B syncPut(Map map, A key, B value) { if (map == null) return null; synchronized(map) { return map.put(key, value); } } static boolean isValidEmailAddress_simple(String s) { return l(s) <= 254 && regexpMatches("^\\S+@\\S+(\\.[^\\.\\s]+)+$", s); } static int indexOfIC(List a, String b) { return indexOfIgnoreCase(a, b); } static int indexOfIC(List a, String b, int i) { return indexOfIgnoreCase(a, b, i); } static int indexOfIC(String a, String b) { return indexOfIgnoreCase(a, b); } static int indexOfIC(String a, String b, int i) { return indexOfIgnoreCase(a, b, i); } static String dropStuffBeforeVerticalBar(String s) { int i = indexOf(s, '|'); return i < 0 ? s : substring(s, i+1); } static String sfu(Object o) { return structureForUser(o); } static A getOrKeep(Map map, A a) { if (map == null) return a; A v = map.get(a); return v != null ? v : a; } static IF1 swic$(String prefix) { return s -> swic(s, prefix); } static void syncRemoveAllExceptLast(List l) { if (l == null) return; synchronized(collectionMutex(l)) { removeSubList(l, 0, l(l)-1); } } static MMOPattern mmo2_parsePattern(String s) { // drop outer brackets and tokenize s = trim(tok_deRoundBracket(trim(s))); List tok = javaTokWithBrackets(s); // commas (or) - "car, vehicle" List l = tok_splitAtComma(tok); if (l(l) > 1) return new MMOPattern.Or(lambdaMap(__117 -> mmo2_parsePattern(__117), l)); // plus (and) - "give + money" l = tok_splitAtPlus(tok); if (l(l) > 1) return new MMOPattern.And(lambdaMap(__118 -> mmo2_parsePattern(__118), l)); // exclam (not) - "!something" if (startsWith(s, "!")) return new MMOPattern.Not(mmo2_parsePattern(dropFirst(s))); // ^... - match start of line if (startsWith(s, "^")) return new MMOPattern.StartOfLine(mmo2_parsePattern(dropFirst(s))); // ...$ - match end of line if (endsWith(s, "$")) return new MMOPattern.EndOfLine(mmo2_parsePattern(dropLast(s))); // ...*0.5 - weighted pattern l = tok_splitAtAsterisk(tok); if (l(l) == 2) return new MMOPattern.Weighted(parseDouble(second(l)), mmo2_parsePattern(first(l))); // phrase (possibly quoted) return new MMOPattern.Phrase(unquote(s), isQuoted(s)); } static boolean mmo2_matchWithTypos(String pattern, String s) { return mmo2_matchWithTypos(pattern, s, 1); } static boolean mmo2_matchWithTypos(String pattern, String s, int maxTypos) { return mmo2_matchWithTypos(mmo2_parsePattern(pattern), s, maxTypos); } static boolean mmo2_matchWithTypos(MMOPattern pattern, String s) { return mmo2_matchWithTypos(pattern, s, 1); } static boolean mmo2_matchWithTypos(MMOPattern pattern, String s, int maxTypos) { if (maxTypos == 0) return mmo2_match(pattern, s); Integer score = mmo2_levenWithSwapsScore(pattern, s); return score != null && score <= maxTypos; } static List parseBusinessHours_pcall(String s) { try { return parseBusinessHours(s); } catch (Throwable __e) { _handleException(__e); } return null; } // convert things like "18-8" to "18-24, 0-8" static List splitBusinessHoursAtMidnight(List l) { return concatLists(map(l, r -> r.end < r.start && r.start > 12 ? ll(intRange(r.start, 24*60), intRange(0, r.end)) : ll(r))); } static int minuteInDay() { return minuteInDay(java.util.Calendar.getInstance()); } static int minuteInDay(java.util.Calendar c) { return hours(c)*60+minutes(c); } static int minuteInDay(long time) { return minuteInDay(calendarFromTime(time)); } static int minuteInDay(TimeZone tz) { return minuteInDay(now(), tz); } static int minuteInDay(long time, TimeZone tz) { return minuteInDay(calendarFromTime(time, tz)); } static TimeZone timeZone(String name) { return TimeZone.getTimeZone(name); } static boolean anyIntRangeContains(Iterable ranges, int i) { return any(ranges, r -> intRangeContains(r, i)); } static int syncLengthLevel2(Collection l) { int sum = 0; for (Collection c : cloneList(l)) sum += syncL(c); return sum; } static List concatLists_syncIndividual(Iterable... lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) syncAddAll(l, list); return l; } static List concatLists_syncIndividual(Collection> lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) syncAddAll(l, list); return l; } static List syncListPlus(Collection l, A... more) { if (l == null) return asList(more); synchronized(collectionMutex(l)) { return concatLists(l, asList(more)); } } static void syncClear(Collection c) { if (c != null) synchronized(collectionMutex(c)) { c.clear(); } } static void syncClear(Map map) { if (map != null) synchronized(collectionMutex(map)) { map.clear(); } } static A syncPopLast(List l) { if (l == null) return null; synchronized(l) { return popLast(l); } } static List syncPopLast(int n, List l) { if (l == null) return null; synchronized(l) { return popLast(n, l); } } static List syncShallowCloneElements(List l) { return syncLambdaMap(__119 -> shallowClone(__119), l); } static A syncLast(List l) { if (l == null) return null; synchronized(l) { return last(l); } } static String selectedItem(JList l) { return getSelectedItem(l); } static String selectedItem(JComboBox cb) { return getSelectedItem(cb); } static JComboBox jComboBox(final String... items) { return jcombobox(items); } static JComboBox jComboBox(Collection items) { return jcombobox(items); } static JComboBox jComboBox(Collection items, String selectedItem) { return jcombobox(items, selectedItem); } static int showForm_defaultGap = 4; static int showForm_gapBetweenColumns = 10; static JPanel showFormTitled(final String title, final Object... _parts) { JDesktopPane desktop = mainDesktopPane(); if (desktop != null) return showInternalFrameFormTitled(desktop, title, _parts); return swing(new F0() { public JPanel get() { try { final Var frame = new Var(); JPanel panel = showForm_makePanel(false, _parts); frame.set(showForm_makeFrame(title, panel)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final new Var frame;\r\n JPanel panel = showForm_makePanel(false, _p..."; }}); } static JPanel showForm_makePanel(Boolean internalFrame, Object... _parts) { List out = showForm_arrange1(showForm_makeComponents(internalFrame, _parts)); return vstackWithSpacing(out, showForm_defaultGap); } static Set allInterfacesImplementedBy(Class c) { if (c == null) return null; HashSet set = new HashSet(); allInterfacesImplementedBy_find(c, set); return set; } static void allInterfacesImplementedBy_find(Class c, Set set) { if (c.isInterface() && !set.add(c)) return; do { for (Class intf : c.getInterfaces()) allInterfacesImplementedBy_find(intf, set); } while ((c = c.getSuperclass()) != null); } static Method findMethod(Object o, String method, Object... args) { return findMethod_cached(o, method, args); } static boolean findMethod_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 Method findStaticMethod(Class c, String method, Object... args) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (!m.getName().equals(method)) continue; if ((m.getModifiers() & Modifier.STATIC) == 0 || !findStaticMethod_checkArgs(m, args)) continue; return m; } c = c.getSuperclass(); } return null; } static boolean findStaticMethod_checkArgs(Method m, Object[] args) { Class[] types = m.getParameterTypes(); if (types.length != args.length) return false; for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) return false; return true; } static List quoteAll(Collection l) { List x = new ArrayList(); for (String s : l) x.add(quote(s)); return x; } static boolean arraysEqual(Object[] a, Object[] b) { if (a.length != b.length) return false; for (int i = 0; i < a.length; i++) if (neq(a[i], b[i])) return false; return true; } static String humanizeShortName(Object o) { return humanizeLabel(shortName(o)); } static A postProcess(Object f, A a) { return callPostProcessor(f, a); } static A postProcess(IF1 f, A a) { return callPostProcessor(f, a); } static Map> getFieldOrder_cache = weakMap(); static List getFieldOrder(Object o) { return getFieldOrder(_getClass(o)); } static List getFieldOrder(Class c) { if (c == null) return null; return getOrCreate(getFieldOrder_cache, c, () -> splitAtSpace(toStringOpt(getOpt(c, "_fieldOrder")))); } static Map conceptToMap_gen_withNullValues(Object c) { Map map = litorderedmap("id" , str(getLong(c, "id"))); for (String field : conceptFields_gen(c)) map.put(field, getOpt(c, field)); return map; } static List nonStaticNonTransientFieldObjectsOfType(Class type, Object o) { return filter(nonStaticNonTransientFieldObjects(o), f -> eq(f.getType(), type)); } static B lastValue(NavigableMap map) { if (map == null) return null; Map.Entry entry = map.lastEntry(); return entry == null ? null : entry.getValue(); } static Map.Entry lastEntry(NavigableMap map) { return map == null ? null : map.lastEntry(); } static void removeLastKey(SortedMap map) { if (nempty(map)) map.remove(lastKey(map)); } static List valuesAsList(Map map) { return valuesList(map); } static List cloneKeys(Map map) { return cloneList(keys(map)); } static String optCastString(Object o) { return optCastToString(o); } static boolean isSingleLine(String s) { return !containsNewLine(s); } // TODO: optimize static boolean isUntrimmed(String s) { return neq(s, trim(s)); } static boolean englishStringToBool(String s) { return swicOneOf(s, "y", "t", "1"); } // returns number of changes static int cSmartSet(Concept c, Object... values) { try { if (c == null) return 0; warnIfOddCount(values = expandParams(c.getClass(), values)); int changes = 0; for (int i = 0; i+1 < l(values); i += 2) if (_cSmartSetField(c, (String) values[i], values[i+1])) ++changes; return changes; } catch (Exception __e) { throw rethrow(__e); } } static int cSmartSet_withConverter_pcall(Concept c, Object... values) { return cSmartSet_withConverter_pcall(false, c, values); } static int cSmartSet_withConverter_pcall(boolean verbose, Concept c, Object... values) { return cSmartSet_withConverter_pcall(verbose, new DefaultValueConverterForField(), c, values); } // returns number of changes static int cSmartSet_withConverter_pcall(ValueConverterForField converter, Concept c, Object... values) { return cSmartSet_withConverter_pcall(false, converter, c, values); } static int cSmartSet_withConverter_pcall(boolean verbose, ValueConverterForField converter, Concept c, Object... values) { try { if (c == null) return 0; warnIfOddCount(values = unrollAndExpandParams(c.getClass(), values)); int changes = 0; for (int i = 0; i+1 < l(values); i += 2) { try { if (cSmartSetField_withConverter(c, (String) values[i], values[i+1], converter, verbose)) ++changes; } catch (Throwable __e) { _handleException(__e); }} return changes; } catch (Exception __e) { throw rethrow(__e); } } static boolean hasField(Object o, String field) { return findField2(o, field) != null; } static Concept _getConcept(long id) { return getConcept(id); } static Concept _getConcept(Concepts concepts, long id) { return getConcept(concepts, id); } static A _getConcept(Class cc, long id) { return getConcept(cc, id); } static A _getConcept(Concepts concepts, Class cc, long id) { return getConcept(concepts, cc, id); } static Map orderedMapPutOrCreate(Map map, A key, B value) { if (key != null && value != null) { if (map == null) map = new LinkedHashMap(); map.put(key, value); } return map; } static A trueFalseNull(Boolean b, A onTrue, A onFalse, A onNull) { return b == null ? onNull : b.booleanValue() ? onTrue : onFalse; } static boolean hasBackRefs(Concept c) { return c != null && syncNempty(c.backRefs); } static Class getTypeArgumentAsClass(Type type) { if (type instanceof ParameterizedType) return typeToClass(first(((ParameterizedType) type).getActualTypeArguments())); return null; } static Type genericFieldType(Object o, String field) { Field f = getField(o, field); return f == null ? null : f.getGenericType(); } static List scoredSearch(String query, Iterable data) { Map scores = new HashMap(); List searchTerms = scoredSearch_prepare(query); if (empty(searchTerms)) return asList(data); for (String s : data) { int score = scoredSearch_score(s, searchTerms); if (score != 0) scores.put(s, score); } return keysSortedByValuesDesc(scores); } static Map putOrCreate(Map map, A key, B value) { if (map == null) map = new HashMap(); map.put(key, value); return map; } static Set addToSet_create(Set set, A a) { return createOrAddToSet(set, a); } static Set keySet(Map map) { return map == null ? new HashSet() : map.keySet(); } static Set keySet(Object map) { return keys((Map) map); } static Set keySet(MultiSet ms) { return ms.keySet(); } static Set keySet(MultiMap mm) { return mm.keySet(); } static int keysSize(MultiMap mm) { return lKeys(mm); } static A reverseGet(List l, int idx) { if (l == null || idx < 0) return null; int n = l(l); return idx < n ? l.get(n-1-idx) : null; } static String formatDouble_significant2(double d, int digits) { try { digits -= max(0, Math.floor(Math.log10(abs(d))+1)); return formatDouble(d, digits); } catch (Throwable _e) { print("Had number: " + d + ", digits: " + digits); throw rethrow(_e); } } // TODO: use actualUserHome()? // (there was a problem with onLocallyInferiorJavaX() always triggering inside #1013896) static File pathToJavaxJar() { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.pathToJavaXJar(); return pathToJavaxJar_noResourceLoader(); } static File pathToJavaxJar_noResourceLoader() { try { int x = latestInstalledJavaX(); File xfile = new File(userHome(), ".javax/x" + Math.max(x, 30) + ".jar"); if (!xfile.isFile()) { print("Saving " + f2s(xfile)); String url = x30JarServerURL(); byte[] data = loadBinaryPage(url); if (data.length < 1000000) throw fail("Could not load " + url); saveBinaryFile(xfile.getPath(), data); } return xfile; } catch (Exception __e) { throw rethrow(__e); } } static Set> entrySet(Map map) { return _entrySet(map); } static Method hashMap_findKey_method; static A hashMap_findKey(HashMap map, Object key) { try { if (hashMap_findKey_method == null) hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode"); Map.Entry entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key); // java.util.Map.Entry entry = (java.util.Map.Entry) call(hash, 'getNode, hashMap_internalHash(key), wkey); return entry == null ? null : entry.getKey(); } catch (Exception __e) { throw rethrow(__e); } } static boolean isA(Either e) { return eitherIsA(e); } static Set synchroSet() { return synchroHashSet(); } static Set synchroSet(Set set) { return Collections.synchronizedSet(set); } static Object process(String processorID, Object in) { return process(processorID, in, "in"); } static Object process(String processorID, Object in, String outVar) { try { Class processor = hotwire(processorID); set(processor, "in", in); call(processor, "main", new Object[] {new String[0]}); return get(processor, outVar); } catch (Exception e) { throw new RuntimeException("Error in #" + parseSnippetID(processorID), e); } } static List syncCloneAndClearList(Collection l) { if (l == null) return emptyList(); synchronized(collectionMutex(l)) { List l2 = cloneList(l); l.clear(); return l2; } } static NavigableSet navigableKeys(NavigableMap map) { return map == null ? new TreeSet() : map.navigableKeySet(); } static NavigableSet navigableKeys(MultiSet ms) { return ((NavigableMap) ms.map).navigableKeySet(); } static NavigableSet navigableKeys(MultiMap mm) { return ((NavigableMap) mm.data).navigableKeySet(); } static A vstackCompute(VStack.Computable computation) { return vStackCompute(computation); } static void withCancelPoint(VF1 r) { CancelPoint cp = newCancelPoint(); try { try { callF(r, cp); } catch (Throwable e) { e = innerException(e); if (!(e instanceof CancelToCancelPoint && ((CancelToCancelPoint) e).cp == cp)) rethrow(e); } } finally { _close(cp); }} static void withCancelPoint(IVF1 r) { CancelPoint cp = newCancelPoint(); try { try { r.get(cp); } catch (Throwable e) { e = innerException(e); if (!(e instanceof CancelToCancelPoint && ((CancelToCancelPoint) e).cp == cp)) rethrow(e); } } finally { _close(cp); }} static RuntimeException cancelTo(CancelPoint cp) { if (cp.closed) throw fail("cancel point closed"); throw new CancelToCancelPoint(cp); } static List scoredSearch_prepare(String query) { return map(__120 -> replacePlusWithSpace(__120), splitAtSpace(query)); } static void putUnlessZero(Map map, A key, int value) { if (map != null && key != null && value != 0) map.put(key, value); } static void putUnlessZero(Map map, A key, double value) { if (map != null && key != null && value != 0) map.put(key, value); } // Search for some words in some texts and return a total score static int scoredSearch_score(Iterable l, List words) { int score = 0; if (l != null) for (String s : l) score += scoredSearch_score(s, words); return score; } static int scoredSearch_score(String s, List words) { int score = 0; if (nempty(s)) for (String word : unnullForIteration(words)) score += scoredSearch_score_single(s, word); return score; } static int scoredSearch_score(String s, String query) { return scoredSearch_score(s, scoredSearch_prepare(query)); } // Search for some words in some texts and return a total score static double scoredSearch_scoreWeighted2(Collection> l, List words) { double score = 0; if (l != null) for (Pair __0 : l) { String s = pairA(__0); double weight = pairB(__0); score += scoredSearch_score(s, words)*weight; } return score; } static List keysSortedByValuesDesc(final Map map) { List l = new ArrayList(map.keySet()); sort(l, mapComparatorDesc(map)); return l; } static Scored scoredWithMapValue(Map map, A a) { return new Scored(a, toDouble(mapGet(map, a))); } static A keyWithHighestValue(Map map) { return keyWithBiggestValue(map); } static int hashCodeFor(Object a) { return a == null ? 0 : a.hashCode(); } static String hTitleClean(String title) { return htmldecode(dropTagsAndHTMLComments(title)); } static String nemptyLines(Iterable l) { return lines(nempties(allToStrOrNull(l))); } static String nemptyLines(Object... l) { return nemptyLinesLL(l); } static String ahref_possiblyTargetBlank(String link, Object contents, boolean targetBlank, Object... params) { return ahref(link, contents, targetBlank ? paramsPlus(params, "target" , "_blank") : params); } static String ahref_possiblyTargetBlank(boolean targetBlank, String link, Object contents, Object... params) { return ahref_possiblyTargetBlank(link, contents, targetBlank, params); } static void forEach(Iterable l, IVF1 f) { if (f != null && l != null) for (A a : l) callF(f, a); } static void forEach(IVF1 f, Iterable l) { forEach(l, f); } static void forEach(A[] l, IVF1 f) { if (f != null && l != null) for (A a : l) callF(f, a); } static A setAll(A o, Map fields) { if (fields == null) return o; for (String field : keys(fields)) set(o, field, fields.get(field)); return o; } static A setAll(A o, Object... values) { //values = expandParams(c.getClass(), values); failIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; set(o, field, value); } return o; } static Object[] filterParams(Object[] params, Object... keys) { Set set = asSet(keys); List l = new ArrayList(); for (int i = 0; i < l(params); i += 2) if (contains(set, params[i])) { l.add(params[i]); l.add(params[i+1]); } return asObjectArray(l); } static Object lambdaToToString(IF0 x) { return x == null ? null : new Object() { public String toString() { return str(x.get()); } }; } static boolean notNullOrEmptyString(Object o) { return o != null && !eq(o, ""); } static A setTitle(A c, String title) { return setFrameTitle(c, title); } static A setTitle(String title, A c) { return setFrameTitle(c, title); } static String plural(String s) { return getPlural(s); } static String humanizeLabel(String s) { return humanizeFormLabel(s); } static ThreadLocal componentPopupMenu_mouseEvent; static void componentPopupMenu_init() { { swing(new Runnable() { public void run() { try { if (componentPopupMenu_mouseEvent == null) componentPopupMenu_mouseEvent = (ThreadLocal) vm_generalMap_get("mouseEvent"); if (componentPopupMenu_mouseEvent == null) vm_generalMap_put("componentPopupMenu_mouseEvent" , componentPopupMenu_mouseEvent = new ThreadLocal()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (componentPopupMenu_mouseEvent == null)\r\n componentPopupMenu_mouseEve..."; }}); } } // menuMaker = voidfunc(JPopupMenu) static void componentPopupMenu(final JComponent component, final Object menuMaker) { if (component == null || menuMaker == null) return; { swing(new Runnable() { public void run() { try { Object adapter = componentPopupMenu_initForComponent(component); ((List) _get(adapter, "maker")).add(menuMaker); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Object adapter = componentPopupMenu_initForComponent(component);\r\n ((List)..."; }}); } } static Object componentPopupMenu_initForComponent(final JComponent component) { return component == null ? null : swing(new F0() { public Object get() { try { componentPopupMenu_init(); Object adapter = findComponentPopupMenuListener_gen(component); if (adapter == null) { componentPopupMenu_Adapter a = new componentPopupMenu_Adapter(); //addMouseListener_inFront(component, a); component.addMouseListener(a); adapter = a; } return adapter; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "componentPopupMenu_init();\r\n O adapter = findComponentPopupMenuListener_ge..."; }}); } static class componentPopupMenu_Adapter extends MouseAdapter { List maker = new ArrayList(); boolean internalFrameLeftButtonMagic = false; boolean allowScrolling = true; Point pressedAt; public void mousePressed(MouseEvent e) { displayMenu(e); pressedAt = internalFrameLeftButtonMagic && e.getClickCount() == 1 && internalFrameActive(e.getComponent()) ? e.getLocationOnScreen() : null; } public void mouseReleased(MouseEvent e) { // TODO: show a little less often on left mouse click if (internalFrameLeftButtonMagic && eq(pressedAt, e.getLocationOnScreen())) displayMenu2(e); else displayMenu(e); } void displayMenu(MouseEvent e) { if (e.getSource() instanceof JInternalFrame) return; if (e.isPopupTrigger()) displayMenu2(e); } void populate(JPopupMenu menu, MouseEvent e) { AutoCloseable __1 = tempSetTL(componentPopupMenu_mouseEvent, e); try { for (Object menuMaker : maker) pcallF(menuMaker, menu); vmBus_send("showingPopupMenu", e.getComponent(), menu); } finally { _close(__1); }} void displayMenu2(MouseEvent e) { JPopupMenu menu = new JPopupMenu(); int emptyCount = menu.getComponentCount(); populate(menu, e); if (menu.getComponentCount() == emptyCount) return; if (allowScrolling) { menu = new JPopupMenu(); JMenuScroller scroller = JMenuScroller.setScrollerFor(menu); scroller.fillMenu = new VF1() { public void get(JPopupMenu m) { try { populate(m, e) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "populate(m, e)"; }}; } menu.show(e.getComponent(), e.getX(), e.getY()); } } static A setToolTipText(final A c, final Object toolTip) { if (c == null) return null; { swing(new Runnable() { public void run() { try { String s = nullIfEmpty(str(toolTip)); if (neq(s, c.getToolTipText())) c.setToolTipText(s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "String s = nullIfEmpty(str(toolTip));\r\n if (neq(s, c.getToolTipText()))\r\n ..."; }}); } return c; } static A setToolTipText(Object toolTip, A c) { return setToolTipText(c, toolTip); } static void addMenuItem(JPopupMenu menu, String text, Object action) { menu.add(jmenuItem(text, action)); } static void addMenuItem(JPopupMenu menu, JMenuItem menuItem) { menu.add(menuItem); } static void addMenuItem(JMenu menu, String text, Object action) { menu.add(jmenuItem(text, action)); } static void addMenuItem(Menu menu, String text, Object action) { menu.add(menuItem(text, action)); } static void addMenuItem(JMenu menu, JMenuItem menuItem) { menu.add(menuItem); } static void addMenuItem(JMenuBar menuBar, String text, Runnable action) { addMenuItem(menuBar, jmenuItem(text, action)); } static void addMenuItem(JMenuBar menuBar, JMenuItem menuItem) { addDirectMenuItem(menuBar, menuItem); } static String copyTextToClipboard(Object _text) { String text = str(_text); StringSelection selection = new StringSelection(text); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, selection); vmBus_send("newClipboardContents", text); return text; } static double fileAgeInSeconds(File f) { return f == null ? -1 : msToSeconds(now()-fileModificationTime(f)); } static Path toPath(File f) { return f == null ? null : f.toPath(); } static void printExceptionShort(Throwable e) { printExceptionShort("", e); } static void printExceptionShort(String prefix, Throwable e) { print(prefix, exceptionToStringShort(e)); } // will create the file or update its last modified timestamp static File touchFile(File file) { try { closeRandomAccessFile(newRandomAccessFile(mkdirsForFile(file), "rw")); return file; } catch (Exception __e) { throw rethrow(__e); } } // firstDelay = delay static FixedRateTimer doEvery(long delay, final Object r) { return doEvery(delay, delay, r); } static FixedRateTimer doEvery(long delay, long firstDelay, final Object r) { FixedRateTimer timer = new FixedRateTimer(shorten(programID() + ": " + r, 80)); timer.scheduleAtFixedRate(smartTimerTask(r, timer, toInt(delay)), toInt(firstDelay), toInt(delay)); return vmBus_timerStarted(timer); } // reversed argument order for fun static FixedRateTimer doEvery(double initialSeconds, double delaySeconds, final Object r) { return doEvery(toMS(delaySeconds), toMS(initialSeconds), r); } static FixedRateTimer doEvery(double delaySeconds, final Object r) { return doEvery(toMS(delaySeconds), r); } static void touchExistingFile(File file) { try { if (file == null) return; java.nio.file.Files.setLastModifiedTime(toPath(file), java.nio.file.attribute.FileTime.from(java.time.Instant.now())); } catch (Exception __e) { throw rethrow(__e); } } static A _print(String s, A a) { return print(s, a); } static A _print(A a) { return print(a); } static void _print() { print(); } static boolean _eq(Object a, Object b) { return eq(a, b); } static double ratioToPercent(double x, double y) { return x*100/y; } static Object firstElementNotSubclassing(Iterable l, Class c) { return firstThat(l, o -> !isInstanceOf(o, c)); } static boolean allUnique(Collection l) { return l(uniquify(l)) == l(l); } static List cloneAndClear(Collection l) { return cloneAndClearList(l); } static Class _run(String progID, String... args) { Class main = hotwire(progID); callMain(main, args); return main; } static A syncPopFirst(List l) { if (empty(l)) return null; synchronized(l) { A a = first(l); l.remove(0); return a; } } static void register(Concept c) { registerConcept(c); } static List cloneKeys_noSync(Map map) { return cloneList_noSync(keys(map)); } static IterableIterator navigableMultiSetMapValuesIterator_concurrent(final MultiSetMap mm) { return navigableMultiSetMapValuesIterator_concurrent(mm, mm.data); } static IterableIterator navigableMultiSetMapValuesIterator_concurrent(final MultiSetMap mm, Object mutex) { return iteratorFromFunction(new F0() { Iterator> it = concurrentlyIterateValues((NavigableMap) mm.data, mutex); Iterator it2; public B get() { while (it2 == null || !it2.hasNext()) { if (!it.hasNext()) return null; it2 = iterator(asList(it.next())); } return it2.next(); } }); } static MultiSet multiSetMapToMultiSet(MultiSetMap mm) { MultiSet ms = new MultiSet(); for (A a : keys(mm)) ms.add(a, l(mm.get(a))); return ms; } static String hscriptsrc(String src) { return hjavascript_src(src); } static String linesLL(Object... x) { return lines(ll(x)); } static List syncGetAndClear(Collection l) { return syncCloneAndClearList(l); } static void cancelAndInterruptThread(Thread t) { if (t == null) return; cancelThread(t); t.interrupt(); } static void stepAll(Steppable s) { if (s != null) while (s.step()) { ping(); } } // based on: https://algs4.cs.princeton.edu/33balanced/RedBlackBST.java.html // TODO: implement NavigableSet // RAM use in a CompressedOOPS JVM (bytes per element): // ~12 when elements are inserted in sorted order // ~13.7 when elements are inserted in random order // // (Obviously more for very small sets.) static class HyperCompactTreeSet extends AbstractSet { // A symbol table implemented using a left-leaning red-black BST. // This is the 2-3 version. // Note: We sometimes cast the nullSentinel to A // which is technically incorrect but ok because of type erasure. // May want to fix just to be clean on the source level too. private static final boolean RED = true; private static final boolean BLACK = false; // replacement for null elements private static final Object nullSentinel = new Object(); private Node root; // root of the BST int size; // size of tree set // BST helper node data type abstract static class Node { A val; // associated data Node left() { return null; } // get left subtree abstract Node setLeft(Node left); // set left subtree - return potentially replaced node Node right() { return null; } // get right subtree abstract Node setRight(Node right); // set right subtree - return potentially replaced node abstract boolean color(); abstract Node convertToBlack(); abstract Node convertToRed(); abstract Node invertColor(); Node convertToColor(boolean color) { return color == RED ? convertToRed() : convertToBlack(); } abstract boolean isLeaf(); } // This represents a common case near a red leaf - a black node // containing a red leaf in the left slot and null in the right slot. // Combined with the direct storage of black leaf children in NonLeaf, // this is all we need to get rid of all the leaf overhead. Yay! static class SpecialNode extends Node { A leftVal; SpecialNode(A leftVal, A val) { this.val = val; this.leftVal = leftVal;} boolean color() { return BLACK; } Node convertToBlack() { return this; } Node convertToRed() { return newNode(RED, val, left(), right()); } Node invertColor() { return convertToRed(); } Node left() { return newLeaf(RED, leftVal); } Node setLeft(Node left) { // Can we keep the optimized representation? (Probably this // is never going to be true.) if (left != null && left.isLeaf() && left.color() == RED) { leftVal = left.val; return this; } else return newNode(BLACK, val, left, right()); } Node right() { return null; } Node setRight(Node right) { if (right == null) return this; return newNode(color(), val, left(), right); } boolean isLeaf() { return false; } } abstract static class NonLeaf extends Node { // either a Node or a (sentinelled) direct user value Object left, right; // color of leaf if left is a user value boolean defaultLeftLeafColor() { return BLACK; } // color of leaf if right is a user value boolean defaultRightLeafColor() { return BLACK; } Node left() { return left == null ? null : left instanceof Node ? (Node) left : newLeaf(defaultLeftLeafColor(), (A) left); } void setLeft_noMorph(Node left) { this.left = left != null && left.isLeaf() && left.color() == defaultLeftLeafColor() ? left.val : left; } void setRight_noMorph(Node right) { this.right = right != null && right.isLeaf() && right.color() == defaultRightLeafColor() ? right.val : right; } Node setLeft(Node left) { if (color() == BLACK && right == null && left != null && left.isLeaf() && left.color() == RED) return new SpecialNode(left.val, val); setLeft_noMorph(left); if (left == null && right() == null) return newLeaf(color(), val); return this; } Node right() { return right == null ? null : right instanceof Node ? (Node) right : newLeaf(defaultRightLeafColor(), (A) right); } Node setRight(Node right) { // Setting right to null may produce either a leaf or a // special node, so we just go through newNode. if (right == null && this.right != null) return newNode(color(), val, left(), null); // New right is not null, so we compress (if possible) and store it setRight_noMorph(right); return this; } boolean isLeaf() { return false; } } static class BlackNode extends NonLeaf { BlackNode(A val) { this.val = val;} boolean color() { return BLACK; } Node convertToBlack() { return this; } Node convertToRed() { return newNode(RED, val, left(), right()); } Node invertColor() { return convertToRed(); } } static class RedNode extends NonLeaf { RedNode(A val) { this.val = val;} boolean color() { return RED; } Node convertToBlack() { return newNode(BLACK, val, left(), right()); } Node convertToRed() { return this; } Node invertColor() { return convertToBlack(); } } abstract static class Leaf extends Node { boolean isLeaf() { return true; } Node setLeft(Node left) { return left == null ? this : newNode(color(), val, left, null); } Node setRight(Node right) { return right == null ? this : newNode(color(), val, null, right); } } static class BlackLeaf extends Leaf { BlackLeaf(A val) { this.val = val;} boolean color() { return BLACK; } Node convertToBlack() { return this; } Node convertToRed() { return new RedLeaf(val); } Node invertColor() { return convertToRed(); } } static class RedLeaf extends Leaf { RedLeaf(A val) { this.val = val;} boolean color() { return RED; } Node convertToBlack() { return new BlackLeaf(val); } Node convertToRed() { return this; } Node invertColor() { return convertToBlack(); } } HyperCompactTreeSet() {} HyperCompactTreeSet(Collection cl) { addAll(cl); } private static Object deSentinel(Object o) { return o == nullSentinel ? null : o; } private static Object sentinel(Object o) { return o == null ? nullSentinel : o; } // returns false on null (algorithm needs this) static boolean isRed(Node x) { return x != null && x.color() == RED; } static Node newLeaf(boolean color, A val) { return color == RED ? new RedLeaf(val) : new BlackLeaf(val); } static Node newNode(boolean color, A val, Node left, Node right) { // Make leaf (always a temporary object now) if (left == null && right == null) return newLeaf(color, val); // Make special node if (color == BLACK && right == null && left != null && left.isLeaf() && left.color() == RED) return new SpecialNode(left.val, val); // Make normal non-leaf NonLeaf node = color == RED ? new RedNode(val) : new BlackNode(val); node.setLeft_noMorph(left); node.setRight_noMorph(right); return node; } public int size() { return size; } public boolean isEmpty() { return root == null; } public boolean add(A val) { val = (A) sentinel(val); int oldSize = size; root = put(root, val); root = root.convertToBlack(); return size > oldSize; } // insert the value in the subtree rooted at h private Node put(Node h, A val) { if (h == null) { ++size; return new RedLeaf(val); } int cmp = compare_deSentinel(val, h.val); if (cmp < 0) h = h.setLeft(put(h.left(), val)); else if (cmp > 0) h = h.setRight(put(h.right(), val)) ; else { /*h.val = val;*/ } // no overwriting // fix-up any right-leaning links if (isRed(h.right()) && !isRed(h.left())) h = rotateLeft(h); if (isRed(h.left()) && isRed(h.left().left())) h = rotateRight(h); if (isRed(h.left()) && isRed(h.right())) h = flipColors(h); return h; } final int compare_deSentinel(A a, A b) { return compare((A) deSentinel(a), (A) deSentinel(b)); } // override me if you wish int compare(A a, A b) { return cmp(a, b); } public boolean remove(Object key) { if (!contains(key)) return false; key = sentinel(key); // if both children of root are black, set root to red if (!isRed(root.left()) && !isRed(root.right())) root = root.convertToRed(); root = delete(root, (A) key); if (!isEmpty()) root = root.convertToBlack(); // assert check(); return true; } // delete the key-value pair with the given key rooted at h private Node delete(Node h, A key) { // assert get(h, key) != null; if (compare_deSentinel(key, h.val) < 0) { if (!isRed(h.left()) && !isRed(h.left().left())) h = moveRedLeft(h); h = h.setLeft(delete(h.left(), key)); } else { if (isRed(h.left())) h = rotateRight(h); if (compare_deSentinel(key, h.val) == 0 && (h.right() == null)) { --size; return null; } if (!isRed(h.right()) && !isRed(h.right().left())) h = moveRedRight(h); if (compare_deSentinel(key, h.val) == 0) { --size; Node x = min(h.right()); h.val = x.val; // h.val = get(h.right(), min(h.right()).val); // h.val = min(h.right()).val; h = h.setRight(deleteMin(h.right())); } else h = h.setRight(delete(h.right(), key)); } return balance(h); } // make a left-leaning link lean to the right private Node rotateRight(Node h) { // assert (h != null) && isRed(h.left()); Node x = h.left(); h = h.setLeft(x.right()); x = x.setRight(h); x = x.convertToColor(x.right().color()); x = x.setRight(x.right().convertToRed()); return x; } // make a right-leaning link lean to the left private Node rotateLeft(Node h) { // assert (h != null) && isRed(h.right()); Node x = h.right(); h = h.setRight(x.left()); x = x.setLeft(h); x = x.convertToColor(x.left().color()); x = x.setLeft(x.left().convertToRed()); return x; } // flip the colors of a node and its two children private Node flipColors(Node h) { // h must have opposite color of its two children // assert (h != null) && (h.left() != null) && (h.right() != null); // assert (!isRed(h) && isRed(h.left()) && isRed(h.right())) // || (isRed(h) && !isRed(h.left()) && !isRed(h.right())); h = h.setLeft(h.left().invertColor()); h = h.setRight(h.right().invertColor()); return h.invertColor(); } // Assuming that h is red and both h.left() and h.left().left() // are black, make h.left() or one of its children red. private Node moveRedLeft(Node h) { // assert (h != null); // assert isRed(h) && !isRed(h.left()) && !isRed(h.left().left()); h = flipColors(h); if (isRed(h.right().left())) { h = h.setRight(rotateRight(h.right())); h = rotateLeft(h); h = flipColors(h); } return h; } // Assuming that h is red and both h.right() and h.right().left() // are black, make h.right() or one of its children red. private Node moveRedRight(Node h) { // assert (h != null); // assert isRed(h) && !isRed(h.right()) && !isRed(h.right().left()); h = flipColors(h); if (isRed(h.left().left())) { h = rotateRight(h); h = flipColors(h); } return h; } // restore red-black tree invariant private Node balance(Node h) { // assert (h != null); if (isRed(h.right())) h = rotateLeft(h); if (isRed(h.left()) && isRed(h.left().left())) h = rotateRight(h); if (isRed(h.left()) && isRed(h.right())) h = flipColors(h); return h; } /** * Returns the height of the BST (for debugging). * @return the height of the BST (a 1-node tree has height 0) */ public int height() { return height(root); } private int height(Node x) { if (x == null) return -1; return 1 + Math.max(height(x.left()), height(x.right())); } public boolean contains(Object val) { return find(root, (A) sentinel(val)) != null; } public A find(A probeVal) { probeVal = (A) sentinel(probeVal); Node n = find(root, probeVal); return n == null ? null : n.val; } // value associated with the given key in subtree rooted at x; null if no such key private A get(Node x, A key) { x = find(x, key); return x == null ? null : x.val; } Node find(Node x, A key) { while (x != null) { int cmp = compare_deSentinel(key, x.val); if (cmp < 0) x = x.left(); else if (cmp > 0) x = x.right(); else return x; } return null; } private boolean check() { if (!is23()) println("Not a 2-3 tree"); if (!isBalanced()) println("Not balanced"); return is23() && isBalanced(); } // Does the tree have no red right links, and at most one (left) // red links in a row on any path? private boolean is23() { return is23(root); } private boolean is23(Node x) { if (x == null) return true; if (isRed(x.right())) return false; if (x != root && isRed(x) && isRed(x.left())) return false; return is23(x.left()) && is23(x.right()); } // do all paths from root to leaf have same number of black edges? private boolean isBalanced() { int black = 0; // number of black links on path from root to min Node x = root; while (x != null) { if (!isRed(x)) black++; x = x.left(); } return isBalanced(root, black); } // does every path from the root to a leaf have the given number of black links? private boolean isBalanced(Node x, int black) { if (x == null) return black == 0; if (!isRed(x)) black--; return isBalanced(x.left(), black) && isBalanced(x.right(), black); } public void clear() { root = null; size = 0; } // the smallest key in subtree rooted at x; null if no such key private Node min(Node x) { // assert x != null; while (x.left() != null) x = x.left(); return x; } private Node deleteMin(Node h) { if (h.left() == null) return null; if (!isRed(h.left()) && !isRed(h.left().left())) h = moveRedLeft(h); h = h.setLeft(deleteMin(h.left())); return balance(h); } public Iterator iterator() { return new MyIterator(); } class MyIterator extends IterableIterator { List> path = new ArrayList(); MyIterator() { fetch(root); } void fetch(Node node) { while (node != null) { path.add(node); node = node.left(); } } public boolean hasNext() { return !path.isEmpty(); } public A next() { if (path.isEmpty()) throw fail("no more elements"); Node node = popLast(path); // last node is always a leaf, so left is null // so proceed to fetch right branch fetch(node.right()); return (A) deSentinel(node.val); } } // Returns the smallest key in the symbol table greater than or equal to {@code key}. public A ceiling(A key) { key = (A) sentinel(key); Node x = ceiling(root, key); return x == null ? null : x.val; } // the smallest key in the subtree rooted at x greater than or equal to the given key Node ceiling(Node x, A key) { if (x == null) return null; int cmp = compare_deSentinel(key, x.val); if (cmp == 0) return x; if (cmp > 0) return ceiling(x.right(), key); Node t = ceiling(x.left(), key); if (t != null) return t; else return x; } public A floor(A key) { key = (A) sentinel(key); Node x = floor(root, key); return x == null ? null : x.val; } // the largest key in the subtree rooted at x less than or equal to the given key Node floor(Node x, A key) { if (x == null) return null; int cmp = compare_deSentinel(key, x.val); if (cmp == 0) return x; if (cmp < 0) return floor(x.left(), key); Node t = floor(x.right(), key); if (t != null) return t; else return x; } void testInternalStructure() { // one leaf object (root) is allowed - could even optimize that assertTrue(countLeafObjects() <= 1); } // count leaf objects we didn't optimize away int countLeafObjects() { return countLeafObjects(root); } int countLeafObjects(Node node) { if (node instanceof Leaf) return 1; if (node instanceof NonLeaf) return countLeafObjects(optCast(Node.class, ((NonLeaf) node).left)) + countLeafObjects(optCast(Node.class, ((NonLeaf) node).right)); return 0; } Collection unoptimizedNodes() { List out = new ArrayList(); findUnoptimizedNodes(out); return out; } void findUnoptimizedNodes(List out) { findUnoptimizedNodes(root, out); } void findUnoptimizedNodes(Node node, List out) { if (node == null) return; if (node instanceof NonLeaf) { if (isUnoptimizedNode((NonLeaf) node)) out.add((NonLeaf) node); findUnoptimizedNodes(optCast(Node.class, ((NonLeaf) node).left), out); findUnoptimizedNodes(optCast(Node.class, ((NonLeaf) node).right), out); } } boolean isUnoptimizedNode(Node node) { if (node instanceof NonLeaf) return ((NonLeaf) node).left instanceof Leaf || ((NonLeaf) node).right instanceof Leaf; return false; } // Compact me (minimize space use). Returns a compacted copy. // This only has an effect when elements were inserted in non-sorted // or and/or elements were removed. HyperCompactTreeSet compact() { return new HyperCompactTreeSet(this); } } // step = e.g. 100 // value = 0 to count static String pageNav2(String baseLink, int count, int value, int step, String nVar, Object... __) { List l = new ArrayList(); baseLink = unnull(baseLink) + (contains(baseLink, "?") ? "&" : "?") + urlencode(nVar) + "="; if (value > 0) l.add(ahref(baseLink + max(0, value-step), stringPar("leftArrow", __, htmlencode(unicode_leftPointingTriangle())))); for (int i = 0; i < count; i += step) { int n = i/step+1; if (pageNav2_showPage(value, i, step, count)) if (value == i) l.add(b(n)); else l.add(ahref(baseLink + i, n)); } if (value+step < count) l.add(ahref(baseLink + (value+step), stringPar("rightArrow", __, htmlencode(unicode_rightPointingTriangle())))); return p("Pages: " + lines(l)); } static boolean pageNav2_showPage(int actual, int i, int step, int count) { int diff = abs(actual-i)/step; return i == 0 || i >= (count-1)/step*step || diff <= 10 || diff <= 100 && ((i/step) % 10) == 9 || diff <= 1000 && ((i/step) % 100) == 99 || ((i/step) % 1000) == 999; } static IntRange intRange(int start, int end) { return new IntRange(start, end); } static boolean stdEq2(Object a, Object b) { if (a == null) return b == null; if (b == null) return false; if (a.getClass() != b.getClass()) return false; for (String field : allFields(a)) if (neq(getOpt(a, field), getOpt(b, field))) return false; return true; } static int stdHash2(Object a) { if (a == null) return 0; return stdHash(a, toStringArray(allFields(a))); } static String h1_title(String s) { return htitle_h1(s); } static String dbBotName(String progIDWithCase) { return fsI_flex(progIDWithCase) + " Concepts"; } static Map findBot_cache = synchroHashMap(); static int findBot_timeout = 5000; static DialogIO findBot(String searchPattern) { // first split off sub-bot suffix String subBot = null; int i = searchPattern.indexOf('/'); if (i >= 0 && (isJavaIdentifier(searchPattern.substring(0, i)) || isInteger(searchPattern.substring(0, i)))) { subBot = searchPattern.substring(i+1); searchPattern = searchPattern.substring(0, i); if (!isInteger(searchPattern)) searchPattern = "Multi-Port at " + searchPattern + "."; } // assume it's a port if it's an integer if (isInteger(searchPattern)) return talkToSubBot(subBot, talkTo(parseInt(searchPattern))); if (eq(searchPattern, "remote")) return talkToSubBot(subBot, talkTo("second.tinybrain.de", 4999)); Integer port = findBot_cache.get(searchPattern); if (port != null) try { DialogIO io = talkTo("localhost", port); io.waitForLine(/*findBot_timeout*/); // TODO: implement String line = io.readLineNoBlock(); if (indexOfIgnoreCase(line, searchPattern) == 0) { call(io, "pushback", line); // put hello string back in return talkToSubBot(subBot, io); } } catch (Exception e) { e.printStackTrace(); } List bots = quickBotScan(); // find top-level bots for (ProgramScan.Program p : bots) { if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) { // strict matching - start of hello string only, but case-insensitive findBot_cache.put(searchPattern, p.port); return talkToSubBot(subBot, talkTo("localhost", p.port)); } } // find sub-bots for (ProgramScan.Program p : bots) { String botName = firstPartOfHelloString(p.helloString); boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM."); boolean shouldRecurse = startsWithIgnoreCase(botName, "Multi-Port") || isVM; if (shouldRecurse) try { Map subBots = (Map) unstructure(sendToLocalBotQuietly(p.port, "list bots")); for (Number vport : subBots.keySet()) { String name = subBots.get(vport); if (startsWithIgnoreCase(name, searchPattern)) return talkToSubBot(vport.longValue(), talkTo("localhost", p.port)); } } catch (Throwable __e) { print(exceptionToStringShort(__e)); } } return null; } static void nohupJavax(final String javaxargs) { { startThread(new Runnable() { public void run() { try { call(hotwireOnce("#1008562"), "nohupJavax", javaxargs); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "call(hotwireOnce(\"#1008562\"), \"nohupJavax\", javaxargs);"; }}); } } static void nohupJavax(final String javaxargs, final String vmArgs) { { startThread(new Runnable() { public void run() { try { call(hotwireOnce("#1008562"), "nohupJavax", javaxargs, vmArgs); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "call(hotwireOnce(\"#1008562\"), \"nohupJavax\", javaxargs, vmArgs);"; }}); } } static long waitForBotStartUp_timeoutSeconds = 60; // returns address or fails static String waitForBotStartUp(String botName) { for (int i = 0; i < waitForBotStartUp_timeoutSeconds; i++) { sleepSeconds(i == 0 ? 0 : 1); String addr = getBotAddress(botName); if (addr != null) return addr; } throw fail("Bot not found: " + quote(botName)); } static Object rpc(String botName, String method, Object... args) { return unstructure_matchOK2OrFail( sendToLocalBot(botName, rpc_makeCall(method, args))); } static Object rpc(DialogIO bot, String method, Object... args) { return unstructure_matchOK2OrFail( bot.ask(rpc_makeCall(method, args))); } static String rpc_makeCall(String method, Object... args) { if (empty(args)) return "call " + method; return format("call *", concatLists((List) ll(method), asList(args))); } static boolean step(Steppable steppable) { return steppable == null ? null : steppable.step(); } static Object trimIfString(Object o) { return o instanceof String ? trim((String) o) : o; } static int toIntPercent(double ratio) { return roundToInt(ratio*100); } // when used with map function static int toIntPercent(float ratio) { return toIntPercent((double) ratio); } static Boolean not(Boolean b) { return b == null ? null : !b; } static JPanel jSection(Component c) { return jSection("", c); } static JPanel jSection(final String title, final Component c) { return swing(new F0() { public JPanel get() { try { Border border = BorderFactory.createBevelBorder(BevelBorder.LOWERED); border = BorderFactory.createTitledBorder(border, title); JSection panel = new JSection(c); panel.setBorder(border); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Border border = BorderFactory.createBevelBorder(BevelBorder.LOWERED);\r\n bo..."; }}); } static JPanel jSection(String title) { return jSection(title, jpanel()); } static JPanel jSection(LiveValue lv, Component c) { return jLiveValueSection(lv, c); } static JScrollPane jscroll_copyBackground(Component c) { return setScrollPaneBackground(getBackground(c), jscroll(c)); } static JFastLogView_noWrap jFastLogView_noWrap() { return jFastLogView_noWrap(""); } static JFastLogView_noWrap jFastLogView_noWrap(String text) { return withTypeWriterFont(swingNu(JFastLogView_noWrap.class, text)); } static String getPrintLog() { return str(print_log); } static A componentPopupMenuItem(A c, final String name, final Object action) { componentPopupMenu(c, new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, name, action); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, name, action);"; }}); return c; } static void componentPopupMenuItem(JComponent c, final JMenuItem menuItem) { componentPopupMenu(c, new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, menuItem); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, menuItem);"; }}); } static void dm_clearModulePrintLog() { clearStringBuffer_gen(dm_current_mandatory()._actualPrintLog()); } static A awtEvery(A component, long delay, Object r) { swingEvery(component, delay, r); return component; } static A awtEvery(A component, long delay, long firstDelay, Object r) { swingEvery(component, delay, firstDelay, r); return component; } static void awtEvery(RootPaneContainer frame, long delay, Object r) { swingEvery(frame, delay, r); } static A awtEvery(A component, double delaySeconds, Object r) { return awtEvery(component, toMS(delaySeconds), r); } static boolean scrollAllTheWayDown_bruteForce = true; static A scrollAllTheWayDown(A c) { if (c != null) { swing(new Runnable() { public void run() { try { int h = c.getHeight()-1; if (scrollAllTheWayDown_bruteForce) h = max(h, 100000); c.scrollRectToVisible(new Rectangle(0, h, 1, 1)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "int h = c.getHeight()-1;\r\n if (scrollAllTheWayDown_bruteForce) h = max(h, ..."; }}); } return c; } // independent timer static void awtLater(int delay, final Object r) { swingLater(delay, r); } static void awtLater(Object r) { swingLater(r); } // dependent timer (runs only when component is visible) static void awtLater(JComponent component, int delay, Object r) { installTimer(component, r, delay, delay, false); } static void awtLater(JFrame frame, int delay, Object r) { awtLater(frame.getRootPane(), delay, r); } static StringBuffer liveLocalPrintLog_realMC(Object o) { Object realMC = getMainClass(o); if (realMC != mc()) return (StringBuffer) getOpt(realMC, "local_log"); return liveLocalPrintLog(); } static Set emptySet() { return new HashSet(); } static Map.Entry firstEntry(Map map) { return empty(map) ? null : first(map.entrySet()); } static volatile boolean readLine_noReadLine = false; static String readLine_lastInput; static String readLine_prefix = "[] "; static String readLine() { if (readLine_noReadLine) return null; String s = readLineHidden(); if (s != null) { readLine_lastInput = s; print(readLine_prefix + s); } return s; } 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 List syncList() { return synchroList(); } static List syncList(List l) { return synchroList(l); } static Dimension getPreferredSize(final Component c) { return c == null ? null : swing(new F0() { public Dimension get() { try { return c.getPreferredSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getPreferredSize();"; }}); } static int getWidth(Component c) { return c == null ? 0 : (int) swingCall(c, "getWidth"); } static int getHeight(Component c) { return c == null ? 0 : (int) swingCall(c, "getHeight"); } static Color getBackground(final Component c) { return c == null ? null : swing(new F0() { public Color get() { try { return c.getBackground(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getBackground();"; }}); } static FontMetrics componentFontMetrics(JComponent c) { Font font = getFont(c); return font == null ? null : swing(new F0() { public FontMetrics get() { try { return c.getFontMetrics(font); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getFontMetrics(font);"; }}); } static int idiv_ceil(int a, int b) { return (a+b-1)/b; } static int idiv_ceil(long a, long b) { return toInt_checked((a+b-1)/b); } static List asSyncList(Iterable l) { return asSynchroList(l); } static A _revalidate(A c) { return revalidate(c); } static void _revalidate(JFrame f) { revalidate(f); } static void _revalidate(JInternalFrame f) { revalidate(f); } static String getText(final AbstractButton c) { return c == null ? "" : (String) swingAndWait(new F0() { public Object get() { try { return c.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getText();"; }}); } static String getText(final JTextComponent c) { return c == null ? "" : (String) swingAndWait(new F0() { public Object get() { try { return c.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getText();"; }}); } static String getText(final JLabel l) { return l == null ? "" : (String) swingAndWait(new F0() { public Object get() { try { return l.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret l.getText();"; }}); } // returns the contents of text field for editable combo box static String getText(final JComboBox cb) { if (cb == null) return null; if (isEditableComboBox(cb)) return unnull((String) cb.getEditor().getItem()); else return str(cb.getSelectedItem()); } static String lines_rtrimIf(boolean rtrim, List lines) { return rtrim ? lines_rtrim(lines) : lines(lines); } static Object _onAWTEnter_f; static AutoCloseable _onAWTEnter() { return (AutoCloseable) callF(_onAWTEnter_f); } static JDesktopPane mainDesktopPane_value; static JDesktopPane mainDesktopPane() { return mainDesktopPane_value; } static void cancelTimer(javax.swing.Timer timer) { if (timer != null) timer.stop(); } static void cancelTimer(java.util.Timer timer) { if (timer != null) timer.cancel(); } static void cancelTimer(Object o) { if (o instanceof java.util.Timer) cancelTimer((java.util.Timer) o); else if (o instanceof javax.swing.Timer) cancelTimer((javax.swing.Timer) o); else if (o instanceof AutoCloseable) { try { ((AutoCloseable) o).close(); } catch (Throwable __e) { _handleException(__e); }} } volatile static boolean conceptsAndBot_running = false; static boolean conceptsAndBot_thinOnStart = true; static void conceptsAndBot() { conceptsAndBot(null); } static void conceptsAndBot(Integer autoSaveInterval) { if (conceptsAndBot_running) return; conceptsAndBot_running = true; Concepts cc = db_mainConcepts(); try { if (cc.useFileLock) { if (!cc.fileLock().tryToLock()) { ensureDBNotRunning(dbBotStandardName()); cc.fileLock().forceLock(); } } else ensureDBNotRunning(dbBotStandardName()); } catch (Throwable _e) { cc.dontSave = true; // SAFETY throw rethrow(_e); } cc.persist(autoSaveInterval); dbBot(false); if (conceptsAndBot_thinOnStart) { try { thinAProgramsBackups(getDBProgramID(), true); } catch (Throwable __e) { _handleException(__e); }} } static void addToStringTree(StringTree2 tree, List tokC, A leafValue) { if (leafValue == null) return; if (empty(tokC)) { tree.leafValue = leafValue; return; } String s = first(tokC); StringTree2 t = tree.getOrAdd(s); addToStringTree(t, dropFirst(tokC), leafValue); } static TokCondition toTokCondition(ITokCondition condition) { return condition == null ? null : condition instanceof TokCondition ? (TokCondition) condition : new TokCondition() { public boolean get(List tok, int i) { return condition.get(tok, i); } }; } static String applyTranspilationFunction(IVF1> f, String s) { if (f == null) return s; List tok = javaTok(s); f.get(tok); return join(tok); } // out: func(L tok, int cIndex) -> S // condition: func(L tok, int nIndex) -> S [yeah it's inconsistent] static String jreplace_dyn(String s, String in, Object out) { return jreplace_dyn(s, in, out, null); } static String jreplace_dyn(String s, String in, Object out, Object condition) { List tok = javaTok(s); jreplace_dyn(tok, in, out, condition); return join(tok); } // leaves tok properly tokenized // returns true iff anything was replaced static boolean jreplace_dyn(List tok, String in, Object out) { return jreplace_dyn(tok, in, out, false, true, null); } static boolean jreplace_dyn(List tok, String in, IF0 out) { return jreplace_dyn(tok, in, if0ToIF2(out)); } static boolean jreplace_dyn(List tok, String in, IF2, Integer, String> out) { return jreplace_dyn(tok, in, (Object) out); } static boolean jreplace_dyn(List tok, String in, IF2, Integer, String> out, IF2, Integer, Boolean> condition) { return jreplace_dyn(tok, in, (Object) out, (Object) condition); } static boolean jreplace_dyn(List tok, String in, Object out, Object condition) { return jreplace_dyn(tok, in, out, false, true, condition); } static boolean jreplace_dyn(List tok, String in, Object out, boolean ignoreCase, boolean reTok, Object condition) { List tokin = javaTok(in); jfind_preprocess(tokin); String[] toks = toStringArray(codeTokensOnly(tokin)); boolean anyChange = false; for (int n = 0; n < 10000; n++) { int i = findCodeTokens(tok, 1, ignoreCase, toks, condition); if (i < 0) return anyChange; String expansion = (String) (callF(out, tok, i)); int end = i+l(tokin)-2; clearAllTokens(tok, i, end); // C to C tok.set(i, expansion); if (reTok) // would this ever be false?? reTok(tok, i, end); anyChange = true; } throw fail("woot? 10000! " + quote(in) + " => " + quote(out)); } static List splitAtJavaToken(String s, String splitToken) { return splitByJavaToken(s, splitToken); } // "i call you $name" static void tok_transpileIfQuoted_dollarVars(List tok) { jreplace_dyn(tok, "if ", new F2, Integer, String>() { public String get(List tok, Integer cIdx) { try { String pat = unquote(tok.get(cIdx+2)); List vars = new ArrayList(); String pat2 = dollarVarsToStars(pat, vars); int iCurly = cIdx+4; tok_statementToBlock(tok, iCurly); tokAppend_reTok(tok, iCurly, joinWithSpace(mapWithIndexStartingAt1(vars, (i, var) -> ("S " + var + " = $" + i + ";")))); return ("if (match(" + (quote(pat2)) + ", s, m))"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "S pat = unquote(tok.get(cIdx+2));\r\n new LS vars;\r\n S pat2 = dollarVarsT..."; }}, new TokCondition() { public boolean get(final List tok, final int i) { return containsDollarVars(unquote(tok.get(i+3))); }}); } static boolean startsAndEndsWith(String a, char c) { return startsWith(a, c) && endsWith(a, c) && l(a) >= 2; } static boolean startsAndEndsWith(String a, String b) { return startsWith(a, b) && endsWith(a, b) && l(a) >= l(b)*2; } public static List parseSnippetIDs(Collection snippetIDs) { List l = new ArrayList(); for (String id : snippetIDs) l.add(str(parseSnippetID(id))); return l; } static Map trimValues(Map map) { Map map2 = similarEmptyMap(map); for (A key : keys(map)) map2.put(key, trim(map.get(key))); return map2; } static Map transformKeys(Map map, Object f) { HashMap m = new HashMap(); for (Object key : keys(map)) m.put(callF(f, key), map.get(key)); return m; } static Map standardCredentialsMap() { String user = standardCredentialsUser(); String pass = standardCredentialsPass(); if (nempty(user) && nempty(pass)) return litmap("_user" , user, "_pass" , pass); return litmap(); } static String regexp_findSnippetIDs() { return "(?:#|\\blib\\s+|(?:^|\\n)\\s*!)([0-9]+)"; } static String snippetLink(String id) { return empty(id) ? id : snippetLink(parseSnippetID(id)); } static String snippetLink(long id) { return "https://code.botcompany.de/" + id; } static SnippetTitles getSnippetTitle_cached_cache = new SnippetTitles(); static String getSnippetTitle_cached(String id) { return getSnippetTitle_cached_cache.get(id); } static void getSnippetTitle_cached_put(String id, String title) { getSnippetTitle_cached_cache.put(id, title); } static boolean getSnippetTitle_cached_has(String id) { return getSnippetTitle_cached_cache.has(id); } static String getSnippetTitle_cached_probe(String id) { return getSnippetTitle_cached_cache.probe(id); } static void mreplace(Matcher m, StringBuffer buf, String s) { m.appendReplacement(buf, Matcher.quoteReplacement(s)); } static Map stdFunctions_uncached() { return stdFunctions_uncached(new HashMap()); } static Map stdFunctions_uncached(Map map) { parseStdFunctionsList(loadSnippetSilently("#1006654"), map); parseStdFunctionsList(loadSnippetSilently("#761"), map); return map; } static List tlft_j(String text) { return toLinesFullTrim_java(text); } static String dropNumberPrefix(String s) { return dropFirst(s, indexOfNonDigit(s)); } static String nlToBr_withIndents(String s) { s = nlToBr(s); StringBuilder buf = new StringBuilder(); int i = 0; while (i < l(s)) { while (charAt(s, i) == ' ') { ++i; buf.append(" "); } int j = smartIndexOf(s, i, '\n')+1; buf.append(substring(s, i, j)); i = j; } return str(buf); } static String htmlEncode(String s) { return htmlencode(s); } static int cmpAlphaNum(String a, String b) { return alphaNumComparator().compare(a, b); } static List lazilyMap(IF1 f, List l) { return lazyMap((Object) f, l); } static List lazilyMap(final Object f, final List l) { return new RandomAccessAbstractList() { final int size = l(l); HashMap data = new HashMap(); public int size() { return size; } public Object get(int i) { if (data.containsKey(i)) return data.get(i); Object o = callF(f, l.get(i)); data.put(i, o); return o; } }; } static Map mapMapToMap(IF2> f, Map m) { if (m == null) return null; Map map = similarEmptyMap(m); for (Map.Entry e : m.entrySet()) { Pair p = f.get(e.getKey(), e.getValue()); map.put(p.a, p.b); } return map; } static Map mapMapToMap(Map m, IF2> f) { return mapMapToMap(f, m); } static String unicode_downPointingTriangle() { return charToString(0x25BC); } static String unicode_upPointingTriangle() { return charToString(0x25B2); } static Object[] litObjectArray(Object... l) { return l; } static String htmldecode(final String input) { if (input == null) return null; final int MIN_ESCAPE = 2; final int MAX_ESCAPE = 6; StringWriter writer = null; int len = input.length(); int i = 1; int st = 0; while (true) { // look for '&' while (i < len && input.charAt(i-1) != '&') i++; if (i >= len) break; // found '&', look for ';' int j = i; while (j < len && j < i + MAX_ESCAPE + 1 && input.charAt(j) != ';') j++; if (j == len || j < i + MIN_ESCAPE || j == i + MAX_ESCAPE + 1) { i++; continue; } // found escape if (input.charAt(i) == '#') { // numeric escape int k = i + 1; int radix = 10; final char firstChar = input.charAt(k); if (firstChar == 'x' || firstChar == 'X') { k++; radix = 16; } try { int entityValue = Integer.parseInt(input.substring(k, j), radix); if (writer == null) writer = new StringWriter(input.length()); writer.append(input.substring(st, i - 1)); if (entityValue > 0xFFFF) { final char[] chrs = Character.toChars(entityValue); writer.write(chrs[0]); writer.write(chrs[1]); } else { writer.write(entityValue); } } catch (NumberFormatException ex) { i++; continue; } } else { // named escape CharSequence value = htmldecode_lookupMap().get(input.substring(i, j)); if (value == null) { i++; continue; } if (writer == null) writer = new StringWriter(input.length()); writer.append(input.substring(st, i - 1)); writer.append(value); } // skip escape st = j + 1; i = st; } if (writer != null) { writer.append(input.substring(st, len)); return writer.toString(); } return input; } static HashMap htmldecode_lookupMap_cache; static HashMap htmldecode_lookupMap() { if (htmldecode_lookupMap_cache == null) htmldecode_lookupMap_cache = htmldecode_lookupMap_load(); return htmldecode_lookupMap_cache; } static HashMap htmldecode_lookupMap_load() { var map = new HashMap(); for (CharSequence[] seq : htmldecode_escapes()) map.put(seq[1].toString(), seq[0]); return map; } static List dropTagsAndHTMLComments(List tok) { return dropHTMLComments(dropTags(tok)); } static String dropTagsAndHTMLComments(String html) { return join(dropTagsAndHTMLComments(htmlTok(html))); } static Object[] paramsPlus_skipFirst(Object[] a1, Object... a2) { if (odd(l(a1))) return itemPlusArray(first(a1), paramsPlus(dropFirst(a1), a2)); return paramsPlus(a1, a2); } static String hcheckboxWithText(String name, String text, boolean checked, Object... params) { String id = randomID(); return hcheckbox(name, checked, paramsPlus(params, "id", id)) + " " + hlabelFor(id, htmlEncode2(text)); } static String hcheckboxWithText(String name, String text) { return hcheckboxWithText(name, text, false); } static String hlabelFor(String id, Object contents) { String s = strOrEmpty(contents); return empty(s) ? "" : htag("label", s, "for" , id); } static String stringIfTrue(boolean b, String s) { return b ? s : ""; } static String programTitle() { return getProgramName(); } static JFrame getFrame(final Object _o) { return swing(new F0() { public JFrame get() { try { Object o = _o; if (o instanceof ButtonGroup) o = first(buttonsInGroup((ButtonGroup) o)); if (!(o instanceof Component)) return null; Component c = (Component) o; while (c != null) { if (c instanceof JFrame) return (JFrame) c; c = c.getParent(); } return null; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O o = _o;\r\n if (o instanceof ButtonGroup) o = first(buttonsInGroup((Button..."; }}); } static A setFrameTitle(A c, final String title) { final Frame f = getAWTFrame(c); if (f != null) { swing(new Runnable() { public void run() { try { f.setTitle(title); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.setTitle(title);"; }}); } return c; } static A setFrameTitle(String title, A c) { return setFrameTitle(c, title); } // magically find a field called "frame" in main class :-) static JFrame setFrameTitle(String title) { Object f = getOpt(mc(), "frame"); if (f instanceof JFrame) return setFrameTitle((JFrame) f, title); return null; } static JFrame setFrameIconLater(Component c, final String imageID) { final JFrame frame = getFrame(c); if (frame != null) startThread("Loading Icon", new Runnable() { public void run() { try { final Image i = imageIcon(or2(imageID, "#1005557")).getImage(); swingLater(new Runnable() { public void run() { try { frame.setIconImage(i); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "frame.setIconImage(i);"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final Image i = imageIcon(or2(imageID, \"#1005557\")).getImage();\r\n swingL..."; }}); return frame; } static void _initFrame(JFrame f) { myFrames_list.put(f, Boolean.TRUE); standardTitlePopupMenu(f); } static Rectangle defaultNewFrameBounds_r = new Rectangle(300, 100, 500, 400); static Rectangle defaultNewFrameBounds() { return swing(new F0() { public Rectangle get() { try { defaultNewFrameBounds_r.translate(60, 20); if (!screenRectangle().contains(defaultNewFrameBounds_r)) defaultNewFrameBounds_r.setLocation(30+random(30), 20+random(20)); return new Rectangle(defaultNewFrameBounds_r); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "defaultNewFrameBounds_r.translate(60, 20);\r\n if (!screenRectangle().contai..."; }}); } static void hideConsole() { final JFrame frame = consoleFrame(); if (frame != null) { autoVMExit(); swingLater(new Runnable() { public void run() { try { frame.setVisible(false); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "frame.setVisible(false);"; }}); } } static String unicodeFromCodePoint(int codePoint) { return codePointToString(codePoint); } static List regexpFirstGroups(String pat, String s) { if (s == null) return null; Matcher m = regexp(pat, s); return m.find() ? regexpGetGroups(m) : null; } static boolean checkFields(Object x, Object... data) { for (int i = 0; i < l(data); i += 2) if (neq(getOpt(x, (String) data[i]), data[i+1])) return false; return true; } static void raiseFlagAndDo(Flag flag, Runnable r) { if (flag.raise()) callF(r); } static void _handleException_addHandler(Object handler) { addIfNotThere(_handleException_onException, handler); } static void _handleException_addHandler(IVF1 handler) { _handleException_addHandler((Object) handler); } static A assertNull(A a) { assertTrue(a == null); return a; } static A assertNull(String msg, A a) { assertTrue(msg, a == null); return a; } static JInternalFrame getInternalFrame(final Object _o) { return _o == null ? null : swing(new F0() { public JInternalFrame get() { try { Object o = _o; if (o instanceof ButtonGroup) o = first(buttonsInGroup((ButtonGroup) o)); if (!(o instanceof Component)) return null; Component c = (Component) o; while (c != null) { if (c instanceof JInternalFrame) return (JInternalFrame) c; c = c.getParent(); } return null; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O o = _o;\r\n if (o instanceof ButtonGroup) o = first(buttonsInGroup((Button..."; }}); } static A internalFrameTitle(A c, final String title) { final JInternalFrame f = getInternalFrame(c); if (f != null) { swing(new Runnable() { public void run() { try { f.setTitle(unnull(title)); toolTip(getInternalFrameTitlePaneComponent(f), title); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.setTitle(unnull(title));\r\n toolTip(getInternalFrameTitlePaneComponent(f)..."; }}); } return c; } static A internalFrameTitle(String title, A c) { return internalFrameTitle(c, title); } static String internalFrameTitle(Component c) { final JInternalFrame f = getInternalFrame(c); return f == null ? null : swing(new F0() { public String get() { try { return f.getTitle(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret f.getTitle();"; }}); } static A frameTitle(A c, String title) { return setFrameTitle(c, title); } static A frameTitle(String title, A c) { return setFrameTitle(c, title); } static String frameTitle(Component c) { return getFrameTitle(c); } static Set _registerAutoCloseable_set = synchroHashSet(); static void _registerAutoCloseable(AutoCloseable c) { addIfNotNull(_registerAutoCloseable_set, c); } static void cleanMeUp__registerAutoCloseable() { closeAutoCloseables(getAndClearList(_registerAutoCloseable_set)); } static A callFInRealOrMyMC(Object realm, F0 f) { return f == null ? null : f.get(); } static B callFInRealOrMyMC(Object realm, F1 f, A a) { return f == null ? null : f.get(a); } static C callFInRealOrMyMC(Object realm, F2 f, A a, B b) { return f == null ? null : f.get(a, b); } static void callFInRealOrMyMC(Object realm, VF1 f, A a) { if (f != null) f.get(a); } static Object callFInRealOrMyMC(Object realm, Object f, Object... args) { if (f instanceof String) { Class mc = getMainClass(realm); if (hasMethod(mc, ((String) f), args)) return call(mc, ((String) f), args); } return callF(f, args); } static A shallowCloneToClass(Class c, Object o) { if (o == null) return null; A a = nuInstance(c); copyFields(o, a); return a; } static Object dm_getStem(Object moduleOrID) { if (isString(moduleOrID) && isIdentifier(((String) moduleOrID))) moduleOrID = dm_getService(((String) moduleOrID)); if (isStringOrIntOrLong(moduleOrID)) return dm_getStemByID(moduleOrID); return or(getOpt(dm_getModule(moduleOrID), "_host"), moduleOrID); } static Method findMethod_precise_onTypes(Object o, String method, Class... argTypes) { try { if (o instanceof Class) { _MethodCache cache = callOpt_getCache((Class) o); List methods = cache.cache.get(method); Lowest best = new Lowest(); if (methods != null) for (Method m : methods) { { if (!(isStaticMethod(m))) continue; } int score = methodApplicabilityScore_onTypes(m, argTypes); if (score < Integer.MAX_VALUE) { /*if (preciseCall_debug) print("Method score: " + m + " " + score);*/ best.put(m, score); } } return best.get(); } if (o == null) return null; _MethodCache cache = callOpt_getCache(o.getClass()); List methods = cache.cache.get(method); Lowest best = new Lowest(); if (methods != null) for (Method m : methods) { int score = methodApplicabilityScore_onTypes(m, argTypes); if (score < Integer.MAX_VALUE) best.put(m, score); } return best.get(); } catch (Exception __e) { throw rethrow(__e); } } static void dm_onTopInput(final VF1 r) { final DynModule m = dm_current_mandatory(); dm_ownResource(vmBus_onMessage("topInput", new VF1() { public void get(final String s) { try { m.enter(); callF(r, s) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.enter();\r\n callF(r, s)"; }})); } static void dm_onTopInput(final Runnable r) { final DynModule m = dm_current_mandatory(); dm_ownResource(vmBus_onMessage("topInput", new VF1() { public void get(final String s) { try { m.enter(); callF(r) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.enter();\r\n callF(r)"; }})); } static void pClose(AutoCloseable c) { close_pcall(c); } static A foldl(F2 f, A seed, Iterable l) { A a = seed; if (l != null) for(B b : l) a = callF(f, a, b); return a; } static A foldl(F2 f, A seed, B[] l) { A a = seed; if (l != null) for(B b : l) a = callF(f, a, b); return a; } static A foldl(Object f, A seed, B[] l) { A a = seed; if (l != null) for(B b : l) a = (A) callF(f, a, b); return a; } static boolean setAdd(Collection c, A a) { if (c == null || c.contains(a)) return false; c.add(a); return true; } static ChangeListener changeListener(final Object r) { return new ChangeListener() { public void stateChanged(ChangeEvent e) { pcallF(r); } }; } static ItemListener itemListener(final Object r) { return new ItemListener() { public void itemStateChanged(ItemEvent e) { pcallF(r); } }; } static boolean isEditableComboBox(final JComboBox cb) { return cb != null && swing(new F0() { public Boolean get() { try { return cb.isEditable(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret cb.isEditable();"; }}); } static JTextField textFieldFromComboBox(JComboBox cb) { return (JTextField) cb.getEditor().getEditorComponent(); } static JComboBox onSelectedItem(final JComboBox cb, final VF1 f) { addActionListener(cb, new Runnable() { public void run() { try { pcallF(f, selectedItem(cb)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(f, selectedItem(cb))"; }}); return cb; } static void internalFramePopupMenuItem(Container f, final String name, final Object action) { internalFrameTitlePopupMenuItem(((JInternalFrame) f), name, action); } static void callAction(Action a) { if (a != null) a.actionPerformed(null); } static TreeSet asTreeSet(Collection set) { return set == null ? null : set instanceof TreeSet ? (TreeSet) set : new TreeSet(set); } static Map humanizeFormLabel_replacements = litmap("id" , "ID", "md5" , "MD5"); static String humanizeFormLabel(String s) { if (containsSpace(s)) return s; return firstToUpper( joinWithSpace(replaceElementsUsingMap(splitCamelCase(s), humanizeFormLabel_replacements)).replace("I D", "ID") ); } // menuMaker = voidfunc(JPopupMenu) static void internalFrameTitlePopupMenu(final JInternalFrame f, final Object... params) { { swing(new Runnable() { public void run() { try { Object menuMaker; if (l(params) == 1 && !(params[0] instanceof JMenu)) menuMaker = params[0]; else menuMaker = new VF1() { public void get(JPopupMenu menu) { try { addMenuItems(menu, params) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItems(menu, params)"; }}; Component titleComponent = getInternalFrameTitlePaneComponent(f); if (!(titleComponent instanceof JComponent)) print("Can't add internal frame title popup menu!"); else { componentPopupMenu((JComponent) titleComponent, menuMaker); setOpt(componentPopupMenu_initForComponent((JComponent) titleComponent), "internalFrameLeftButtonMagic" , isTrue(vm_generalMap_get("internalFramePopupOnLeftButton"))); } // Also add to the frame so we can show the items e.g. in a JFrame menu componentPopupMenu(f, menuMaker); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Object menuMaker;\r\n if (l(params) == 1 && !(params[0] instanceof JMenu))\r\n..."; }}); } } // r : runnable or voidfunc(bool) static JCheckBoxMenuItem jCheckBoxMenuItem(String text, boolean checked, final Object r) { final JCheckBoxMenuItem mi = swing(() -> new JCheckBoxMenuItem(text, checked)); addActionListener(mi, new Runnable() { public void run() { try { callF(r, isChecked(mi)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(r, isChecked(mi))"; }}); return mi; } static JCheckBoxMenuItem jCheckBoxMenuItem(String text, boolean checked, IVF1 r) { return jCheckBoxMenuItem(text, checked, (Object) r); } static void dm_doubleFieldSetterDialog(String field, Object... __) { final DynModule m = dm_current_mandatory(); final String humanized = humanizeFormLabel(field); final JTextField tf = jtextfield(get(m, field)); showFormTitled(or2(stringOptPar(__, "formTitle"), "Set " + humanized), humanized + ":", tf, new Runnable() { public void run() { try { m.setField(field, parseDouble(gtt(tf))); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, parseDouble(gtt(tf)));\r\n callF(optPar(__, \"onSet\"));"; }}); } static void dm_floatFieldSetterDialog(String field, Object... __) { DynModule m = dm_current_mandatory(); String humanized = humanizeFormLabel(field); JTextField tf = jtextfield(get(m, field)); showFormTitled(or2(stringOptPar(__, "formTitle"), "Set " + humanized), humanized + ":", tf, new Runnable() { public void run() { try { m.setField(field, parseFloat(gtt(tf))); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, parseFloat(gtt(tf)));\r\n callF(optPar(__, \"onSet\"));"; }}); } static void dm_stringFieldDialog(final String field, final Object... __) { final DynModule m = dm_current_mandatory(); final String humanized = humanizeFormLabel(field); final JTextField tf = jtextfield(get(m, field)); showFormTitled(or2(stringOptPar(__, "formTitle"), "Set " + humanized), humanized + ":", tf, new Runnable() { public void run() { try { m.setField(field, gtt(tf)); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, gtt(tf));\r\n callF(optPar(__, \"onSet\"));"; }}); } static void dm_fileFieldDialog(final String field, final Object... __) { DynModule m = dm_current_mandatory(); String humanized = humanizeFormLabel(field); JTextField tf = jtextfield(strOrNull(get(m, field))); showFormTitled(or2(stringOptPar(__, "formTitle"), "Set " + humanized), humanized + ":", tf, new Runnable() { public void run() { try { m.setField(field, toFile_nullOnEmpty(gtt(tf))); callF(optPar(__, "onSet")); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "m.setField(field, toFile_nullOnEmpty(gtt(tf)));\r\n callF(optPar(__, \"onSe..."; }}); } static F0 dm_rEnter() { return dm_rEnter(dm_current()); } static F0 dm_rEnter(DynModule mod) { return mod == null ? null : new F0() { public AutoCloseable get() { try { return mod.enter(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret mod.enter();"; }}; } static Runnable dm_rEnter(Runnable r) { return dm_rEnter(dm_current(), r); } static Runnable dm_rEnter(DynModule mod, Runnable r) { return mod == null || r == null ? r : new Runnable() { public void run() { try { AutoCloseable __1 = mod.enter(); try { r.run(); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp mod.enter(); r.run();"; }}; } static Q dm_ownQ(Q q) { q.rst.enter = dm_rEnter(dm_current_mandatory()); dm_ownResource(q); return q; } static Q startQ() { return new Q(); } static Q startQ(String name) { return new Q(name); } static A setToolTip(A c, Object toolTip) { return setToolTipText(c, toolTip); } static A setToolTip(Object toolTip, A c) { return setToolTipText(c, toolTip); } static void setToolTip(TrayIcon trayIcon, String toolTip) { setTrayIconToolTip(trayIcon, toolTip); } static JLabel jimage(Image img) { return jImageLabel(img); } static JLabel jimage(String imageID) { return jImageLabel(imageID); } static String getSnippetTitle(String id) { if (id == null) return null; if (!isSnippetID(id)) return "?"; IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.getSnippetTitle(id); return getSnippetTitle_noResourceLoader(id); } static String getSnippetTitle_noResourceLoader(String id) { try { if (isLocalSnippetID(id)) return localSnippetTitle(id); long parsedID = parseSnippetID(id); String url; if (isImageServerSnippet(parsedID)) url = imageServerURL() + "title/" + parsedID + muricaCredentialsQuery(); else if (isGeneralFileServerSnippet(parsedID)) url = "http://butter.botcompany.de:8080/files/name/" + parsedID; else url = tb_mainServer() + "/tb-int/getfield.php?id=" + parsedID + "&field=title" + standardCredentials_noCookies(); String title = trim(loadPageSilently(url)); if (title != null) try { saveTextFileIfChanged(snippetTitle_cacheFile(id), title); } catch (Throwable __e) { print(exceptionToStringShort(__e)); } return or(title, "?"); } catch (Exception __e) { throw rethrow(__e); } } static String getSnippetTitle(long id) { return getSnippetTitle(fsI(id)); } static void cancelTimers(Collection timers) { for (Object timer : timers) cancelTimer(timer); } static List lookupAllOpt(Map map, Collection l) { List out = new ArrayList(); if (l != null) for (A a : l) addIfNotNull(out, map.get(a)); return out; } static List lookupAllOpt(Collection l, Map map) { return lookupAllOpt(map, l); } static > List getVars(Iterable l) { return lambdaMap(__121 -> getVar(__121), l); } static String getInnerMessage(Throwable e) { if (e == null) return null; return getInnerException(e).getMessage(); } static List sortByFileName(List l) { return sortFilesByName(l); } // sorted by descending date static List conceptBackupFiles(String progID) { String regexp = "^(.*)\\.backup(20\\d\\d)(\\d\\d)(\\d\\d)-(\\d\\d)$"; File dir = programDir(progID); List l = new ArrayList(); for (File f : listFilesNotDirs(dir, newFile(dir, "backups"))) { String s = f.getName(); String originalName = regexpFirstGroup(regexp, s); { if (!(eq(originalName, "concepts.structure.gz"))) continue; } l.add(f); } return sortFilesByNameDesc(l); } static void clearConceptsOf(String progID) { getProgramFile(progID, "concepts.structure").delete(); getProgramFile(progID, "concepts.structure.gz").delete(); getProgramFile(progID, "idCounter.structure").delete(); } static Lock saveLock_lock = fairLock(); static Lock saveLock() { return saveLock_lock; } static String conceptsFileName() { return "concepts.structure.gz"; } static List callF_all(Collection l, Object... args) { return map(l, f -> callF(f, args)); } static File tempFileFor(File f) { return new File(f.getPath() + "_temp"); } public static String rtrimSpaces(String s) { if (s == null) return null; int i = s.length(); while (i > 0 && " \t".indexOf(s.charAt(i-1)) >= 0) --i; return i < s.length() ? s.substring(0, i) : s; } static int year() { return localYear(); } static int year(long now) { return localYear(now); } static int year(long now, TimeZone tz) { return parseInt(simpleDateFormat("y", tz).format(now)); } static int month() { return localMonth(); } static int month(long now) { return localMonth(now); } static int month(long now, TimeZone tz) { return parseInt(simpleDateFormat("M", tz).format(now)); } static int dayOfMonth() { return localDayOfMonth(); } static int dayOfMonth(long now) { return localDayOfMonth(now); } static int dayOfMonth(long now, TimeZone tz) { return parseInt(simpleDateFormat("d", tz).format(now)); } static String padLeft(String s, char c, int n) { return rep(c, n-l(s)) + s; } // default to space static String padLeft(String s, int n) { return padLeft(s, ' ', n); } static java.util.Calendar calendarFromTime(long time, TimeZone tz) { java.util.Calendar c = java.util.Calendar.getInstance(tz); c.setTimeInMillis(time); return c; } static java.util.Calendar calendarFromTime(long time) { java.util.Calendar c = java.util.Calendar.getInstance(); c.setTimeInMillis(time); return c; } static String defaultTimerName_name; static String defaultTimerName() { if (defaultTimerName_name == null) defaultTimerName_name = "A timer by " + programID(); return defaultTimerName_name; } // r may return false to cancel timer static TimerTask smartTimerTask(Object r, java.util.Timer timer, long delay) { return new SmartTimerTask(r, timer, delay, _threadInfo()); } static class SmartTimerTask extends TimerTask implements IFieldsToList{ Object r; java.util.Timer timer; long delay; Object threadInfo; SmartTimerTask() {} SmartTimerTask(Object r, java.util.Timer timer, long delay, Object threadInfo) { this.threadInfo = threadInfo; this.delay = delay; this.timer = timer; this.r = r;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + r + ", " + timer + ", " + delay + ", " + threadInfo + ")"; }public Object[] _fieldsToList() { return new Object[] {r, timer, delay, threadInfo}; } long lastRun; public void run() { if (!licensed()) timer.cancel(); else { _threadInheritInfo(threadInfo); AutoCloseable __1 = tempActivity(r); try { lastRun = fixTimestamp(lastRun); long now = now(); if (now >= lastRun + delay*0.9) { lastRun = now; if (eq(false, pcallF(r))) timer.cancel(); } } finally { _close(__1); }} } } static String dynamicClassName(Object o) { if (o instanceof DynamicObject && ((DynamicObject) o).className != null) return "main$" + ((DynamicObject) o).className; return className(o); } static String loadTextFilePossiblyGZipped(String fileName) { return loadTextFilePossiblyGZipped(fileName, null); } static String loadTextFilePossiblyGZipped(String fileName, String defaultContents) { File gz = new File(fileName + ".gz"); return gz.exists() ? loadGZTextFile(gz) : loadTextFile(fileName, defaultContents); } static String loadTextFilePossiblyGZipped(File fileName) { return loadTextFilePossiblyGZipped(fileName, null); } static String loadTextFilePossiblyGZipped(File fileName, String defaultContents) { return loadTextFilePossiblyGZipped(fileName.getPath(), defaultContents); } static void assertNotOnAWTThread() { assertFalse("Can't do this in AWT thread", isAWTThread()); } static Object getOptMC(String field) { return getOpt(mc(), field); } static void addToContainer(Container a, Component... b) { if (a == null) return; { swing(new Runnable() { public void run() { try { for (Component c : unnullForIteration(b)) if (c != null) a.add(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "for (Component c : unnullForIteration(b))\r\n if (c != null) \r\n a.a..."; }}); } } // o is either a map already (string->object) or an arbitrary object, // in which case its fields are converted into a map. static Map objectToMap(Object o) { try { if (o instanceof Map) return (Map) o; TreeMap map = new TreeMap(); Class c = o.getClass(); while (c != Object.class) { Field[] fields = c.getDeclaredFields(); for (final Field field : fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) continue; field.setAccessible(true); final Object value = field.get(o); if (value != null) map.put(field.getName(), value); } c = c.getSuperclass(); } // XXX NEW - hopefully this doesn't break anything if (o instanceof DynamicObject) putAll(map, ((DynamicObject) o).fieldValues); return map; } catch (Exception __e) { throw rethrow(__e); } } // same for a collection (convert each element) static List> objectToMap(Iterable l) { if (l == null) return null; List x = new ArrayList(); for (Object o : l) x.add(objectToMap(o)); return x; } static TreeMap generalizedCIMap() { return new TreeMap(generalizedCIComparator()); } static Object dm_callModule(Object moduleOrID, String method, Object... args) { Object mod = dm_getModule(moduleOrID); if (mod == null) return null; AutoCloseable __1 = dm_enter(mod); try { return call(mod, method, args); } finally { _close(__1); }} static Object dm_current_mandatory_generic() { return assertNotNull("No module set", dm_current_generic()); } static boolean dm_isFirstSiblingModule_debug = false; static boolean dm_isFirstSiblingModule(Object mod) { String libID = dm_moduleLibID(mod); if (libID == null) return false; List siblings = dm_modulesWithLibID(libID); if (dm_isFirstSiblingModule_debug) printVars("libID", libID, "siblings", siblings); if (empty(siblings)) return true; // no idea why this happens sometimes return eq(dm_moduleID(mod), lowestByAlphaNum(siblings)); } static String dm_moduleLibID() { return programID() + "/" + shortClassName(dm_current_mandatory_generic()); } static String dm_moduleLibID(Object mod) { if (mod == null) return null; mod = dm_getModule(mod); if (hasSuperclassShortNamed(mod, "DynModule")) return programID(mod) + "/" + shortClassName(mod); return null; } static void dm_ownTimer(Object resource, Object closerHelper) { call(dm_current_generic(), "ownTimer", resource, closerHelper); } static A dm_ownTimer(A resource) { call(dm_current_generic(), "ownTimer", resource); return resource; } static void ensureConceptClassIsIndexed(Class c) { ensureConceptClassIsIndexed(db_mainConcepts(), c); } static void ensureConceptClassIsIndexed(Concepts cc, Class c) { if (cc != null && c != null && !isConceptClassIndexed(cc, c)) indexRandomConceptField(cc, c); } static A toolTip(A c, final Object toolTip) { return setToolTipText(c, toolTip); } static A toolTip(Object toolTip, A c) { return setToolTipText(toolTip, c); } // menuParams: text, Runnable, ... // also: position := 'left/'center/'right static JButton jPopDownButton(String text, final Object... menuParams) { return jbutton((empty(text) ? "" : text + " ") + unicode_downPointingTriangle(), new Runnable() { public void run() { try { JPopupMenu menu = new JPopupMenu(); int emptyCount = menu.getComponentCount(); String position = (String) (optPar_ignoreOddLength("position", menuParams)); // TODO: this fails when you include a separator without a following null fillJPopupMenu(menu, paramsWithout(menuParams, "position")); if (menu.getComponentCount() != emptyCount) { JButton btn = heldInstance(JButton.class); int x = 0; if (eq(position, "center")) x = (btn.getWidth()-getPreferredWidth(menu))/2; else if (eq(position, "right")) x = btn.getWidth()-getPreferredWidth(menu); menu.show(btn, x, btn.getHeight()); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "new JPopupMenu menu;\r\n int emptyCount = menu.getComponentCount();\r\n Str..."; }}); } // one array plus more elements added in front static Object[] objectArrayPlus_inFront(Object[] a1, Object... a2) { return concatArrays(a2, a1); } // TODO: extract stuff to head etc. static String fixHTML(Object contents) { String s = str(contents); return hhtml(hbody(s)); } static void print_setPrefixForThread(final String prefix) { interceptPrintInThisThread(empty(prefix) ? null : new _PrintIndent(prefix)); } public static String rtrim(String s) { if (s == null) return null; int i = s.length(); while (i > 0 && " \t\r\n".indexOf(s.charAt(i-1)) >= 0) --i; return i < s.length() ? s.substring(0, i) : s; } static Object callMainBot(String method, Object... args) { return call(mainBot(), method, args); } static Object subBot_serveInputStream(InputStream in, String mimeType) { return call(mainBot(), "serveInputStream", in, mimeType); } static int bufferedInputStream_bufferSize = 65536; static BufferedInputStream bufferedInputStream(int bufSize, File f) { try { return bufferedInputStream(bufSize, newFileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedInputStream bufferedInputStream(File f) { try { return bufferedInputStream(newFileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedInputStream bufferedInputStream(InputStream in) { return new BufferedInputStream(in, bufferedInputStream_bufferSize); } static BufferedInputStream bufferedInputStream(int bufSize, InputStream in) { return new BufferedInputStream(in, bufSize); } static boolean isLocalSnippetID(String snippetID) { return isSnippetID(snippetID) && isLocalSnippetID(psI(snippetID)); } static boolean isLocalSnippetID(long snippetID) { return snippetID >= 1000 && snippetID <= 9999; } static String loadLocalSnippet(String snippetID) { return loadLocalSnippet(psI(snippetID)); } static String loadLocalSnippet(long snippetID) { return loadTextFile(localSnippetFile(snippetID)); } static String standardCredentials() { String user = standardCredentialsUser(); String pass = standardCredentialsPass(); if (nempty(user) && nempty(pass)) return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass); return ""; } static String webChatBotLogsHTML() { return withDBLock(new F0() { public String get() { try { List l = new ArrayList(); for (Conversation conv : sortByCalculatedFieldDesc(list(Conversation.class), new F1() { public Object get(Conversation c) { try { return empty(c.msgs) ? c.created : last(c.msgs).time; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "empty(c.msgs) ? c.created : last(c.msgs).time"; }})) { List> dialogs = reversed(unnull(conv.oldDialogs)); l.add(webChatBotLogsHTML_formatDialog(str(conv.id + "/" + (l(dialogs)+1)), conv.msgs)); int i = l(dialogs); for (List msgs : dialogs) l.add(webChatBotLogsHTML_formatDialog(conv.id + "/" + (i--), msgs)); } return h3_htitle("Chat Logs") + ul(l, null, "style" , "margin-top: 1em"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "new L l;\r\n for (Conversation conv : sortByCalculatedFieldDesc(list Conv..."; }}); } static String webChatBotLogsHTML_formatDialog(String id, List msgs) { long startTime = collectMinLong(msgs, "time"); long endTime = collectMaxLong(msgs, "time"); List lc = new ArrayList(); for (Msg m : msgs) if (m.fromUser) lc.add("U: " + i(htmlencode(m.text))); else lc.add("B: " + htmlencode(m.text)); String time1 = formatDateAndTime(startTime); String time2 = formatDateAndTime(endTime); time2 = shortenEndTime(time2, time1); return id + " [" + htmlencode(time1) + " - " + htmlencode(time2) + "]" + ul(lc); } static int parseIntOpt(String s) { return parseIntOpt(s, 0); } static int parseIntOpt(String s, int defValue) { return isInteger(s) ? parseInt(s) : defValue; } static String h3_htitle(String s) { return htitle(s) + h3(s); } static String renderFileInfo(File f) { return f == null ? "-" : f2s(f) + " " + (f.isFile() ? "(file, " + n2(fileSize(f)) + " bytes)" : f.isDirectory() ? "(dir)" : "(not found)"); } static File fileInSameDir(File f, String newName) { return newFile(parentFile(f), newName); } static String ymd_minus_hms() { return ymd() + "-" + hms(); } static boolean match3(String pat, String s) { return match3(pat, s, null); } static boolean match3(String pat, String s, Matches matches) { if (pat == null || s == null) return false; return match3(pat, parse3_cachedInput(s), matches); } static boolean match3(String pat, List toks, Matches matches) { List tokpat = parse3_cachedPattern(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; if (matches != null) matches.m = m; return true; } static List rgbToIntList(RGB rgb) { return rgb == null ? null : ll(rgb.redInt(), rgb.greenInt(), rgb.blueInt()); } static ExpiringMap2 loadSnippet_simpleCache_map = new ExpiringMap2(10000); static Lock loadSnippet_simpleCache_lock = lock(); // timeout for loadPage static int loadSnippet_simpleCache_timeout = 60000; static String loadSnippet_simpleCache(String id) { if (id == null) return null; Lock __0 = loadSnippet_simpleCache_lock; lock(__0); try { id = fsI(id); // get from cache String src = loadSnippet_simpleCache_map.get(id); if (src != null) return src; // load & put in cache Integer oldTimeout = setThreadLocal(loadPage_forcedTimeout_byThread, loadSnippet_simpleCache_timeout); try { src = loadSnippet(id); if (src != null) loadSnippet_simpleCache_map.put(id, src); return src; } finally { loadPage_forcedTimeout_byThread.set(oldTimeout); } } finally { unlock(__0); } } // tag = tag name // newTag = full new contents including outer tag // replaces only one occurrence static String hreplacetag(String html, String tag, String newTag) { List tok = htmlcoarsetok(html); List> tags = findContainerTag(tok, tag); if (empty(tags)) return html; List theTag = first(tags); List actualTag = subList(theTag, 1, l(theTag)-1); return join(replaceSubList(cloneList(tok), actualTag, litlist(newTag))); } static Object subBot_serveWithContentType(String text, String contentType) { return callMainBot("serveByteArray", toUtf8(unnull(text)), contentType); } static String fileExtension(File f) { if (f == null) return null; return fileExtension(f.getName()); } static String fileExtension(String s) { return substring(s, smartLastIndexOf(s, '.')); } static String hhmm() { return hhmm(now()); } static String hhmm(long time) { return new SimpleDateFormat("HHmm").format(time); } static ThreadLocal dataToTable_useStruct = threadLocalWithDefault(true); static void dataToTable_dynSet(List l, int i, Object s) { while (i >= l.size()) l.add(""); l.set(i, s); } static List dataToTable_makeRow(Object x, List cols) { if (instanceOf(x, "DynamicObject")) x = get_raw(x, "fieldValues"); if (x instanceof Map) { Map m = (Map) x; List row = new ArrayList(); for (Object _field : keysWithoutHidden(m)) { String field = (String) _field; Object value = m.get(field); int col = cols.indexOf(field); if (col < 0) { cols.add(field); col = cols.size()-1; } dataToTable_dynSet(row, col, dataToTable_wrapValue(value)); } return row; } // XXX new if (x instanceof List) return allToString((List) x); return litlist(structureOrText(x)); } static Object dataToTable_wrapValue(Object o) { if (o instanceof BufferedImage) return o; if (o instanceof MakesBufferedImage) return ((MakesBufferedImage) o).getBufferedImage(); if (o instanceof Boolean) return o; return dataToTable_useStruct.get() ? structureOrTextForUser(o) : strOrNull(o); } static String structureOrText(Object o) { return o instanceof String ? (String) o : structure(o); } static String hopeningtag(String tag, Map params) { return hopeningTag(tag, params); } static String hopeningtag(String tag, Object... params) { return hopeningTag(tag, params); } static List rawEmojisList() { return (List) jsonDecodeMap(loadTextFile(loadLibrary("#1400427"))).get("emojis"); } static void typeWriterConsole() { if (isHeadless()) return; Font f = typeWriterFont(); consoleFont(f); consoleInputFont(f); } static Object[] paramsPlus_inFront(Object[] a1, Object... a2) { return paramsPlus(a2, a1); } static String htd(Object contents, Object... params) { return htag("td", contents, params); } // lists files only - who needs to list directories?! // lists in typical .zip fashion - dir1/dir2/bla.txt static List listZip(File inZip) { return listZipFile(inZip); } static List listZip(String inZip) { return listZipFile(inZip); } static String addSlash(String s) { return empty(s) || s.endsWith("/") ? s : s + "/"; } static String hStylesheetSnippet_maxCache(String snippetID) { return tag("link", "", "rel" , "stylesheet", "type" , "text/css", "href" , snippetRawURL_maxCache(snippetID, "text/css")); } static String hjssnippet_maxCache(String snippetID) { return hfulltag("script", "", "src" , snippetRawURL_maxCache(snippetID, "text/javascript")); } static List dropFirstTwoAndLastTwo(List l) { return dropFirstAndLast(2, l); } static void replaceTokens(List tok, IntRange r, String s) { replaceTokens(tok, r.start, r.end, s); } static void replaceTokens(List tok, int i, int j, String s) { clearAllTokens(tok, i+1, j); tok.set(i, s); } static void replaceTokens(List tok, String s) { clearAllTokens(tok, 1, l(tok)); tok.set(0, s); } static void addFirst(List c, A a) { if (c != null) c.add(0, a); } static String oggMimeType() { return "audio/ogg"; } static String wavMimeType() { return "audio/wav"; } static String m4aMimeType() { return "audio/mp4"; } static String hinputtag(Object contents, Object... params) { return htag("input", contents, params); } static Object[] paramsPlus_noOverwrite(Object[] a1, Object... a2) { if (a2 == null) return a1; if (a1 == null) return a2; if (l(a1) == 1 && a1[0] instanceof Map) return new Object[] { mapPlus((Map) a1[0], a2) }; assertEvenLength(a1); assertEvenLength(a2); Map map = paramsToOrderedMap(a1); int n = l(a2); for (int i = 0; i < n; i += 2) mapPut_noOverwrite(map, a2[i], a2[i+1]); return mapToParams(map); } static boolean isSubclass(Class a, Class b) { return a != null && b != null && b.isAssignableFrom(a); } static A replaceIfEqual(A a, A b, A c) { return eq(a, b) ? c : a; } static String localDateWithMinutes(long time) { SimpleDateFormat format = simpleDateFormat_local("yyyy/MM/dd HH:mm"); return format.format(time); } static String localDateWithMinutes() { return localDateWithMinutes(now()); } static String pnlToStringWithEmptyLines(Iterable l) { return pnlToString(map(l, o -> o + "\n")); } static String str_toK(long l) { return n2(toK(l)) + " K"; } static void saveBinaryFileVerbose(File f, byte[] data) { boolean exists = f.exists(); saveBinaryFile(f, data); print((!exists ? "Created" : "Updated") + " file " + f2s(f) + " (" + f.length() + " bytes)"); } static int nfilter(Iterable c, IF1 pred) { return nfilter(pred, c); } static int nfilter(IF1 pred, Iterable c) { int n = 0; if (c != null) for (A o : c) if (pred.get(o)) ++n; return n; } static int nfilter(Iterable c, Object pred) { int n = 0; if (c != null) for (Object o : c) if (isTrue(callF(pred, o))) ++n; return n; } static int nfilter(Object pred, Iterable c) { return nfilter(c, pred); } static String windowsLineBreaks(String s) { return s == null ? null : s.replaceAll("(? String mapToLines_rtrim(Iterable l, IF1 f) { return mapToLines_rtrim(f, l); } static String newLinesToSpaces(String s) { return s == null ? null : fixNewLines(s).replace("\n", " "); } // does not store null values static MultiMap treeMultiMapIndexByField(Collection c, String field) { MultiMap map = treeMultiMap(); for (Object a : c) { Object val = getOpt(a, field); if (val != null) map.put(val, a); } return map; } static MultiMap treeMultiMapIndexByField(String field, Collection c) { return treeMultiMapIndexByField(c, field); } static Cache> countryDialCodes_cache = new Cache<>(() -> countryDialCodes_load()); static List countryDialCodes() { return countryDialCodes_cache.get(); } static List countryDialCodes_load() { return sortedByField("dialCode", filter(c -> nempty(c.dialCode), map((List>) loadJSONFile(loadLibrary("#1400429")), m -> new CountryDialCode(m.get("name"), m.get("code"), m.get("dial_code"))))); } static List nempties(Collection c) { return filterNempty(c); } static List splitAtComma(String s) { return empty(s) ? emptyList() : asList(s.split(",")); } // countryCode = GB, US, DE, IN, ... static CountryDialCode countryDialCodeByCountryCode(String countryCode) { return objectWhere(countryDialCodes(), "countryCode" , upper(countryCode)); } static String dropPrefixTrim(String prefix, String s) { return trim(dropPrefix(prefix, s)); } static String smallestTransparentGIFDataURI() { return "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; } static A lowestByField(Iterable l, String field) { A lowest = null; Object lowestValue = null; for (A a : unnull(l)) { Object val = getOpt(a, field); if (lowest == null || cmp(val, lowestValue) < 0) { lowest = a; lowestValue = val; } } return lowest; } static A lowestByField(String field, Iterable l) { return lowestByField(l, field); } static java.util.Timer doLater(long delay, final Object r) { ping(); final java.util.Timer timer = new java.util.Timer(); timer.schedule(timerTask(r, timer), delay); return vmBus_timerStarted(timer); } static java.util.Timer doLater(double delaySeconds, final Object r) { return doLater(toMS(delaySeconds), r); } static List mapNonCodeTokens(Object f, List l) { List out = emptyList(l); for (int i = 0; i < l(l); i++) { Object o = l.get(i); out.add(even(i) ? callF(f, o) : o); } return out; } static List mapNonCodeTokens(List l, Object f) { return mapNonCodeTokens(f, l); } static List mapNonCodeTokens(List tok, IF1 f) { return mapNonCodeTokens(tok, (Object) f); } static List mapNonCodeTokens(IF1 f, List tok) { return mapNonCodeTokens(tok, f); } static String emojisToUnicode(String s) { return regexReplace(s, ":(\\w+):", matcher -> lookupOrKeep(emojiShortNameMap(), matcher.group())); } static boolean subBot_isHttps() { Object httpd = subBot_httpd(); return eqOneOf(httpd, getOpt(mainBot(), "serveHttps_server"), getOpt(mainBot(), "serveHttpsWithWebsockets_server")) || contains((Collection) getOpt(mainBot(), "serveHttpsWithWebsockets_multiplePorts_servers"), httpd); } static String domain() { return domainName(); } static TreeSet toCaseInsensitiveSet(Iterable c) { if (isCISet(c)) return (TreeSet) c; TreeSet set = caseInsensitiveSet(); addAll(set, c); return set; } static TreeSet toCaseInsensitiveSet(String... x) { TreeSet set = caseInsensitiveSet(); addAll(set, x); return set; } static java.text.SimpleDateFormat simpleDateFormat(String format, TimeZone timeZone) { java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(format); sdf.setTimeZone(timeZone); return sdf; } static String timeInTimeZone(String timeZone) { return timeInTimeZone(timeZone, now()); } static String timeInTimeZone(String timeZone, long time) { return simpleDateFormat_timeZone("HH:mm", timeZone).format(time); } static String timeInTimeZone(TimeZone timeZone, long time) { return simpleDateFormat("HH:mm", timeZone).format(time); } static String ipToCountry2020_dataSnippetID = "#1400400"; static Map ipToCountry2020_cache = mruCache(100); static Lock ipToCountry2020_lock = lock(); static File ipToCountry2020_dataDir() { return javaxCachesDir("ipToCountry2020"); } static String ipToCountry2020(String ip) { return ipToCountry2020(ipToInt(ip)); } static String ipToCountry2020(long ipNum) { return mapGetOrCreate(ipToCountry2020_cache, ipNum, () -> ipToCountry2020_uncached(ipNum)); } static String ipToCountry2020_uncached(long ipNum) { { Lock __0 = ipToCountry2020_lock; lock(__0); try { if (directoryEmpty(ipToCountry2020_dataDir())) unzipSnippet(ipToCountry2020_dataSnippetID, ipToCountry2020_dataDir()); } finally { unlock(__0); } } String line = pairB(binarySearchForLineInTextFile(newFile(ipToCountry2020_dataDir(), "IP2LOCATION-LITE-DB1.CSV"), s -> { List l = tok_splitAtComma_unquote(s); long a = parseLongOpt(first(l)), b = parseLongOpt(second(l)); return ipNum > b ? 1 : ipNum < a ? -1 : 0; })); return get(tok_splitAtComma_unquote(line), 2); } static String deSquareBracket(String s) { if (startsWith(s, "[") && endsWith(s, "]")) return substring(s, 1, l(s)-1); return s; } static JTextField jTextField() { return jTextField(""); } static JTextField jTextField(final String text) { return swing(new F0() { public JTextField get() { try { JTextField tf = new JTextField(unnull(text)); standardTextFieldPopupMenu(tf); jenableUndoRedo(tf); tf.selectAll(); return tf; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JTextField tf = new JTextField(unnull(text));\r\n standardTextFieldPopupMenu..."; }}); } static JTextField jTextField(Object o) { return jTextField(strOrEmpty(o)); } static JPanel showFormTitled_customLayout(final F1 layouter, final String title, final Object... parts) { return showFormTitled_customArrangement(false, new F1, JPanel>() { public JPanel get(List components) { try { return customLayoutPanel(layouter, components); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "customLayoutPanel(layouter, components)"; }}, title, parts); } static int formLayouter1_yPlus = 5; // make higher (bug fixing) static int formLayouter1_fixer2 = 0; static F1 formLayouter1() { final int yplus = formLayouter1_yPlus, fixer2 = formLayouter1_fixer2; return new F1() { public Dimension get(Container parent) { try { List l = getComponents(parent); BitSet bigOnes = new BitSet(); for (int i = 0; i < l(l); i++) if (containsATextArea(l.get(i))) add(bigOnes, i); int nBigOnes = bigOnes.cardinality(); int mandatoryHeight = totalPreferredHeight(listWithoutIndicesInBitSet(l, bigOnes)); int gap = 4, outerSpacing = 5; Dimension size = parent.getSize(); int gapsAndMargins = outerSpacing*2-(l(l)-1)*gap; int totalSpace = size.height-gapsAndMargins-fixer2; int liberalSpace = totalSpace-mandatoryHeight; double perBigOne = doubleRatio(liberalSpace, nBigOnes); double y = outerSpacing; for (int i = 0; i < l(l); i++) { Component c = l.get(i); boolean big = contains(bigOnes, i); double h = big ? perBigOne : c.getPreferredSize().height; int actualY = iround(y); c.setBounds(10, actualY, size.width-outerSpacing*2, iround(y+h)-actualY); y += h+gap; } // return preferred size Dimension pref = componentsBoundingSize(parent, outerSpacing); if (parent.getHeight() <= 0) { // pre-showing - maybe we should always do this int tph = totalPreferredHeight(l); pref.height = tph+gapsAndMargins+yplus; } return pref; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "L l = getComponents(parent);\r\n new BitSet bigOnes;\r\n for i o..."; }}; } static String getTextTrim(JTextComponent c) { return trim(getText(c)); } // tested for editable combo box - returns the contents of text field static String getTextTrim(JComboBox cb) { return trim(getText(cb)); } static String getTextTrim(JComponent c) { if (c instanceof JLabel) return trim(((JLabel) c).getText()); if (c instanceof JComboBox) return getTextTrim((JComboBox) c); return getTextTrim((JTextComponent) c); } static String programName() { return getProgramName(); } static void callF_thread(final Object f, final Object... args) { if (f != null) { startThread(new Runnable() { public void run() { try { callF(f, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(f, args);"; }}); } } static A renameSubmitButton(A form, String newName) { renameButton(form, showFormSubmitButtonName(), newName); return form; } static A renameSubmitButton(String newName, A form) { return renameSubmitButton(form, newName); } static String htmlTagFullParamString(String tag) { int i = indexOfSpaceEtc(tag); return trimSubstring(i, dropSuffix(">", tag)); } static void clearTokens(List tok) { clearAllTokens(tok); } static void clearTokens(List tok, int i, int j) { clearAllTokens(tok, i, j); } static void clearTokens(List tok, IntRange r) { clearAllTokens(tok, r.start, r.end); } static void clearToken(List tok, int i) { tok.set(i, ""); } static IterableIterator> iterateListWithIndex(Collection l) { return iterateListWithIndex(l, 0); } static IterableIterator> iterateListWithIndex(Collection l, int startAt) { return iff(new Object() { Iterator it = iterator(l); int i = startAt; public Object get() { if (!it.hasNext()) return endMarker(); return pair(i++, it.next()); } }); } static List asVirtualList(A[] a) { return wrapArrayAsList(a); } static String unparseCSSParameter(Map map) { return joinWithSemicolon(map(map, new F2() { public String get(String key, String value) { try { return key + ": " + value; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "key + \": \" + value"; }})); } static Map mapPlus_noOverwrite(Map m, Object... data) { m = cloneMap(m); litmap_impl_noOverwrite(m, data); return m; } static Map parseCSSParameter(String s) { Map map = new LinkedHashMap(); for (String x : trimAll(splitAt(s, ";"))) { List y = trimAll(splitAtColon(x)); if (l(y) == 2) map.put(first(y), second(y)); } return map; } static String squareBracketed(String s) { return "[" + s + "]"; } static int numberOfWords2(String s) { return l(words2(s)); } static String joinPairWithColon(Pair p) { return p == null ? "" : str(p.a) + ": " + str(p.b); } static A printIndent(A o) { print(indentx(str(o))); return o; } static A printIndent(String indent, A o) { print(indentx(indent, str(o))); return o; } static void printIndent(int indent, Object o) { print(indentx(indent, str(o))); } static void assertStartsWith(List a, List b) { if (!startsWith(a, b)) throw fail(a + " does not start with " + b); } static void assertStartsWith(String a, String b) { if (!startsWith(a, b)) throw fail(quote(a) + " does not start with " + quote(b)); } static A nextToLast(List l) { return get(l, l(l)-2); } static String dropTrailingSlash(String s) { return dropSuffix("/", s); } static boolean isHTMLRefreshTag(String tag) { if (!rtagIs("meta", tag)) return false; Map params = ciTagParams(tag); return eqic(mapGet(params, "http-equiv"), "refresh"); } // tok = parsed with htmlTok static List findAllTagsNamed(List tok, String tag) { List out = new ArrayList(); for (int i = 1; i < l(tok); i += 2) if (isTag(tok.get(i), tag)) out.add(tok.get(i)); return out; } static List findAllTagsNamed(String tag, List tok) { return findAllTagsNamed(tok, tag); } // Note the reversed parameters static List findAllTagsNamed(String tag, String html) { return findAllTagsNamed(htmlTok(html), tag); } static String[] conceptFields_drop = {"className", "fieldValues", "id", "created", "_modified", "refs", "backRefs", "_concepts"}; static Set conceptFields(Concept c) { return setMinus(mergeSets(allNonStaticNonTransientFields(c), keys(c.fieldValues)), conceptFields_drop); } static Set conceptFields(Class c) { return setMinus(allNonStaticNonTransientFields(c), conceptFields_drop); } static A oneOf(List l) { return empty(l) ? null : l.get(new Random().nextInt(l.size())); } static char oneOf(String s) { return empty(s) ? '?' : s.charAt(random(l(s))); } static String oneOf(String... l) { return oneOf(asList(l)); } static A collectionGet(Collection c, int idx) { if (c == null || idx < 0 || idx >= l(c)) return null; if (c instanceof List) return listGet((List) c, idx); Iterator it = c.iterator(); for (int i = 0; i < idx; i++) if (it.hasNext()) it.next(); else return null; return it.hasNext() ? it.next() : null; } static Pair entryToPair(Map.Entry e) { return mapEntryToPair(e); } static Set> entries(Map map) { return _entrySet(map); } static String newLinesToSpaces_trim(String s) { return s == null ? null : trim(s).replaceAll("\\s*\r?\n\\s*", " "); } static boolean regexpMatches(String pat, String s) { return pat != null && s != null && regexp(pat, s).matches(); } static boolean regexpMatches(java.util.regex.Pattern pat, String s) { return pat != null && s != null && regexp(pat, s).matches(); } static String tok_deRoundBracket(String s) { return tok_isRoundBracketed(s) ? join(dropFirstThreeAndLastThree(javaTok(s))) : s; } static List javaTokWithBrackets(String s) { return javaTokPlusBrackets(s); } static List tok_splitAtComma(String s) { return tok_splitAtComma(javaTok(s)); } static List tok_splitAtComma(List tok) { List out = new ArrayList(); for (int i = 0; i < l(tok); i++) { int j = smartIndexOf(tok, ",", i); out.add(joinSubList(tok, i+1, j-1)); i = j; } return out; } static List tok_splitAtPlus(String s) { return tok_splitAtPlus(javaTokWithAllBrackets_cached(s)); } static List tok_splitAtPlus(List tok) { return splitAtTokens(tok, "+"); } static List tok_splitAtAsterisk(String s) { return tok_splitAtAsterisk(javaTokWithAllBrackets_cached(s)); } static List tok_splitAtAsterisk(List tok) { return splitAtTokens(tok, "*"); } static boolean mmo2_match(MMOPattern pattern, String s) { return mmo2_match(pattern, s, false, false); } static boolean mmo2_match(MMOPattern pattern, String s, boolean startOfLine, boolean endOfLine) { if (pattern == null) return false; String s2 = trim(s); if (pattern instanceof MMOPattern.StartOfLine) return mmo2_match(((MMOPattern.StartOfLine) pattern).p, s2, true, endOfLine); if (pattern instanceof MMOPattern.EndOfLine) return mmo2_match(((MMOPattern.EndOfLine) pattern).p, s2, startOfLine, true); if (pattern instanceof MMOPattern.Phrase) { String p = ((MMOPattern.Phrase) pattern).phrase; if (((MMOPattern.Phrase) pattern).quoted) return cicWithSmartWordBoundary(s2, p); if (startsWith(p, "#")) return eqic(p, s2); return match3_startOrEndOfLine(p, s2, startOfLine, endOfLine); } if (pattern instanceof MMOPattern.And) return all(((MMOPattern.And) pattern).l, pat -> mmo2_match(pat, s2, startOfLine, endOfLine)); if (pattern instanceof MMOPattern.Or) return any(((MMOPattern.Or) pattern).l, pat -> mmo2_match(pat, s2, startOfLine, endOfLine)); if (pattern instanceof MMOPattern.Not) return !mmo2_match(((MMOPattern.Not) pattern).p, s2, startOfLine, endOfLine); throw fail("what. " + pattern); } static boolean mmo2_match(String pattern, String s) { return mmo2_match(mmo2_parsePattern(pattern), s); } // potentially slow (not using levenLimited) // returns edit distance or null if no match at all static Integer mmo2_levenWithSwapsScore(MMOPattern pattern, String s) { return mmo2_levenWithSwapsScore(pattern, s, false, false); } static Integer mmo2_levenWithSwapsScore(MMOPattern pattern, String s, boolean startOfLine, boolean endOfLine) { String s2 = trim(s); if (pattern == null) return null; if (startsWith(s2, "#")) return null; // hashtags not handled here if (pattern instanceof MMOPattern.StartOfLine) return mmo2_levenWithSwapsScore(((MMOPattern.StartOfLine) pattern).p, s2, true, endOfLine); if (pattern instanceof MMOPattern.EndOfLine) return mmo2_levenWithSwapsScore(((MMOPattern.EndOfLine) pattern).p, s2, startOfLine, true); if (pattern instanceof MMOPattern.Phrase) { if (((MMOPattern.Phrase) pattern).quoted) return mmo2_match(((MMOPattern.Phrase) pattern), s2) ? (Integer) 0 : null; String p = ((MMOPattern.Phrase) pattern).phrase; if (startsWith(p, "#")) return null; // hashtags not handled here if (endsWith(p, "!")) return find3(p, s) ? 0 : null; else return find3_levenWithSwapsDistance(p, s2); } if (pattern instanceof MMOPattern.And) { int score = 0; for (MMOPattern pat : ((MMOPattern.And) pattern).l) { Integer score2 = mmo2_levenWithSwapsScore(pat, s2, startOfLine, endOfLine); if (score2 == null) return null; score += score2; } return score; } if (pattern instanceof MMOPattern.Or) { Integer min = null; for (MMOPattern pat : ((MMOPattern.Or) pattern).l) min = min_withNull(min, mmo2_levenWithSwapsScore(pat, s2, startOfLine, endOfLine)); return min; } // not clauses are not checked for typos if (pattern instanceof MMOPattern.Not) return mmo2_match(pattern, s2) ? 0 : null; throw fail("what. " + pattern); } static Integer mmo2_levenWithSwapsScore(String pattern, String s) { return mmo2_levenWithSwapsScore(mmo2_parsePattern(pattern), s, false, false); } // returns minute ranges from 0 to 24*60 static List parseBusinessHours(String s) { List parts = nempties(tok_splitAtComma(s)); return map(parts, part -> { List l = splitAtMinus(part); IntRange r = intRange( parseHourAndOptionalMinutesToMinutes(first(l)), parseHourAndOptionalMinutesToMinutes(second(l))); if (r.end < r.start && r.start <= 12*60 && r.end < 12*60) r.end += 12*60; return r; }); } static boolean intRangeContains(IntRange r, int i) { return r != null && i >= r.start && i < r.end; } static boolean syncAddAll(Collection c, Iterable b) { if (c == null || b == null) return false; synchronized(c) { return c.addAll(asCollection(b)); } } static List syncLambdaMap(IF1 f, Collection l) { if (l == null) return emptyList(); synchronized(collectionMutex(l)) { return map(l, f); } } static A shallowClone(A o) { return (A) shallowClone_impl(o); } static A shallowClone(A o, A emptyClone) { return copyFields(o, emptyClone); } static Object shallowClone_impl(Object o) { if (o == null) return o; if (o instanceof List) return cloneList((List) o); if (o instanceof Map) return cloneMap((Map) o); if (o instanceof String || o instanceof Number || o instanceof Boolean) return o; if (o instanceof Object[]) { Object[] l = (Object[]) o; return l.clone(); } // clone an arbitrary custom object //print("Cloning custom: " + o); Class c = o.getClass(); final Object clone = nuEmptyObject(c); copyFields(o, clone); return clone; } static JComboBox jcombobox(final String... items) { return swing(new F0() { public JComboBox get() { try { return new JComboBox(items); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret new JComboBox(items);"; }}); } static JComboBox jcombobox(Collection items) { return jcombobox(toStringArray(items)); } static JComboBox jcombobox(Collection items, String selectedItem) { return selectItem(selectedItem, jcombobox(items)); } static JPanel showInternalFrameFormTitled(final JDesktopPane desktop, final String title, final Object... _parts) { JPanel panel = showForm_makePanel(true, _parts); showForm_makeInternalFrame(desktop, title, panel); return panel; } static JFrame showForm_makeFrame(String title, JPanel panel) { return handleEscapeKey(minFrameWidth(showPackedFrame(title, withMargin(panel)), 400)); } static List showForm_arrange1(List> l) { int minW = showForm_leftWidth(l); List out = new ArrayList(); for (List row : l) out.add(westAndCenter(withRightMargin(showForm_gapBetweenColumns, jMinWidthAtLeast(minW, first(row))), second(row))); return out; } static List> showForm_makeComponents(final Boolean internalFrame, Object... _parts) { List> l = new ArrayList(); List parts = asList(_parts); JButton submitButton = null; for (int i = 0; i < l(parts); i++) { final Object o = parts.get(i), next = get(parts, i+1); if (o instanceof String && next instanceof Component) setComponentID((Component) next, (String) o); if (o instanceof Component || o instanceof String || next instanceof Component) { // smartAdd accepts strings l.add(mapLL(__122 -> wrapForSmartAdd_jComponent(__122), o == null ? new JPanel() : o instanceof String ? humanizeFormLabel((String) o) : o, next)); if (next instanceof JButton && submitButton == null) submitButton = (JButton) next; i++; } else if (isRunnable(o)) l.add(mapLL(__123 -> wrapForSmartAdd_jComponent(__123), null, submitButton = jbutton(showFormSubmitButtonName(), new Runnable() { public void run() { try { Object result = call(o); print("Result of form runnable: " + result + ". Button: " + heldInstance(JButton.class)); if (neq(Boolean.FALSE, result)) { if (isTrue(internalFrame)) disposeInternalFrame(heldInstance(JButton.class)); else if (isFalse(internalFrame)) disposeFrame(heldInstance(JButton.class)); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Object result = call(o);\r\n print(\"Result of form runnable: \" + result ..."; }}))); else print("showForm: Unknown element type: " + getClassName(o)); } if (submitButton != null) { final JButton _submitButton = submitButton; onEnterInAllTextFields(concatLists(l), new Runnable() { public void run() { try { clickButton(_submitButton) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "clickButton(_submitButton)"; }}); } // massage labels for (List row : l) { JComponent left = first(row); if (left instanceof JLabel) makeBold((JLabel) left).setVerticalAlignment(JLabel.TOP); } return l; } static int vstackWithSpacing_default = 10; static JPanel vstackWithSpacing(final List parts) { return vstackWithSpacing(parts, vstackWithSpacing_default); } static JPanel vstackWithSpacing(final List parts, final int spacing) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.weightx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.insets = new Insets(spacing/2, 0, spacing/2, 0); // well... smartAddWithLayout(panel, gbc, toObjectArray(nonNulls(parts))); //gbc = (GridBagConstraints) gbc.clone(); //gbc.fill = GridBagConstraints.BOTH; gbc.weighty = 1; gbc.insets = new Insets(0, 0, 0, 0); panel.add(jrigid(), gbc); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new GridBagLayout);\r\n new GridBagConstraints gbc..."; }}); } static JPanel vstackWithSpacing(Component... parts) { return vstackWithSpacing(asList(parts), vstackWithSpacing_default); } static JPanel vstackWithSpacing(int spacing, Component... parts) { return vstackWithSpacing(asList(parts), spacing); } static Method findMethod_cached(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { _MethodCache cache = callOpt_getCache((Class) o); List methods = cache.cache.get(method); if (methods != null) for (Method m : methods) if (isStaticMethod(m) && findMethod_checkArgs(m, args, false)) return m; return null; } else { _MethodCache cache = callOpt_getCache(o.getClass()); List methods = cache.cache.get(method); if (methods != null) for (Method m : methods) if (findMethod_checkArgs(m, args, false)) return m; return null; } } catch (Exception __e) { throw rethrow(__e); } } static A callPostProcessor(Object f, A a) { return f == null ? a : (A) callF(f, a); } static A callPostProcessor(IF1 f, A a) { return f == null ? a : f.get(a); } static Map weakMap() { return newWeakHashMap(); } // allows null keys but not null values static B getOrCreate(Map map, A key, Class c) { try { B b = map.get(key); if (b == null) map.put(key, b = c.newInstance()); return b; } catch (Exception __e) { throw rethrow(__e); } } // f : func -> B static B getOrCreate(Map map, A key, Object f) { try { B b = map.get(key); if (b == null) map.put(key, b = (B) callF(f)); return b; } catch (Exception __e) { throw rethrow(__e); } } static B getOrCreate(IF0 f, Map map, A key) { return getOrCreate(map, key, f); } static B getOrCreate(Map map, A key, IF0 f) { B b = map.get(key); if (b == null) map.put(key, b = f.get()); return b; } static B getOrCreate(Class c, Map map, A key) { return getOrCreate(map, key, c); } static String toStringOpt(Object o) { return o instanceof String ? ((String) o) : null; } static long getLong(Object o, String field) { return toLong(getOpt(o, field)); } static long getLong(String field, Object o) { return getLong(o, field); } static String[] conceptFields_gen_drop = {"className", "fieldValues", "id", "created", "_modified", "refs", "backRefs", "_concepts"}; static Set conceptFields_gen(Object c) { return setMinus(mergeTreeSets(allNonStaticNonTransientFields(c), keys((Map) getOpt_raw(c, "fieldValues"))), conceptFields_gen_drop); } static Set conceptFields_gen(Class c) { return setMinus(allNonStaticNonTransientFields(c), conceptFields_gen_drop); } static List nonStaticNonTransientFieldObjects(Object o) { if (o == null) return null; List fields = new ArrayList(); Class _c = _getClass(o); do { for (Field f : _c.getDeclaredFields()) if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) == 0) fields.add(makeAccessible(f)); _c = _c.getSuperclass(); } while (_c != null); return fields; } static A lastKey(SortedMap map) { return empty(map) ? null : map.lastKey(); } static List valuesList(Map map) { return cloneListSynchronizingOn(values(map), map); } static List valuesList(MultiMap mm) { return mm == null ? emptyList() : concatLists(values(mm.data)); } static String optCastToString(Object o) { return o instanceof String ? (String) o : null; } static boolean swicOneOf(String s, String... l) { for (String x : l) if (swic(s, x)) return true; return false; } static boolean swicOneOf(String s, Matches m, String... l) { for (String x : l) if (swic(s, x, m)) return true; return false; } // returns true if change // DUPLICATED in convertToField AND DefaultValueConverterForField... static boolean _cSmartSetField(Concept c, String field, Object value) { try { Field f = setOpt_findField(c.getClass(), field); if (f != null && value != null) { Class actualType = f.getType(); Class type = primitiveToBoxedTypeOpt(actualType); // int magic if (type == Integer.class && !(value instanceof Integer)) { if (!setField_trueIfChanged(f, c, toInt(trimIfString(value)))) return false; if (!isTransient(f)) c.change(); return true; } // bool magic if (type == Boolean.class && value instanceof String) if (actualType == Boolean.class && eq(value, "")) return _csetField(c, field, null); else return _csetField(c, field, eqicOneOf((String) value, "1", "true", "yes")); // GlobalID magic } return _csetField(c, field, value); } catch (Exception __e) { throw rethrow(__e); } } static Object[] unrollAndExpandParams(Class c, Object[] params) { return expandParams(c, unrollParams(params)); } static boolean cSmartSetField_withConverter(Concept c, String field, Object value) { return cSmartSetField_withConverter(c, field, value, false); } static boolean cSmartSetField_withConverter(Concept c, String field, Object value, boolean verbose) { return cSmartSetField_withConverter(c, field, value, new DefaultValueConverterForField(), verbose); } // returns true if change static boolean cSmartSetField_withConverter(Concept c, String field, Object value, ValueConverterForField converter) { return cSmartSetField_withConverter(c, field, value, converter, false); } static boolean cSmartSetField_withConverter(Concept c, String field, Object value, ValueConverterForField converter, boolean verbose) { try { Field f = setOpt_findField(c.getClass(), field); if (f != null) { if (verbose) print("cSmartSetField_withConverter: setting " + f + " = " + value); if (converter != null && value != null) { OrError result = converter.convertValue(c, f, value); if (result == null) throw fail("Unknown conversion " + className(value) + " => " + f); if (!result.ok()) throw fail("Unknown conversion " + className(value) + " => " + f + ": " + result.error()); value = result.get(); } if (verbose) print("cSmartSetField_withConverter: converted value=" + value); // convert L to RefL if (value instanceof List && f.getType() == Concept.RefL.class) { Concept.RefL l = (Concept.RefL) (f.get(c)); if (verbose) print("cSmartSetField_withConverter: RefL=" + l); l.replaceWithList((List) value); if (verbose) print("cSmartSetField_withConverter: backrefs=" + c.backRefs); return true; } // convert concept to Ref if (value instanceof Concept && f.getType() == Concept.Ref.class) return ((Concept.Ref) f.get(c)).set((Concept) value); } return _csetField(c, field, value); } catch (Exception __e) { throw rethrow(__e); } } static Field findField2(Object o, String field) { Class c = o.getClass(); HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } if (map == getOpt_special) { if (o instanceof Class) return findField2_findStaticField((Class) o, field); } return map.get(field); } static Field findField2_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Class typeToClass(Type type) { if (type == null) return null; if (type instanceof Class) return ((Class) type); if (type instanceof ParameterizedType) return optCast(Class.class, ((ParameterizedType) type).getRawType()); return null; } static Set createOrAddToSet(Set set, A a) { if (set == null) set = new HashSet(); set.add(a); return set; } static int lKeys(MultiMap mm) { return mm == null ? 0 : mm.keysSize(); } static int latestInstalledJavaX() { File[] files = new File(userHome(), ".javax").listFiles(); int v = 0; if (files != null) for (File f : files) { Matcher m = regexpMatcher("x(\\d\\d\\d?)\\.jar", f.getName()); if (m.matches()) v = Math.max(v, Integer.parseInt(m.group(1))); } return v; } static String x30JarServerURL() { return "http://botcompany.de:8081/x30.jar"; } static int hashMap_internalHash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } static boolean eitherIsA(Either e) { return e != null && e.isA(); } static A vStackCompute(VStack.Computable computation) { if (computation == null) return null; VStack stack = new VStack(computation); stepAll(stack); return (A) stack.result(); } static CancelPoint newCancelPoint() { return new CancelPoint(); } static String replacePlusWithSpace(String s) { return replace(s, '+', ' '); } static int scoredSearch_score_single(String s, String query) { int i = indexOfIC_underscore(s, query); if (i < 0) return 0; if (i > 0) return 1; return l(s) == l(query) ? 3 : 2; } static Comparator mapComparatorDesc(final Map map) { return new Comparator() { public int compare(A a, A b) { return cmp(map.get(b), map.get(a)); } }; } static A keyWithBiggestValue(Map map) { A best = null; Number bestScore = null; for (A key : keys(map)) { Number score = map.get(key); if (best == null || cmp(score, bestScore) > 0) { best = key; bestScore = score; } } return best; } static List allToStrOrNull(Iterable c) { return lmap(__124 -> strOrNull(__124), c); } static List allToStrOrNull(Object... c) { return lmap(__125 -> strOrNull(__125), c); } static String nemptyLinesLL(Object... l) { return nemptyLines(ll(l)); } static void failIfOddCount(Object... list) { if (odd(l(list))) throw fail("Odd list size: " + list); } static Object[] asObjectArray(Collection l) { return toObjectArray(l); } static MouseListener findComponentPopupMenuListener_gen(final JComponent c) { return c == null ? null : swing(() -> firstWithClassShortNamed("componentPopupMenu_Adapter", c.getMouseListeners()) ); } static boolean internalFrameActive(Component c) { final JInternalFrame f = getInternalFrame(c); return f != null && swing(new F0() { public Boolean get() { try { return f.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret f.isSelected();"; }}); } static boolean jmenuItem_newThreads = false; static JMenuItem jmenuItem(final String text) { return jMenuItem(text, null); } static JMenuItem jmenuItem(final String text, final Object r) { return swing(new F0() { public JMenuItem get() { try { Pair p = jmenu_autoMnemonic(dropPrefix("[disabled] ", text)); JMenuItem mi = new JMenuItem(p.a); if (startsWith(text, "[disabled] ")) disableMenuItem(mi); if (p.b != 0) mi.setMnemonic(p.b); mi.addActionListener(jmenuItem_newThreads ? actionListenerInNewThread(r) : actionListener(r)); return mi; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Pair p = jmenu_autoMnemonic(dropPrefix(\"[disabled] \", text));\r\n JM..."; }}); } static MenuItem menuItem(String text, final Object r) { MenuItem mi = new MenuItem(text); mi.addActionListener(actionListener(r)); return mi; } static void addDirectMenuItem(JMenuBar mb, String text, Object action) { if (mb != null) { swing(new Runnable() { public void run() { try { addDirectMenuItem(mb, directJMenuItem(text, action)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addDirectMenuItem(mb, directJMenuItem(text, action));"; }}); } } static void addDirectMenuItem(Component c, String text, Object action) { addDirectMenuItem(addMenuBar(c), text, action); } static void addDirectMenuItem(JMenuBar mb, JMenuItem menuItem) { if (mb != null) { swing(new Runnable() { public void run() { try { mb.add(menuItem); revalidate(mb); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "mb.add(menuItem);\r\n revalidate(mb);"; }}); } } static double msToSeconds(long ms) { return toSeconds(ms); } static long fileModificationTime(File f) { return f == null ? 0 : f.lastModified(); } static void closeRandomAccessFile(RandomAccessFile f) { if (f != null) try { f.close(); callJavaX("dropIO", f); } catch (Throwable e) { printStackTrace(e); } } static RandomAccessFile newRandomAccessFile(File path, String mode) { try { boolean forWrite = mode.indexOf('w') >= 0; if (forWrite) mkdirsForFile(path); RandomAccessFile f = new RandomAccessFile(path, mode); callJavaX("registerIO", f, path, forWrite); return f; } catch (Exception __e) { throw rethrow(__e); } } static A vmBus_timerStarted(A timer) { vmBus_send("timerStarted", timer, costCenter()); return timer; } static List cloneAndClearList(Collection l) { List l2 = cloneList(l); l.clear(); return l2; } static ArrayList cloneList_noSync(Iterable l) { return l instanceof Collection ? cloneList_noSync((Collection) l) : asList(l); } static ArrayList cloneList_noSync(Collection l) { if (l == null) return new ArrayList(); return new ArrayList(l); } // f: func -> A (stream ends when f returns null) static IterableIterator iteratorFromFunction(final Object f) { class IFF extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = (A) callF(f); done = a == null; } }; return new IFF(); } // optimized version for F0 argument static IterableIterator iteratorFromFunction(F0 f) { return iteratorFromFunction_f0(f); } static IterableIterator iteratorFromFunction(IF0 f) { return iteratorFromFunction_if0(f); } // iterate safely (& quickly) in the face of concurrent modifications static IterableIterator concurrentlyIterateValues(NavigableMap map) { return concurrentlyIterateValues(map, map); } static IterableIterator concurrentlyIterateValues(final NavigableMap map, Object mutex) { return iteratorFromFunction(new F0() { Iterator it = keys(map).iterator(); A key; B get() { synchronized(mutex) { try { if (!it.hasNext()) return null; return map.get(key = it.next()); } catch (ConcurrentModificationException e) { print("Re-iterating"); it = map.tailMap(key, false).keySet().iterator(); if (!it.hasNext()) return null; return map.get(key = it.next()); // Can't throw another exception } } } }); } static void cancelThread(Thread t) { if (t == null) return; ping(); /*O mc = getWeakRef((WeakReference) vm_generalWeakSubMap("thread2mc").get(t)); ifdef cancelThread_verbose print("cancelThread: mc=" + mc); endifdef if (mc != null) { Map ping_actions = cast get(mc, 'ping_actions); synchronized(ping_actions) { ping_actions.put(t, "cancelled"); set(mc, ping_anyActions := true); } } else*/ synchronized(ping_actions) { ping_actions.put(t, "cancelled"); ping_anyActions = true; } } static double floor(double d) { return Math.floor(d); } static String unicode_leftPointingTriangle() { return unicodeFromCodePoint(0x25C2); } static String unicode_rightPointingTriangle() { return charToString(0x25B8); } static int stdHash(Object a, String... fields) { if (a == null) return 0; int hash = getClassName(a).hashCode(); for (String field : fields) hash = boostHashCombine(hash, hashCode(getOpt(a, field))); return hash; } static String htitle_h1(String s) { return htitle_noEncode(dropTags(s)) + h1(s); } static String fsI_flex(String s) { return startsWithDigit(s) ? "#" + s : s; } static DialogIO talkToSubBot(final long vport, final DialogIO io) { return talkToSubBot(String.valueOf(vport), io); } static DialogIO talkToSubBot(final String subBot, final DialogIO io) { if (subBot == null) return io; return new talkToSubBot_IO(subBot, io); } static class talkToSubBot_IO extends DialogIO { String subBot; DialogIO io; talkToSubBot_IO(String subBot, DialogIO io) { this.io = io; this.subBot = subBot;} // delegate all but sendLine boolean isStillConnected() { return io.isStillConnected(); } String readLineImpl() { return io.readLineImpl(); } boolean isLocalConnection() { return io.isLocalConnection(); } Socket getSocket() { return io.getSocket(); } public void close() { try { io.close(); } catch (Exception __e) { throw rethrow(__e); } } void sendLine(String line) { io.sendLine(format3("please forward to bot *: *", subBot, line)); } } static DialogIO talkTo(int port) { return talkTo("localhost", port); } static int talkTo_defaultTimeout = 10000; // This is the CONNECT timeout static int talkTo_timeoutForReads = 0; // Timeout waiting for answers (0 = no timeout) static ThreadLocal> talkTo_byThread = new ThreadLocal(); static DialogIO talkTo(String ip, int port) { try { String full = ip + ":" + port; Map map = talkTo_byThread.get(); if (map != null && map.containsKey(full)) return map.get(full); if (isLocalhost(ip) && port == vmPort()) return talkToThisVM(); return new talkTo_IO(ip, port); } catch (Exception __e) { throw rethrow(__e); } } static class talkTo_IO extends DialogIO { String ip; int port; Socket s; Writer w; BufferedReader in; talkTo_IO(String ip, int port) { this.port = port; this.ip = ip; try { s = new Socket(); try { if (talkTo_timeoutForReads != 0) s.setSoTimeout(talkTo_timeoutForReads); s.connect(new InetSocketAddress(ip, port), talkTo_defaultTimeout); } catch (Throwable e) { throw fail("Tried talking to " + ip + ":" + port, e); } w = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8")); } catch (Exception __e) { throw rethrow(__e); } } boolean isLocalConnection() { return s.getInetAddress().isLoopbackAddress(); } boolean isStillConnected() { return !(eos || s.isClosed()); } void sendLine(String line) { try { Lock __0 = lock; lock(__0); try { w.write(line + "\n"); w.flush(); } finally { unlock(__0); } } catch (Exception __e) { throw rethrow(__e); } } String readLineImpl() { try { return in.readLine(); } catch (Exception __e) { throw rethrow(__e); } } public void close() { try { if (!noClose) s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } } static List quickBotScan() { return ProgramScan.quickBotScan(); } static List quickBotScan(int[] preferredPorts) { return ProgramScan.quickBotScan(preferredPorts); } static List quickBotScan(String searchPattern) { List l = new ArrayList(); for (ProgramScan.Program p : ProgramScan.quickBotScan()) if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) l.add(p); return l; } static String firstPartOfHelloString(String s) { int i = s.lastIndexOf('/'); return i < 0 ? s : rtrim(s.substring(0, i)); } static String sendToLocalBotQuietly(String bot, String text, Object... args) { text = format3(text, args); DialogIO channel = newFindBot2(bot); try { if (channel == null) throw fail(quote(bot) + " not found"); try { channel.readLine(); channel.sendLine(text); String s = channel.readLine(); return s; } catch (Throwable e) { e.printStackTrace(); return null; } } finally { _close(channel); }} static String sendToLocalBotQuietly(int port, String text, Object... args) { text = format3(text, args); DialogIO channel = talkTo(port); try { try { channel.readLine(); channel.sendLine(text); String s = channel.readLine(); return s; } catch (Throwable e) { e.printStackTrace(); return null; } } finally { _close(channel); }} static Class hotwireOnce(String programID) { return hotwireCached(programID, false); } static String getBotAddress(String bot) { List l = fullBotScan(bot); return empty(l) ? null : first(l).address; } static Object unstructure_matchOK2OrFail(String s) { if (swic(s, "ok ")) return unstructure_startingAtIndex(s, 3); else throw fail(s); } static String sendToLocalBot(String bot, String text, Object... args) { text = format3(text, args); DialogIO channel = findBot(bot); try { if (channel == null) throw fail(quote(bot) + " not found"); try { channel.readLine(); print(bot + "> " + shorten(text, 80)); channel.sendLine(text); String s = channel.readLine(); print(bot + "< " + shorten(s, 80)); return s; } catch (Throwable e) { e.printStackTrace(); return null; } } finally { _close(channel); }} static String sendToLocalBot(int port, String text, Object... args) { text = format3(text, args); DialogIO channel = talkTo(port); try { try { channel.readLine(); print(port + "> " + shorten(text, 80)); channel.sendLine(text); String s = channel.readLine(); print(port + "< " + shorten(s, 80)); return s; } catch (Throwable e) { e.printStackTrace(); return null; } } finally { _close(channel); }} static String format(String pat, Object... args) { return format3(pat, args); } static int roundToInt(double d) { return (int) Math.round(d); } static JPanel jpanel(LayoutManager layout) { return swingNu(JPanel.class, layout); } static JPanel jpanel() { return swingNu(JPanel.class); } static class jLiveValueSection_class extends SingleComponentPanel { LiveValue liveValue; String getTitle() { return strOrEmpty(liveValue.get()); } jLiveValueSection_class(LiveValue lv, Component c) { super(c); bindLiveValueListenerToComponent(this, liveValue = lv, new Runnable() { public void run() { try { swingLater(new Runnable() { public void run() { try { setBorder(BorderFactory.createTitledBorder( BorderFactory.createBevelBorder(BevelBorder.LOWERED), getTitle())); revalidate(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setBorder(BorderFactory.createTitledBorder(\r\n BorderFactory.createBeve..."; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "swingLater(r {\r\n setBorder(BorderFactory.createTitledBorder(\r\n Bo..."; }}); } } static JPanel jLiveValueSection(LiveValue lv, Component c) { return swingNu(jLiveValueSection_class.class, lv, c); } static JScrollPane setScrollPaneBackground(Color c, JScrollPane sp) { if (sp != null && c != null) { swing(new Runnable() { public void run() { try { sp.getViewport().setBackground(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "sp.getViewport().setBackground(c);"; }}); } return sp; } static A withTypeWriterFont(A c) { return setFont(c, typeWriterFont()); } static void clearStringBuffer_gen(Object buf) { if (buf != null) { call(buf, "setLength", 0); call(buf, "trimToSize"); } } static void swingEvery(JComponent component, long delay, Object r) { installTimer(component, delay, r); } static void swingEvery(JComponent component, long delay, long firstDelay, Object r) { installTimer(component, r, delay, firstDelay); } static void swingEvery(RootPaneContainer frame, long delay, Object r) { installTimer(frame, delay, r); } static void swingEvery(RootPaneContainer frame, long delay, long firstDelay, Object r) { installTimer(frame, delay, firstDelay, r); } // first delay = delay static Timer installTimer(JComponent component, Object r, long delay) { return installTimer(component, r, delay, delay); } // first delay = delay static Timer installTimer(RootPaneContainer frame, long delay, Object r) { return installTimer(frame.getRootPane(), r, delay, delay); } // first delay = delay static Timer installTimer(JComponent component, long delay, Object r) { return installTimer(component, r, delay, delay); } static Timer installTimer(JComponent component, long delay, long firstDelay, Object r) { return installTimer(component, r, delay, firstDelay); } static Timer installTimer(final JComponent component, final Object r, final long delay, final long firstDelay) { return installTimer(component, r, delay, firstDelay, true); } static Timer installTimer(final JComponent component, final Object r, final long delay, final long firstDelay, final boolean repeats) { if (component == null) return null; return (Timer) swingAndWait(new F0() { public Object get() { try { final Var timer = new Var(); timer.set(new Timer(toInt(delay), new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { AutoCloseable __1 = tempActivity(r); try { try { if (!allPaused()) if (isFalse(callF(r))) cancelTimer(timer.get()); } catch (Throwable __e) { _handleException(__e); } } finally { _close(__1); }} catch (Throwable __e) { messageBox(__e); }}})); timer.get().setInitialDelay(toInt(firstDelay)); timer.get().setRepeats(repeats); bindTimerToComponent(timer.get(), component); return timer.get(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final new Var timer;\r\n timer.set(new Timer(toInt(delay), actionList..."; }}); } static Timer installTimer(RootPaneContainer frame, long delay, long firstDelay, Object r) { return installTimer(frame.getRootPane(), delay, firstDelay, r); } static StringBuffer liveLocalPrintLog() { return local_log; } static String readLineHidden() { try { if (get(javax(), "readLine_reader") == null) set(javax(), "readLine_reader" , new BufferedReader(new InputStreamReader(System.in, "UTF-8"))); try { return ((BufferedReader) get(javax(), "readLine_reader")).readLine(); } finally { consoleClearInput(); } } catch (Exception __e) { throw rethrow(__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(); if (s == null) return tok; 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 == (char) 0x201C || c == (char) 0x201D) c = '"'; // normalize quotes if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { char _c = s.charAt(j); if (_c == (char) 0x201C || _c == (char) 0x201D) _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 boolean isNonNegativeInteger(String s) { int n = l(s); if (n == 0) return false; int i = 0; while (i < n) { char c = s.charAt(i); if (c < '0' || c > '9') return false; ++i; } return true; } static Object swingCall(final Object o, final String method, final Object... args) { return swing(new F0() { public Object get() { try { return call(o, method, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret call(o, method, args);"; }}); } static Font getFont(JComponent c) { return c == null ? null : swing(new F0() { public Font get() { try { return c.getFont(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getFont();"; }}); } static List asSynchroList(Iterable l) { return syncList(cloneList(l)); } static A revalidate(final A c) { if (c == null || !c.isShowing()) return c; { swing(new Runnable() { public void run() { try { // magic combo to actually relayout and repaint c.revalidate(); c.repaint(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "// magic combo to actually relayout and repaint\r\n c.revalidate();\r\n c.r..."; }}); } return c; } static void revalidate(JFrame f) { revalidate((Component) f); } static void revalidate(JInternalFrame f) { revalidate((Component) f); } static void ensureDBNotRunning(String name) { if (hasBot(name)) { try { String framesBot = dropSuffix(".", name) + " Frames"; print("Trying to activate frames of running DB: " + framesBot); if (isOK(sendOpt(framesBot, "activate frames")) && isMainProgram()) cleanKill(); } catch (Throwable __e) { _handleException(__e); } throw fail("Already running: " + name); } } static void ensureDBNotRunning() { ensureDBNotRunning(dbBotStandardName()); } static String dbBotStandardName() { String home = userHome(); String name = dbBotName(getDBProgramID()); if (neq(home, actualUserHome())) name += " " + quote(home); return name + "."; } static volatile Android3 dbBot_instance; static Android3 dbBot() { return dbBot(true); } static Android3 dbBot(boolean ensureNotRunning) { return dbBot(dbBotStandardName(), ensureNotRunning); } static Android3 dbBot(String name) { return dbBot(name, true); } static Android3 dbBot(String name, boolean ensureNotRunning) { if (ensureNotRunning) ensureDBNotRunning(name); return dbBot_instance = methodsBot2(name, assertNotNull(db_mainConcepts()), db_standardExposedMethods(), db_mainConcepts().lock); } static void thinAProgramsBackups(String progID, boolean doIt) { List files = new ArrayList(); Map ageMap = new HashMap(); // minutes (last group) are optional java.util.regex.Pattern pat = regexp("^(.*)\\.backup(20\\d\\d)(\\d\\d)(\\d\\d)-(\\d\\d)(\\d*)$"); //print("Processing backups of program " + progID); File dir = programDir(progID); for (File f : listFilesNotDirs(dir, newFile(dir, "backups"))) { String s = f.getName(); java.util.regex.Matcher matcher = pat.matcher(s); { if (!(matcher.find())) continue; } String originalName = matcher.group(1); { if (!(eq(originalName, "concepts.structure.gz"))) continue; } //print("Found backup: " + sfu(matcherGroups(matcher))); int year = matcherInt(matcher, 2); int month = matcherInt(matcher, 3); int day = matcherInt(matcher, 4); int hour = matcherInt(matcher, 5); int minute = matcherInt(matcher, 6); long time = timestampFromYMDHM(year, month, day, hour, minute); double age = ((now()-time)/1000.0/60/60/24); //print("Age: " + age + " days"); ageMap.put(f, age); files.add(f); } int numDeleted = 0; sortByMap_inPlace(files, ageMap); double lastAge = -1; for (File f : files) { double age = ageMap.get(f); if (!thinAProgramsBackups_shouldKeep(age, lastAge)) { //print("Deleting: " + f); ++numDeleted; if (doIt) { print("Deleting: " + f); f.delete(); } } else { //print("Keeping: " + f); lastAge = age; } } if (numDeleted != 0) print((doIt ? "Deleted: " : "Would delete: ") + n(numDeleted, "file")); } // age = age in days static boolean thinAProgramsBackups_shouldKeep(double age, double lastAge) { return defaultAgeBasedBackupRetentionStrategy_shouldKeep(age, lastAge); } // auto-magic type parameter might be a bad idea. supply explicitly if // in doubt. static IF2 if0ToIF2(IF0 f) { return f == null ? null : (a, b) -> f.get(); } static List splitByJavaToken(String s, String splitToken) { List tok = javaTok(s); List l = new ArrayList(); int i = 1; while (i < l(tok)) { int j = smartIndexOf(tok, splitToken, i); l.add(join(subList(tok, i, j-1))); i = j+2; } return l; } static String dollarVarsToStars(String s) { return dollarVarsToStars(s, null); } static String dollarVarsToStars(String s, List varNames_out) { List tok = javaTok(s); for (int i = 1; i < l(tok); i += 2) { String t = tok.get(i); if (isDollarVar(t)) { listAdd(varNames_out, t); tok.set(i, "*"); } else if (eq(t, "*")) listAdd(varNames_out, "*"); } return join(tok); } // add {} to a statement if it isn't a block already // i = beginning of statement or block static void tok_statementToBlock(List tok, int i) { if (neqGet(tok, i, "{")) { int iEnd = tok_findEndOfStatement(tok, i)-1; tokAppend_reTok(tok, iEnd, " }"); tokPrepend_reTok(tok, i, "{ "); } } static void tokAppend_reTok(List tok, int i, String s) { tok.set(i, tok.get(i)+s); reTok(tok, i, i+1); } static List mapWithIndexStartingAt1(Collection l, IF2 f) { int n = l(l), i = 0; List out = emptyList(n); for (A a : unnullForIteration(l)) out.add(f.get(++i, a)); return out; } static boolean containsDollarVars(String s) { for (String t : javaTokC(s)) if (isDollarVar(t)) return true; return false; } static String standardCredentialsUser() { return trim(loadTextFile( oneOfTheFiles( javaxSecretDir("tinybrain-username"), userDir(".tinybrain/username")))); } static String standardCredentialsPass() { return trim(loadTextFile( oneOfTheFiles( javaxSecretDir("tinybrain-userpass"), userDir(".tinybrain/userpass")))); } // map contains function name -> snippet id static Map parseStdFunctionsList(String snippetSrc) { return parseStdFunctionsList(snippetSrc, new LinkedHashMap()); } static Map parseStdFunctionsList(String snippetSrc, Map map) { List tok = javaTok(snippetSrc); int i = findCodeTokens(tok, "standardFunctions", "=", "litlist", "("); int opening = i+6; int closing = indexOf(tok, ")", opening)-1; for (i = opening+2; i < closing; i += 4) { String[] f = unquote(tok.get(i)).split("/"); map.put(f[1], f[0]); } return map; } static String loadSnippetSilently(Snippet s) { return loadSnippetQuietly(s); } static String loadSnippetSilently(String snippetID) { return loadSnippetQuietly(snippetID); } static List toLinesFullTrim_java(String text) { // TODO: shouldn't it be tlft(javaDropComments(text))? return tlft(joinLines(map(__126 -> javaDropComments(__126), tlft(text)))); } static int indexOfNonDigit(String s) { int n = l(s); for (int i = 0; i < n; i++) if (!isDigit(s.charAt(i))) return i; return -1; } static AlphanumComparator alphaNumComparator_instance; static Comparator alphaNumComparator() { if (alphaNumComparator_instance == null) alphaNumComparator_instance = new AlphanumComparator(); return alphaNumComparator_instance; } static String[][] htmldecode_escapes() { return htmldecode_ESCAPES; } static final String[][] htmldecode_ESCAPES = { {"\"", "quot"}, // " - double-quote {"&", "amp"}, // & - ampersand {"<", "lt"}, // < - less-than {">", "gt"}, // > - greater-than // Mapping to escape ISO-8859-1 characters to their named HTML 3.x equivalents. {"\u00A0", "nbsp"}, // non-breaking space {"\u00A1", "iexcl"}, // inverted exclamation mark {"\u00A2", "cent"}, // cent sign {"\u00A3", "pound"}, // pound sign {"\u00A4", "curren"}, // currency sign {"\u00A5", "yen"}, // yen sign = yuan sign {"\u00A6", "brvbar"}, // broken bar = broken vertical bar {"\u00A7", "sect"}, // section sign {"\u00A8", "uml"}, // diaeresis = spacing diaeresis {"\u00A9", "copy"}, // copyright sign {"\u00AA", "ordf"}, // feminine ordinal indicator {"\u00AB", "laquo"}, // left-pointing double angle quotation mark = left pointing guillemet {"\u00AC", "not"}, // not sign {"\u00AD", "shy"}, // soft hyphen = discretionary hyphen {"\u00AE", "reg"}, // registered trademark sign {"\u00AF", "macr"}, // macron = spacing macron = overline = APL overbar {"\u00B0", "deg"}, // degree sign {"\u00B1", "plusmn"}, // plus-minus sign = plus-or-minus sign {"\u00B2", "sup2"}, // superscript two = superscript digit two = squared {"\u00B3", "sup3"}, // superscript three = superscript digit three = cubed {"\u00B4", "acute"}, // acute accent = spacing acute {"\u00B5", "micro"}, // micro sign {"\u00B6", "para"}, // pilcrow sign = paragraph sign {"\u00B7", "middot"}, // middle dot = Georgian comma = Greek middle dot {"\u00B8", "cedil"}, // cedilla = spacing cedilla {"\u00B9", "sup1"}, // superscript one = superscript digit one {"\u00BA", "ordm"}, // masculine ordinal indicator {"\u00BB", "raquo"}, // right-pointing double angle quotation mark = right pointing guillemet {"\u00BC", "frac14"}, // vulgar fraction one quarter = fraction one quarter {"\u00BD", "frac12"}, // vulgar fraction one half = fraction one half {"\u00BE", "frac34"}, // vulgar fraction three quarters = fraction three quarters {"\u00BF", "iquest"}, // inverted question mark = turned question mark {"\u00C0", "Agrave"}, // ? - uppercase A, grave accent {"\u00C1", "Aacute"}, // ? - uppercase A, acute accent {"\u00C2", "Acirc"}, // ? - uppercase A, circumflex accent {"\u00C3", "Atilde"}, // ? - uppercase A, tilde {"\u00C4", "Auml"}, // ? - uppercase A, umlaut {"\u00C5", "Aring"}, // ? - uppercase A, ring {"\u00C6", "AElig"}, // ? - uppercase AE {"\u00C7", "Ccedil"}, // ? - uppercase C, cedilla {"\u00C8", "Egrave"}, // ? - uppercase E, grave accent {"\u00C9", "Eacute"}, // ? - uppercase E, acute accent {"\u00CA", "Ecirc"}, // ? - uppercase E, circumflex accent {"\u00CB", "Euml"}, // ? - uppercase E, umlaut {"\u00CC", "Igrave"}, // ? - uppercase I, grave accent {"\u00CD", "Iacute"}, // ? - uppercase I, acute accent {"\u00CE", "Icirc"}, // ? - uppercase I, circumflex accent {"\u00CF", "Iuml"}, // ? - uppercase I, umlaut {"\u00D0", "ETH"}, // ? - uppercase Eth, Icelandic {"\u00D1", "Ntilde"}, // ? - uppercase N, tilde {"\u00D2", "Ograve"}, // ? - uppercase O, grave accent {"\u00D3", "Oacute"}, // ? - uppercase O, acute accent {"\u00D4", "Ocirc"}, // ? - uppercase O, circumflex accent {"\u00D5", "Otilde"}, // ? - uppercase O, tilde {"\u00D6", "Ouml"}, // ? - uppercase O, umlaut {"\u00D7", "times"}, // multiplication sign {"\u00D8", "Oslash"}, // ? - uppercase O, slash {"\u00D9", "Ugrave"}, // ? - uppercase U, grave accent {"\u00DA", "Uacute"}, // ? - uppercase U, acute accent {"\u00DB", "Ucirc"}, // ? - uppercase U, circumflex accent {"\u00DC", "Uuml"}, // ? - uppercase U, umlaut {"\u00DD", "Yacute"}, // ? - uppercase Y, acute accent {"\u00DE", "THORN"}, // ? - uppercase THORN, Icelandic {"\u00DF", "szlig"}, // ? - lowercase sharps, German {"\u00E0", "agrave"}, // ? - lowercase a, grave accent {"\u00E1", "aacute"}, // ? - lowercase a, acute accent {"\u00E2", "acirc"}, // ? - lowercase a, circumflex accent {"\u00E3", "atilde"}, // ? - lowercase a, tilde {"\u00E4", "auml"}, // ? - lowercase a, umlaut {"\u00E5", "aring"}, // ? - lowercase a, ring {"\u00E6", "aelig"}, // ? - lowercase ae {"\u00E7", "ccedil"}, // ? - lowercase c, cedilla {"\u00E8", "egrave"}, // ? - lowercase e, grave accent {"\u00E9", "eacute"}, // ? - lowercase e, acute accent {"\u00EA", "ecirc"}, // ? - lowercase e, circumflex accent {"\u00EB", "euml"}, // ? - lowercase e, umlaut {"\u00EC", "igrave"}, // ? - lowercase i, grave accent {"\u00ED", "iacute"}, // ? - lowercase i, acute accent {"\u00EE", "icirc"}, // ? - lowercase i, circumflex accent {"\u00EF", "iuml"}, // ? - lowercase i, umlaut {"\u00F0", "eth"}, // ? - lowercase eth, Icelandic {"\u00F1", "ntilde"}, // ? - lowercase n, tilde {"\u00F2", "ograve"}, // ? - lowercase o, grave accent {"\u00F3", "oacute"}, // ? - lowercase o, acute accent {"\u00F4", "ocirc"}, // ? - lowercase o, circumflex accent {"\u00F5", "otilde"}, // ? - lowercase o, tilde {"\u00F6", "ouml"}, // ? - lowercase o, umlaut {"\u00F7", "divide"}, // division sign {"\u00F8", "oslash"}, // ? - lowercase o, slash {"\u00F9", "ugrave"}, // ? - lowercase u, grave accent {"\u00FA", "uacute"}, // ? - lowercase u, acute accent {"\u00FB", "ucirc"}, // ? - lowercase u, circumflex accent {"\u00FC", "uuml"}, // ? - lowercase u, umlaut {"\u00FD", "yacute"}, // ? - lowercase y, acute accent {"\u00FE", "thorn"}, // ? - lowercase thorn, Icelandic {"\u00FF", "yuml"}, // ? - lowercase y, umlaut {"\u2013", "ndash"}, {"\u2018", "lsquo"}, {"\u2019", "rsquo"}, {"\u201D", "rdquo"}, {"\u201C", "ldquo"}, {"\u2014", "mdash"}, {"'", "apos"}, // the controversial (but who cares!) ' // stackoverflow.com/questions/2083754/why-shouldnt-apos-be-used-to-escape-single-quotes }; static List dropHTMLComments(List tok) { List l = new ArrayList(); for (int idx = 0; idx < l(tok); idx++) { String t = tok.get(idx); if (even(idx)) { int i; while ((i = t.indexOf("", i+4); t = substring(t, 0, i) + substring(t, j+3); } } l.add(t); } return l; } static String dropTags(String html) { return dropAllTags(html); } static List dropTags(List tok) { return dropAllTags(tok); } static A[] itemPlusArray(A a, A[] l) { return singlePlusArray(a, l); } static String getProgramName_cache; static String getProgramName() { Lock __0 = downloadLock(); lock(__0); try { if (getProgramName_cache == null) getProgramName_cache = getSnippetTitleOpt(programID()); return getProgramName_cache; } finally { unlock(__0); } } static void _onLoad_getProgramName() { { startThread(new Runnable() { public void run() { try { getProgramName(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "getProgramName();"; }}); } } static List buttonsInGroup(ButtonGroup g) { if (g == null) return ll(); return asList(g.getElements()); } static Frame getAWTFrame(final Object _o) { return swing(new F0() { public Frame get() { try { Object o = _o; /* ifdef HaveProcessing if (o instanceof PApplet) o = ((PApplet) o).getSurface(); endifdef */ if (o instanceof ButtonGroup) o = first(buttonsInGroup((ButtonGroup) o)); if (!(o instanceof Component)) return null; Component c = (Component) o; while (c != null) { if (c instanceof Frame) return (Frame) c; c = c.getParent(); } return null; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O o = _o;\r\n /*\r\n ifdef HaveProcessing\r\n if (o instanceof PApplet) ..."; }}); } static int imageIcon_cacheSize = 10; static boolean imageIcon_verbose = false; static Map imageIcon_cache; static Lock imageIcon_lock = lock(); static ThreadLocal imageIcon_fixGIF = new ThreadLocal(); // not going through BufferedImage preserves animations static ImageIcon imageIcon(String imageID) { try { if (imageID == null) return null; Lock __0 = imageIcon_lock; lock(__0); try { if (imageIcon_cache == null) imageIcon_cache = new MRUCache(imageIcon_cacheSize); imageID = fsI(imageID); ImageIcon ii = imageIcon_cache.get(imageID); if (ii == null) { if (imageIcon_verbose) print("Loading image icon: " + imageID); File f = loadBinarySnippet(imageID); Boolean b = imageIcon_fixGIF.get(); if (!isFalse(b)) ii = new ImageIcon(loadBufferedImageFixingGIFs(f)); else ii = new ImageIcon(f.toURI().toURL()); } else imageIcon_cache.remove(imageID); // move to front of cache on access imageIcon_cache.put(imageID, ii); return ii; } finally { unlock(__0); } } catch (Exception __e) { throw rethrow(__e); } } // doesn't fix GIFs static ImageIcon imageIcon(File f) { try { return new ImageIcon(f.toURI().toURL()); } catch (Exception __e) { throw rethrow(__e); } } static ImageIcon imageIcon(Image img) { return new ImageIcon(img); } static void standardTitlePopupMenu(final JFrame frame) { // standard right-click behavior on titles if (!isSubstanceLAF()) return; titlePopupMenu(frame, new VF1() { public void get(JPopupMenu menu) { try { boolean alwaysOnTop = frame.isAlwaysOnTop(); menu.add(jmenuItem("Restart Program", "restart")); menu.add(jmenuItem("Duplicate Program", "duplicateThisProgram")); menu.add(jmenuItem("Show Console", "showConsole")); menu.add(jCheckBoxMenuItem("Always On Top", alwaysOnTop, new Runnable() { public void run() { try { toggleAlwaysOnTop(frame) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "toggleAlwaysOnTop(frame)"; }})); /*ifndef standardTitlePopupMenu_noShootWindow { menu.add(jMenuItem("Shoot Window", r { shootWindowGUI_external(frame, 500) })); } endifndef*/ //addMenuItem(menu, "Bigger fonts", f swingBiggerFonts); //addMenuItem(menu, "Smaller fonts", f swingSmallerFonts); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "bool alwaysOnTop = frame.isAlwaysOnTop();\r\n ifndef standardTitlePopupMenu_..."; }}); } static Rectangle screenRectangle() { return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); } static Rectangle screenRectangle(GraphicsDevice device) { if (device == null) return null; DisplayMode mode = device.getDisplayMode(); return new Rectangle(0, 0, mode.getWidth(), mode.getHeight()); } static JFrame consoleFrame() { return (JFrame) getOpt(get(getJavaX(), "console"), "frame"); } static void autoVMExit() { call(getJavaX(), "autoVMExit"); } static String codePointToString(int codePoint) { return new String(Character.toChars(codePoint)); } static List regexpGetGroups(Matcher matcher) { int n = matcher.groupCount(); List l = new ArrayList(); for (int i = 1; i <= n; i++) l.add(matcher.group(i)); return l; } // performs find() static List regexpGetGroups(String pat, String s) { Matcher m = regexpMatcher(pat, s); if (m.find()) return regexpGetGroups(m); return null; } static JComponent getInternalFrameTitlePaneComponent(JInternalFrame f) { return (JComponent) childWithClassNameEndingWith(f, "InternalFrameTitlePane"); } static String getFrameTitle(Component c) { JFrame f = getFrame(c); return f == null ? null : f.getTitle(); } static void closeAutoCloseables(Collection l) { if (l != null) for (AutoCloseable c : l) { try { c.close(); } catch (Throwable __e) { _handleException(__e); }} } static boolean hasMethod(Object o, String method, Object... args) { return findMethod_cached(o, method, args) != null; } static A nuInstance(Class c) { return nuEmptyObject(c); } static A copyFields(Object x, A y, String... fields) { if (empty(fields)) { // assume we should copy all fields Map map = objectToMap(x); for (String field : map.keySet()) setOpt(y, field, map.get(field)); } else for (String field : fields) { Object o = getOpt(x, field); if (o != null) setOpt(y, field, o); } return y; } static A copyFields(Object x, A y, Collection fields) { return copyFields(x, y, asStringArray(fields)); } static Object dm_getStemByID(Object id) { return dm_callOS("getModuleByID", str(id)); } // return Int.MAX_VALUE if not applicable // Lower score is better static int methodApplicabilityScore_onTypes(Method m, Class[] argTypes) { Class[] types = m.getParameterTypes(); if (types.length != argTypes.length) return Integer.MAX_VALUE; int score = 0; for (int i = 0; i < types.length; i++) { Class a = argTypes[i]; Class c = types[i]; if (c == a) {} // perfect match else if (isSubclassOf(a, c)) ++score; else return Integer.MAX_VALUE; } return score; } static void addActionListener(JTextField tf, final Runnable action) { onEnter(tf, action); } static void addActionListener(final JComboBox cb, final Runnable action) { if (cb != null) { swing(new Runnable() { public void run() { try { cb.addActionListener(actionListener(action)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "cb.addActionListener(actionListener(action));"; }}); } } static void addActionListener(final AbstractButton b, final Runnable action) { if (b != null) { swing(new Runnable() { public void run() { try { b.addActionListener(actionListener(action)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "b.addActionListener(actionListener(action));"; }}); } } static void internalFrameTitlePopupMenuItem(JInternalFrame f, final String name, final Object action) { internalFrameTitlePopupMenu(f, new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, name, action) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, name, action)"; }}); } static boolean containsSpace(String s) { return containsSpaces(s); } static List replaceElementsUsingMap(Iterable l, final Map map) { return map(l, new F1() { public A get(A a) { try { return getOrKeep(map, a); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "getOrKeep(map, a)"; }}); } static List splitCamelCase(String s) { return ai_splitCamelCase(s); } static void addMenuItems(JMenu m, Object... x) { fillJMenu(m, x); } static void addMenuItems(JPopupMenu m, Object... x) { fillJPopupMenu(m, x); } static boolean isChecked(JCheckBox checkBox) { return checkBox != null && (boolean) swing(new F0() { public Boolean get() { try { return checkBox.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret checkBox.isSelected();"; }}); } static boolean isChecked(JCheckBoxMenuItem mi) { return mi != null && (boolean) swing(new F0() { public Boolean get() { try { return mi.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret mi.isSelected();"; }}); } static boolean isChecked(JRadioButton rb) { return rb != null && (boolean) swing(() -> rb.isSelected()); } static File toFile_nullOnEmpty(String s) { return empty(s) ? null : new File(s); } static DynModule dm_current() { return dm_currentModule(); } static void setTrayIconToolTip(TrayIcon trayIcon, String toolTip) { if (trayIcon != null) trayIcon.setToolTip(toolTip); } static JLabel jImageLabel(Image img) { return swingNu(JLabel.class, imageIcon(img)); } static JLabel jImageLabel(javax.swing.Icon icon) { return swingNu(JLabel.class, icon); } static JLabel jImageLabel(String imageID) { return jImageLabel(imageIcon(imageID)); } static JLabel jImageLabel(String text, String imageID) { final JLabel l = swingNu(JLabel.class, text, imageIcon(imageID), JLabel.CENTER); { swing(new Runnable() { public void run() { try { l.setVerticalTextPosition(SwingConstants.BOTTOM); l.setHorizontalTextPosition(SwingConstants.CENTER); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "l.setVerticalTextPosition(SwingConstants.BOTTOM);\r\n l.setHorizontalTextPos..."; }}); } return l; } static String localSnippetTitle(String snippetID) { if (!isLocalSnippetID(snippetID)) return null; File f = localSnippetFile(snippetID); if (!f.exists()) return null; return or2(getFileInfoField(dropExtension(f), "Title"), "Unnamed"); } static String muricaCredentialsQuery() { return htmlQuery(muricaCredentials()); } static boolean isGeneralFileServerSnippet(long id) { return id >= 1400000 && id < 1500000; } static String standardCredentials_noCookies() { return standardCredentials() + "&noCookies=1"; } static boolean saveTextFileIfChanged(File f, String contents) { return saveTextFileIfDifferent(f, contents); } static File snippetTitle_cacheFile(String snippetID) { return javaxCachesDir("Snippet Titles/" + psI(snippetID)); } static List sortFilesByName(List l) { sort(l, (a, b) -> stdcompare(a.getName(), b.getName())); return l; } static List listFilesNotDirs(String dir) { return listFilesOnly(dir); } static List listFilesNotDirs(File... dirs) { return listFilesOnly(dirs); } static String regexpFirstGroup(String pat, String s) { Matcher m = regexpMatcher(pat, s); if (m.find()) return m.group(1); else return null; } static List sortFilesByNameDesc(List l) { sort(l, (a, b) -> stdcompare(b.getName(), a.getName())); return l; } static int localYear() { return localYear(now()); } static int localYear(long time) { return parseInt(simpleDateFormat_local("yyyy").format(time)); } static int localMonth(long time) { return parseInt(simpleDateFormat_local("MM").format(time)); } static int localMonth() { return localMonth(now()); } static int localDayOfMonth(long time) { return parseInt(simpleDateFormat_local("dd").format(time)); } static int localDayOfMonth() { return localDayOfMonth(now()); } static AutoCloseable tempActivity(Object r) { return null; } static long fixTimestamp(long timestamp) { return timestamp > now() ? 0 : timestamp; } static String loadGZTextFile(File file) { try { if (!file.isFile()) return null; ping(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream fis = new FileInputStream(file); try { GZIPInputStream gis = newGZIPInputStream(fis); byte[] buffer = new byte[1024]; int len; while ((len = gis.read(buffer)) != -1) baos.write(buffer, 0, len); baos.close(); return fromUtf8(baos.toByteArray()); // TODO: use a Reader } finally { _close(fis); }} catch (Exception __e) { throw rethrow(__e); } } // non-strings must be Comparable to each other // probably this is not that useful after all static generalizedCIComparator_C generalizedCIComparator_cache; static generalizedCIComparator_C generalizedCIComparator() { if (generalizedCIComparator_cache == null) generalizedCIComparator_cache = generalizedCIComparator_load(); return generalizedCIComparator_cache; } static generalizedCIComparator_C generalizedCIComparator_load() { return new generalizedCIComparator_C(); } final static class generalizedCIComparator_C implements Comparator { public int compare(Object o1, Object o2) { if (o1 instanceof String) if (o2 instanceof String) return betterCIComparator().compare(((String) o1), ((String) o2)); else return -1; else if (o2 instanceof String) return 1; else return cmp(o1, o2); } } static List dm_modulesWithLibID(final String moduleLibID) { return filter(dm_moduleIDs() , new F1() { public Boolean get(String m) { try { return eq(dm_moduleLibID(m), moduleLibID); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "eq(dm_moduleLibID(m), moduleLibID)"; }}); } static String dm_moduleID(Object module) { return strOrNull(getOpt(dm_getStem(module), "id")); } static String dm_moduleID() { return dm_moduleID(dm_current_mandatory_generic()); } static String lowestByAlphaNum(Collection c) { String best = null; for (String s : unnullForIteration(c)) if (best == null || alphaNumComparator().compare(s, best) < 0) best = s; return best; } static boolean hasSuperclassShortNamed(Object o, String name) { Class c = _getClass(o); while (c != null) if (shortClassNameIs(c, name)) return true; else c = c.getSuperclass(); return false; } static boolean isConceptClassIndexed(Class c) { return isConceptClassIndexed(db_mainConcepts(), c); } static boolean isConceptClassIndexed(Concepts concepts, Class c) { return concepts.conceptCounterForClass(c) != null; } static void indexRandomConceptField(Class c) { indexRandomConceptField(db_mainConcepts(), c); } static void indexRandomConceptField(Concepts cc, Class c) { String field = or(first(conceptFields(c)), "_dummy"); print("Indexing " + c + "." + field); indexConceptField(cc, c, field); assertTrue("Concept class indexed", isConceptClassIndexed(cc, c)); { long _startTime_0 = sysNow(); try { print("Got " + nConcepts(countConcepts(cc, c))); } finally { _startTime_0 = sysNow()-_startTime_0; saveTiming(_startTime_0); } } } static JButton jbutton(String text, Object action) { return newButton(text, action); } // button without action static JButton jbutton(String text) { return newButton(text, null); } /*static JButton jbutton(BufferedImage img, O action) { ret setButtonImage(img, jbutton("", action)); }*/ static JButton jbutton(Action action) { return swingNu(JButton.class, action); } // now also takes a map as single array entry static A optPar_ignoreOddLength(Object[] opt, String name, A defaultValue) { int n = l(opt); if (n == 1 && opt[0] instanceof Map) { Map map = (Map) (opt[0]); return map.containsKey(name) ? (A) map.get(name) : defaultValue; } for (int i = 0; i+1 < l(opt); i += 2) if (eq(opt[i], name)) return (A) opt[i+1]; return defaultValue; } static Object optPar_ignoreOddLength(Object[] opt, String name) { return optPar_ignoreOddLength(opt, name, null); } static Object optPar_ignoreOddLength(String name, Object[] params) { return optPar_ignoreOddLength(params, name); } static void fillJPopupMenu(JPopupMenu m, Object... x) { if (x == null) return; for (int i = 0; i < l(x); i++) { Object o = x[i], y = get(x, i+1); if (o instanceof IVF1) callF(o, m); else if (o instanceof List) fillJPopupMenu(m, asArray((List) o)); else if (isMenuSeparatorIndicator(o)) m.addSeparator(); else if (o instanceof LiveValue && ((LiveValue) o).getType() == String.class && isRunnableX(y)) { final LiveValue lv = (LiveValue) o; final JMenuItem mi = jmenuItem("", y); bindLiveValueListenerToComponent(mi, lv, new Runnable() { public void run() { try { String s = lv.get(); if (isCurlyBracketed(s)) { setEnabled(mi, false); s = unCurlyBracket(s); } else setEnabled(mi, true); setText(mi, s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "String s = lv.get();\r\n if (isCurlyBracketed(s)) {\r\n setEnable..."; }}); m.add(mi); } else if (o instanceof String && isRunnableX(y)) { m.add(jmenuItem((String) o, y)); ++i; } else if (o instanceof JMenuItem) m.add((JMenuItem) o); // "call" might use wrong method else if (o instanceof String || o instanceof Action || o instanceof Component) call(m, "add", o); else if (o != null) print("Unknown menu item: " + o); } } static int getPreferredWidth(Component c) { return preferredWidth(c); } // f can return false to suppress regular printing // call print_raw within f to actually print something // f preferrably is F1 static Object interceptPrintInThisThread(Object f) { Object old = print_byThread().get(); print_byThread().set(f); return old; } static File localSnippetFile(long snippetID) { return localSnippetsDir(snippetID + ".text"); } static File localSnippetFile(String snippetID) { return localSnippetFile(parseSnippetID(snippetID)); } static long collectMinLong(Collection c, String field) { long x = Long.MAX_VALUE; for (Object o : unnull(c)) { Long l = (Long) (getOpt(o, field)); if (l != null) x = min(x, l); } return x; } static long collectMaxLong(Collection c, String field) { long x = Long.MIN_VALUE; for (Object o : unnull(c)) { Long l = (Long) (getOpt(o, field)); if (l != null) x = max(x, l); } return x; } static String i(String s, Object... params) { return tag("i", s, params); } static String formatDateAndTime(long timestamp) { return formatDate(timestamp); } static String formatDateAndTime() { return formatDate(); } static String shortenEndTime(String endTime, String startTime) { int i = endTime.lastIndexOf(' ')+1; if (i > 0 && eq(substring(startTime, 0, i), substring(endTime, 0, i))) return trim(substring(endTime, i)); return endTime; } static File parentFile(File f) { return dirOfFile(f); } static String hms() { return hms(now()); } static String hms(long time) { return new SimpleDateFormat("HHmmss").format(time); } static Map> parse3_cachedInput_cache = synchronizedMRUCache(1000); static List parse3_cachedInput(String s) { List tok = parse3_cachedInput_cache.get(s); if (tok == null) parse3_cachedInput_cache.put(s, tok = parse3(s)); return tok; } static Map> parse3_cachedPattern_cache = synchronizedMRUCache(1000); static synchronized List parse3_cachedPattern(String s) { List tok = parse3_cachedPattern_cache.get(s); if (tok == null) parse3_cachedPattern_cache.put(s, tok = parse3(s)); return tok; } static List replaceSubList(List l, List x, List y) { return replaceSublist(l, x, y); } static List replaceSubList(List l, int fromIndex, int toIndex, List y) { return replaceSublist(l, fromIndex, toIndex, y); } static int smartLastIndexOf(String s, char c) { if (s == null) return 0; int i = s.lastIndexOf(c); return i >= 0 ? i : l(s); } static int smartLastIndexOf(List l, A sub) { int i = lastIndexOf(l, sub); return i < 0 ? l(l) : i; } static ThreadLocal threadLocalWithDefault(A defaultValue) { return new ThreadLocal() { public A initialValue() { return defaultValue; } }; } static List keysWithoutHidden(Map map) { return filter(keys(map) , new F1() { public Boolean get(Object o) { try { return !eq(o, "[hidden]") && !isStringStartingWith(o, "[hidden] "); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "!eq(o, \"[hidden]\") && !isStringStartingWith(o, \"[hidden] \")"; }}); } static String structureOrTextForUser(Object o) { return o == null ? "" : o instanceof String ? (String) o : structureForUser(o); } static void consoleFont(Font font) { callOpt(getConsoleTextArea_gen(), "setFont", font); } static void consoleInputFont(final Font f) { { swing(new Runnable() { public void run() { try { JTextField input = consoleInputField(); if (input != null) { input.setFont(f); revalidateFrame(input); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JTextField input = consoleInputField();\r\n if (input != null) {\r\n inpu..."; }}); } } // lists files only - who needs to list directories?! // lists in typical .zip fashion - dir1/dir2/bla.txt static List listZipFile(File inZip) { try { ZipFile zipFile = new ZipFile(inZip); try { return listZipFile(zipFile); } finally { zipFile.close(); } } catch (Exception __e) { throw rethrow(__e); } } static List listZipFile(ZipFile zipFile) { try { List list = new ArrayList(); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = (ZipEntry) entries.nextElement(); if (!entry.isDirectory()) list.add(entry.getName()); } return list; } catch (Exception __e) { throw rethrow(__e); } } static List listZipFile(String name) { if (isSnippetID(name)) return listZipFile(loadLibrary(name)); return listZipFile(getProgramFile(name)); } static String snippetRawURL_maxCache(String snippetID) { return snippetRawURL_maxCache(snippetID, null); } static String snippetRawURL_maxCache(String snippetID, String contentType) { return "https://botcompany.de/serve/" + psI(snippetID) + htmlQuery("ct" , contentType, "maxCache" , 1); } static List dropFirstAndLast(int n, List l) { return cloneSubList(l, n, l(l)-n); } static List dropFirstAndLast(int m, int n, List l) { return cloneSubList(l, m, l(l)-n); } static List dropFirstAndLast(List l) { return dropFirstAndLast(1, l); } static String dropFirstAndLast(String s) { return substring(s, 1, l(s)-1); } static void mapPut_noOverwrite(Map map, A key, B value) { if (map != null && key != null && value != null && !map.containsKey(key)) map.put(key, value); } static String pnlToString(String prefix, Iterable l) { return hijackPrint(new Runnable() { public void run() { try { pnl(prefix, l) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pnl(prefix, l)"; }}); } static String pnlToString(final Iterable l) { return hijackPrint(new Runnable() { public void run() { try { pnl(l) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pnl(l)"; }}); } static String pnlToString(final A[] l) { return hijackPrint(new Runnable() { public void run() { try { pnl(l) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pnl(l)"; }}); } static String pnlToString(final Map map) { return hijackPrint(new Runnable() { public void run() { try { pnl(map) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pnl(map)"; }}); } static String pnlToString(MultiMap map) { return pnlToString(multiMapToMap(map)); } static MultiMap treeMultiMap() { return new MultiMap(true); } static MultiMap treeMultiMap(Comparator comparator) { return new MultiMap(new TreeMap>(comparator)); } static List sortedByField(Collection c, final String field) { List l = new ArrayList(c); sort(l, new Comparator() { public int compare(A a, A b) { return cmp(getOpt(a, field), getOpt(b, field)); } }); return l; } static List sortedByField(String field, Collection c) { return sortedByField(c, field); } static Object loadJSONFile(File f) { return jsonDecode(loadTextFile(f)); } static List filterNempty(Collection c) { List l = new ArrayList(); for (String x : unnull(c)) if (nempty(x)) l.add(x); return l; } static A objectWhere(Collection c, Object... data) { return findWhere(c, data); } static TimerTask timerTask(final Object r, final java.util.Timer timer) { return new TimerTask() { public void run() { if (!licensed()) timer.cancel(); else pcallF(r); } }; } static A lookupOrKeep(Map map, A key) { return map != null && map.containsKey(key) ? map.get(key) : key; } static Object subBot_httpd() { Object httpd = getThreadLocal((ThreadLocal) getOpt(mainBot(), "MyHTTPD_current")); if (httpd == null) httpd = getThreadLocal((ThreadLocal) getOpt(mainBot(), "WebSocketHTTPD_current")); return httpd; } static String domainName() { Object session = call(getMainBot(), "getSession"); Map headers = (Map) (call(session, "getHeaders")); String host = (String) (headers.get("host")); if (host == null) return null; return dropFrom(host, ":"); } static SimpleDateFormat simpleDateFormat_timeZone(String format, String timeZone) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(timeZone(timeZone)); return sdf; } static Map mruCache(int maxSize) { return synchronizedMRUCache(maxSize); } static long ipToInt(String ip) { Matches m = new Matches(); assertTrue(jmatch("*.*.*.*", ip, m)); return parseLong(m.unq(3)) | parseLong(m.unq(2)) << 8 | parseLong(m.unq(1)) << 16 | parseLong(m.unq(0)) << 24; } static B mapGetOrCreate(Map map, A key, Class c) { return getOrCreate(map, key, c); } // f : func -> B static B mapGetOrCreate(Map map, A key, Object f) { return getOrCreate(map, key, f); } static B mapGetOrCreate(Map map, A key, IF0 f) { return getOrCreate(map, key, (Object) f); } static B mapGetOrCreate(Class c, Map map, A key) { return getOrCreate(c, map, key); } static boolean directoryEmpty(File f) { return directoryIsEmpty(f); } static void unzipSnippet(String snippetID, File toDir) { print("Unzipping snippet " + snippetID + " to " + toDir); zip2dir(loadLibrary(snippetID), toDir); } // returns pair(character range, line) or null if not found // if no exact match found, return line above // nav takes a line and returns -1 (move up), 0 (found) or 1 (move down) static Pair binarySearchForLineInTextFile(File file, IF1 nav) { long length = l(file); int bufSize = 1024; RandomAccessFile raf = randomAccessFileForReading(file); try { long min = 0, max = length; int direction = 0; Pair possibleResult = null; while (min < max) { ping(); long middle = (min+max)/2; long lineStart = raf_findBeginningOfLine(raf, middle, bufSize); long lineEnd = raf_findEndOfLine(raf, middle, bufSize); String line = fromUtf8(raf_readFilePart(raf, lineStart, (int) (lineEnd-1-lineStart))); direction = nav.get(line); possibleResult = pair(new LongRange(lineStart, lineEnd), line); if (direction == 0) return possibleResult; // asserts are to assure that loop terminates if (direction < 0) max = assertLessThan(max, lineStart); else min = assertBiggerThan(min, lineEnd); } if (direction >= 0) return possibleResult; long lineStart = raf_findBeginningOfLine(raf, min-1, bufSize); String line = fromUtf8(raf_readFilePart(raf, lineStart, (int) (min-1-lineStart))); return pair(new LongRange(lineStart, min), line); } finally { _close(raf); }} static List tok_splitAtComma_unquote(String s) { List tok = javaTok(s); List out = new ArrayList(); for (int i = 0; i < l(tok); i++) { int j = smartIndexOf(tok, ",", i); out.add(unquote(trimJoinSubList(tok, i, j))); i = j; } return out; } static long parseLongOpt(String s) { return isInteger(s) ? parseLong(s) : 0; } static JTextField standardTextFieldPopupMenu(final JTextField tf) { final WeakReference ref = weakRef(tf); componentPopupMenuItem(tf, "Copy text to clipboard", new Runnable() { public void run() { try { copyTextToClipboard(ref.get().getText()) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyTextToClipboard(ref.get().getText())"; }}); componentPopupMenuItem(tf, "Paste", new Runnable() { public void run() { try { ref.get().paste() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ref.get().paste()"; }}); return tf; } static JPanel showFormTitled_customArrangement(final boolean internalFrame, final F1, JPanel> arrange, final String title, final Object... _parts) { return swing(new F0() { public JPanel get() { try { List out = showForm_arrange1(showForm_makeComponents(internalFrame, _parts)); JPanel panel = callF(arrange, out); showForm_makeFrame(title, panel); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "L out = showForm_arrange1(showForm_makeComponents(internalFrame, ..."; }}); } // layouter: voidfunc(Container) // or func(Container) -> Dimension [preferred size] // or func(Container) -> Pair(Dimension, Dimension) [preferred, minimum] static JPanel customLayoutPanel(final Object layouter) { return jpanel(layoutManagerFromFunction(layouter)); } static JPanel customLayoutPanel(final Object layouter, final List components) { return addAllComponents(customLayoutPanel(layouter), components); } static List getComponents(final Component c) { return !(c instanceof Container) ? emptyList() : asList(swing(new F0() { public Component[] get() { try { return ((Container) c).getComponents(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret ((Container) c).getComponents();"; }})); } static boolean containsATextArea(Component c) { return childOfType(c, JTextArea.class) != null; } static int totalPreferredHeight(List l) { int h = 0; for (Component c : unnull(l)) h += c.getPreferredSize().height; return h; } static List listWithoutIndicesInBitSet(List l, BitSet bs) { List out = new ArrayList(); for (int i = 0; i < l(l); i++) if (!bs.get(i)) out.add(l.get(i)); return out; } static Dimension componentsBoundingSize(Container container, int insetBottomRight) { Rectangle r = new Rectangle(0, 0, 0, 0); for (Component c : container.getComponents()) r = rectangleUnion(r, c.getBounds()); return new Dimension(r.x+r.width+insetBottomRight, r.y+r.height+insetBottomRight); } static JButton renameButton(JComponent c, String name) { JButton b = first(childrenOfType(c, JButton.class)); if (b != null) b.setText(name); return b; } static JButton renameButton(JComponent c, String oldName, String newName) { JButton b = findButton(c, oldName); if (b != null) b.setText(newName); return b; } static String showFormSubmitButtonName() { return "Submit"; } // f: func -> A | endMarker static IterableIterator iff(Object f) { return iteratorFromFunction_withEndMarker(f); } // can't use type parameter because end marker static IterableIterator iff(F0 f) { return iteratorFromFunction_withEndMarker(f); } static IterableIterator iff(IF0 f) { return iteratorFromFunction_withEndMarker(f); } static Object endMarker() { return iteratorFromFunction_endMarker; } static List wrapArrayAsList(A[] a) { return a == null ? null : Arrays.asList(a); } static String joinWithSemicolon(Iterable l) { return join("; ", l); } static void litmap_impl_noOverwrite(Map map, Object... x) { if (x != null) for (int i = 0; i < x.length-1; i += 2) putIfNotThere(map, x[i], x[i+1]); } static List splitAtColon(String s) { return empty(s) ? emptyList() : asList(s.split(":")); } static List words2(String s) { List l = new ArrayList(); int n = l(s); for (int i = 0; i < n; ) { int j = i; while (j < n && isLetterOrDigit(s.charAt(j))) ++j; if (j > i) l.add(substring(s, i, j)); while (j < n && !isLetterOrDigit(s.charAt(j))) ++j; i = j; } return l; } static String indentx(String s) { return indentx(indent_default, s); } static String indentx(int n, String s) { return dropSuffix(repeat(' ', n), indent(n, s)); } static String indentx(String indent, String s) { return dropSuffix(indent, indent(indent, s)); } static boolean rtagIs(String tag, String token) { return tagIs(token, tag); } static Map ciTagParams(String tag) { return asCIMap(tagParams(tag)); } static Set setMinus(Set set, Object... stuff) { Set s2 = cloneSet(set); for (Object o : stuff) s2.remove(o); return s2; } static Set mergeSets(Collection... l) { return joinSets(l); } static Set allNonStaticNonTransientFields(Object o) { TreeSet fields = new TreeSet(); Class _c = _getClass(o); do { for (Field f : _c.getDeclaredFields()) if ((f.getModifiers() & (Modifier.STATIC|Modifier.TRANSIENT)) == 0) fields.add(f.getName()); _c = _c.getSuperclass(); } while (_c != null); return fields; } static A listGet(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } static Pair mapEntryToPair(Map.Entry e) { return e == null ? null : pair(e.getKey(), e.getValue()); } static boolean tok_isRoundBracketed(String s) { List tok = tok_combineRoundBrackets_keep(javaTok(s)); return l(tok) == 3 && startsWithAndEndsWith(tok.get(1), "(", ")"); } static List dropFirstThreeAndLastThree(List l) { return dropFirstAndLast(3, l); } static List javaTokPlusBrackets(String s) { return tok_combineRoundOrCurlyBrackets_keep(javaTok(s)); } static Map> javaTokWithAllBrackets_cached_cache = synchronizedMRUCache(defaultTokenizerCacheSize()); static List javaTokWithAllBrackets_cached(String s) { List tok = javaTokWithAllBrackets_cached_cache.get(s); if (tok == null) javaTokWithAllBrackets_cached_cache.put(s, tok = javaTokWithAllBrackets(s)); return tok; } static List splitAtTokens(String s, List tokens) { return splitAtTokens(javaTok(s), tokens); } static List splitAtTokens(List tok, List tokens) { List l = new ArrayList(); int i = 0; while (i < l(tok)) { int j = indexOfSubList(tok, tokens, i); if (i >= l(tok)) break; if (j < 0) j = l(tok); l.add(trimJoin(tok.subList(i, j))); i = j+l(tokens); } return l; } static List splitAtTokens(List tok, String... tokens) { List l = new ArrayList(); int i = 0; while (i < l(tok)) { int j = indexOfSubList(tok, tokens, i); if (i >= l(tok)) break; if (j < 0) j = l(tok); l.add(trimJoin(tok.subList(i, j))); i = j+l(tokens); } return l; } static boolean cicWithSmartWordBoundary(String a, String b) { return containsRegexpIC(a, phraseToRegExp(b)); } static boolean match3_startOrEndOfLine(String pat, String s, boolean startOfLine, boolean endOfLine) { if (startOfLine) return endOfLine ? match3(pat, s) : matchStart(pat, s); else return endOfLine ? matchEnd(pat, s) : find3(pat, s); } static boolean all(Object pred, Iterable l) { if (l != null) for (Object o : l) if (!isTrue(callF(pred, o))) return false; return true; } static boolean all(Iterable l, IF1 f) { if (l != null) for (A a : l) if (!f.get(a)) return false; return true; } static boolean all(IF1 f, Iterable l) { return all(l, f); } static boolean find3(String pat, String s) { return find3(pat, s, null); } static boolean find3(String pat, String s, Matches matches) { return find3(pat, parse3_cachedInput(s), matches); } static boolean find3(String pat, List toks, Matches matches) { List tokpat = parse3_cachedPattern(pat); String[] m = find2(tokpat, toks); //print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m)); if (m == null) return false; if (matches != null) matches.m = m; return true; } static Integer find3_levenWithSwapsDistance(String pat, String input) { List tokPat = parse3_cachedPattern(pat); List tok = parse3_cachedInput(input); int end = tok.size()-tokPat.size()+1; Integer min = null; for (int idx = 0; idx < end; idx += 2) { int score = 0; for (int i = 1; i < tokPat.size(); i += 2) { String p = tokPat.get(i), t = tok.get(idx+i); score += levenWithSwapsIC(p, t); } min = min_withNull(min, score); } return min; } static Integer min_withNull(Integer a, Integer b) { return a == null ? b : b == null ? a : (Integer) min(a, b); } static List splitAtMinus(String s) { return trimAll(asList(s.split("\\-"))); } static Integer parseHourAndOptionalMinutesToMinutes(String s) { List l = trimAll(splitAtColon(s)); if (empty(l)) return null; int minute = parseInt(first(l))*60; if (l(l) > 1) minute += parseInt(second(l)); return minute; } static Collection asCollection(Iterable l) { return l == null ? null : l instanceof Collection ? ((Collection) l) : asList(l); } static JComboBox selectItem(A item, JComboBox cb) { if (cb != null) { swing(new Runnable() { public void run() { try { cb.setSelectedItem(item); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "cb.setSelectedItem(item);"; }}); } return cb; } static JComboBox selectItem(JComboBox cb, A item) { return selectItem(item, cb); } static JList selectItem(JList list, A item) { { swing(new Runnable() { public void run() { try { selectRow(list, jlist_indexOf(list, item)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "selectRow(list, jlist_indexOf(list, item));"; }}); } return list; } static JInternalFrame showForm_makeInternalFrame(JDesktopPane desktop, String title, JPanel panel) { JInternalFrame f = addInternalFrame(desktop, title, withMargin(panel)); minInternalFrameWidth(f, 400); packInternalFrameVertically(f); centerInternalFrame(f); // TODO: handleEscapeKey(f); return f; } static JFrame handleEscapeKey(final JFrame frame) { KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); frame.getRootPane().registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { frame.dispose(); } }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); return frame; } static JFrame minFrameWidth(JFrame frame, int w) { if (frame != null && frame.getWidth() < w) frame.setSize(w, frame.getHeight()); return frame; } static JFrame minFrameWidth(int w, JFrame frame) { return minFrameWidth(frame, w); } static JFrame showPackedFrame(String title, Component contents) { return packFrame(showFrame(title, contents)); } static JFrame showPackedFrame(Component contents) { return packFrame(showFrame(contents)); } static int showForm_leftWidth_safetyMargin = 10; static int showForm_leftWidth(List> l) { forEachLevel2(l, x -> vmBus_send("updateLayoutNow", x)); int minW = 0; for (List row : l) minW = max(minW, getMinimumSize(first(row)).width); return minW + or((Integer) vmBus_query("formSafetyMargin"), showForm_leftWidth_safetyMargin); } static JPanel westAndCenter(final Component w, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.WEST, wrap(w)); panel.add(BorderLayout.CENTER, wrap(c)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new BorderLayout);\r\n panel.add(BorderLayout.WEST..."; }}); } static int withRightMargin_defaultWidth = 6; static JPanel withRightMargin(Component c) { return withRightMargin(withRightMargin_defaultWidth, c); } static JPanel withRightMargin(final int w, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, w)); p.add(c); return p; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel p = new JPanel(new BorderLayout);\r\n p.setBorder(BorderFactory.creat..."; }}); } static A jMinWidthAtLeast(int w, final A c) { if (c == null) return null; return swing(new F0() { public A get() { try { Dimension size = c.getMinimumSize(); Dimension d = new Dimension(max(w, size.width), size.height); c.setMinimumSize(d); return jPreferWidth(d.width, c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Dimension size = c.getMinimumSize();\r\n Dimension d = new Dimension(max(w, ..."; }}); } static void setComponentID(Component c, String id) { if (c != null) componentID_map.put(c, id); } static List mapLL(Object f, Object... data) { return map(f, ll(data)); } static List mapLL(IF1 f, A... data) { return map(f, ll(data)); } static JComponent wrapForSmartAdd_jComponent(Object o) { return componentToJComponent(wrapForSmartAdd(o)); } static boolean isRunnable(Object o) { return o instanceof Runnable || hasMethod(o, "get"); } static void disposeInternalFrame(Component c) { final JInternalFrame f = getInternalFrame(c); if (f != null) { swing(new Runnable() { public void run() { try { vmBus_send("disposingInternalFrame", f); f.dispose(); setOpt(f, "lastFocusOwner" , null); // Help GC } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "vmBus_send disposingInternalFrame(f);\r\n f.dispose();\r\n setOpt(f, lastFo..."; }}); } } static void disposeFrame(final Component c) { disposeWindow(c); } static void onEnterInAllTextFields(JComponent c, Object action) { if (action == null) return; for (Component tf : allChildren(c)) onEnterIfTextField(tf, action); } static void onEnterInAllTextFields(List c, Object action) { for (Object o : unnull(c)) if (o instanceof JComponent) onEnterInAllTextFields((JComponent) o, action); } static void clickButton(final JButton b) { if (b != null) { swing(new Runnable() { public void run() { try { if (b.isEnabled()) b.doClick(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (b.isEnabled())\r\n b.doClick();"; }}); } } static A makeBold(final A c) { if (c != null) { swing(new Runnable() { public void run() { try { c.setFont(c.getFont().deriveFont(java.awt.Font.BOLD)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "c.setFont(c.getFont().deriveFont(java.awt.Font.BOLD));"; }}); } return c; } static JPanel smartAddWithLayout(JPanel panel, Object layout, List parts) { for (Object o : parts) panel.add(wrapForSmartAdd(o), layout); return panel; } static JPanel smartAddWithLayout(JPanel panel, Object layout, Object... parts) { return smartAddWithLayout(panel, layout, asList(flattenArray2(parts))); } static Component jrigid() { return javax.swing.Box.createRigidArea(new Dimension(0, 0)); } static Set mergeTreeSets(Collection... l) { TreeSet set = new TreeSet(); for (Collection o : l) if (o != null) set.addAll(o); return set; } static boolean setField_trueIfChanged(Field f, Object o, Object value) { try { if (eq(f.get(o), value)) return false; f.set(o, value); return true; } catch (Exception __e) { throw rethrow(__e); } } static int indexOfIC_underscore(String a, String b) { int la = l(a), lb = l(b); if (la < lb) return -1; int n = la-lb; elsewhere: for (int i = 0; i <= n; i++) { for (int j = 0; j < lb; j++) { char c2 = b.charAt(j); if (c2 == '_' || eqic(c2, a.charAt(i+j))) { /* matching char */ } else continue elsewhere; } return i; } return -1; } static A firstWithClassShortNamed(String shortName, Iterable l) { if (l != null) for (A o : l) if (eq(shortClassName(o), shortName)) return o; return null; } static A firstWithClassShortNamed(String shortName, A[] l) { if (l != null) for (A o : l) if (eq(shortClassName(o), shortName)) return o; return null; } static JMenuItem jMenuItem(final String text) { return jmenuItem(text); } static JMenuItem jMenuItem(String text, Object r) { return jmenuItem(text, r); } static Pair jmenu_autoMnemonic(String s) { int i = indexOf(s, '&'); if (i >= 0 && i < l(s) && isLetterOrDigit(s.charAt(i+1))) return pair(substring(s, 0, i) + substring(s, i+1), (int) s.charAt(i+1)); return pair(s, 0); } static JMenuItem disableMenuItem(final JMenuItem mi) { if (mi != null) { swing(new Runnable() { public void run() { try { mi.setEnabled(false); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "mi.setEnabled(false);"; }}); } return mi; } static ActionListener actionListenerInNewThread(final Object runnable) { return actionListenerInNewThread(runnable, null); } static ActionListener actionListenerInNewThread(final Object runnable, final Object instanceToHold) { if (runnable instanceof ActionListener) return (ActionListener) runnable; return new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { startThread("Action Listener", new Runnable() { public void run() { try { AutoCloseable __1 = holdInstance(instanceToHold); try { callF(runnable); } finally { _close(__1); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "AutoCloseable __1 = holdInstance(instanceToHold); try {\r\n callF(runnable..."; }}); } catch (Throwable __e) { messageBox(__e); }}}; } static JMenuItem directJMenuItem(Action a) { return new JMenuItem(a) { public Dimension getMaximumSize() { return new Dimension(super.getPreferredSize().width, super.getMaximumSize().height); } }; } static JMenuItem directJMenuItem(String text, Object action) { return directJMenuItem(abstractAction(text, action)); } static JMenuBar addMenuBar(final Component c) { return swing(new F0() { public JMenuBar get() { try { RootPaneContainer f = getPossiblyInternalFrame(c); if (f == null) return null; JMenuBar bar = (JMenuBar) (call(f, "getJMenuBar")); if (bar == null) { setMenuBar(f, bar = new JMenuBar()); revalidate((Component) f); } return bar; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "RootPaneContainer f = getPossiblyInternalFrame(c);\r\n if (f == null) null;\r..."; }}); } static Object callJavaX(String method, Object... args) { return callOpt(getJavaX(), method, args); } static Object costCenter() { return mc(); } static IterableIterator iteratorFromFunction_f0(final F0 f) { class IFF2 extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } }; return new IFF2(); } static IterableIterator iteratorFromFunction_if0(final IF0 f) { class IFF2 extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } }; return new IFF2(); } static int hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static String htitle_noEncode(String title) { return tag("title", title); } static boolean isLocalhost(String ip) { return isLoopbackIP(ip) || eqic(ip, "localhost"); } static int vmPort() { return myVMPort(); } static DialogIO talkToThisVM() { return new talkToThisVM_IO(); } static class talkToThisVM_IO extends DialogIO { List answers = ll(thisVMGreeting()); boolean isLocalConnection() { return true; } boolean isStillConnected() { return true; } int getPort() { return vmPort(); } void sendLine(String line) { answers.add(or2(sendToThisVM_newThread(line), "?")); } String readLineImpl() { try { return popFirst(answers); } catch (Exception __e) { throw rethrow(__e); } } public void close() {} Socket getSocket() { return null; } } static Map newFindBot2_cache = synchroHashMap(); static boolean newFindBot2_verbose = false; static DialogIO newFindBot2(String name) { Integer port = newFindBot2_cache.get(name); if (port != null) { if (newFindBot2_verbose) print("newFindBot2: testing " + name + " => " + port); DialogIO io = talkTo(port); String q = format("has bot *", name); String s = io.ask(q); if (match("yes", s)) { io = talkToSubBot(name, io); call(io, "pushback", "?"); // put some hello string in (yes, this should be improved.) return io; } // bot not there anymore - remove cache entry newFindBot2_cache.remove(name); if (newFindBot2_verbose) print("newFindBot2: dropping " + name + " => " + port); } DialogIO io = findBot(name); if (io != null) { newFindBot2_cache.put(name, io.getPort()); if (newFindBot2_verbose) print("newFindBot2: remembering " + name + " => " + port); } return io; } static TreeMap hotwireCached_cache = new TreeMap(); static Lock hotwireCached_lock = lock(); static Class hotwireCached(String programID) { return hotwireCached(programID, true); } static Class hotwireCached(String programID, boolean runMain) { return hotwireCached(programID, runMain, false); } static Class hotwireCached(String programID, boolean runMain, boolean dependent) { Lock __0 = hotwireCached_lock; lock(__0); try { programID = formatSnippetID(programID); Class c = hotwireCached_cache.get(programID); if (c == null) { c = hotwire(programID); if (dependent) makeDependent(c); if (runMain) callMain(c); hotwireCached_cache.put(programID, c); } return c; } finally { unlock(__0); } } static class ScannedBot implements IFieldsToList{ static final String _fieldOrder = "helloString address"; String helloString; String address; ScannedBot() {} ScannedBot(String helloString, String address) { this.address = address; this.helloString = helloString;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + helloString + ", " + address + ")"; } public boolean equals(Object o) { if (!(o instanceof ScannedBot)) return false; ScannedBot __1 = (ScannedBot) o; return eq(helloString, __1.helloString) && eq(address, __1.address); } public int hashCode() { int h = 1660478935; h = boostHashCombine(h, _hashCode(helloString)); h = boostHashCombine(h, _hashCode(address)); return h; } public Object[] _fieldsToList() { return new Object[] {helloString, address}; } } static List fullBotScan() { return fullBotScan(""); } static List fullBotScan(String searchPattern) { List bots = new ArrayList(); for (ProgramScan.Program p : quickBotScan()) { String botName = firstPartOfHelloString(p.helloString); boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM."); boolean shouldRecurse = swic(botName, "Multi-Port") || isVM; if (swic(botName, searchPattern)) bots.add(new ScannedBot(botName, "" + p.port)); if (shouldRecurse) try { Map subBots = (Map) unstructure(sendToLocalBotQuietly(p.port, "list bots")); for (Number vport : subBots.keySet()) { botName = subBots.get(vport); if (swic(botName, searchPattern)) bots.add(new ScannedBot(botName, p.port + "/" + vport)); } } catch (Exception e) { e.printStackTrace(); } } return bots; } static Object unstructure_startingAtIndex(String s, int i) { return unstructure_tok(javaTokC_noMLS_iterator(s, i), false, null); } static A bindLiveValueListenerToComponent(A component, final LiveValue lv, final Runnable listener) { if (lv != null) bindToComponent(component, new Runnable() { public void run() { try { lv.onChangeAndNow(listener); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef bindLiveValueListenerToComponent_debug\r\n print(\"bindLiveValueL..."; }}, new Runnable() { public void run() { try { lv.removeOnChangeListener(listener) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "lv.removeOnChangeListener(listener)"; }}); return component; } static A setFont(final Font font, final A a) { if (a != null) { swing(new Runnable() { public void run() { try { a.setFont(font); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "a.setFont(font);"; }}); } return a; } static A setFont(A a, Font font) { return setFont(font, a); } static A setFont(final String fontID, float fontSize, final A a) { return setFont(loadFont_cached(fontID, fontSize), a); } static boolean allPaused() { return ping_pauseAll; } static void bindTimerToComponent(final Timer timer, JFrame f) { bindTimerToComponent(timer, f.getRootPane()); } static void bindTimerToComponent(final Timer timer, JComponent c) { if (c.isShowing()) timer.start(); c.addAncestorListener(new AncestorListener() { public void ancestorAdded(AncestorEvent event) { timer.start(); } public void ancestorRemoved(AncestorEvent event) { timer.stop(); } public void ancestorMoved(AncestorEvent event) { } }); } static void consoleClearInput() { consoleSetInput(""); } static void duplicateThisProgram() { nohupJavax(trim(programID() + " " + smartJoin((String[]) get(getJavaX(), "fullArgs")))); } static void showConsole() { callOpt(get(javax(), "console"), "showConsole"); } // requires ugly casting when used (O -> A) static Object iteratorFromFunction_endMarker = new Object(); // f: func -> A | endMarker static IterableIterator iteratorFromFunction_withEndMarker(final Object f) { class IFF extends IterableIterator { A a; boolean have, done; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; have = false; return _a; } void getNext() { if (done || have) return; Object o = callF(f); if (o == iteratorFromFunction_endMarker) { done = true; return; } a = (A) o; have = true; } }; return new IFF(); } // optimized version for F0 argument; TODO: do same for IF0 static IterableIterator iteratorFromFunction_withEndMarker(final F0 f) { return iteratorFromFunction_withEndMarker_f0(f); } static Map componentID_map = weakHashMap(); static String componentID(Component c) { return c == null ? null : componentID_map.get(c); } static boolean hasBot(String searchPattern) { try { DialogIO io = findBot(searchPattern); if (io != null) { io.close(); return true; } else return false; } catch (Exception __e) { throw rethrow(__e); } } static boolean isOK(String s) { s = trim(s); return swic(s, "ok ") || eqic(s, "ok") || matchStart("ok", s); } static String sendOpt(String bot, String text, Object... args) { return sendToLocalBotOpt(bot, text, args); } static boolean isMainProgram() { return creator() == null; } static void cleanKill() { cleanKillVM(); } static Android3 methodsBot2(String name, final Object receiver, final List exposedMethods) { return methodsBot2(name, receiver, exposedMethods, null); } static Android3 methodsBot2(String name, final Object receiver, final List exposedMethods, final Lock lock) { Android3 android = new Android3(); android.greeting = name; android.console = false; android.responder = new Responder() { String answer(String s, List history) { return exposeMethods2(receiver, s, exposedMethods, lock); } }; return makeBot(android); } static List db_standardExposedMethods_list = ll("xlist", "xnew", "xset", "xdelete", "xget", "xclass", "xfullgrab", "xshutdown", "xchangeCount", "xcount"); static List db_standardExposedMethods() { return db_standardExposedMethods_list; } static int matcherInt(Matcher m, int i) { return parseInt(m.group(i)); } // month = 1 to 12 static long timestampFromYMDHM(int y, int m, int d, int h, int minutes) { return new GregorianCalendar(y, m-1, d, h, minutes).getTimeInMillis(); } static List sortByMap_inPlace(List l, Map map) { sort(l, mapComparator(map)); return l; } // age = age in days, lastAge = age of last (more recent) file kept (also in days) static boolean defaultAgeBasedBackupRetentionStrategy_shouldKeep(double age, double lastAge) { if (age <= 1/12.0) return true; // keep all backups within last hour if (age <= 0.5 && age >= lastAge+1/12.0) return true; // keep hourly backups within last 12 hours if (age <= 7 && age >= lastAge+1) return true; // keep every daily backup from this week if (age <= 28 && age >= lastAge+7) return true; // weekly backups for 3 more weeks if (age >= lastAge+365.0/12) return true; // after 4 weeks, switch to monthly (roundabout) return false; } static boolean isDollarVar(String s) { // Possible BREAKING CHANGE (although probably OK): now also accepting $1 etc. return startsWith(s, '$') && l(s) > 1; // OLD: ret startsWith(s, '$') && isJavaIdentifierAfter(s, 1); } static void listAdd(Collection c, A a) { if (c != null) c.add(a); } static boolean neqGet(List l, int i, Object o) { return neq(get(l, i), o); } // Return value is index of semicolon/curly brace+1 static int tok_findEndOfStatement(List tok, int i) { // Is it a block? if (eq(get(tok, i), "{")) return findEndOfBlock(tok, i); // It's a regular statement. Special handling of "for" and "if" int j = i; boolean special = false; while (j < l(tok) && neq(tok.get(j), ";")) { String t = get(tok, j); if (eqOneOf(t, "for", "if")) special = true; if (eqOneOf(t, "{", "(")) { j = findEndOfBracketPart(tok, j)+1; if (special && eq(t, "{")) return j-1; } else j += 2; } return j+1; } static void tokPrepend_reTok(List tok, int i, String s) { tokPrepend(tok, i, s); reTok(tok, i, i+1); } static File oneOfTheFiles(String... paths) { if (paths != null) for (String path : paths) if (fileExists(path)) return newFile(path); return null; } static File oneOfTheFiles(File... files) { return oneOfTheFiles(asList(files)); } static File oneOfTheFiles(Iterable files) { if (files != null) for (File f : files) if (fileExists(f)) return f; return null; } static String loadSnippetQuietly(Snippet s) { return loadSnippetQuietly(s.id); } static String loadSnippetQuietly(String snippetID) { loadSnippet_silent.set(true); try { return loadSnippet(snippetID); } finally { loadSnippet_silent.set(null); } } static String joinLines(List lines) { return fromLines(lines); } static String joinLines(String glue, String text) { return join(glue, toLines(text)); } static String javaDropComments(String s) { return javaDropAllComments(s); } static A[] singlePlusArray(A a, A[] l) { A[] out = newObjectArrayOfSameType(l, l(l)+1); out[0] = a; arraycopy(l, 0, out, 1, l(l)); return out; } static Lock downloadLock_lock = fairLock(); static Lock downloadLock() { return downloadLock_lock; } static String getSnippetTitleOpt(String s) { return isSnippetID(s) ? getSnippetTitle(s) : s; } static boolean loadBufferedImageFixingGIFs_debug = false; static ThreadLocal> loadBufferedImageFixingGIFs_output = new ThreadLocal(); static Image loadBufferedImageFixingGIFs(File file) { try { if (!file.exists()) return null; // Load anything but GIF the normal way if (!isGIF(file)) return ImageIO.read(file); if (loadBufferedImageFixingGIFs_debug) print("loadBufferedImageFixingGIFs" + ": checking gif"); // Get GIF reader ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next(); // Give it the stream to decode from reader.setInput(ImageIO.createImageInputStream(file)); int numImages = reader.getNumImages(true); // Get 'metaFormatName'. Need first frame for that. IIOMetadata imageMetaData = reader.getImageMetadata(0); String metaFormatName = imageMetaData.getNativeMetadataFormatName(); // Find out if GIF is bugged boolean foundBug = false; for (int i = 0; i < numImages && !foundBug; i++) { // Get metadata IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName); // Find GraphicControlExtension node int nNodes = root.getLength(); for (int j = 0; j < nNodes; j++) { org.w3c.dom.Node node = root.item(j); if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) { // Get delay value String delay = ((IIOMetadataNode)node).getAttribute("delayTime"); // Check if delay is bugged if (Integer.parseInt(delay) == 0) { foundBug = true; } break; } } } if (loadBufferedImageFixingGIFs_debug) print("loadBufferedImageFixingGIFs" + ": " + f2s(file) + " foundBug=" + foundBug); // Load non-bugged GIF the normal way Image image; if (!foundBug) { image = Toolkit.getDefaultToolkit().createImage(f2s(file)); } else { // Prepare streams for image encoding ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); { ImageOutputStream ios = ImageIO.createImageOutputStream(baoStream); try { // Get GIF writer that's compatible with reader ImageWriter writer = ImageIO.getImageWriter(reader); // Give it the stream to encode to writer.setOutput(ios); writer.prepareWriteSequence(null); for (int i = 0; i < numImages; i++) { // Get input image BufferedImage frameIn = reader.read(i); // Get input metadata IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName); // Find GraphicControlExtension node int nNodes = root.getLength(); for (int j = 0; j < nNodes; j++) { org.w3c.dom.Node node = root.item(j); if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) { // Get delay value String delay = ((IIOMetadataNode)node).getAttribute("delayTime"); // Check if delay is bugged if (Integer.parseInt(delay) == 0) { // Overwrite with a valid delay value ((IIOMetadataNode)node).setAttribute("delayTime", "10"); } break; } } // Create output metadata IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(frameIn), null); // Copy metadata to output metadata metadata.setFromTree(metadata.getNativeMetadataFormatName(), root); // Create output image IIOImage frameOut = new IIOImage(frameIn, null, metadata); // Encode output image writer.writeToSequence(frameOut, writer.getDefaultWriteParam()); } writer.endWriteSequence(); } finally { _close(ios); }} // Create image using encoded data byte[] data = baoStream.toByteArray(); setVar(loadBufferedImageFixingGIFs_output.get(), data); if (loadBufferedImageFixingGIFs_debug) print("Data size: " + l(data)); image = Toolkit.getDefaultToolkit().createImage(data); } return image; } catch (Exception __e) { throw rethrow(__e); } } static boolean isSubstanceLAF() { return substanceLookAndFeelEnabled(); } // menuMaker = voidfunc(JPopupMenu) // return true if menu could be added static boolean titlePopupMenu(final Component c, final Object menuMaker) { JComponent titleBar = getTitlePaneComponent(getPossiblyInternalFrame(c)); if (titleBar == null) { print("Can't add title right click!"); return false; } else { componentPopupMenu(titleBar, menuMaker); return true; } } static void toggleAlwaysOnTop(JFrame frame) { frame.setAlwaysOnTop(!frame.isAlwaysOnTop()); } static Component childWithClassNameEndingWith(Component c, String suffix) { if (endsWith(className(c), suffix)) return c; Component x; for (Component comp : getComponents(c)) if ((x = childWithClassNameEndingWith(comp, suffix)) != null) return x; return null; } static boolean containsSpaces(String s) { return indexOf(s, ' ') >= 0; } static List ai_splitCamelCase(String s) { int j = 0; List l = new ArrayList(); // new addition if (isAllUpperCase(s)) { l.add(s); return l; } for (int i = 0; i < l(s); i++) if (i > j && isUpperCaseLetter(s.charAt(i))) { l.add(substring(s, j, i)); j = i; } if (j < l(s)) l.add(substring(s, j)); return l; } static void fillJMenu(final JMenu m, Object... x) { //ifdef fillJMenu_debug //print("fillJMenu " + m); //endifdef if (x == null) return; for (int i = 0; i < l(x); i++) { Object o = x[i], y = get(x, i+1); if (o instanceof List) fillJMenu(m, asArray((List) o)); else if (isMenuSeparatorIndicator(o)) { if (menuItemCount(m) != 0) // auto-skip separator if at top m.addSeparator(); } else if (o instanceof LiveValue && ((LiveValue) o).getType() == String.class && isRunnableX(y)) { final LiveValue lv = (LiveValue) o; final JMenuItem mi = jmenuItem(or2(unCurlyBracket(lv.get()), "..."), y); bindLiveValueListenerToComponent(mi, lv, new Runnable() { public void run() { try { String s = lv.get(); if (isCurlyBracketed(s)) { setEnabled(mi, false); s = unCurlyBracket(s); } else setEnabled(mi, true); setText(mi, s); revalidate(m); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "String s = lv.get();\r\n if (isCurlyBracketed(s)) {\r\n setEnable..."; }}); print("bound live value " + lv + " to menu item " + mi); m.add(mi); ++i; } else if (o instanceof String && isRunnableX(y)) { m.add(jmenuItem((String) o, y)); ++i; } else if (o instanceof JMenuItem) m.add((JMenuItem) o); // "call" might use wrong method else if (o instanceof String || o instanceof Action || o instanceof Component) call(m, "add", o); else if (o == null && y instanceof Runnable) ++i; // text == null => disabled item else if (o != null) print("Unknown menu item: " + o); } } static String getFileInfoField(File f, String field) { return getOneLineFileInfoField(f, field); } static File dropExtension(File f) { return f == null ? null : fileInSameDir(f, dropExtension(f.getName())); } static String dropExtension(String s) { return takeFirst(s, smartLastIndexOf(s, '.')); } static Object[] muricaCredentials() { String pass = muricaPassword(); return nempty(pass) ? new Object[] {"_pass", pass } : new Object[0]; } static boolean saveTextFileIfDifferent(File f, String contents) { if (eq(loadTextFile(f), contents)) return false; // TODO: optimize { saveTextFile(f, contents); return true; } } static List listFilesOnly(String dir) { return listFilesOnly(new File(dir)); } static List listFilesOnly(File... dirs) { return concatMap(rcurry("listFilesWithSuffix", ""), dirs); } static String fromUtf8(byte[] bytes) { try { return bytes == null ? null : new String(bytes, utf8charset()); } catch (Exception __e) { throw rethrow(__e); } } static List dm_moduleIDs() { return dm_listModuleIDs(); } static boolean shortClassNameIs(Object o, String name) { return eq(shortClassName(o), name); } static boolean shortClassNameIs(String name, Object o) { return shortClassNameIs(o, name); } static ThreadLocal saveTiming_last = new ThreadLocal(); static void saveTiming(long ms) { print(ms + " ms"); saveTiming_noPrint(ms); } static void saveTiming_noPrint(long ms) { saveTiming_last.set(ms); } static ThreadLocal saveTiming_tl() { return saveTiming_last; } static boolean newButton_autoToolTip = true; // action can be Runnable or a function name static JButton newButton(final String text, final Object action) { return swing(new F0() { public JButton get() { try { String text2 = dropPrefix("[disabled] ", text); final JButton btn = new JButton(text2); if (l(text2) < l(text)) btn.setEnabled(false); if (newButton_autoToolTip) { btn.setToolTipText(btn.getText()); //onChangeAndNow(btn, r { btn.setToolTipText(btn.getText()) }); } // submitButtonOnEnter(btn); // test this first if (action != null) btn.addActionListener(actionListener(action, btn)); return btn; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "S text2 = dropPrefix(\"[disabled] \", text);\r\n final JButton btn = new JButt..."; }}); } static Object[] asArray(List l) { return toObjectArray(l); } static A[] asArray(Class type, List l) { return (A[]) l.toArray((Object[]) Array.newInstance(type, l.size())); } static boolean isMenuSeparatorIndicator(Object o) { return eqOneOf(o, "***", "---", "===", ""); } static boolean isRunnableX(Object o) { if (o == null) return false; if (o instanceof String) return hasMethod(mc(), (String) o); return o instanceof Runnable || hasMethod(o, "get"); } static boolean isCurlyBracketed(String s) { return isCurlyBraced(s); } static A setEnabled(A c, boolean enable) { if (c != null) { swing(new Runnable() { public void run() { try { c.setEnabled(enable); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "c.setEnabled(enable);"; }}); } return c; } static A setEnabled(boolean enable, A c) { return setEnabled(c, enable); } static String unCurlyBracket(String s) { return tok_unCurlyBracket(s); } static int preferredWidth(Component c) { return c == null ? 0 : getPreferredSize(c).width; } static File localSnippetsDir() { return javaxDataDir("Personal Programs"); } static File localSnippetsDir(String sub) { return newFile(localSnippetsDir(), sub); } static String formatDate() { return formatDate(now()); } static String formatDate(long timestamp) { return timestamp == 0 ? "-" : str(new Date(timestamp)); } static String formatDate(long timestamp, String format, TimeZone tz) { return simpleDateFormat(format, tz).format(timestamp); } static File dirOfFile(File f) { return f == null ? null : f.getParentFile(); } static List parse3(String s) { return dropPunctuation(javaTokPlusPeriod(s)); } static boolean isStringStartingWith(Object o, String prefix) { return o instanceof String && ((String) o).startsWith(prefix); } static Object getConsoleTextArea_gen() { return getOpt(get(getJavaX(), "console"), "textArea"); } static JTextField consoleInputField() { Object console = get(getJavaX(), "console"); return (JTextField) getOpt(console, "tfInput"); } static void revalidateFrame(Component c) { revalidate(getFrame(c)); } static String hijackPrint(Runnable r) { return hijackPrint((Object) r); } static String hijackPrint(Object r) { final StringBuilder buf = new StringBuilder(); Object old = interceptPrintInThisThread(new F1() { public Boolean get(String s) { buf.append(s); return false; } }); try { callF(r); return str(buf); } finally { interceptPrintInThisThread(old); } } static A pnl(A l) { return pnl("", l); } static A pnl(String prefix, A l) { printNumberedLines(prefix, l); return l; } static A[] pnl(A[] l) { return pnl("", l); } static A[] pnl(String prefix, A[] l) { printNumberedLines(prefix, l); return l; } static A pnl(A map) { printNumberedLines(map); return map; } static A pnl(String prefix, A map) { printNumberedLines(prefix, map); return map; } static String pnl(String s) { printNumberedLines(lines(s)); return s; } static MultiSet pnl(MultiSet ms) { pnl(ms == null ? null : ms.asMap()); return ms; } static MultiMap pnl(MultiMap mm) { pnl(mm == null ? null : mm.asMap()); return mm; } static Map> multiMapToMap(MultiMap m) { return m == null ? null : m.data; } static A findWhere(Collection c, Object... data) { if (c != null) for (A x : c) if (checkFields(x, data)) return x; return null; } static String dropFrom(String s, String x) { if (s == null) return null; int i = s.indexOf(x); if (i < 0) return s; return substring(s, 0, i); } static boolean directoryIsEmpty(File f) { return !fileExists(f) || isDirectory(f) && empty(listFiles(f)); } static void zip2dir(File inZip, String outDir) { zip2dir(inZip, newFile(outDir)); } static void zip2dir(File inZip, File outDir) { zip2dir(inZip, outDir, ""); } static void zip2dir(File inZip, String outDir, String prefix) { zip2dir(inZip, newFile(outDir), prefix); } static void zip2dir(File inZip, File outDir, String prefix) { try { if (prefix.length() != 0 && !prefix.endsWith("/")) prefix += "/"; ZipFile zipFile = new ZipFile(inZip); try { Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = (ZipEntry) entries.nextElement(); if (entry.isDirectory()) continue; // We care about files only, really. Directories are made on the fly anyway. if (!entry.getName().startsWith(prefix)) continue; File outFile = new File(outDir, entry.getName()); print("Unzipping " + entry.getName() + " to " + outFile.getAbsolutePath()); stream2file(zipFile.getInputStream(entry), outFile); } } finally { _close(zipFile); }} catch (Exception __e) { throw rethrow(__e); } } static RandomAccessFile randomAccessFileForReading(File path) { try { return newRandomAccessFile(path, "r"); } catch (Exception __e) { throw rethrow(__e); } } static long raf_findBeginningOfLine(RandomAccessFile raf, long pos, int bufSize) { try { byte[] buf = new byte[bufSize]; while (pos > 0) { long start = Math.max(pos-bufSize, 0); raf.seek(start); raf.readFully(buf, 0, (int) Math.min(pos-start, bufSize)); int idx = lastIndexOf_byteArray(buf, (byte) '\n'); if (idx >= 0) return start+idx+1; pos = start; } return 0; } catch (Exception __e) { throw rethrow(__e); } } static long raf_findEndOfLine(RandomAccessFile raf, long pos, int bufSize) { try { byte[] buf = new byte[bufSize]; long length = raf.length(); while (pos < length) { raf.seek(pos); raf.readFully(buf, 0, (int) Math.min(length-pos, bufSize)); int idx = indexOf_byteArray(buf, (byte) '\n'); if (idx >= 0) return pos+idx+1; pos += bufSize; } return length; } catch (Exception __e) { throw rethrow(__e); } } static byte[] raf_readFilePart(RandomAccessFile raf, long start, int l) { try { byte[] buf = new byte[l]; raf.seek(start); raf.readFully(buf); return buf; } catch (Exception __e) { throw rethrow(__e); } } static A assertLessThan(A a, A b) { assertTrue(cmp(b, a) < 0); return b; } static A assertBiggerThan(A a, A b) { assertTrue(cmp(b, a) > 0); return b; } static String trimJoinSubList(List l, int i, int j) { return trim(join(subList(l, i, j))); } static String trimJoinSubList(List l, int i) { return trim(join(subList(l, i))); } // layouter: voidfunc(Container) // or func(Container) -> Dimension [preferred size] // or func(Container) -> Pair(Dimension, Dimension) [preferred, minimum] static LayoutManager layoutManagerFromFunction(final Object layouter) { return new AbstractLayoutManager() { public void layoutContainer(Container parent) { Object size = pcallF(layouter, parent); if (size instanceof Dimension) preferredSize = (Dimension) size; else if (size instanceof Pair) { preferredSize = (Dimension) ((Pair) size).a; minimumSize = (Dimension) ((Pair) size).b; } } }; } static A addAllComponents(final A c, final List components) { if (nempty(components)) { swing(new Runnable() { public void run() { try { for (Component x : components) c.add(x); revalidate(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "for (Component x : components)\r\n c.add(x);\r\n revalidate(c);"; }}); } return c; } static A childOfType(Component c, Class theClass) { return first(childrenOfType(c, theClass)); } static A childOfType(Class theClass, Component c) { return childOfType(c, theClass); } static Rectangle rectangleUnion(Rectangle a, Rectangle b) { return a == null ? b : b == null ? a : a.union(b); } static List childrenOfType(Component c, Class theClass) { List l = new ArrayList(); scanForComponents(c, theClass, l); return l; } static List childrenOfType(Class theClass, Component c) { return childrenOfType(c, theClass); } static JButton findButton(Component c, String name) { for (JButton b : childrenOfType(c, JButton.class)) if (eq(b.getText(), name)) return b; for (JButton b : childrenOfType(getFrame(c), JButton.class)) if (eq(b.getText(), name)) return b; return null; } static JButton findButton(Component c) { return childOfType(c, JButton.class); } static void putIfNotThere(Map map, A key, B value) { if (map != null && key != null && value != null && !map.containsKey(key)) map.put(key, value); } static int indent_default = 2; static String indent(int indent) { return repeat(' ', indent); } static String indent(int indent, String s) { return indent(repeat(' ', indent), s); } static String indent(String indent, String s) { return indent + s.replace("\n", "\n" + indent); } static String indent(String s) { return indent(indent_default, s); } static List indent(String indent, List lines) { List l = new ArrayList(); if (lines != null) for (String s : lines) l.add(indent + s); return l; } static boolean tagIs(String token, String tag) { if (!startsWith(token, "<")) return false; int l = l(tag); return regionMatchesIC(token, 1, tag, 0, l) && eqOneOf(charAt(token, l+1), '>', ' '); } static TreeMap asCIMap(Map map) { return asCaseInsensitiveMap(map); } static Map tagParams(String tag) { return getHtmlTagParameters(tag); } // TODO: does not detect set type (hash/tree) when it's synchronized static Set cloneSet(Collection set) { if (set == null) return new HashSet(); synchronized(collectionMutex(set)) { Set s = similarEmptySet(set); s.addAll(set); return s; } } static List tok_combineRoundBrackets_keep(List tok) { List l = new ArrayList(); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && eq(t, "(")) { int j = findEndOfBracketPart(tok, i); l.add(joinSubList(tok, i, j)); i = j-1; } else l.add(t); } return l; } static boolean startsWithAndEndsWith(String s, String prefix, String suffix) { return startsWith(s, prefix) && endsWith(s, suffix); } static List tok_combineRoundOrCurlyBrackets_keep(List tok) { List l = new ArrayList(); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && eqOneOf(t, "{", "(")) { int j = findEndOfBracketPart(tok, i); l.add(joinSubList(tok, i, j)); i = j-1; } else l.add(t); } return l; } static int defaultTokenizerCacheSize() { return 1000; } static List javaTokWithAllBrackets(String s) { return javaTokPlusBrackets2(s); } static String trimJoin(List s) { return trim(join(s)); } static boolean containsRegexpIC(String s, String pat) { return compileRegexpIC(pat).matcher(s).find(); } static String phraseToRegExp(String b) { return (startsWithLetterOrDigit(b) ? "\\b" : "") + regexpQuote(b) + (endsWithLetterOrDigit(b) ? "\\b" : ""); } static boolean matchStart(String pat, String s) { return matchStart(pat, s, null); } // matches are as you expect, plus an extra item for the rest string static boolean matchStart(String pat, String s, Matches matches) { if (s == null) return false; return matchStart(pat, parse3_cachedInput(s), matches); } static boolean matchStart(String pat, List toks, Matches matches) { if (toks == null) return false; List tokpat = parse3_cachedPattern(pat); if (toks.size() < tokpat.size()) return false; String[] m = match2(tokpat, toks.subList(0, tokpat.size())); //print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m)); if (m == null) return false; if (matches != null) { matches.m = new String[m.length+1]; arraycopy(m, matches.m); matches.m[m.length] = joinSubList(toks, tokpat.size(), toks.size()); // for Matches.rest() } return true; } static boolean matchEnd(String pat, String s) { return matchEnd(pat, s, null); } // matches are as you expect, plus an extra item for the rest string static boolean matchEnd(String pat, String s, Matches matches) { if (s == null) return false; List tokpat = parse3(pat), toks = parse3(s); if (toks.size() < tokpat.size()) return false; String[] m = match2(tokpat, takeLast(l(tokpat), toks)); if (m == null) return false; if (matches != null) { matches.m = new String[m.length+1]; arraycopy(m, matches.m); matches.m[m.length] = join(dropLast(l(tokpat), toks)); // for Matches.rest() } return true; } // We dropped the "***" support here (use match3 for that) static String[] find2(List pat, List tok) { for (int idx = 0; idx < tok.size(); idx += 2) { // TODO: subtract pat size from end index String[] result = find2(pat, tok, idx); if (result != null) return result; } return null; } static String[] find2(List pat, List tok, int idx) { if (idx+pat.size() > tok.size()) return null; List result = new ArrayList(); for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(idx+i); if (eq(p, "*")) result.add(t); else if (!p.equalsIgnoreCase(t)) return null; } return toStringArray(result); } static int levenWithSwapsIC(String s, String t) { // degenerate cases if (s.equals(t)) return 0; int ls = s.length(), lt = t.length(); if (ls == 0) return lt; if (lt == 0) return ls; int[] vx = new int[lt + 1]; int[] v0 = new int[lt + 1]; int[] v1 = new int[lt + 1]; for (int i = 0; i < v0.length; i++) v0[i] = i; for (int i = 0; i < ls; i++) { v1[0] = i + 1; for (int j = 0; j < lt; j++) { int cost = equalsIgnoreCase(s.charAt(i), t.charAt(j)) ? 0 : 1; int d = min3( v1[j] + 1, // delete v0[j + 1] + 1, // insert v0[j] + cost); // edit if (i > 0 && j > 0 && equalsIgnoreCase(s.charAt(i), t.charAt(j-1)) && equalsIgnoreCase(s.charAt(i-1), t.charAt(j))) d = min(d, vx[j-1] + 1); // transposition v1[j + 1] = d; } // rotate arrays int[] v = vx; vx = v0; v0 = v1; v1 = v; } return v0[lt]; } static void selectRow(final JTable table, final int i) { if (table != null) { swing(new Runnable() { public void run() { try { if (i >= 0 && i < table.getRowCount()) { table.setRowSelectionInterval(i, i); scrollRowToVisible(table, i); } else table.clearSelection(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (i >= 0 && i < table.getRowCount()) {\r\n table.setRowSelectionInterval..."; }}); } } static void selectRow(final JList list, final int i) { if (list != null) { swing(new Runnable() { public void run() { try { if (i >= 0 && i < listRowCount(list)) list.setSelectedIndex(i); else list.clearSelection(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (i >= 0 && i < listRowCount(list))\r\n list.setSelectedIndex(i);\r\n e..."; }}); } } static int jlist_indexOf(JList list, A item) { return swing(new F0() { public Integer get() { try { ListModel model = list.getModel(); int n = model.getSize(); for (int i = 0; i < n; i++) if (eq(model.getElementAt(i), item)) return i; return -1; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ListModel model = list.getModel();\r\n int n = model.getSize();\r\n for ..."; }}); } static ThreadLocal addInternalFrame_dontSelect = new ThreadLocal(); static ThreadLocal addInternalFrame_layer = new ThreadLocal(); static ThreadLocal addInternalFrame_toBack = new ThreadLocal(); static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final int x, final int y, final int w, final int h) { return addInternalFrame(desktop, title, x, y, w, h, null); } static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final int x, final int y, final int w, final int h, final Component contents) { return addInternalFrame(desktop, title, rect(x, y, w, h), contents); } static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final Component contents) { return addInternalFrame(desktop, title, null, contents); } static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final Rect r, final Component contents) { final boolean dontSelect = isTrue(optParam(addInternalFrame_dontSelect)); final boolean toBack = isTrue(optParam(addInternalFrame_toBack)); final Integer layer = optParam(addInternalFrame_layer); return swing(new F0() { public JInternalFrame get() { try { JInternalFrame frame; if (contents instanceof JInternalFrame) frame = (JInternalFrame) contents; else { frame = jInternalFrame(title); setInternalFrameContents(frame, contents); } frame.setVisible(true); desktop.add(frame, layer); if (r != null) setBounds(frame, r); else internalFrameDefaultPosition(frame); if (dontSelect) if (toBack) frame.toBack(); else frame.toFront(); else frame.setSelected(true); return fixInternalFrame(frame); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JInternalFrame frame;\r\n if (contents instanceof JInternalFrame)\r\n fra..."; }}); } static JInternalFrame addInternalFrame(JDesktopPane desktop, String title) { return addInternalFrame(desktop, title, jpanel()); } static JInternalFrame minInternalFrameWidth(final JInternalFrame frame, final int w) { { swing(new Runnable() { public void run() { try { if (frame != null && frame.getWidth() < w) frame.setSize(w, frame.getHeight()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (frame != null && frame.getWidth() < w)\r\n frame.setSize(w, frame.getH..."; }}); } return frame; } static JInternalFrame minInternalFrameWidth(int w, JInternalFrame frame) { return minInternalFrameWidth(frame, w); } static A packInternalFrameVertically(A c) { return packInternalFrameVertically(-1, c); } static A packInternalFrameVertically(int width, A c) { final JInternalFrame win = getInternalFrame(c); if (win == null) return c; final int w = width < 0 ? win.getWidth() : width; { swing(new Runnable() { public void run() { try { win.pack(); win.setSize(w, win.getHeight()); fixInternalFrame(win); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "win.pack();\r\n win.setSize(w, win.getHeight());\r\n fixInternalFrame(win);"; }}); } return c; } static JInternalFrame centerInternalFrame(final JInternalFrame f) { { swing(new Runnable() { public void run() { try { Container c = f.getParent(); if (c != null) { //print("Container type: " + className(c) + ", bounds: " + c.getBounds()); f.setLocation((c.getWidth()-f.getWidth())/2, (c.getHeight()-f.getHeight())/2); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Container c = f.getParent();\r\n if (c != null) {\r\n //print(\"Container ..."; }}); } return f; } static JInternalFrame centerInternalFrame(final int w, final int h, final JInternalFrame f) { { swing(new Runnable() { public void run() { try { f.setSize(w, h); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.setSize(w, h);"; }}); } return centerInternalFrame(f); } static int packFrame_minw = 150, packFrame_minh = 50; static A packFrame(final A c) { { swing(new Runnable() { public void run() { try { Window w = getWindow(c); if (w != null) { w.pack(); int maxW = getScreenWidth()-50, maxH = getScreenHeight()-50; w.setSize( min(maxW, max(w.getWidth(), packFrame_minw)), min(maxH, max(w.getHeight(), packFrame_minh))); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Window w = getWindow(c);\r\n if (w != null) {\r\n w.pack();\r\n int ma..."; }}); } return c; } static JFrame packFrame(ButtonGroup g) { return packFrame(getFrame(g)); } static JFrame showFrame() { return makeFrame(); } static JFrame showFrame(Object content) { return makeFrame(content); } static JFrame showFrame(String title) { return makeFrame(title); } static JFrame showFrame(String title, Object content) { return makeFrame(title, content); } static JFrame showFrame(final JFrame f) { if (f != null) { swing(new Runnable() { public void run() { try { if (frameTooSmall(f)) frameStandardSize(f); if (!f.isVisible()) f.setVisible(true); // XXX if (f.getState() == Frame.ICONIFIED) f.setState(Frame.NORMAL); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (frameTooSmall(f)) frameStandardSize(f);\r\n if (!f.isVisible()) f.setVis..."; }}); } return f; } // make or update frame static JFrame showFrame(String title, Object content, JFrame frame) { if (frame == null) return showFrame(title, content); else { frame.setTitle(title); setFrameContents(frame, content); return frame; } } static > void forEachLevel2(Iterable l, IVF1 f) { if (l != null) for (B b : l) forEach(b, f); } static > void forEachLevel2(IVF1 f, Iterable l) { forEachLevel2(f, l); } static Dimension getMinimumSize(final Component c) { return c == null ? null : swing(new F0() { public Dimension get() { try { return c.getMinimumSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getMinimumSize();"; }}); } static A jPreferWidth(int w, A c) { Dimension size = c.getPreferredSize(); c.setPreferredSize(new Dimension(/*max(w, size.width) ??? */w, size.height)); return c; } static Component wrapForSmartAdd(Object o) { if (o == null) return jpanel(); if (o instanceof String) return jlabel((String) o); return wrap(o); } static List allChildren(Component c) { return childrenOfType(c, Component.class); } static void onEnterIfTextField(Component c, Object action) { if (action == null) return; if (c instanceof JTextField) onEnter((JTextField) c, action); else if (c instanceof JComboBox) onEnter((JComboBox) c, action); } static Object[] flattenArray2(Object... a) { List l = new ArrayList(); if (a != null) for (Object x : a) if (x instanceof Object[]) l.addAll(asList((Object[]) x)); else if (x instanceof Collection) l.addAll((Collection) x); else l.add(x); return asObjectArray(l); } static RootPaneContainer getPossiblyInternalFrame(Component c) { JInternalFrame f = getInternalFrame(c); if (f != null) return f; return optCast(RootPaneContainer.class, getWindow(c)); } static void setMenuBar(final JMenuBar mb, final RootPaneContainer f) { { swing(new Runnable() { public void run() { try { call(f, "setJMenuBar", mb); revalidate((Component) f); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "call(f, \"setJMenuBar\", mb);\r\n revalidate((Component) f);"; }}); } } static void setMenuBar(RootPaneContainer f, JMenuBar mb) { setMenuBar(mb, f); } static boolean isLoopbackIP(String ip) { return eq(ip, "127.0.0.1"); } static int myVMPort() { List records = (List) (get(getJavaX(), "record_list")); Object android = last(records); return or0((Integer) get(android, "port")); } static String thisVMGreeting() { List record_list = (List) (get(getJavaX(), "record_list")); Object android = first(record_list); // Should be of class Android3 return getString(android, "greeting"); } static String sendToThisVM_newThread(String s, Object... args) { final String _s = format(s, args); try { return (String) evalInNewThread(new F0() { public Object get() { try { return callStaticAnswerMethod(getJavaX(), _s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret callStaticAnswerMethod(getJavaX(), _s);"; }}); } catch (Throwable e) { e = getInnerException(e); printStackTrace(e); return str(e); } } static A popFirst(List l) { if (empty(l)) return null; A a = first(l); l.remove(0); return a; } static A popFirst(Collection l) { if (empty(l)) return null; A a = first(l); l.remove(a); return a; } static List popFirst(int n, List l) { List part = cloneSubList(l, 0, n); removeSubList(l, 0, n); return part; } static AppendableChain popFirst(AppendableChain a) { return a == null ? null : a.popFirst(); } static Object makeDependent_postProcess; static void makeDependent(Object c) { if (c == null) return; assertTrue("Not a class", c instanceof Class); dependentClasses(); // cleans up the list hotwire_classes.add(new WeakReference(c)); Object local_log = getOpt(mc(), "local_log"); if (local_log != null) setOpt(c, "local_log", local_log); /*if (isTrue(getOpt(c, 'ping_actions_shareable))) setOpt(c, +ping_actions);*/ Object print_byThread = getOpt(mc(), "print_byThread"); if (print_byThread != null) setOpt(c, "print_byThread", print_byThread); callF(makeDependent_postProcess, c); } static A bindToComponent(final A component, final Runnable onShow, final Runnable onUnShow) { { swing(new Runnable() { public void run() { try { final Var < Boolean > flag = new Var<>(false); component.addAncestorListener(new AncestorListener() { public void ancestorAdded(AncestorEvent event) { if (flag.get()) print("Warning: bindToComponent logic failure"); flag.set(true); pcallF(onShow); } public void ancestorRemoved(AncestorEvent event) { if (!flag.get()) print("Warning: bindToComponent logic failure"); flag.set(false); pcallF(onUnShow); } public void ancestorMoved(AncestorEvent event) { } }); if (component.isShowing()) { // Hopefully this matches the AncestorListener logic flag.set(true); pcallF(onShow); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final Var flag = new(false);\r\n component.addAncestorListener(new ..."; }}); } return component; } static A bindToComponent(A component, Runnable onShow) { return bindToComponent(component, onShow, null); } static Map loadFont_cached_cache = new HashMap(); static synchronized Font loadFont_cached(String snippetID) { try { snippetID = formatSnippetID(snippetID); Font f = loadFont_cached_cache.get(snippetID); if (f == null) loadFont_cached_cache.put(snippetID, f = loadFont(snippetID, 12f)); return f; } catch (Exception __e) { throw rethrow(__e); } } static synchronized Font loadFont_cached(String snippetID, float size) { try { return loadFont_cached(snippetID).deriveFont(size); } catch (Exception __e) { throw rethrow(__e); } } static void consoleSetInput(final String text) { if (headless()) return; setTextAndSelectAll(consoleInputField(), text); focusConsole(); } static List listFilesWithSuffix(File dir, String suffix) { List l = new ArrayList(); for (File f : listFiles(dir)) if (!f.isDirectory() && (empty(suffix) || endsWithIgnoreCase(f.getName(), suffix))) l.add(f); return l; } // Try to get the quoting right... static String smartJoin(String[] args) { if (empty(args)) return ""; if (args.length == 1) return args[0]; String[] a = new String[args.length]; for (int i = 0; i < a.length; i++) a[i] = !isJavaIdentifier(args[i]) && !isQuoted(args[i]) ? quote(args[i]) : args[i]; return join(" ", a); } static String smartJoin(List args) { return smartJoin(toStringArray(args)); } static IterableIterator iteratorFromFunction_withEndMarker_f0(final F0 f) { class IFF2 extends IterableIterator { A a; boolean have, done; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; have = false; return _a; } void getNext() { if (done || have) return; Object o = f.get(); if (o == iteratorFromFunction_endMarker) { done = true; return; } a = (A) o; have = true; } }; return new IFF2(); } static String sendToLocalBotOpt(String bot, String text, Object... args) { if (bot == null) return null; text = format(text, args); DialogIO channel = findBot(bot); try { if (channel == null) { print(quote(bot) + " not found, skipping send: " + quote(text)); return null; } try { channel.readLine(); print(shorten(bot + "> " + text, 200)); channel.sendLine(text); String s = channel.readLine(); print(shorten(bot + "< " + s, 200)); return s; } catch (Throwable e) { e.printStackTrace(); return null; } } finally { _close(channel); }} static boolean exposeMethods2_debug = false; static String exposeMethods2(Object receiver, String s, List methodNames) { return exposeMethods2(receiver, s, methodNames, null); } static String exposeMethods2(Object receiver, String s, List methodNames, Lock lock) { Matches m = new Matches(); if (exposeMethods2_debug) print("Received: " + s); if (match("call *", s, m)) { List l; if (isIdentifier(m.unq(0))) l = ll(m.unq(0)); else l = (List) unstructure(m.unq(0)); // we used to have safeUnstructure here String method = getString(l, 0); if (!contains(methodNames, method)) throw fail("Method not allowed: " + method); if (lock != null) lock.lock(); try { if (exposeMethods2_debug) print("Calling: " + method); Object o = call(receiver, method, asObjectArray(subList(l, 1))); if (exposeMethods2_debug) print("Got: " + getClassName(o)); return ok2(structure(o)); } finally { if (lock != null) lock.unlock(); } } if (match("list methods", s)) return ok2(structure(methodNames)); return null; } static int makeBot(String greeting) { return makeAndroid3(greeting).port; } static Android3 makeBot(Android3 a) { makeAndroid3(a); return a; } static Android3 makeBot(String greeting, Object responder) { Android3 a = new Android3(greeting); a.responder = makeResponder(responder); makeBot(a); return a; } static Android3 makeBot() { return makeAndroid3(defaultBotName()); } static Comparator mapComparator(final Map map) { return new Comparator() { public int compare(A a, A b) { return cmp(map.get(a), map.get(b)); } }; } // i must point at the (possibly imaginary) opening bracket ("{") // index returned is index of closing bracket + 1 (or l(cnc)) static int findEndOfBlock(List cnc, int i) { int j = i+2, level = 1, n = cnc.size(); while (j < n) { String t = cnc.get(j); if ("{".equals(t)) ++level; else if ("}".equals(t)) --level; if (level == 0) return j+1; j += 2; } return n; } // i must point at the (possibly imaginary) opening bracket (any of the 2 types, not type parameters) // index returned is index of closing bracket + 1 static int findEndOfBracketPart(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (eqOneOf(cnc.get(j), "{", "(")) ++level; else if (eqOneOf(cnc.get(j), "}", ")")) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } static void tokPrepend(List tok, String s) { tokPrepend(tok, 0, s); } static void tokPrepend(List tok, int i, String s) { tok.set(i, s + tok.get(i)); } static String javaDropAllComments(String s) { return join(javaDropAllComments(javaTok(s))); } static List javaDropAllComments(List tok) { for (int i = 0; i < l(tok); i += 2) tok.set(i, tok_javaDropCommentsFromWhitespace(tok.get(i))); return tok; } static byte[] isGIF_magic = bytesFromHex("47494638"); // Actual signature is longer, but we're lazy static boolean isGIF(byte[] data) { return byteArrayStartsWith(data, isGIF_magic); } static boolean isGIF(File f) { return isGIF(loadBeginningOfBinaryFile(f, l(isGIF_magic))); } static void setVar(IVar v, A value) { if (v != null) v.set(value); } static IVF1 setVar(IVar v) { return a -> { if (v != null) v.set(a); }; } static boolean substanceLookAndFeelEnabled() { return startsWith(getLookAndFeel(), "org.pushingpixels."); } static JComponent getTitlePaneComponent(RootPaneContainer window) { if (window instanceof JInternalFrame) return getInternalFrameTitlePaneComponent((JInternalFrame) window); if (!substanceLookAndFeelEnabled() || window == null) return null; JRootPane rootPane = window.getRootPane(); if (rootPane != null) { Object /*SubstanceRootPaneUI*/ ui = rootPane.getUI(); return (JComponent) call(ui, "getTitlePane"); } return null; } static boolean isAllUpperCase(String s) { return hasLettersAllUpperCase(s); } static boolean isUpperCaseLetter(char c) { return Character.isUpperCase(c); } static int menuItemCount(JMenu menu) { return menu == null ? 0 : swing(() -> menu.getItemCount()); } static String getOneLineFileInfoField(File f, String field) { File infoFile = associatedInfosFile(f); List lines = lines(loadTextFile(infoFile)); return firstStartingWithIC_drop(lines, field + ": "); } static Object rcurry(final Object f, final Object arg) { int n = numberOfFunctionArguments(f); if (n == 0) throw fail("function takes no arguments"); if (n == 1) return new F0() { Object get() { return callF(f, arg); } }; if (n == 2) return new F1() { Object get(Object a) { return callF(f, a, arg); } }; throw todo("currying a function with " + n + "arguments"); } static List dm_listModuleIDs() { return map(__127 -> dm_moduleID(__127), dm_listStems()); } static boolean isCurlyBraced(String s) { List tok = tok_combineCurlyBrackets_keep(javaTok(s)); return l(tok) == 3 && startsWithAndEndsWith(tok.get(1), "{", "}"); } static String tok_unCurlyBracket(String s) { return isCurlyBraced(s) ? join(dropFirstThreeAndLastThree(javaTok(s))) : s; } static List dropPunctuation_keep = ll("*", "<", ">"); 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 L printNumberedLines(L l) { printNumberedLines((Collection) l); ret l; } static L printNumberedLines(S prefix, L l) { printNumberedLines(prefix, (Collection) l); ret l; }*/ static void printNumberedLines(Map map) { printNumberedLines(mapToLines(map)); } static void printNumberedLines(String prefix, Map map) { printNumberedLines(prefix, mapToLines(map)); } static A printNumberedLines(A l) { int i = 0; if (l != null) for (Object a : cloneList(l)) print((++i) + ". " + str(a)); return l; } static A printNumberedLines(String prefix, A l) { int i = 0; if (l != null) for (Object a : cloneList(l)) print(prefix + (++i) + ". " + str(a)); return l; } static void printNumberedLines(Object[] l) { printNumberedLines("", l); } static void printNumberedLines(String prefix, Object[] l) { printNumberedLines(prefix, wrapAsList(l)); } static void printNumberedLines(Object o) { printNumberedLines(lines(str(o))); } static boolean isDirectory(File f) { return f != null && f.isDirectory(); } static boolean isDirectory(String path) { return path != null && isDirectory(newFile(path)); } static File[] listFiles(File dir) { File[] files = dir.listFiles(); return files == null ? new File[0] : files; } static File[] listFiles(String dir) { return listFiles(new File(dir)); } // closes the input stream too. static void stream2file(InputStream in, File out) { try { mkdirsForFile(out); FileOutputStream fos = new FileOutputStream(out); copyStream(in, fos); in.close(); fos.close(); } catch (Exception __e) { throw rethrow(__e); } } static int lastIndexOf_byteArray(byte[] a, byte b) { for (int i = l(a)-1; i >=0; i--) if (a[i] == b) return i; return -1; } static int indexOf_byteArray(byte[] a, byte b) { int n = l(a); for (int i = 0; i < n; i++) if (a[i] == b) return i; return -1; } static void scanForComponents(final Component c, final Class theClass, final List l) { if (theClass.isInstance(c)) l.add((A) c); if (c instanceof Container) { swing(new Runnable() { public void run() { try { for (Component comp : ((Container) c).getComponents()) scanForComponents(comp, theClass, l); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "for (Component comp : ((Container) c).getComponents())\r\n scanForComponen..."; }}); } } static TreeMap asCaseInsensitiveMap(Map map) { if (isCIMap(map)) return (TreeMap) map; TreeMap m = ciMap(); putAll(m, map); return m; } static boolean getHtmlTagParameters_debug = false; static Map getHtmlTagParameters(String tag) { if (empty(tag)) return null; List tok = codeTokens(tok_joinMinusIdentifiers(htmlFineTok(tag))); if (getHtmlTagParameters_debug) printStruct(tok); assertEquals("<", tok.get(0)); int i = 1; if (eq(tok.get(1), "/")) ++i; String name = tok.get(i++); if (!isMinusIdentifier(name)) throw fail(tag + " (" + name + ")"); Map map = new HashMap(); while (i < l(tok)) { String t = tok.get(i); if (eqOneOf(t, "/", ">")) break; if (!isMinusIdentifier(t)) throw fail(tag + " (" + t + ")"); ++i; String value = "1"; if (eq(tok.get(i), "=")) { ++i; value = htmlunquote(tok.get(i++)); } map.put(t, value); } return map; } static List javaTokPlusBrackets2(String s) { return tok_combineRoundCurlySquareBrackets_keep(javaTok(s)); } static boolean startsWithLetterOrDigit(String s) { return nempty(s) && Character.isLetterOrDigit(s.charAt(0)); } static List takeLast(List l, int n) { return newSubList(l, l(l)-n); } static List takeLast(int n, List l) { return takeLast(l, n); } static String takeLast(int n, String s) { return substring(s, l(s)-n); } static String takeLast(String s, int n) { return substring(s, l(s)-n); } static int min3(int a, int b, int c) { return min(min(a, b), c); } static double min3(double a, double b, double c) { return min(min(a, b), c); } static void scrollRowToVisible(JTable t, int rowIndex) { int colIndex = 0; if (!(t.getParent() instanceof JViewport)) return; JViewport viewport = (JViewport) t.getParent(); Rectangle rect = t.getCellRect(rowIndex, colIndex, true); Rectangle viewRect = viewport.getViewRect(); int x = viewRect.x; int y = viewRect.y; if (rect.x >= viewRect.x && rect.x <= (viewRect.x + viewRect.width - rect.width)){ } else if (rect.x < viewRect.x){ x = rect.x; } else if (rect.x > (viewRect.x + viewRect.width - rect.width)) { x = rect.x - viewRect.width + rect.width; } if (rect.y >= viewRect.y && rect.y <= (viewRect.y + viewRect.height - rect.height)){ } else if (rect.y < viewRect.y){ y = rect.y; } else if (rect.y > (viewRect.y + viewRect.height - rect.height)){ y = rect.y - viewRect.height + rect.height; } viewport.setViewPosition(new Point(x,y)); } static int listRowCount(JList list) { return list == null ? 0 : swing(new F0() { public Integer get() { try { return list.getModel().getSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret list.getModel().getSize();"; }}); } static Rect rect(int x, int y, int w, int h) { return new Rect(x, y, w, h); } static Rect rect(Pt p, int w, int h) { return new Rect(p.x, p.y, w, h); } static Rect rect(int w, int h) { return new Rect(0, 0, w, h); } static boolean jInternalFrame_iconifiable = true; static JInternalFrame jInternalFrame() { return jInternalFrame(""); } static JInternalFrame jInternalFrame(final String title) { return swing(new F0() { public JInternalFrame get() { try { JInternalFrame f = new JInternalFrame(title, true, true, true, jInternalFrame_iconifiable); f.setVisible(true); return f; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JInternalFrame f = new JInternalFrame(title, true, true, true, jInternalFrame..."; }}); } static void setInternalFrameContents(final Component c, final Object contents) { { swing(new Runnable() { public void run() { try { JInternalFrame frame = getInternalFrame(c); if (frame == null) return; frame.getContentPane().removeAll(); frame.getContentPane().setLayout(new BorderLayout()); if (contents != null) frame.getContentPane().add(wrap(contents)); revalidate(frame); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JInternalFrame frame = getInternalFrame(c);\r\n if (frame == null) return;\r\n..."; }}); } } static A setBounds(final int x, final int y, final int w, final int h, final A a) { if (a != null) { swing(new Runnable() { public void run() { try { a.setBounds(x, y, w, h); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "a.setBounds(x, y, w, h);"; }}); } return a; } static A setBounds(A a, Rect r) { if (a != null && r != null) { swing(new Runnable() { public void run() { try { a.setBounds(toRectangle(r)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "a.setBounds(toRectangle(r));"; }}); } return a; } static A setBounds(Rect r, A a) { return setBounds(a, r); } static A setBounds(A a, int x, int y, int w, int h) { return setBounds(x, y, w, h, a); } static void internalFrameDefaultPosition(JInternalFrame f) { f.setSize(500, 300); centerInternalFrame(f); } static int fixInternalFrame_borderTopLeft = 0; static int fixInternalFrame_borderBottomRight = 40; // for title bar static JInternalFrame fixInternalFrame(final JInternalFrame f) { return swing(new F0() { public JInternalFrame get() { try { Container c = f.getParent(); if (c == null) return f; Rect r = toRect(f.getBounds()); int a = fixInternalFrame_borderTopLeft, b = fixInternalFrame_borderBottomRight; Rect outer = new Rect(a, a, c.getWidth()-b, c.getHeight()-b); if (!rectContains(outer, r)) f.setLocation( max(a, min(r.x, outer.x2())), max(a, min(r.y, outer.y2()))); if (r.w > c.getWidth() || r.h > c.getHeight()) f.setSize(c.getWidth()-a, c.getHeight()-a); return f; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Container c = f.getParent();\r\n if (c == null) ret f;\r\n Rect r = toRect(..."; }}); } static int getScreenWidth() { return getScreenSize().width; } static int getScreenHeight() { return getScreenSize().height; } static boolean frameTooSmall(JFrame frame) { return frame.getWidth() < 100 || frame.getHeight() < 50; } static void frameStandardSize(JFrame frame) { frame.setBounds(300, 100, 500, 400); } static void setFrameContents(final Component c, final Object contents) { swing(new Runnable() { public void run() { try { JFrame frame = getFrame(c); if (frame == null) return; frame.getContentPane().removeAll(); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(wrap(contents)); revalidate(frame); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JFrame frame = getFrame(c);\r\n if (frame == null) return;\r\n frame.getContent..."; }}); } static int or0(Integer i) { return i == null ? 0 : i; } static long or0(Long l) { return l == null ? 0L : l; } static double or0(Double d) { return d == null ? 0.0 : d; } static Object evalInNewThread(final Object f) { final Flag flag = new Flag(); final Var var = new Var(); final Var exception = new Var(); { startThread(new Runnable() { public void run() { try { try { var.set(callF(f)); } catch (Throwable e) { exception.set(e); } flag.raise(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "try {\r\n var.set(callF(f));\r\n } catch (Throwable e) {\r\n exception..."; }}); } flag.waitUntilUp(); if (exception.has()) throw rethrow(exception.get()); return var.get(); } static String callStaticAnswerMethod(List bots, String s) { for (Object 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(Object 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) { return callStaticAnswerMethod(mc(), s); } static String callStaticAnswerMethod(String s, List history) { return callStaticAnswerMethod(mc(), s, history); } static String callStaticAnswerMethod(Object c, String s, List history) { String answer = (String) callOpt(c, "answer", s, history); if (answer == null) answer = (String) callOpt(c, "answer", s); return emptyToNull(answer); } static List dependentClasses() { return cleanUpAndGetWeakReferencesList(hotwire_classes); } static Font loadFont(String snippetID) { try { return loadFont(snippetID, 12f); } catch (Exception __e) { throw rethrow(__e); } } static Font loadFont(InputStream in) { try { return Font.createFont(Font.TRUETYPE_FONT, in); } catch (Exception __e) { throw rethrow(__e); } } static Font loadFont(String snippetID, float fontSize) { return loadFont(loadLibrary(snippetID), fontSize); } static Font loadFont(File f, float fontSize) { try { return Font.createFont(Font.TRUETYPE_FONT, f).deriveFont(fontSize); } catch (Exception __e) { throw rethrow(__e); } } static Font loadFont(InputStream in, float fontSize) { try { return Font.createFont(Font.TRUETYPE_FONT, in).deriveFont(fontSize); } catch (Exception __e) { throw rethrow(__e); } } static JTextField setTextAndSelectAll(final JTextField tf, final String text) { if (tf != null) { swing(new Runnable() { public void run() { try { tf.setText(text); tf.selectAll(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "tf.setText(text);\r\n tf.selectAll();"; }}); } return tf; } static void focusConsole(String s) { setConsoleInput(s); focusConsole(); } static void focusConsole() { JComponent tf = consoleInputFieldOrComboBox(); if (tf != null) { //print("Focusing console"); tf.requestFocus(); } } static List> hotwire_classes = synchroList(); static Class hotwireDependent(String src) { Class c = hotwire(src); makeDependent(c); return c; } static String ok2(String s) { return "ok " + s; } // An "Android" is a program that accepts text questions (on console or TCP) and outputs one response text per question //please include function myJavaSource. // for getting my known commands static boolean makeAndroid3_disable = false; // disable all android making static class Android3 implements AutoCloseable { String greeting; boolean publicOverride = false; // optionally set this in client int startPort = 5000; // optionally set this in client Responder responder; boolean console = true; boolean quiet = false; // no messages on console boolean daemon = false; boolean incomingSilent = false; int incomingPrintLimit = 200; boolean useMultiPort = true; boolean recordHistory = false; boolean verbose = false; int answerPrintLimit = 500; boolean newLineAboveAnswer, newLineBelowAnswer; // set by system int port; long vport; DialogHandler handler; ServerSocket server; Android3(String greeting) { this.greeting = greeting;} Android3() {} public void close() { dispose(); } synchronized void dispose() { if (server != null) { try { server.close(); } catch (IOException e) { print("[internal] " + e); } server = null; } if (vport != 0) { try { print("Disposing " + this); removeFromMultiPort(vport); vport = 0; } catch (Throwable __e) { _handleException(__e); }} } public String toString() { return "Bot: " + greeting + " [vport " + vport + "]"; } } 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 (makeAndroid3_disable) return a; if (a.responder == null) a.responder = new Responder() { String answer(String s, List history) { return callStaticAnswerMethod(s, history); } }; if (!a.quiet) print("[bot] " + a.greeting); if (a.console && (readLine_noReadLine || makeAndroid3_consoleInUse())) a.console = false; record(a); if (a.useMultiPort) a.vport = addToMultiPort(a.greeting, makeAndroid3_verboseResponder(a)); if (a.console) makeAndroid3_handleConsole(a); if (a.useMultiPort) return a; a.handler = makeAndroid3_makeDialogHandler(a); if (a.quiet) startDialogServer_quiet.set(true); try { a.port = a.daemon ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler) : startDialogServerOnPortAbove(a.startPort, a.handler); } finally { startDialogServer_quiet.set(null); } a.server = startDialogServer_serverSocket; return a; } static void makeAndroid3_handleConsole(final Android3 a) { // Console handling stuff if (!a.quiet) print("You may also type on this console."); { startThread(new Runnable() { public void run() { try { List history = new ArrayList(); while (licensed()) { String line; try { line = readLine(); } catch (Throwable e) { print(getInnerMessage(e)); break; } if (line == null) break; /*if (eq(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 rethrow(__e); } } public String toString() { return "List history = new ArrayList();\r\n while (licensed()) {\r\n Stri..."; }}); } } 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(shorten(s, a.incomingPrintLimit)); if (eq(line, "bye")) { io.sendLine("bye stranger"); return; } Matches m = new Matches(); if (a.recordHistory) 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 try { makeAndroid3_io.set(io); answer = makeAndroid3_getAnswer(line, history, a); } finally { makeAndroid3_io.set(null); } if (a.recordHistory) history.add(answer); io.sendLine(answer); //appendToLog(logFile, s); } } }}; } static String makeAndroid3_getAnswer(String line, List history, Android3 a) { String answer, originalAnswer; try { originalAnswer = a.responder.answer(line, history); answer = makeAndroid3_fallback(line, history, originalAnswer); } catch (Throwable e) { e = getInnerException(e); printStackTrace(e); originalAnswer = answer = e.toString(); } if (!a.incomingSilent) { if (originalAnswer == null) originalAnswer = "?"; if (a.newLineAboveAnswer) print(); print(">" + dropFirst(indentx(2, shorten(rtrim(originalAnswer), a.answerPrintLimit)))); if (a.newLineBelowAnswer) print(); } 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() { if (isTrue(vm_generalMap_get("consoleInUse"))) return true; for (Object o : record_list) if (o instanceof Android3 && ((Android3) o).console) return true; return false; } static Responder makeAndroid3_verboseResponder(final Android3 a) { return new Responder() { String answer(String s, List history) { if (a.verbose) print("> " + shorten(s, a.incomingPrintLimit)); String answer = a.responder.answer(s, history); if (a.verbose) print("< " + shorten(answer, a.incomingPrintLimit)); return answer; } }; } static ThreadLocal makeAndroid3_io = new ThreadLocal(); static Android3 makeAndroid3() { return makeAndroid3(getProgramTitle() + "."); } static String makeResponder_callAnswerMethod(Object bot, String s, List history) { String answer = (String) callOpt(bot, "answer", s, history); if (answer == null) answer = (String) callOpt(bot, "answer", s); return answer; } static Responder makeResponder(final Object bot) { if (bot instanceof Responder) return (Responder) bot; if (bot instanceof String) { String f = (String) bot; return new Responder() { String answer(String s, List history) { String answer = (String) callOptMC((String) bot, s, history); if (answer == null) answer = (String) callOptMC((String) bot, s); return answer; } }; } return new Responder() { String answer(String s, List history) { return makeResponder_callAnswerMethod(bot, s, history); } }; } static String defaultBotName() { return getProgramTitle() + "."; } static String tok_javaDropCommentsFromWhitespace(String s) { int l = l(s), j = 0; StringBuilder buf = new StringBuilder(); while (j < l) { char c = s.charAt(j); char d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else { buf.append(c); ++j; } } return str(buf); } static byte[] bytesFromHex(String s) { return hexToBytes(s); } static boolean byteArrayStartsWith(byte[] a, byte[] b) { if (a == null || b == null) return false; if (a.length < b.length) return false; for (int i = 0; i < b.length; i++) if (a[i] != b[i]) return false; return true; } static byte[] loadBeginningOfBinaryFile(File file, int maxBytes) { return loadBinaryFilePart(file, 0, maxBytes); } static String getLookAndFeel() { return getClassName(UIManager.getLookAndFeel()); } static boolean hasLettersAllUpperCase(String s) { return hasLetters(s) && !containsLowerCase(s); } static File associatedInfosFile(File f) { return replaceExtension(f, ".infos"); } static String firstStartingWithIC_drop(Collection l, final String prefix) { for (String s : unnull(l)) if (swic(s, prefix)) return substring(s, l(prefix)); return null; } static String firstStartingWithIC_drop(String prefix, Collection l) { return firstStartingWithIC_drop(l, prefix); } static int numberOfFunctionArguments(Object f) { if (f instanceof F0) return 0; if (f instanceof F1) return 1; if (f instanceof F2) return 2; if (f instanceof VF1) return 1; if (f instanceof VF2) return 2; if (f instanceof String) return numberOfMethodArguments(mc(), (String) f); return numberOfMethodArguments(f, "get"); } static List dm_listStems() { return (List) dm_callOS("onModules"); } static List tok_combineCurlyBrackets_keep(List tok) { List l = new ArrayList(); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && eq(t, "{")) { int j = findEndOfCurlyBracketPart(tok, i); l.add(joinSubList(tok, i, j)); i = j-1; } else l.add(t); } return l; } static List nlTok(String s) { return javaTokPlusPeriod(s); } static List wrapAsList(A[] a) { return wrapArrayAsList(a); } static boolean isCIMap(Map m) { return m instanceof TreeMap && ((TreeMap) m).comparator() == caseInsensitiveComparator(); } static List tok_joinMinusIdentifiers(List tok) { for (int i = 1; i+4 < l(tok); i += 2) if (isMinusIdentifier(get(tok, i)) && eq(get(tok, i+2), "-") && isIdentifier(get(tok, i+4))) { replaceSublist(tok, i, i+5, ll(join(subList(tok, i, i+5)))); i -= 2; } return tok; } static List htmlFineTok(String s) { List tok = new ArrayList(); int l = s.length(); int i = 0, n = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else break; } tok.add(quickSubstring(s, i, j)); ++n; i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener /*|| s.charAt(j) == '\n'*/) { // allow multi-line strings ++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 ++j; tok.add(quickSubstring(s, i, j)); ++n; i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } static boolean isMinusIdentifier(String s) { if (empty(s)) return false; if (!Character.isJavaIdentifierStart(s.charAt(0)) && !s.startsWith("-")) return false; for (int i = 1; i < s.length(); i++) if (!Character.isJavaIdentifierPart(s.charAt(i)) && s.charAt(i) != '-') return false; return true; } static String htmlunquote(String s) { if (s.startsWith("'") && s.endsWith("'") && s.length() >= 2 || s.startsWith("\"") && s.endsWith("\"") && s.length() >= 2) s = s.substring(1, s.length()-1); return htmldecode(s); } static List tok_combineRoundCurlySquareBrackets_keep(List tok) { List l = new ArrayList(); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && eqOneOf(t, "{", "(", "[")) { int j = findEndOfBracketPart2(tok, i); l.add(joinSubList(tok, i, j)); i = j-1; } else l.add(t); } return l; } static Rectangle toRectangle(Rect r) { return r == null ? null : r.getRectangle(); } static Rect toRect(Rectangle r) { return r == null ? null : new Rect(r); } static Rect toRect(RectangularShape r) { return r == null ? null : toRect(r.getBounds()); } static Rect toRect(Rect r) { return r; } static boolean rectContains(int x1, int y1, int w, int h, Pt p) { return p.x >= x1 && p.y >= y1 && p.x < x1+w && p.y < y1+h; } static boolean rectContains(Rect a, Rect b) { return b.x >= a.x && b.y >= a.y && b.x2() <= a.x2() && b.y2() <= a.y2(); } static boolean rectContains(Rect a, int x, int y) { return a != null && a.contains(x, y); } static String emptyToNull(String s) { return eq(s, "") ? null : s; } static Map emptyToNull(Map map) { return empty(map) ? null : map; } static List cleanUpAndGetWeakReferencesList(List> l) { if (l == null) return null; synchronized(l) { List out = new ArrayList(); for (int i = 0; i < l(l); i++) { A a = l.get(i).get(); if (a == null) l.remove(i--); else out.add(a); } return out; } } static void setConsoleInput(String text) { consoleSetInput(text); } static JComponent consoleInputFieldOrComboBox() { Object console = get(getJavaX(), "console"); JComboBox cb = (JComboBox) (getOpt(console, "cbInput")); if (cb != null) return cb; return (JTextField) getOpt(console, "tfInput"); } static void removeFromMultiPort(long vport) { if (vport == 0) return; for (Object port : getMultiPorts()) call(port, "removePort", vport); } static List record_list = synchroList(); static void record(Object o) { record_list.add(o); } 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 == null) return 0; if (ports.isEmpty()) throw 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 AtomicInteger dialogServer_clients = new AtomicInteger(); static boolean dialogServer_printConnects = false; static ThreadLocal startDialogServer_quiet = new ThreadLocal(); static Set dialogServer_knownClients = synchroTreeSet(); static int startDialogServerOnPortAbove(int port, DialogHandler handler) { while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler)) ++port; return port; } static int startDialogServerOnPortAboveDaemon(int port, DialogHandler handler) { while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler, true)) ++port; return port; } static void startDialogServer(int port, DialogHandler handler) { if (!startDialogServerIfPortAvailable(port, handler)) throw 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) && neq(client, "/127.0.0.1")) { 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 (Exception __e) { throw rethrow(__e); } } String readLineImpl() { try { return in.readLine(); } catch (Exception __e) { throw rethrow(__e); } } public void close() { try { s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } }; try { handler.run(io); } finally { if (!io.noClose) 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(); if (!isTrue(getAndClearThreadLocal(startDialogServer_quiet))) print("Dialog server on port " + port + " started."); return true; } static boolean publicCommOn() { return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication"))); } static String processID_cached; // try to get our current process ID static String getPID() { if (processID_cached == null) { String name = ManagementFactory.getRuntimeMXBean().getName(); processID_cached = name.replaceAll("@.*", ""); } return processID_cached; } static String getInjectionID() { return (String) call(getJavaX(), "getInjectionID", getMainClass()); } static String getProgramTitle() { return getProgramName(); } static Object callOptMC(String method, Object... args) { return callOpt(mc(), method, args); } static byte[] loadBinaryFilePart(File file, long start, long end) { try { RandomAccessFile raf = new RandomAccessFile(file, "r"); int n = toInt(min(raf.length(), end-start)); byte[] buffer = new byte[n]; try { raf.seek(start); raf.readFully(buffer, 0, n); return buffer; } finally { raf.close(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean hasLetters(String s) { for (int i = 0; i < s.length(); i++) if (Character.isLetter(s.charAt(i))) return true; return false; } static boolean containsLowerCase(String s) { for (int i = 0; i < l(s); i++) if (isLowerCase(s.charAt(i))) return true; return false; } static File replaceExtension(File f, String extOld, String extNew) { return newFile(replaceExtension(f2s(f), extOld, extNew)); } static File replaceExtension(File f, String extNew) { return replaceExtension(f, fileExtension(f), extNew); } static String replaceExtension(String s, String extOld, String extNew) { s = dropSuffixIC(addPrefixOptIfNempty(".", extOld), s); return s + addPrefixOptIfNempty(".", extNew); } static String replaceExtension(String name, String extNew) { return replaceExtension(name, fileExtension(name), extNew); } static int numberOfMethodArguments(Object o, String method) { Class c; boolean mustBeStatic = false; if (o instanceof Class) { c = (Class) o; mustBeStatic = true; } else c = o.getClass(); Class _c = c; int n = -1; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (!m.getName().equals(method)) continue; if (mustBeStatic && !methodIsStatic(m)) continue; int nn = l(m.getParameterTypes()); if (n == -1) n = nn; else if (n != nn) throw fail("Variable number of method arguments: " + _c + "." + method); } c = c.getSuperclass(); } if (n == -1) throw fail("Method not found: " + _c + "." + method); return n; } // i must point at the (possibly imaginary) opening bracket // index returned is index of closing bracket + 1 static int findEndOfCurlyBracketPart(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (eq(cnc.get(j), "{")) ++level; else if (eq(cnc.get(j), "}")) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } // i must point at the (possibly imaginary) opening bracket (any of the 3 types, not type parameters) // index returned is index of closing bracket + 1 static int findEndOfBracketPart2(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (eqOneOf(cnc.get(j), "{", "(", "[")) ++level; else if (eqOneOf(cnc.get(j), "}", ")", "]")) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } static List getMultiPorts() { return (List) callOpt(getJavaX(), "getMultiPorts"); } // start multi-port if none exists in current VM. static void startMultiPort() { List mp = getMultiPorts(); if (mp != null && mp.isEmpty()) { nohupJavax("#1001639"); throw fail("Upgrading JavaX, please restart this program afterwards."); //callMain(hotwire("#1001672")); } } static Set synchroTreeSet() { return Collections.synchronizedSet(new TreeSet()); } static Set synchroTreeSet(TreeSet set) { return Collections.synchronizedSet(set); } static boolean forbiddenPort(int port) { return port == 5037; // adb } static boolean isLowerCase(char c) { return Character.isLowerCase(c); } static String dropSuffixIC(String suffix, String s) { return s == null ? null : ewic(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static String addPrefixOptIfNempty(String prefix, String s) { return addPrefixIfNotEmpty2(prefix, s); } static String addPrefixIfNotEmpty2(String prefix, String s) { return empty(s) ? "" : addPrefix(prefix, s); } static class JSection extends SingleComponentPanel { JSection(Component c) { super(c); } String getTitle() { Border border = getBorder(); if (border instanceof TitledBorder) return ((TitledBorder) border).getTitle(); return null; } } static abstract class LiveValue { abstract Class getType(); abstract A get(); abstract void onChange(Runnable l); abstract void removeOnChangeListener(Runnable l); void onChangeAndNow(Runnable l) { onChange(l); callF(l); } } // from GSON project, simplified static final class ParameterizedTypeImpl implements ParameterizedType { private final Type ownerType; private final Type rawType; private final Type[] typeArguments; ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { this.typeArguments = typeArguments; this.rawType = rawType; this.ownerType = ownerType;} public Type[] getActualTypeArguments() { return typeArguments; } public Type getRawType() { return rawType; } public Type getOwnerType() { return ownerType; } @Override public boolean equals(Object other) { if (other instanceof ParameterizedType) return eq(ownerType, ((ParameterizedType) other).getOwnerType()) && eq(rawType, ((ParameterizedType) other).getRawType()) && eq(asList(typeArguments), asList(((ParameterizedType) other).getActualTypeArguments())); return false; } @Override public int hashCode() { return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ _hashCode(ownerType); } @Override public String toString() { int length = typeArguments.length; if (length == 0) return typeToString(rawType); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(typeToString(rawType)).append("<").append(typeToString(typeArguments[0])); for (int i = 1; i < length; i++) { stringBuilder.append(", ").append(typeToString(typeArguments[i])); } return stringBuilder.append(">").toString(); } static String typeToString(Type type) { return type instanceof Class ? ((Class) type).getName() : type.toString(); } } static class Str extends Concept { String name; List otherNames = new ArrayList(); Str() {} Str(String name) { this.name = name;} public String toString() { return name; } } static class CountingOutputStream extends FilterOutputStream { long counter; CountingOutputStream(OutputStream out) { super(out); } @Override public void write(int b) throws IOException { ++counter; out.write(b); } @Override public void write(byte[] b) throws IOException { counter += b.length; out.write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { if (len == 0) return; counter += len; out.write(b, off, len); } long getFilePointer() { return counter; } } static class CancelToCancelPoint extends QuickException { CancelPoint cp; CancelToCancelPoint(CancelPoint cp) { this.cp = cp;} } final static class LongRange { long start, end; LongRange() {} LongRange(long start, long end) { this.end = end; this.start = start;} public boolean equals(Object o) { if (o instanceof LongRange) return start == ((LongRange) o).start && end == ((LongRange) o).end; return false; } public int hashCode() { return boostHashCombine(hashOfLong(start), hashOfLong(end)); } long length() { return end-start; } static String _fieldOrder = "start end"; public String toString() { return "[" + start + ";" + end + "]"; } } // AppendableChain has one "smart" head element (with size counter // and pointer to the chain's last element), all the other nodes are // maximally simple (MinimalChain). // This allows O(1) front insertion, front removal and back insertion // (not removal at the back though) which is fine for what I need this // for (event queues). // // Stefan Reich, Oct 21 static class AppendableChain extends MinimalChain implements Iterable { MinimalChain last; // pointer to last element in chain (which may be us) int size; // total length of chain AppendableChain() {} // only used internally AppendableChain(A element) { this.element = element; size = 1; last = this; } // intermediate constructor called by itemPlusChain() AppendableChain(A element, AppendableChain next) { this.next = next; this.element = element; if (next == null) return; MinimalChain b = new MinimalChain(); b.element = next.element; b.next = next.next; this.next = b; last = next.last; size = next.size+1; } public String toString() { return str(toList()); } // append at the end boolean add(A a) { MinimalChain newLast = new MinimalChain(a); last.next = newLast; last = newLast; ++size; return true; } // drop first element AppendableChain popFirst() { if (next == null) return null; element = next.element; if (last == next) last = this; next = next.next; --size; return this; } ArrayList toList() { ArrayList l = emptyList(size); MinimalChain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } //public Iterator iterator() { ret toList().iterator(); } class ACIt extends IterableIterator < A > { MinimalChain c = AppendableChain.this; public boolean hasNext() { return c != null; } public A next() { var a = c.element; c = c.next; return a; } } public IterableIterator iterator() { return new ACIt(); } } static class SnippetTitles { Map titles = synchroHashMap(); String get(String snippetID) { if (snippetID == null) return null; long id = parseSnippetID(snippetID); if (titles.containsKey(id)) return titles.get(id); else { String title = getSnippetTitle_safe(snippetID); titles.put(id, title); return title; } } // like get, but only check the cache String probe(String id) { return titles.get(parseSnippetID(id)); } // advanced uses void put(String id, String title) { titles.put(parseSnippetID(id), title); } boolean has(String id) { return titles.containsKey(parseSnippetID(id)); } void clear() { titles.clear(); } } static class JavaScript extends Var { JavaScript() {} JavaScript(String src) { super(src); } String javaScriptSource() { return get(); } public String toString() { return hjs(get()); } } static class _PrintIndent extends F1 { String prefix; boolean beginningOfLine = true; _PrintIndent() {} _PrintIndent(String prefix) { this.prefix = prefix;} Boolean get(String s) { if (empty(s)) return false; boolean nl = s.endsWith("\n"); if (nl) s = dropLast(s); s = s.replace("\n", "\n" + prefix); print_raw(beginningOfLine ? prefix + s : s); if (nl) print_raw("\n"); beginningOfLine = nl; return false; } } static class SingleComponentPanel extends JPanel { SingleComponentPanel() { super(new BorderLayout()); } SingleComponentPanel(Component component) { this(); if (component != null) setComponent(component); } final void set(Component component) { setComponent(component); } void setComponent(Component component) { { swing(new Runnable() { public void run() { try { removeAll(); if (component != null) add(BorderLayout.CENTER, wrap(component)); _revalidate(SingleComponentPanel.this); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "removeAll();\r\n if (component != null)\r\n add(BorderLayout.CENTER, ..."; }}); } } // Sometimes we need this? void setComponentAndRevalidateParent(Component component) { setComponent(component); _revalidate(_getParent(this)); } void noComponent() { setComponent(null); } Component getComponent() { return getComponentCount() == 0 ? null : getComponent(0); } } static class FixedRateTimer extends java.util.Timer implements AutoCloseable { FixedRateTimer() { this(false); } FixedRateTimer(boolean daemon) { this(defaultTimerName(), daemon); } FixedRateTimer(String name) { this(name, false); } FixedRateTimer(String name, boolean daemon) { super(name, daemon); _registerTimer(this); } List entries = synchroList(); static class Entry implements IFieldsToList{ TimerTask task; long firstTime; long period; Entry() {} Entry(TimerTask task, long firstTime, long period) { this.period = period; this.firstTime = firstTime; this.task = task;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + task + ", " + firstTime + ", " + period + ")"; }public Object[] _fieldsToList() { return new Object[] {task, firstTime, period}; } } // Note: not all methods overridden; only use these ones public void scheduleAtFixedRate(TimerTask task, long delay, long period) { entries.add(new Entry(task, now()+delay, period)); super.scheduleAtFixedRate(task, delay, period); } public void cancel() { entries.clear(); super.cancel(); } public int purge() { entries.clear(); return super.purge(); } FixedRateTimer changeRate(int newPeriod) { Object r = ((SmartTimerTask) first(entries).task).r; cancel(); return doEvery(newPeriod, r); } public void close() { cancel(); } } static class T3 { A a; B b; C c; T3() {} T3(A a, B b, C c) { this.c = c; this.b = b; this.a = a;} T3(T3 t) { a = t.a; b = t.b; c = t.c; } public int hashCode() { return _hashCode(a) + 2*_hashCode(b) - 4*_hashCode(c); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof T3)) return false; T3 t = (T3) o; return eq(a, t.a) && eq(b, t.b) && eq(c, t.c); } public String toString() { return "(" + quoteBorderless(a) + ", " + quoteBorderless(b) + ", " + quoteBorderless(c) + ")"; } } final static class Rect implements IFieldsToList{ static final String _fieldOrder = "x y w h"; int x; int y; int w; int h; Rect() {} Rect(int x, int y, int w, int h) { this.h = h; this.w = w; this.y = y; this.x = x;} public boolean equals(Object o) { if (!(o instanceof Rect)) return false; Rect __1 = (Rect) o; return x == __1.x && y == __1.y && w == __1.w && h == __1.h; } public int hashCode() { int h = 2543108; h = boostHashCombine(h, _hashCode(x)); h = boostHashCombine(h, _hashCode(y)); h = boostHashCombine(h, _hashCode(w)); h = boostHashCombine(h, _hashCode(h)); return h; } public Object[] _fieldsToList() { return new Object[] {x, y, w, h}; } Rect(Rectangle r) { x = r.x; y = r.y; w = r.width; h = r.height; } Rect(Pt p, int w, int h) { this.h = h; this.w = w; x = p.x; y = p.y; } Rect(Rect r) { x = r.x; y = r.y; w = r.w; h = r.h; } Rectangle getRectangle() { return new Rectangle(x, y, w, h); } public String toString() { return x + "," + y + " / " + w + "," + h; } int x1() { return x; } int y1() { return y; } int x2() { return x + w; } int y2() { return y + h; } boolean contains(Pt p) { return contains(p.x, p.y); } boolean contains(int _x, int _y) { return _x >= x && _y >= y && _x < x+w && _y < y+h; } boolean empty() { return w <= 0 || h <= 0; } } /** * A class that provides scrolling capabilities to a long menu dropdown or * popup menu. A number of items can optionally be frozen at the top and/or * bottom of the menu. *

* Implementation note: The default number of items to display * at a time is 15, and the default scrolling interval is 125 milliseconds. *

* * @version 1.5.0 04/05/12 * @author Darryl * https://tips4java.wordpress.com/2009/02/01/menu-scroller/ */ static class JMenuScroller { VF1 fillMenu; private JPopupMenu menu; private Component[] menuItems; private MenuScrollItem upItem; private MenuScrollItem downItem; private final MenuScrollListener menuListener = new MenuScrollListener(); private int scrollCount; private int interval; private int topFixedCount; private int bottomFixedCount; private int firstIndex = 0; private int keepVisibleIndex = -1; /** * Registers a menu to be scrolled with the default number of items to * display at a time and the default scrolling interval. * * @param menu the menu * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JMenu menu) { return new JMenuScroller(menu); } /** * Registers a popup menu to be scrolled with the default number of items to * display at a time and the default scrolling interval. * * @param menu the popup menu * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JPopupMenu menu) { return new JMenuScroller(menu); } /** * Registers a menu to be scrolled with the default number of items to * display at a time and the specified scrolling interval. * * @param menu the menu * @param scrollCount the number of items to display at a time * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount is 0 or negative */ public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount) { return new JMenuScroller(menu, scrollCount); } /** * Registers a popup menu to be scrolled with the default number of items to * display at a time and the specified scrolling interval. * * @param menu the popup menu * @param scrollCount the number of items to display at a time * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount is 0 or negative */ public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) { return new JMenuScroller(menu, scrollCount); } /** * Registers a menu to be scrolled, with the specified number of items to * display at a time and the specified scrolling interval. * * @param menu the menu * @param scrollCount the number of items to be displayed at a time * @param interval the scroll interval, in milliseconds * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval) { return new JMenuScroller(menu, scrollCount, interval); } /** * Registers a popup menu to be scrolled, with the specified number of items to * display at a time and the specified scrolling interval. * * @param menu the popup menu * @param scrollCount the number of items to be displayed at a time * @param interval the scroll interval, in milliseconds * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) { return new JMenuScroller(menu, scrollCount, interval); } /** * Registers a menu to be scrolled, with the specified number of items * to display in the scrolling region, the specified scrolling interval, * and the specified numbers of items fixed at the top and bottom of the * menu. * * @param menu the menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0. * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { return new JMenuScroller(menu, scrollCount, interval, topFixedCount, bottomFixedCount); } /** * Registers a popup menu to be scrolled, with the specified number of items * to display in the scrolling region, the specified scrolling interval, * and the specified numbers of items fixed at the top and bottom of the * popup menu. * * @param menu the popup menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0 * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { return new JMenuScroller(menu, scrollCount, interval, topFixedCount, bottomFixedCount); } /** * Constructs a JMenuScroller that scrolls a menu with the * default number of items to display at a time, and default scrolling * interval. * * @param menu the menu */ public JMenuScroller(JMenu menu) { this(menu, 15); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * default number of items to display at a time, and default scrolling * interval. * * @param menu the popup menu */ public JMenuScroller(JPopupMenu menu) { this(menu, 15); } /** * Constructs a JMenuScroller that scrolls a menu with the * specified number of items to display at a time, and default scrolling * interval. * * @param menu the menu * @param scrollCount the number of items to display at a time * @throws IllegalArgumentException if scrollCount is 0 or negative */ public JMenuScroller(JMenu menu, int scrollCount) { this(menu, scrollCount, 150); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * specified number of items to display at a time, and default scrolling * interval. * * @param menu the popup menu * @param scrollCount the number of items to display at a time * @throws IllegalArgumentException if scrollCount is 0 or negative */ public JMenuScroller(JPopupMenu menu, int scrollCount) { this(menu, scrollCount, 150); } /** * Constructs a JMenuScroller that scrolls a menu with the * specified number of items to display at a time, and specified scrolling * interval. * * @param menu the menu * @param scrollCount the number of items to display at a time * @param interval the scroll interval, in milliseconds * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public JMenuScroller(JMenu menu, int scrollCount, int interval) { this(menu, scrollCount, interval, 0, 0); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * specified number of items to display at a time, and specified scrolling * interval. * * @param menu the popup menu * @param scrollCount the number of items to display at a time * @param interval the scroll interval, in milliseconds * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public JMenuScroller(JPopupMenu menu, int scrollCount, int interval) { this(menu, scrollCount, interval, 0, 0); } /** * Constructs a JMenuScroller that scrolls a menu with the * specified number of items to display in the scrolling region, the * specified scrolling interval, and the specified numbers of items fixed at * the top and bottom of the menu. * * @param menu the menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0 * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative */ public JMenuScroller(JMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { this(menu.getPopupMenu(), scrollCount, interval, topFixedCount, bottomFixedCount); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * specified number of items to display in the scrolling region, the * specified scrolling interval, and the specified numbers of items fixed at * the top and bottom of the popup menu. * * @param menu the popup menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0 * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative */ public JMenuScroller(JPopupMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { if (scrollCount <= 0 || interval <= 0) { throw new IllegalArgumentException("scrollCount and interval must be greater than 0"); } if (topFixedCount < 0 || bottomFixedCount < 0) { throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative"); } upItem = new MenuScrollItem(UP, -1); downItem = new MenuScrollItem(DOWN, +1); setScrollCount(scrollCount); setInterval(interval); setTopFixedCount(topFixedCount); setBottomFixedCount(bottomFixedCount); this.menu = menu; menu.addPopupMenuListener(menuListener); } /** * Returns the scroll interval in milliseconds * * @return the scroll interval in milliseconds */ public int getInterval() { return interval; } /** * Sets the scroll interval in milliseconds * * @param interval the scroll interval in milliseconds * @throws IllegalArgumentException if interval is 0 or negative */ public void setInterval(int interval) { if (interval <= 0) { throw new IllegalArgumentException("interval must be greater than 0"); } upItem.setInterval(interval); downItem.setInterval(interval); this.interval = interval; } /** * Returns the number of items in the scrolling portion of the menu. * * @return the number of items to display at a time */ public int getscrollCount() { return scrollCount; } /** * Sets the number of items in the scrolling portion of the menu. * * @param scrollCount the number of items to display at a time * @throws IllegalArgumentException if scrollCount is 0 or negative */ public void setScrollCount(int scrollCount) { if (scrollCount <= 0) { throw new IllegalArgumentException("scrollCount must be greater than 0"); } this.scrollCount = scrollCount; // XXX the following line closes all menus then this menu is made. // That doesn't seem right. // MenuSelectionManager.defaultManager().clearSelectedPath(); } /** * Returns the number of items fixed at the top of the menu or popup menu. * * @return the number of items */ public int getTopFixedCount() { return topFixedCount; } /** * Sets the number of items to fix at the top of the menu or popup menu. * * @param topFixedCount the number of items */ public void setTopFixedCount(int topFixedCount) { if (firstIndex <= topFixedCount) { firstIndex = topFixedCount; } else { firstIndex += (topFixedCount - this.topFixedCount); } this.topFixedCount = topFixedCount; } /** * Returns the number of items fixed at the bottom of the menu or popup menu. * * @return the number of items */ public int getBottomFixedCount() { return bottomFixedCount; } /** * Sets the number of items to fix at the bottom of the menu or popup menu. * * @param bottomFixedCount the number of items */ public void setBottomFixedCount(int bottomFixedCount) { this.bottomFixedCount = bottomFixedCount; } /** * Scrolls the specified item into view each time the menu is opened. Call this method with * null to restore the default behavior, which is to show the menu as it last * appeared. * * @param item the item to keep visible * @see #keepVisible(int) */ public void keepVisible(JMenuItem item) { if (item == null) { keepVisibleIndex = -1; } else { int index = menu.getComponentIndex(item); keepVisibleIndex = index; } } /** * Scrolls the item at the specified index into view each time the menu is opened. Call this * method with -1 to restore the default behavior, which is to show the menu as * it last appeared. * * @param index the index of the item to keep visible * @see #keepVisible(javax.swing.JMenuItem) */ public void keepVisible(int index) { keepVisibleIndex = index; } /** * Removes this JMenuScroller from the associated menu and restores the * default behavior of the menu. */ public void dispose() { if (menu != null) { menu.removePopupMenuListener(menuListener); menu = null; } } /** * Ensures that the dispose method of this JMenuScroller is * called when there are no more refrences to it. * * @exception Throwable if an error occurs. * @see JMenuScroller#dispose() */ @Override public void finalize() throws Throwable { dispose(); } private void refreshMenu() { if (menuItems != null && menuItems.length > 0) { firstIndex = Math.max(topFixedCount, firstIndex); firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex); upItem.setEnabled(firstIndex > topFixedCount); downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount); menu.removeAll(); for (int i = 0; i < topFixedCount; i++) { menu.add(menuItems[i]); } if (topFixedCount > 0) { menu.addSeparator(); } menu.add(upItem); for (int i = firstIndex; i < scrollCount + firstIndex; i++) { menu.add(menuItems[i]); } menu.add(downItem); if (bottomFixedCount > 0) { menu.addSeparator(); } for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) { menu.add(menuItems[i]); } JComponent parent = (JComponent) upItem.getParent(); parent.revalidate(); parent.repaint(); } } private class MenuScrollListener implements PopupMenuListener { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { if (fillMenu != null) { clearPopupMenu(menu); callF(fillMenu, menu); } setMenuItems(); } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { if (fillMenu != null) clearPopupMenu(menu); else restoreMenuItems(); } @Override public void popupMenuCanceled(PopupMenuEvent e) { if (fillMenu != null) clearPopupMenu(menu); else restoreMenuItems(); } private void setMenuItems() { menuItems = menu.getComponents(); if (keepVisibleIndex >= topFixedCount && keepVisibleIndex <= menuItems.length - bottomFixedCount && (keepVisibleIndex > firstIndex + scrollCount || keepVisibleIndex < firstIndex)) { firstIndex = Math.min(firstIndex, keepVisibleIndex); firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1); } if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) { refreshMenu(); } } private void restoreMenuItems() { menu.removeAll(); for (Component component : menuItems) { menu.add(component); } } } private class MenuScrollTimer extends javax.swing.Timer { public MenuScrollTimer(final int increment, int interval) { super(interval, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { firstIndex += increment; refreshMenu(); } }); } } private class MenuScrollItem extends JMenuItem implements ChangeListener { private MenuScrollTimer timer; public MenuScrollItem(MenuIcon icon, int increment) { setIcon(icon); setDisabledIcon(icon); timer = new MenuScrollTimer(increment, interval); addChangeListener(this); } public void setInterval(int interval) { timer.setDelay(interval); } @Override public void stateChanged(ChangeEvent e) { if (isArmed() && !timer.isRunning()) { timer.start(); } if (!isArmed() && timer.isRunning()) { timer.stop(); } } } static MenuIcon UP = new MenuIcon(9, 1, 9); static MenuIcon DOWN = new MenuIcon(1, 9, 1); private static class MenuIcon implements Icon { final int[] xPoints = {1, 5, 9}; final int[] yPoints; MenuIcon(int... yPoints) { this.yPoints = yPoints; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { Dimension size = c.getSize(); Graphics g2 = g.create(size.width / 2 - 5, size.height / 2 - 5, 10, 10); g2.setColor(Color.GRAY); g2.drawPolygon(xPoints, yPoints, 3); if (c.isEnabled()) { g2.setColor(Color.BLACK); g2.fillPolygon(xPoints, yPoints, 3); } g2.dispose(); } @Override public int getIconWidth() { return 0; } @Override public int getIconHeight() { return 10; } } } static class Pt implements Comparable { int x, y; Pt() {} Pt(Point p) { x = p.x; y = p.y; } Pt(int x, int y) { this.y = y; this.x = x;} Point getPoint() { return new Point(x, y); } public boolean equals(Object o) { return o instanceof Pt && x == ((Pt) o).x && y == ((Pt) o).y; } public int hashCode() { return boostHashCombine(x, y); } // compare in scan order public int compareTo(Pt p) { if (y != p.y) return cmp(y, p.y); return cmp(x, p.x); } public String toString() { return x + ", " + y; } } static class ProgramScan { static int threads = isWindows() ? 500 : 10; static int timeout = 5000; // hmm... static String ip = "127.0.0.1"; // This range is not used anymore anyway static int quickScanFrom = 10000, quickScanTo = 10999; static int maxNumberOfVMs_android = 4; // Android will always only have one if we don't screw up static int maxNumberOfVMs_nonAndroid = 50; // 100; static int maxNumberOfVMs; static boolean verbose = false; static class Program { int port; String helloString; Program(int port, String helloString) { this.helloString = helloString; this.port = port;} } static List scan() { try { return scan(1, 65535); } catch (Exception __e) { throw rethrow(__e); } } static List scan(int fromPort, int toPort) { return scan(fromPort, toPort, new int[0]); } static List scan(int fromPort, int toPort, int[] preferredPorts) { try { Set preferredPortsSet = new HashSet(asList(preferredPorts)); int scanSize = toPort-fromPort+1; String name = toPort < 10000 ? "bot" : "program"; int threads = isWindows() ? min(500, scanSize) : min(scanSize, 10); final ExecutorService es = Executors.newFixedThreadPool(threads); if (verbose) print(firstToUpper(name) + "-scanning " + ip + " with timeout " + timeout + " ms in " + threads + " threads."); startTiming(); List> futures = new ArrayList(); List ports = new ArrayList(); for (int port : preferredPorts) { futures.add(checkPort(es, ip, port, timeout)); ports.add(port); } for (int port = fromPort; port <= toPort; port++) if (!preferredPortsSet.contains(port) && !forbiddenPort(port)) { futures.add(checkPort(es, ip, port, timeout)); ports.add(port); } es.shutdown(); List programs = new ArrayList(); long time = now(); int i = 0; for (final Future f : futures) { if (verbose) print("Waiting for port " + get(ports, i++) + " at time " + (now()-time)); Program p = f.get(); if (p != null) programs.add(p); } //stopTiming("Port Scan " + scanSize + ", " + n(threads, "threads") + ": ", 250); if (verbose) print("Found " + programs.size() + " " + name + "(s) on " + ip); return programs; } catch (Exception __e) { throw rethrow(__e); } } static Future checkPort(final ExecutorService es, final String ip, final int port, final int timeout) { return es.submit(new Callable() { @Override public Program call() { try { Socket socket = new Socket(); try { socket.setSoTimeout(timeout); socket.connect(new InetSocketAddress(ip, port), timeout); //if (verbose) print("Connected to " + ip + ":" + port); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); String hello = or(in.readLine(), "?"); return new Program(port, hello); } finally { socket.close(); } } catch (Exception ex) { return null; } } }); } static List quickScan() { return scan(quickScanFrom, quickScanTo); } static List quickBotScan() { return quickBotScan(new int[0]); } static List quickBotScan(int[] preferredPorts) { if (maxNumberOfVMs == 0) maxNumberOfVMs = isAndroid() ? maxNumberOfVMs_android : maxNumberOfVMs_nonAndroid; return scan(4999, 5000+maxNumberOfVMs-1, preferredPorts); } } static class ConceptFieldIndex extends ConceptFieldIndexBase { ConceptFieldIndex(Class cc, String field) { super(cc, field); } ConceptFieldIndex(Concepts concepts, Class cc, String field) { super(concepts, cc, field); } void init() { valueToObject = new MultiSetMap(); } void register() { concepts.addFieldIndex(cc, field, this); } } abstract static class AbstractLayoutManager implements LayoutManager { Dimension preferredSize = new Dimension(100, 100); Dimension minimumSize = new Dimension(10, 10); public void addLayoutComponent(String name, Component comp) {} public void removeLayoutComponent(Component comp) {} public Dimension preferredLayoutSize(Container parent) { layoutContainer(parent); //print("preferredSize: " + preferredSize + " (parent size: " + jGetSize(parent) + ")"); return preferredSize; } public Dimension minimumLayoutSize(Container parent) { //print("minimumLayoutSize"); return minimumSize; } } static class Snippet { String id, title, md5, type, text; boolean isPublic = false; Snippet() {} Snippet(String id, String title) { this.title = title; this.id = id;} Snippet(String id, String title, String md5) { this.md5 = md5; this.title = title; this.id = id;} public String toString() { return id + " - " + title; } public boolean equals(Object o) { return stdEq2(this, o); } public int hashCode() { return stdHash2(this); } } abstract static class RandomAccessAbstractList extends AbstractList implements RandomAccess { } static class RGB { public float r, g, b; // can't be final cause persistence RGB() {} 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[] rgb) { this(rgb[0], rgb[1], rgb[2]); } public RGB(int rgb) { this(new Color(rgb)); } public RGB(double brightness) { this.r = this.g = this.b = max(0f, min(1f, (float) brightness)); } public RGB(Color color) { this.r = color.getRed()/255f; this.g = color.getGreen()/255f; this.b = color.getBlue()/255f; } // TODO: 3-char version public RGB(String hex) { int i = l(hex)-6; r = Integer.parseInt(hex.substring(i, i+2), 16)/255f; g = Integer.parseInt(hex.substring(i+2, i+4), 16)/255f; b = Integer.parseInt(hex.substring(i+4, i+6), 16)/255f; } public float getComponent(int i) { return i == 0 ? r : i == 1 ? g : b; } public int getInt(int i) { return i == 0 ? redInt() : i == 1 ? greenInt() : blueInt(); } 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))); } int asInt() { return getColor().getRGB() & 0xFFFFFF; } int getInt() { return getColor().getRGB() & 0xFFFFFF; } int asIntWithAlpha() { return rgbInt(redInt(), greenInt(), blueInt()) | 0xFF000000; } 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(); } int redInt() { return iround(r*255); } int greenInt() { return iround(g*255); } int blueInt() { return iround(b*255); } } static interface MakesBufferedImage extends WidthAndHeight { BufferedImage getBufferedImage(); public default void drawAt(Graphics2D g, int x, int y) { g.drawImage(getBufferedImage(), x, y, null); } } // optimized for search - O(1) for searching for a certain element // set is O(log m) with m = number of occurrences of element final static class ContentsIndexedList extends RandomAccessAbstractList implements IContentsIndexedList, IContentsIndexedList2 { Map>> index = new HashMap(); // tokens by contents, sorted by index final ArrayList> list = new ArrayList(); final static class Elem extends HasIndex { A s; // actual token public String toString() { return "Elem " + quote(s) + "@" + idx; } } ContentsIndexedList() {} ContentsIndexedList(Map>> index) { this.index = index;} // use different type of index (e.g. ciMap) ContentsIndexedList(Collection l) { addAll(l); } public A get(int i) { return list.get(i).s; } public int size() { return list.size(); } public A set(int i, A s) { Elem t = list.get(i); A old = t.s; if (eq(old, s)) return old; removeFromIdx(t); t.s = s; addToIdx(t); return old; } public boolean add(A s) { ++modCount; Elem t = new Elem(); t.s = s; t.idx = size(); list.add(t); addToIdx(t); return true; } public void add(int i, A s) { ++modCount; Elem t = new Elem(); t.s = s; t.idx = i; list.add(i, t); reorder(i+1); addToIdx(t); } public boolean addAll(int i, Collection l) { int n = l.size(); if (n == 0) return false; ++modCount; List> l2 = emptyList(n); int j = i; for (A s : l) { Elem t = new Elem(); t.s = s; t.idx = j++; l2.add(t); } list.addAll(i, l2); reorder(i+n); for (Elem t : l2) addToIdx(t); return true; } public A remove(int i) { ++modCount; Elem t = list.get(i); removeFromIdx(t); list.remove(i); reorder(i); return t.s; } void reorder(int fromIdx) { int n = size(); for (int i = fromIdx; i < n; i++) list.get(i).idx = i; } void removeFromIdx(Elem t) { TreeSet> idx = index.get(t.s); idx.remove(t); if (idx.isEmpty()) index.remove(t.s); } void addToIdx(Elem t) { TreeSet> idx = index.get(t.s); if (idx == null) index.put(t.s, idx = new TreeSet()); idx.add(t); } @Override public int indexOf(Object s) { TreeSet> l = index.get(s); return l == null ? -1 : first(l).idx; } @Override public int lastIndexOf(Object s) { TreeSet> l = index.get(s); return l == null ? -1 : last(l).idx; } @Override public boolean contains(Object s) { return index.containsKey(s); } public void clear() { ++modCount; index.clear(); list.clear(); } protected void removeRange(int fromIndex, int toIndex) { if (fromIndex == toIndex) return; ++modCount; for (int i = fromIndex; i < toIndex; i++) removeFromIdx(list.get(i)); list.subList(fromIndex, toIndex).clear(); reorder(fromIndex); } public int[] indicesOf(Object o) { TreeSet> idx = index.get(o); if (idx == null) return emptyIntArray(); int[] a = new int[idx.size()]; int i = 0; for (Elem t : idx) a[i++] = t.idx; return a; } public TreeSet indicesOf_treeSetOfHasIndex(Object o) { return (TreeSet) index.get(o); } } static class CancelPoint implements AutoCloseable { volatile boolean closed = false; public void close() { closed = true; } } static class ExpiringMap2 extends AbstractMap { Map> byKey = new HashMap(); // key -> pair(expiry sys time, value) PriorityBlockingQueue> queue = new PriorityBlockingQueue(); // queue(pair(expiry sys time, key)) long standardExpiryTime; // ms boolean renewOnOverwrite = true, renewOnGet; Object onChange; boolean useCountdown = true; RestartableCountdown countdown = new RestartableCountdown(); long nextClean; ExpiringMap2() {} ExpiringMap2(long standardExpiryTime) { this.standardExpiryTime = standardExpiryTime;} ExpiringMap2(long standardExpiryTime, Object onChange) { this.onChange = onChange; this.standardExpiryTime = standardExpiryTime;} ExpiringMap2(double standardExpirySeconds) { standardExpiryTime = toMS(standardExpirySeconds); } synchronized boolean clean() { boolean changes = false; Pair p; long time = sysTime(); while ((p = queue.peek()) != null && time >= p.a) { if (useCountdown) { countdown.stop(); nextClean = 0; } p = queue.poll(); Pair v = byKey.get(p.b); if (v != null /*&& v.a == p.a*/) { byKey.remove(p.b); changes = true; change(); } } if (useCountdown) { long needClean = p != null ? p.a : 0; if (needClean != nextClean) { nextClean = p.a; countdown.start(nextClean-time, new Runnable() { public void run() { try { clean(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "clean();"; }}); } } return changes; } void change() { callF(onChange); } synchronized public B put(A a, B b) { clean(); long timeout = sysTime()+standardExpiryTime; Pair p = byKey.get(a); if (p != null && renewOnOverwrite) queue.remove(new Pair(p.a, a)); byKey.put(a, pair(timeout, b)); change(); if (p == null || renewOnOverwrite) queue.add(new Pair(timeout, a)); return pairB(p); } synchronized public B remove(Object a) { clean(); Pair p = byKey.get(a); if (p == null) return null; queue.remove(new Pair(p.a, a)); byKey.remove(a); change(); return p.b; } synchronized public B get(Object a) { clean(); Pair p = byKey.get(a); if (renewOnGet && p != null) { queue.remove(new Pair(p.a, a)); long timeout = sysTime()+standardExpiryTime; byKey.put((A) a, pair(timeout, p.b)); queue.add(new Pair(timeout, a)); } return pairB(p); } synchronized public Set> entrySet() { clean(); // TODO: mutex return synchronizedSet(mapValues(__134 -> pairB(__134), byKey).entrySet()); } synchronized public Set keySet() { clean(); return synchronizedSet(byKey.keySet()); } synchronized public int size() { clean(); return byKey.size(); } void setStandardExpiryTime(long ms) { standardExpiryTime = ms; } synchronized ExpiringMap2 setMap(Map innerMap) { byKey = innerMap; return this; } } // see https://howtodoinjava.com/java/regex/java-regex-validate-international-phone-numbers/ static class InternationalPhoneValidator { String rawInput; String filtered; List dialCodes; // might be multiple countries with same code String countryPart, localPart; boolean valid = false; String error; InternationalPhoneValidator() {} InternationalPhoneValidator(String rawInput) { this.rawInput = rawInput;} // needs rawInput public void run() { filtered = dropPrefix("+", dropSpecialChars(rawInput)); if (!isAllDigits(filtered)) { error = "Bad characters"; return; } splitLocal(); } String dropSpecialChars(String s) { return replaceAll(s, "[\\s\\(\\)\\-]", ""); } void splitLocal() { String countryPart = longestPrefixInNavigableSet(filtered, navigableKeys(countryDialCodes_rawNumbersTreeMultiMap())); if (countryPart == null) { error = "Invalid country code"; return; } dialCodes = countryDialCodes_rawNumbersTreeMultiMap().get(countryPart); localPart = dropPrefix(countryPart, filtered); if (l(filtered) < 7) { error = "Phone number too short"; return; } else if (l(filtered) > 15) { error = "Phone number too long"; return; } valid = true; } } static class Cache { Object maker; // func -> A A value; long loaded; static boolean debug = false; long changeCount; Lock lock = lock(); Cache() {} Cache(Object maker) { this.maker = maker;} Cache(IF0 maker) { this.maker = maker;} A get() { if (hasLock(lock)) return value; // Must be called from within maker Lock __0 = lock; lock(__0); try { if (loaded == 0) { value = make(); changeCount++; loaded = sysNow(); } return value; } finally { unlock(__0); } } void clear() { Lock __1 = lock; lock(__1); try { if (debug && loaded != 0) print("Clearing cache"); value = null; changeCount++; loaded = 0; } finally { unlock(__1); } } // clear if older than x seconds // 0 does not do anything void clear(double seconds) { Lock __2 = lock; lock(__2); try { if (seconds != 0 && loaded != 0 && sysNow() >= loaded+seconds*1000) clear(); } finally { unlock(__2); } } // override void set(A a) { Lock __3 = lock; lock(__3); try { value = a; ++changeCount; loaded = sysNow(); } finally { unlock(__3); } } A make() { return (A) callF(maker); } } static class QuickException extends RuntimeException { public Throwable fillInStackTrace() { return this; } QuickException() {} QuickException(Throwable e) { super(e); } QuickException(String msg) { super(msg); } QuickException(String msg, Throwable e) { super(msg, e); } } static interface IContentsIndexedList2 extends List { TreeSet indicesOf_treeSetOfHasIndex(Object o); } static class MinimalChain implements Iterable { A element; MinimalChain next; MinimalChain() {} MinimalChain(A element) { this.element = element;} MinimalChain(A element, MinimalChain next) { this.next = next; this.element = element;} public String toString() { return str(toList()); } ArrayList toList() { ArrayList l = new ArrayList(); MinimalChain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } void setElement(A a) { element = a; } void setNext(MinimalChain next) { this.next = next; } // TODO: optimize public Iterator iterator() { return toList().iterator(); } A get() { return element; } } static class RestartableCountdown implements AutoCloseable { java.util.Timer timer; // stops the countdown and restarts it synchronized void start(long delayMS, Object action) { stop(); timer = doLater_daemon(delayMS, action); } void start(double delaySeconds, Object action) { start(toMS(delaySeconds), action); } synchronized void stop() { cancelTimer(timer); timer = null; } public void close() { stop(); } } static interface IContentsIndexedList extends List { int[] indicesOf(Object o); } static interface WidthAndHeight { int getWidth(); int getHeight(); } static class HasIndex implements Comparable { int idx; HasIndex() {} HasIndex(int idx) { this.idx = idx;} public int compareTo(HasIndex h) { return idx-h.idx; } } static Map prependEmptyOptionForHSelect(Map map) { Map map2 = litorderedmap("" , ""); putAll(map2, map); return map2; } static LinkedHashMap mapToOrderedMap(Object f, Iterable l) { LinkedHashMap map = new LinkedHashMap(); for (Object o : unnullForIteration(l)) { Pair p = (Pair) (callF(f, o)); map.put(p.a, p.b); } return map; } static LinkedHashMap mapToOrderedMap(IF1> f, Iterable l) { LinkedHashMap map = new LinkedHashMap(); for (A o : unnullForIteration(l)) { Pair p = callF(f, o); map.put(p.a, p.b); } return map; } static LinkedHashMap mapToOrderedMap(Iterable l, IF1> f) { return mapToOrderedMap(f, l); } static boolean conceptsSortedByFieldCI_verbose = false; static Collection conceptsSortedByFieldCI(Class c, String field) { return conceptsSortedByFieldCI(db_mainConcepts(), c, field); } static Collection conceptsSortedByFieldCI(Concepts concepts, Class c, String field) { // indexed IFieldIndex index = concepts.getCIFieldIndex(c, field); if (index instanceof ConceptFieldIndexCI) return (Collection) asList(((ConceptFieldIndexCI) index).objectIterator()); // sort manually if (conceptsSortedByFieldCI_verbose) print("conceptsSortedByFieldCI_verbose: Manual sort of " + c + " for " + field); return sortedByFieldIC(field, concepts.list(c)); } static String hquery(Map params) { return htmlQuery(params); } static String hquery(Object... data) { return htmlQuery(data); } static String loadJQuery() { return ""; } static boolean conceptsWithFieldGreaterThan_verbose = false; static Collection conceptsWithFieldGreaterThan(Class c, String field, Object value) { return conceptsWithFieldGreaterThan(db_mainConcepts(), c, field, value); } static Collection conceptsWithFieldGreaterThan(Concepts concepts, Class c, String field, Object value) { // indexed IFieldIndex index = concepts.getFieldIndex(c, field); if (index instanceof ConceptFieldIndexDesc) return ((ConceptFieldIndexDesc) index).objectsWithValueGreaterThan(value); // filter manually if (conceptsWithFieldGreaterThan_verbose) print("conceptsWithFieldGreaterThan: table scan of " + c + " for field " + field); return objectsWhereFieldGreaterThan(concepts.list(c), field, value); } static String hhiddenWithID(String id) { return hhiddenWithID(id, null); } static String hhiddenWithID(String id, Object value, Object... params) { return tag("input", "", paramsPlus(params, "type" , "hidden", "id", id, "value", value)); } static String hsimpletableheader(String... cols) { return tag("tr", join(lambdaMap(__135 -> th(__135), cols))); } static List lastTwo(List l) { return takeLast(2, l); } static String hparagraphs(Collection l) { return lines(lambdaMap(__136 -> p(__136), l)); } static String hscript_clickableRows() { return hscript("\r\n jQuery(document).ready(function($) {\r\n $(\".clickable-row\").click(function() {\r\n window.location = $(this).data(\"href\");\r\n });\r\n });\r\n "); } static String hdivWithID(String id, Object contents, Object... params) { return hdiv(contents, paramsPlus(params, "id", id)); } // requires JQuery static String hdesktopNotifications() { return hscript("\r\n function sendDesktopNotification(text, options) {\r\n if (\"Notification\" in window && Notification.permission === \"granted\")\r\n new Notification(text, options);\r\n }\r\n window.sendDesktopNotification = sendDesktopNotification;\r\n \r\n function initDesktopNotifications() {\r\n if (!(\"Notification\" in window))\r\n $(\"#notiStatus\").html(\"Desktop notifications not supported in this browser\");\r\n else if (Notification.permission === \"granted\")\r\n $(\"#notiStatus\").html(\"Desktop notifications enabled\");\r\n else if (Notification.permission === \"denied\")\r\n $(\"#notiStatus\").html(\"Desktop notifications denied\");\r\n else {\r\n $(\"#notiStatus\").html(\"Requesting permission for desktop notifications\");\r\n Notification.requestPermission().then(function (permission) {\r\n initDesktopNotifications();\r\n sendDesktopNotification(\"Notifications will look like this!\");\r\n });\r\n }\r\n }\r\n \r\n $(document).ready(initDesktopNotifications);\r\n "); } static String hframeset_cols(String cols, Object contents, Object... params) { return tag("frameset", contents, paramsPlus(params, "cols", cols)); } static String hframeset_rows(String rows, Object contents, Object... params) { return tag("frameset", contents, paramsPlus(params, "rows", rows)); } static String htmlEncode2If(boolean b, String s) { return b ? htmlEncode2(s) : s; } static JPanel centerAndSouthWithMargins(Component c, Component s) { return applyDefaultMargin(centerAndSouth(c, withTopMargin(s))); } static JPanel centerAndSouthWithMargins(int margin, Component c, Component s) { return applyMargin(margin, centerAndSouth(c, withTopMargin(margin, s))); } static SingleComponentPanel singleComponentPanel() { return singleComponentPanel(null); } static SingleComponentPanel singleComponentPanel(final Component c) { return swing(new F0() { public SingleComponentPanel get() { try { return new SingleComponentPanel(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret new SingleComponentPanel(c);"; }}); } static JPanel jrightalignedline(Component... components) { return jrightAlignedLine(components); } static JPanel jrightalignedline(List components) { return jrightAlignedLine(components); } static JPanel centerAndEastWithMargin(Component c, Component e) { return centerAndEastWithMarginInbetween(c, e); } static JCheckBox dm_fieldCheckBox(String field) { return dm_fieldCheckBox(humanizeFormLabel(field), field); } static JCheckBox dm_fieldCheckBox(String text, String field) { // magic parameter swap :) if (isIdentifier(text) && !isIdentifier(field)) { String temp = field; field = text; text = temp; } return jLiveValueCheckBox(text, dm_fieldLiveValue(field)); } static void addComponentsFirst(Container c, Component... l) { containerAddFirst(c, l); } static void scpSet(SingleComponentPanel scp, Component c) { setSCPComponent(scp, c); } static Border getBorder(final JComponent c) { return c == null ? null : swing(new F0() { public Border get() { try { return c.getBorder(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getBorder();"; }}); } static Type getRawType(Type t) { if (t instanceof ParameterizedType) return ((ParameterizedType) t).getRawType(); return t; } static int hashOfLong(long l) { return Long.hashCode(l); } static String getSnippetTitle_safe(String id) { if (isLocalSnippetID(id)) return localSnippetTitle(id); String title = or(loadTextFile(snippetTitle_cacheFile(id)), "?"); try { title = getSnippetTitle(id); } catch (Throwable __e) { _handleException(__e); } return title; } static void setComponent(SingleComponentPanel scp, Component c) { setSCPComponent(scp, c); } static void setComponent(SingleComponentPanel scp, IF0 c) { if (scp != null) setComponent(scp, callF(c)); } static void removeAll(Collection a, Collection b) { if (a != null && b != null) a.removeAll(b); } static void removeAll(Map a, Collection b) { if (a != null && b != null) for (A x : b) a.remove(x); } static boolean removeAll(Collection c, B... b) { return c != null && b != null && c.removeAll(Arrays.asList(b)); } static void removeAll(Map a, A... b) { if (a != null && b != null) for (A x : b) a.remove(x); } static Container _getParent(Component c) { return getParent(c); } static String quoteBorderless(Object o) { if (o == null) return "null"; return quoteBorderless(str(o)); } static String quoteBorderless(String s) { if (s == null) return "null"; StringBuilder out = new StringBuilder((int) (l(s)*1.5)); quoteBorderless_impl(s, out); return out.toString(); } static void quoteBorderless_impl(String s, StringBuilder out) { int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') out.append('\\').append(c); else if (c == '\r') out.append("\\r"); else if (c == '\n') out.append("\\n"); else out.append(c); } } static void clearPopupMenu(final JPopupMenu menu) { if (menu != null) { swing(new Runnable() { public void run() { try { menu.removeAll(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "menu.removeAll();"; }}); } } public static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } static long stopTiming_defaultMin = 10; static long startTiming_startTime; static void startTiming() { startTiming_startTime = now(); } static void stopTiming() { stopTiming(null); } static void stopTiming(String text) { stopTiming(text, stopTiming_defaultMin); } static void stopTiming(String text, long minToPrint) { long time = now()-startTiming_startTime; if (time >= minToPrint) { text = or2(text, "Time: "); print(text + time + " ms"); } } static int rgbInt(int r, int g, int b) { return (clamp(r, 0, 255) << 16) | (clamp(g, 0, 255) << 8) | clamp(b, 0, 255); } static int asInt(Object o) { return toInt(o); } static long sysTime() { return sysNow(); } 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 Map mapValues(Map map, IF1 f) { return mapValues(f, map); } static Map mapValues(IF1 f, Map map) { Map m = similarEmptyMap(map); for (Map.Entry __0 : _entrySet( map)) { A key = __0.getKey(); B val = __0.getValue(); m.put(key, f.get(val)); } return m; } static Map mapValues(Map map, Object func) { return mapValues(func, map); } static boolean isAllDigits(String s) { int n = l(s); for (int i = 0; i < n; i++) if (!isDigit(s.charAt(i))) return false; return true; } static List replaceAll(List l, final A a, final A b) { return map(l, new F1() { public A get(A x) { try { return eq(x, a) ? b : x; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "eq(x, a) ? b : x"; }}); } static String replaceAll(String s, String a, String b) { return s == null ? null : s.replaceAll(a, b); } // return longest prefix of s actually contained in set static String longestPrefixInNavigableSet(String s, NavigableSet set) { if (set == null || s == null) return null; while (licensed()) { String key = set.floor(s); if (key == null) break; // s is in front of whole set => no prefix in there int n = lCommonPrefix(key, s); if (n == l(key)) return key; // found! s = takeFirst(s, n); // shorten and try again } return null; // not found } static Cache> countryDialCodes_rawNumbersTreeMultiMap_cache = new Cache<>(() -> countryDialCodes_rawNumbersTreeMultiMap_load()); static MultiMap countryDialCodes_rawNumbersTreeMultiMap() { return countryDialCodes_rawNumbersTreeMultiMap_cache.get(); } static MultiMap countryDialCodes_rawNumbersTreeMultiMap_load() { return mapToMultiMap( mapKeys(__137 -> digitsOnly(__137), multiMapToMap(countryDialCodesMultiMap()))); } static boolean hasLock(Lock lock) { return ((ReentrantLock) lock).isHeldByCurrentThread(); } static java.util.Timer doLater_daemon(long delay, final Object r) { final java.util.Timer timer = new java.util.Timer(true); timer.schedule(timerTask(r, timer), delay); return timer; } static java.util.Timer doLater_daemon(double delaySeconds, final Object r) { return doLater_daemon(toMS(delaySeconds), r); } static List sortedByFieldIC(Collection c, final String field) { List l = new ArrayList(c); sort(l, new Comparator() { public int compare(A a, A b) { return compareIC((String) getOpt(a, field), (String) getOpt(b, field)); } }); return l; } static List sortedByFieldIC(String field, Collection c) { return sortedByFieldIC(c, field); } static String th(Object contents, Object... params) { return tag("th", contents, params); } static A applyDefaultMargin(final A c) { if (c != null) { swing(new Runnable() { public void run() { try { c.setBorder(BorderFactory.createEmptyBorder(withMargin_defaultWidth, withMargin_defaultWidth, withMargin_defaultWidth, withMargin_defaultWidth)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "c.setBorder(BorderFactory.createEmptyBorder(withMargin_defaultWidth, withMarg..."; }}); } return c; } static JPanel centerAndSouth(final Component c, final Component s) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.CENTER, wrap(c)); if (s != null) panel.add(BorderLayout.SOUTH, wrap(s)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new BorderLayout);\r\n panel.add(BorderLayout.CENT..."; }}); } static int withTopMargin_defaultWidth = 6; static JPanel withTopMargin(Component c) { return withTopMargin(withTopMargin_defaultWidth, c); } static JPanel withTopMargin(final int w, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createEmptyBorder(w, 0, 0, 0)); p.add(c); return p; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel p = new JPanel(new BorderLayout);\r\n p.setBorder(BorderFactory.creat..."; }}); } static A applyMargin(int top, int left, int bottom, int right, A c) { return addMargin(top, left, bottom, right, c); } static A applyMargin(int w, A c) { return addMargin(w, c); } static JPanel jrightAlignedLine(final Component... components) { return swing(new F0() { public RightAlignedLine get() { try { return new RightAlignedLine(components); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret RightAlignedLine(components);"; }}); } static JPanel jrightAlignedLine(List components) { return jrightAlignedLine(asArray(Component.class, components)); } static JPanel centerAndEastWithMarginInbetween(Component c, final Component e) { return centerAndEast(c, withLeftMargin(e)); } static JCheckBox jLiveValueCheckBox(String text, final SimpleLiveValue lv) { final JCheckBox cb = jCheckBox(text); bindCheckBoxToLiveValue(cb, lv); return cb; } static SimpleLiveValue dm_fieldLiveValue(String fieldName) { return dm_fieldLiveValue(dm_current_mandatory(), fieldName); } static SimpleLiveValue dm_fieldLiveValue(final DynModule module, final String fieldName) { Lock __0 = module.lock; lock(__0); try { AutoCloseable __2 = module.enter(); try { // for dm_watchField Class type = getFieldType(module, fieldName); final SimpleLiveValue value = new SimpleLiveValue(type, get(module, fieldName)); dm_watchField(fieldName, new Runnable() { public void run() { try { Object o = get(module, fieldName); value.set(o); ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef dm_fieldLiveValue_debug\r\n print(\"dm_fieldLiveValue: setting \" + fi..."; }}); value.onChange(new Runnable() { public void run() { try { module.setField(fieldName, value.get()); ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef dm_fieldLiveValue_debug\r\n print(\"dm_fieldLiveValue: setting 2 \" + ..."; }}); return value; } finally { _close(__2); }} finally { unlock(__0); } } static void containerAddFirst(final Container c, final Component... l) { if (c != null) { swing(new Runnable() { public void run() { try { for (int i = 0; i < l(l); i++) c.add(l[i], i); revalidate(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "for (int i = 0; i < l(l); i++)\r\n c.add(l[i], i);\r\n revalidate(c);"; }}); } } static void setSCPComponent(SingleComponentPanel scp, Component c) { if (scp != null) scp.setComponent(c); } static Container getParent(final Component c) { return c == null ? null : swing(new F0() { public Container get() { try { return c.getParent(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret c.getParent();"; }}); } static int lCommonPrefix(String a, String b) { int i = 0, n = Math.min(l(a), l(b)); while (i < n && a.charAt(i) == b.charAt(i)) ++i; return i; } static > MultiMap mapToMultiMap(Map map) { if (map == null) return null; MultiMap mm = similarEmptyMultiMap(map); for (Map.Entry> __0 : _entrySet( map)) { A key = __0.getKey(); Collection values = __0.getValue(); mm.putAll(key, values); } return mm; } static String digitsOnly(String s) { return filterChars(__138 -> isDigit(__138), s); } static int compareIC(String s1, String s2) { return compareIgnoreCase_jdk(s1, s2); } static A addMargin(final int top, final int left, final int bottom, final int right, final A c) { if (c != null) { swing(new Runnable() { public void run() { try { Border margin = BorderFactory.createEmptyBorder(top, left, bottom, right); c.setBorder(jCompoundBorder(c.getBorder(), margin)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Border margin = BorderFactory.createEmptyBorder(top, left, bottom, right);\r\n ..."; }}); } return c; } static A addMargin(int w, A c) { return addMargin(w, w, w, w, c); } static JPanel centerAndEast(final Component c, final Component e) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.CENTER, wrap(c)); panel.add(BorderLayout.EAST, wrap(e)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new BorderLayout);\r\n panel.add(BorderLayout.CENT..."; }}); } static int withLeftMargin_defaultWidth = 6; static JPanel withLeftMargin(Component c) { return withLeftMargin(withLeftMargin_defaultWidth, c); } static JPanel withLeftMargin(final int margin, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createEmptyBorder(0, margin, 0, 0)); p.add(c); return p; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel p = new JPanel(new BorderLayout);\r\n p.setBorder(BorderFactory.creat..."; }}); } static JCheckBox jCheckBox() { return swingNu(JCheckBox.class); } static JCheckBox jCheckBox(boolean checked) { return swingNu(JCheckBox.class, "", checked); } static JCheckBox jCheckBox(String text, boolean checked) { return swingNu(JCheckBox.class, text, checked); } static JCheckBox jCheckBox(String text) { return swingNu(JCheckBox.class, text); } static JCheckBox jCheckBox(String text, boolean checked, final Object onChange) { JCheckBox cb = jCheckBox(checked, onChange); cb.setText(text); return cb; } // onChange can be a Runnable or a voidfunc(bool) static JCheckBox jCheckBox(boolean checked, final Object onChange) { final JCheckBox cb = jCheckBox(checked); cb.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { pcallF(onChange, cb.isSelected()); } }); return cb; } static A bindCheckBoxToLiveValue(final A cb, final SimpleLiveValue lv) { bindLiveValueListenerToComponent(cb, lv, new Runnable() { public void run() { try { setChecked(cb, isTrue(lv.get())); ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef bindCheckBoxToLiveValue_debug\r\n print(\"bindCheckBoxToLiveValue: se..."; }}); onChange(cb, new Runnable() { public void run() { try { lv.set(isChecked(cb)); ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef bindCheckBoxToLiveValue_debug\r\n print(\"bindCheckBoxToLiveValue: se..."; }}); return cb; } static Class getFieldType(Object o, String field) { return fieldType(o, field); } static void dm_watchField(String field, Runnable onChange) { new Dyn_FieldWatcher(dm_current_mandatory(), field, onChange); } static MultiMap similarEmptyMultiMap(MultiMap m) { return similarEmptyMultiMap(m == null ? null : m.data); } static MultiMap similarEmptyMultiMap(Map m) { MultiMap mm = new MultiMap(); if (m != null) mm.data = similarEmptyMap(m); return mm; } static String filterChars(IF1 pred, String s) { if (s == null) return null; StringBuilder buf = new StringBuilder(); int n = l(s); char c; for (int i = 0; i < n; i++) if (isTrue(callF(pred, c = s.charAt(i)))) buf.append(c); return str(buf); } static int compareIgnoreCase_jdk(String s1, String s2) { if (s1 == null) return s2 == null ? 0 : -1; if (s2 == null) return 1; int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) return c1-c2; } } } return n1-n2; } static Border jCompoundBorder(Border inner, Border outer) { if (inner == null) return outer; if (outer == null) return inner; return BorderFactory.createCompoundBorder(inner, outer); } static void setChecked(JCheckBox checkBox, boolean b) { if (checkBox != null) { swing(new Runnable() { public void run() { try { if (isChecked(checkBox) != b) checkBox.setSelected(b); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (isChecked(checkBox) != b)\r\n checkBox.setSelected(b);"; }}); } } static void setChecked(JCheckBoxMenuItem mi, boolean b) { if (mi != null) { swing(new Runnable() { public void run() { try { mi.setSelected(b); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "mi.setSelected(b);"; }}); } } static class SimpleLiveValue extends LiveValue { Class type; volatile A value; transient List onChange = synchroList(); SimpleLiveValue(Class type) { this.type = type;} SimpleLiveValue(Class type, A value) { this.value = value; this.type = type;} public Class getType() { return type; } public A get() { return value; } public void onChange(Runnable l) { onChange.add(l); } public void onChangeAndNow(Runnable l) { onChange(l); callF(l); } public void removeOnChangeListener(Runnable l) { onChange.remove(l); } void fireChanged() { pcallFAll(onChange); } void set(A a) { if (neq(value, a)) { value = a; fireChanged(); } } } static class Dyn_FieldWatcher { DynModule module; String field; Object value; Runnable action; transient IF1 cloneValue; Object cloneValue(Object o) { return cloneValue != null ? cloneValue.get(o) : cloneValue_base(o); } final Object cloneValue_fallback(IF1 _f, Object o) { return _f != null ? _f.get(o) : cloneValue_base(o); } Object cloneValue_base(Object o) { return o; } Dyn_FieldWatcher(DynModule module, String field, Runnable action) { this(module, field, action, null); } Dyn_FieldWatcher(DynModule module, String field, Runnable action, IF1 cloneValue) { this.cloneValue = cloneValue; this.action = action; this.field = field; this.module = module; value = cloneValue(get(module, field)); // Let's do both for safety module.onChange(new Runnable() { public void run() { try { check(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "check();"; }}); module.onFieldChange(new VF1() { public void get(String f) { try { if (eq(f, field)) check(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(f, field))\r\n check();"; }}); } void check() { Object newValue = cloneValue(get(module, field)); if (eq(value, newValue)) return; value = newValue; dm_q(module, action); } } static class RightAlignedLine extends JPanel { RightAlignedLine(Component... components) { setLayout(LetterLayout.rightAlignedRow()); for (Component component : components) add(component); } public void add(String text) { add(new JLabel(text)); } } static class LetterLayout implements LayoutManager { private String[] lines; private Map map = new TreeMap(); private Map constraints = new HashMap(); private RC[] rows; private RC[] cols; private Cell[][] cells; private int spacingX = 10, spacingY = 10; private int insetTop, insetBottom, insetLeft, insetRight; private int template; private boolean formWideLeftSide, formWideRightSide; private static final int STALACTITE = 1, LEFT_ALIGNED_ROW = 2, CENTERED_ROW = 3, FORM = 4, RIGHT_ALIGNED_ROW = 5; private boolean debug = false; public void setLeftBorder(int border) { insetLeft = border; } public void setRightBorder(int border) { insetRight = border; } public static JComponent withBorder(JComponent component, int border) { JPanel panel = new JPanel(new LetterLayout("C").setBorder(border)); panel.add("C", component); return panel; } public static JPanel panel(String... lines) { return new JPanel(new LetterLayout(lines)); } public static JPanel stalactitePanel() { return new JPanel(stalactite()); } static class DummyComponent extends JComponent { } /** * info about one matrix cell */ static class Cell { boolean aux = false; // part of a larger cell, but not top-left corner int minWidth, minHeight; Component component; int colspan, rowspan; double weightX, weightY; } /** * info about one matrix row / column */ static class RC { int min; double weightSum; int start; int minEnd; } private LetterLayout(int template) { this.template = template; } public LetterLayout(String... lines) { this.lines = lines; } public void layoutContainer(Container container) { prepareLayout(container); // do layout if (debug) System.out.println("Container size: " + container.getSize()); Insets insets = getInsets(container); for (int r = 0; r < rows.length; r++) { for (int i = 0; i < cols.length;) { Cell cell = cells[i][r]; if (cell.aux) ++i; else { if (cell.component != null) { int x1 = cols[i].start; int y1 = rows[r].start; int x2 = i + cell.colspan < cols.length ? cols[i + cell.colspan].start - spacingX : container.getWidth() - insets.right; int y2 = r + cell.rowspan < rows.length ? rows[r + cell.rowspan].start - spacingY : container.getHeight() - insets.bottom; if (debug) System.out.println("Layouting ("+i+", "+r+", " + cell.component.getClass().getName() + "): "+x1+" "+y1+" "+x2+" "+y2); cell.component.setBounds(x1, y1, x2 - x1, y2 - y1); } i += cells[i][r].colspan; } } } } private void prepareLayout(Container container) { applyTemplate(container); int numRows = lines.length, numCols = lines[0].length(); for (int i = 1; i < numRows; i++) if (lines[i].length() != numCols) throw new IllegalArgumentException("Lines have varying length"); cells = new Cell[numCols][numRows]; rows = new RC[numRows]; cols = new RC[numCols]; for (int r = 0; r < numRows; r++) rows[r] = new RC(); for (int i = 0; i < numCols; i++) cols[i] = new RC(); for (int r = 0; r < numRows; r++) for (int i = 0; i < numCols; i++) cells[i][r] = new Cell(); // define cells for (int r = 0; r < numRows; r++) { String line = lines[r]; for (int i = 0; i < numCols;) { Cell cell = cells[i][r]; if (cell.aux) { ++i; continue; } char ch = line.charAt(i); int iNext = i; do ++iNext; while (iNext < numCols && ch == line.charAt(iNext)); int rNext = r; do ++rNext; while (rNext < numRows && ch == lines[rNext].charAt(i)); cell.weightX = numCols == 1 || iNext > i + 1 ? 1.0 : 0.0; cell.weightY = numRows == 1 || rNext > r + 1 ? 1.0 : 0.0; Component c = map.get(String.valueOf(ch)); cell.component = c; if (c != null) { cell.minWidth = c.getMinimumSize().width + spacingX; cell.minHeight = getMinimumHeight(c) + spacingY; } cell.colspan = iNext - i; cell.rowspan = rNext - r; if (cell.colspan == 1) cols[i].min = Math.max(cols[i].min, cell.minWidth); if (cell.rowspan == 1) rows[r].min = Math.max(rows[r].min, cell.minHeight); for (int r2 = r; r2 < rNext; r2++) for (int i2 = i; i2 < iNext; i2++) if (r2 != r || i2 != i) cells[i2][r2].aux = true; i = iNext; } } // determine minStarts, weightSums while (true) { for (int i = 0; i < numCols; i++) { int minStart = i == 0 ? 0 : cols[i - 1].minEnd; double weightStart = i == 0 ? 0.0 : cols[i - 1].weightSum; for (int r = 0; r < numRows; r++) { Cell cell = cells[i][r]; if (!cell.aux) { RC rc = cols[i + cell.colspan - 1]; rc.minEnd = Math.max(rc.minEnd, minStart + cell.minWidth); rc.weightSum = Math.max(rc.weightSum, weightStart + cell.weightX); } } } for (int r = 0; r < numRows; r++) { int minStart = r == 0 ? 0 : rows[r - 1].minEnd; double weightStart = r == 0 ? 0.0 : rows[r - 1].weightSum; for (int i = 0; i < numCols; i++) { Cell cell = cells[i][r]; if (!cell.aux) { RC rc = rows[r + cell.rowspan - 1]; rc.minEnd = Math.max(rc.minEnd, minStart + cell.minHeight); rc.weightSum = Math.max(rc.weightSum, weightStart + cell.weightY); } } } if (allWeightsZero(cols)) { for (int r = 0; r < numRows; r++) for (int i = 0; i < numCols; i++) cells[i][r].weightX = 1.0; continue; } if (allWeightsZero(rows)) { for (int r = 0; r < numRows; r++) for (int i = 0; i < numCols; i++) cells[i][r].weightY = 1.0; continue; } break; } // determine row, col starts Insets insets = getInsets(container); determineStarts(cols, insets.left, container.getWidth() - insets.left - insets.right + spacingX, spacingX); determineStarts(rows, insets.top, container.getHeight() - insets.top - insets.bottom + spacingY, spacingY); } private boolean allWeightsZero(RC[] rcs) { for (int i = 0; i < rcs.length; i++) if (rcs[i].weightSum != 0.0) return false; return true; } private static int getMinimumHeight(Component c) { /*if (c instanceof JTextArea) { return (int) ((JTextArea) c).getUI().getRootView((JTextArea) c).getPreferredSpan(javax.swing.text.View.Y_AXIS); }*/ return c.getMinimumSize().height; } private void applyTemplate(Container container) { if (template == STALACTITE) { Component[] components = container.getComponents(); lines = new String[components.length + 2]; map.clear(); for (int i = 0; i < components.length; i++) { String s = String.valueOf(makeIndexChar(i)); map.put(s, components[i]); lines[i] = s; } lines[components.length] = lines[components.length + 1] = " "; } else if (template == FORM) { /* old method of calculating numRows: int numRows = 0; for (String key : map.keySet()) { if (key.length() == 1) numRows = Math.max(numRows, Character.toLowerCase(key.charAt(0))-'a'); }*/ Component[] components = container.getComponents(); int numRows = components.length/2; lines = new String[numRows+2]; map.clear(); for (int row = 0; row < numRows; row++) { String lower = String.valueOf(makeIndexChar(row)); String upper = String.valueOf(makeAlternateIndexChar(row)); Component rightComponent = components[row * 2 + 1]; if (rightComponent instanceof DummyComponent) upper = lower; lines[row] = (formWideLeftSide ? lower + lower : lower) + (formWideRightSide ? upper + upper : upper); map.put(lower, components[row*2]); if (!(rightComponent instanceof DummyComponent)) map.put(upper, rightComponent); } lines[numRows] = lines[numRows+1] = (formWideLeftSide ? " " : " ") + (formWideRightSide ? " " : " "); } else if (template == LEFT_ALIGNED_ROW) { lines = new String[] { makeSingleRow(container) + RIGHT_CHAR + RIGHT_CHAR }; } else if (template == CENTERED_ROW) { // LEFT_CHAR + LEFT_CHAR is a 2-character repeating sequence // which indicates a wide component. So you can theoretically // use LetterLayout.LEFT_CHAR as your "main" component's... // but it doesn't seem to work. Just use centerAndEast etc lines = new String[] { "" + LEFT_CHAR + LEFT_CHAR + makeSingleRow(container) + RIGHT_CHAR + RIGHT_CHAR }; } else if (template == RIGHT_ALIGNED_ROW) { lines = new String[] { "" + LEFT_CHAR + LEFT_CHAR + makeSingleRow(container) }; } } private String makeSingleRow(Container container) { Component[] components = container.getComponents(); StringBuffer buf = new StringBuffer(); map.clear(); for (int i = 0; i < components.length; i++) { var c = components[i]; String s = constraints.get(c); if (isOneOfSingleChars(s, LEFT_CHAR, RIGHT_CHAR)) continue; s = str(makeAlternateIndexChar(i)); setConstraints(c, s); buf.append(s); } return buf.toString(); } private static void determineStarts(RC[] rcs, int start, int totalSize, int spacing) { int minTotal = rcs[rcs.length - 1].minEnd; double weightSum = rcs[rcs.length - 1].weightSum; //System.out.println("totalSize="+totalSize+",minTotal="+minTotal+",weightSum="+weightSum); int spare = (int) ((totalSize - minTotal) / (weightSum == 0.0 ? 1.0 : weightSum)); int x = start, minSum = 0; double prevWeightSum = 0.0; for (int i = 0; i < rcs.length; i++) { int width = rcs[i].minEnd - minSum + (int) ((rcs[i].weightSum - prevWeightSum) * spare) - spacing; //System.out.println("i="+i+",prevws="+prevWeightSum+",ws="+rcs[i].weightSum+",min="+rcs[i].min+",width="+width); rcs[i].start = x; x += width + spacing; prevWeightSum = rcs[i].weightSum; minSum = rcs[i].minEnd; } } public void addLayoutComponent(String s, Component component) { setConstraints(component, s); } void setConstraints(Component component, String s) { mapPutOrRemove(map, s, component); mapPutOrRemove(constraints, component, s); } public void removeLayoutComponent(Component component) { map.values().remove(component); constraints.remove(component); } public Dimension minimumLayoutSize(Container container) { prepareLayout(container); Insets insets = getInsets(container); Dimension result = new Dimension( insets.left + cols[cols.length - 1].minEnd + insets.right - spacingX, insets.top + rows[rows.length - 1].minEnd + insets.bottom - spacingY); return result; } private Insets getInsets(Container container) { Insets insets = container.getInsets(); return new Insets(insets.top + insetTop, insets.left + insetLeft, insets.bottom + insetBottom, insets.right + insetRight); } public Dimension preferredLayoutSize(Container container) { return minimumLayoutSize(container); } public LetterLayout setSpacing(int x, int y) { spacingX = x; spacingY = y; return this; } public LetterLayout setSpacing(int spacing) { return setSpacing(spacing, spacing); } public LetterLayout setBorder(int top, int left, int bottom, int right) { insetTop = top; insetLeft = left; insetBottom = bottom; insetRight = right; return this; } public LetterLayout setBorder(int inset) { return setBorder(inset, inset, inset, inset); } public LetterLayout setTopBorder(int inset) { insetTop = inset; return this; } /** * layout components from top to bottom; add components without letters! */ public static LetterLayout stalactite() { return new LetterLayout(STALACTITE); } /** * layout components from left to right; add components without letters! */ public static LetterLayout leftAlignedRow() { return new LetterLayout(LEFT_ALIGNED_ROW); } public static LetterLayout leftAlignedRow(int spacing) { return leftAlignedRow().setSpacing(spacing); } /** * layout components from left to right, center in container; add components without letters! */ public static LetterLayout centeredRow() { return new LetterLayout(CENTERED_ROW); } public static LetterLayout rightAlignedRow() { return new LetterLayout(RIGHT_ALIGNED_ROW); } public static JPanel rightAlignedRowPanel(JComponent... components) { return makePanel(new LetterLayout(RIGHT_ALIGNED_ROW), components); } private static JPanel makePanel(LetterLayout letterLayout, JComponent[] components) { JPanel panel = new JPanel(letterLayout); for (JComponent component : components) { panel.add(component); } return panel; } /** * layout components from top to bottom; two components per row */ public static LetterLayout form() { LetterLayout letterLayout = new LetterLayout(FORM); letterLayout.formWideLeftSide = true; letterLayout.formWideRightSide = true; return letterLayout; } /** * layout components from top to bottom; two components per row * left column is small, right column is wide */ public static LetterLayout formWideRightSide() { LetterLayout letterLayout = new LetterLayout(FORM); letterLayout.formWideRightSide = true; return letterLayout; } public static Component getDummyComponent() { return new DummyComponent(); } public static JPanel newPanel(String... lines) { return new JPanel(new LetterLayout(lines)); } public boolean isDebug() { return debug; } public void setDebug(boolean debug) { this.debug = debug; } public static char makeIndexChar(int idx) { return (char) ('a' + idx*2); } public static char makeAlternateIndexChar(int idx) { return (char) ('b' + idx*2); } public static char LEFT_CHAR = ',', RIGHT_CHAR = '.'; public static void main(String[] args) { System.out.println((int) makeIndexChar(0)); System.out.println((int) makeAlternateIndexChar(0)); System.out.println((int) makeIndexChar(32000)); System.out.println((int) makeAlternateIndexChar(32000)); System.out.println((int) LEFT_CHAR); System.out.println((int) RIGHT_CHAR); } } static Q dm_q() { return dm_current_mandatory().q(); } static void dm_q(Runnable r) { dm_inQ(r); } static void dm_q(DynModule module, Runnable r) { module.q().add(r); } static A dm_q(IF0 f) { return dm_evalInQ(if0ToF0(f)); } static boolean isOneOfSingleChars(String s, char... chars) { if (l(s) != 1) return false; char real = s.charAt(0); for (char c : unnullForIteration(chars)) if (real == c) return true; return false; } static void dm_inQ(Runnable r) { dm_q().add(r); } static void dm_inQ(DynModule mod, Runnable r) { dm_q(mod, r); } static A dm_evalInQ(F0 f) { return dm_evalInQ(dm_current_mandatory(), f); } static A dm_evalInQ(IF0 f) { return dm_evalInQ(dm_current_mandatory(), if0ToF0(f)); } static A dm_evalInQ(DynModule module, F0 f) { return evalInQ(module.q(), f); } static F0 if0ToF0(IF0 f) { return f == null ? null : new F0() { public A get() { try { return f.get(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret f.get();"; }}; } static A evalInQ(Q q, final F0 f) { if (isInQ(q)) return callF(f); final Var> var = new Var(); q.add(new Runnable() { public void run() { try { try { var.set(eitherA(callF(f))); } catch (Throwable e) { var.set(eitherB(e)); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "try {\r\n var.set(eitherA(callF(f)));\r\n } catch (Throwable e) {\r\n ..."; }}); return returnOrThrow_either(waitForVarToBeNotNull(var)); } static boolean isInQ(Q q) { return q != null && isCurrentThread(q.rst.thread); } static A returnOrThrow_either(Either e) { if (isEitherB(e)) throw rethrow(e.b()); return eitherAOpt(e); } static A waitForVarToBeNotNull(Var v) { try { synchronized(v) { while (!v.has()) v.wait(); return v.get(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean isCurrentThread(Thread t) { return t != null && t == currentThread(); } static boolean isEitherB(Either e) { return eitherIsB(e); } static A eitherAOpt(Either e) { return e != null && e.isA() ? e.a() : null; } static boolean eitherIsB(Either e) { return e != null && e.isB(); } }