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 java.util.function.*; 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 java.awt.geom.*; import javax.imageio.*; import java.math.*; // TODO - separate uploadedFiles etc between sites!! import java.text.*; import java.text.SimpleDateFormat; import java.nio.charset.Charset; import java.text.NumberFormat; import java.awt.geom.*; import javax.swing.undo.UndoManager; import java.awt.datatransfer.StringSelection; import java.util.TimeZone; import javax.imageio.metadata.*; import javax.imageio.stream.*; import static x30_pkg.x30_util.DynamicObject; import javax.swing.Icon; import org.fife.ui.autocomplete.*; import org.fife.rsta.ui.CollapsibleSectionPanel; import org.fife.rsta.ui.GoToDialog; import org.fife.rsta.ui.SizeGripIcon; import org.fife.rsta.ui.search.FindDialog; import org.fife.rsta.ui.search.ReplaceDialog; import org.fife.rsta.ui.search.ReplaceToolBar; import org.fife.rsta.ui.search.SearchEvent; import org.fife.rsta.ui.search.SearchListener; import org.fife.rsta.ui.search.FindToolBar; import org.fife.ui.rsyntaxtextarea.ErrorStrip; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rtextarea.RTextScrollPane; import org.fife.ui.rtextarea.SearchContext; import org.fife.ui.rtextarea.SearchEngine; import org.fife.ui.rtextarea.SearchResult; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; import org.fife.ui.rsyntaxtextarea.SyntaxScheme; import java.nio.file.Path; import javax.swing.border.*; import java.util.jar.*; import java.awt.datatransfer.*; import java.awt.dnd.*; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.DataFlavor; import javax.swing.border.TitledBorder; import org.fife.ui.rsyntaxtextarea.*; import javax.swing.event.AncestorListener; import javax.swing.event.AncestorEvent; import javax.swing.Timer; import java.awt.datatransfer.UnsupportedFlavorException; class main { static class Worker extends Concept { String loginName, displayName; //S password; boolean available = false; long lastOnline; // recent timestamp if online Ref<AbstractBotImage> 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<String, String> 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<String, String> 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<Conversation> 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<Msg> 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(__58 -> renderMsgForWorkerChat(__58), 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<Worker> 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") + "<br>" + "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<Req> currentReq = new ThreadLocal(); transient volatile Scorer consistencyCheckResults; transient Lock statsLock = lock(); transient Map<String, String> 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<Req> 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 __30 = enter(); try { editMasterPassword(); } finally { _close(__30); }} 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 __31 = enter(); try { editMailSenderInfo(); } finally { _close(__31); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); editMailSenderInfo();"; }})); } static class Req implements IWebRequest { IWebRequest webRequest; String uri; Map<String, String> params; AuthedDialogID auth; Domain authDomainObj; String authDomain; HTMLFramer1 framer; boolean masterAuthed = false; Conversation conv; String subURI; // rest of URI for sub-object WeakReference<Thread> handlingThread; public String uri() { return uri; } final String subUri(){ return subURI(); } String subURI() { return subURI; } public Map<String, String> params() { return params; } public Map<String, String> headers() { return webRequest.headers(); } public Map<String, String> 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 __25 = 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(__25); }} Req currentReq() { return currentReq.get(); } void requestServed() {} Object html2(IWebRequest request) { htmlencode_forParams_useV2(); { Lock __8 = statsLock; lock(__8); try { requestsServed++; change(); requestServed(); vmBus_logMethodCall("html", "this", this, "requestsServed", requestsServed); } finally { unlock(__8); } } //printVars_str html2(+uri); Req req = webRequestToReq(request); AutoCloseable __26 = tempAdd(requestsInFlight, req); try { if (alwaysRedirectToHttps) { var __16= redirectToHttps(req); if (__16 != null) return __16; } vmBus_logMethodCall("html", "Calling html3"); return html3(req); } finally { _close(__26); }} 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<String, String> 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 __27 = tempThing; try { currentReq.set(req); // keep for error printing //printVars_str html2_botConfig(+uri); String botConfig = params.get("_botConfig"); Map<String, String> 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); { var __17= handleAuth(req, cookie); if (__17 != null) return __17; } // 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")) { { var __18= handleLogout(req); if (__18 != null) return __18; } 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); { var __19= serve2(req); if (__19 != null) return __19; } makeFramer(req); HTMLFramer1 framer = req.framer; //printVars_str html2_beforeServeCRUD(uri := req.uri(), +requestAuthed, +showCRUDToEveryone); if (showCRUDToEveryone || showCRUDToNonMasterUsers && requestAuthed || req.masterAuthed) { var __20= serveCRUD(req); if (__20 != null) return __20; } 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 { var __21= workerChat.html(req); if (__21 != null) return __21; } String message = trim(params.get("btn")); if (empty(message)) message = trim(params.get("message")); if (match("new dialog", message)) { Lock __9 = dbLock(); lock(__9); try { conv.newDialog(); message = null; } finally { unlock(__9); } } 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 __10 = dbLock(); lock(__10); 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(__10); } } 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<Msg> 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 __11 = dbLock(); lock(__11); try { conv.newDialog(); } finally { unlock(__11); } } 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<Msg> 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("<!-- " + conv.allCount() + " " + (newDialog ? actuallyNewDialog ? "NEW DIALOG " : "NO NOTIFY NEW DIALOG " : "") + "-->\n" + html); } } return withHeader(""); } if (eqOneOf(uri, "/script", "/demo")) { Lock __12 = dbLock(); lock(__12); 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 = "<!-- langlinks here -->"; 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("<!-- MSGS HERE -->", ""); html = hreplaceTitle(html, heading); } if (eq(uri, "/demo")) return serveBotDemo(req, html); else return withHeader(subBot_serveJavaScript(html)); } finally { unlock(__12); } } { var __22= serveOtherPage(req); if (__22 != null) return __22; } if (eq(uri, "/")) { var __23= serveHomePage(); if (__23 != null) return __23; } 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("</body>")) + " 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"); { var __24= serveDefaultPage(req); if (__24 != null) return __24; } return completeFrame(req); } finally { _close(__27); }} // end of html2 Object serveDefaultPage(Req req) { return null; } Object completeFrame() { return completeFrame(currentReq()); } transient IF1<Req, Object> completeFrame; Object completeFrame(Req req) { return completeFrame != null ? completeFrame.get(req) : completeFrame_base(req); } final Object completeFrame_fallback(IF1<Req, Object> _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> A getDomainValue(Domain domainObj, IF1<Domain, A> 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<Class> 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<Class> navItemClasses(Req req) { return crudClasses(req); } // make and adapt CRUD for class <A extends Concept> HCRUD makeCRUD(Class<A> c, Req req) { return makeCRUD(c, req, req.framer); } <A extends Concept> HCRUD makeCRUD(Class<A> 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<Concept> l = (List<Concept>) 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<String> 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<Concept> 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 ? "" : "<br>" + 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); } <A extends Concept> String makeClassNavItem(Class<A> c, Req req) { HCRUD_Concepts<A> 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<Class, Boolean> newLinkTargetBlank; boolean newLinkTargetBlank(Class c) { return newLinkTargetBlank != null ? newLinkTargetBlank.get(c) : newLinkTargetBlank_base(c); } final boolean newLinkTargetBlank_fallback(IF1<Class, Boolean> _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<Class> allLinkableClasses() { Req req = newReq(); req.masterAuthed = true; return allLinkableClasses(req); } Collection<Class> allLinkableClasses(Req req) { return joinSets(crudClasses(req), botCmdClasses()); } List<Class> botCmdClasses() { return dynNewBot2_botCmdClasses(); } List<Class> dynNewBot2_botCmdClasses() { List<Class> 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<Class> crudClasses(Req req) { return dynNewBot2_crudClasses(req); } List<Class> dynNewBot2_crudClasses(Req req) { if (req == null ? null : req.masterAuthed) { List<Class> 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<String, Object> 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); } <A extends Concept> HCRUD_Concepts<A> crudData(Class<A> 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<Object> 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<Class, String> 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(__59 -> htmlEncode2_gen(__59), 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" , "<default>"); } // 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<String> specialButtons = litciset("Cancel", "Back", "Abbrechen", "ZurĂĽck"); String dbStats() { Collection<Conversation> 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<Lead> leads) { List<String> fields = ciContentsIndexedList(); List<List> rows = new ArrayList(); for (Lead lead : unnullForIteration(leads)) { List row = new ArrayList(); for (Map.Entry<? extends String, ? extends Object> __28 : _entrySet( unnullForIteration(mapPlus_inFront((Map<String, Object>) (Map) lead.answers, "Date" , formatLocalDateWithMinutes(lead.date.date), "Domain" , lead.domain.get(), "Conversation ID" , conceptID(lead.conversation.get()))))) { String key = __28.getKey(); Object val = __28.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<String> countryCodes = putSetElementsFirst(keys(countryDialCodesMultiMap()), splitAtComma_trim(settings().preferredCountryCodes)); String selected = dialCodeStringForCountryCode(conv.country); return mapToLines(c -> { // c == dial code List<CountryDialCode> 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<BotStep> addTypingDelays(List<BotStep> 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<String, String> headers = (Map<String, String>) (rcall("headers", req)); Map<String, String> params = (Map<String, String>) (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<Lead> 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<UserKeyword> 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<UserKeyword> 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 __13 = lockWhileDoingBotActions ? dbLock() : null; lock(__13); 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) { printStackTrace(__e); } } } finally { unlock(__13); } } // 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> out = new ThreadLocal(); transient ThreadLocal<Conversation> 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<String> think) { out.set(new Out()); String reply = ""; try { reply = think.get(); } catch (Throwable __e) { printStackTrace(__e); } Msg msg = new Msg(false, reply); msg.out = out.get(); conv.add(msg); } Msg msgFromThinkFunction(IF0<String> think) { out.set(new Out()); String reply = ""; try { reply = think.get(); } catch (Throwable __e) { printStackTrace(__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<Msg> msgs) { renderMessages(conv, buf, msgs, msg -> msg == last(msgs)); } void renderMessages(Conversation conv, StringBuilder buf, List<Msg> msgs, IF1<Msg, Boolean> 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<String> 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<String> buttons, Collection<String> selections, String multipleChoiceSeparator); // render single-choice buttons String renderSingleChoice(List<String> buttons) { List<String> 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<String> 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<String> div = new ArrayList(); List<String> inMessage = new ArrayList(); List<String> 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<String> inMessage, List<String> 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<String> 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<String> buttons, Collection<String> selections, String multipleChoiceSeparator) { Set<String> 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) )) + "<br>" + 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<String> 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 ? "" : "<br>" + 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("<div class=\"chat_botname\"><p>" + author + "</p>"); 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 <div class=\"chat_avatar\">\r\n <img src=\"$IMG\"/>\r\n </div>" .replace("$IMG", imgURL)); buf.append("<span class=\"sr-only\">" + (fromWorker != null ? "" : botName + " ") + "says</span>"); buf.append(text); buf.append("</" + tag + ">"); if (fromWorker != null) buf.append("</div>"); if (useTrick) buf.append(hscript("$('#" + id + "').focus();")); } } else // msg from user (no avatar) buf.append(("\r\n <span class=\"sr-only\">You say</span>\r\n <" + tag + " class=\"chat_msg_item chat_msg_item_user\"" + (useTrick ? " aria-live=\"polite\"" : "") + ">$TEXT</" + tag + ">\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<String> 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<String> 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<String> buttons, Collection<String> selections, String multipleChoiceSeparator) { Set<String> 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)) + "<br>" + 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 <div class=\"chat-box-single-line\">\r\n <abbr class=\"timestamp\">DATE</abbr>\r\n </div>".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<Msg> msgs) { List<String> 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 + "<br><br>" + 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<Msg> 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<String> text_multipleChoiceSplit(String input, String multipleChoiceSeparator) { return trimAll(splitAt(input, dropSpaces(multipleChoiceSeparator))); } Msg lastBotMsg(List<Msg> 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<String>() { 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 <if> 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<Concept> seen = new HashSet(); String render(BotStep step) { if (!seen.add(step)) return "[see above] " + htmlEncode2(str(step)); List<String> children = new ArrayList(); if (step instanceof Sequence) for (Pair<Integer, BotStep> __6 : iterateWithIndex1(((Sequence) step).steps)) { int i = pairA(__6); BotStep step2 = pairB(__6); children.add("Step " + i + ": " + renderDialogTree(step2)); } if (step instanceof BotOutgoingQuestion) for (Pair<String, BotStep> __7 : ((BotOutgoingQuestion) step).buttonsWithActions()) { String input = pairA(__7); BotStep step2 = pairB(__7); children.add("If user says " + b(htmlEncode2(quote(last(splitAtVerticalBar(input))))) + "<br><br> => " + (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 __15 = calcVar(conv, "botName"); if (!empty(__15)) return __15; return "Representative"; } void addThingsAboveCRUDTable(Req req, Class c, List<String> 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<Class> cruddableClasses(Req req) { List<Class> classes = crudClasses(req); List<Class> cmdClasses = botCmdClasses(req); return flattenList2(classes, DeliveredDomain.class, cmdClasses); } List<Class> 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<Class>) 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<String, String> 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<String> 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); } <A extends Concept> IF1<Collection<A>, Collection<A>> crudSearchFilter(Req req, HCRUD_Concepts<A> 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<String, Object> map = allConceptFieldsAsMap(a); List<String> 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<Req, String> webSocketHeadStuff; String webSocketHeadStuff(Req req) { return webSocketHeadStuff != null ? webSocketHeadStuff.get(req) : webSocketHeadStuff_base(req); } final String webSocketHeadStuff_fallback(IF1<Req, String> _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<List<List<String>>> renderStats; List<List<String>> renderStats() { return renderStats != null ? renderStats.get() : renderStats_base(); } final List<List<String>> renderStats_fallback(IF0<List<List<String>>> _f) { return _f != null ? _f.get() : renderStats_base(); } List<List<String>> 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<BotStep> step = new Ref(); Ref<Conversation> 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> 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<BotStep> 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<BotStep> 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<String> 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<BotStep> 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<Pair<String, BotStep>> 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(__60 -> dropStuffBeforeVerticalBar(__60), 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> 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> language = new Ref(); String pattern; List<String> examples, counterexamples; Ref<BotStep> action = new Ref(); boolean enabled = true; boolean priority = false; int precedence; transient MMOPattern parsedPattern; public 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> userKeyword = new Ref(); Msg msg; EvtMatchedUserKeyword() {} EvtMatchedUserKeyword(UserKeyword userKeyword, Msg msg) { this.userKeyword.set(userKeyword); this.msg = msg; } } static class EvtJumpTo extends Evt { Ref<BotStep> target = new Ref(); EvtJumpTo() {} EvtJumpTo(BotStep target) { this.target.set(target); } } static class EvtCallSubroutine extends Evt { Ref<BotStep> 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<AbstractBotImage> image = new Ref(); Avatar getActualAvatar() { return this; } boolean onShift() { List<IntRange> 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<Conversation> conv; new Ref<Avatar> avatar; new L<ActiveSequence> stack; }*/ // end of concepts // special options for a message static class Out extends DynamicObject { List<String> 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<String> 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<Domain> restrictedToDomain = new Ref(); Worker loggedIn; // who is logged in with this cookie long lastSeen; String domain() { return !restrictedToDomain.has() ? null : restrictedToDomain.get().domainAndPath; } } static class ActiveSequence { Sequence originalSequence; List<BotStep> 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<ActiveSequence> stack; UndoState() {} UndoState(List<ActiveSequence> 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<Domain> domainObj = new Ref(); List<List<Msg>> oldDialogs = new ArrayList(); List<Msg> 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> avatar = new Ref(); List<ActiveSequence> stack = new ArrayList(); List<UndoState> undoStates; int executedSteps; IInputHandler inputHandler; Ref<Language> language = new Ref(); //Long lastProposedDate; Map<String, String> answers = litcimap(); Map<String, String> botConfig; RefL<Concept> 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<Msg> 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(); } } // end of Conversation abstract static class ScheduledAction extends Concept implements Runnable { long time; } abstract static class ScheduledActionOnConversation extends ScheduledAction { Ref<Conversation> 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() { try { nextStep(conv.get(), executedSteps); } catch (Exception __e) { throw rethrow(__e); } } } static class OnNewDialog extends ScheduledActionOnConversation { OnNewDialog() {} OnNewDialog(Conversation conv) { this.conv.set(conv); } public void run() { try { botMod().startMainScript(conv.get()); } catch (Exception __e) { throw rethrow(__e); } } } 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() { try { 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); } catch (Exception __e) { throw rethrow(__e); } } } // 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<AbstractBotImage> botImage = new Ref(); String botName; // if empty, keep default Ref<AbstractAvatar> avatar = new Ref(); String headerColorLeft, headerColorRight; // ditto Boolean autoOpenBot; Ref<BotStep> 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<BotStep> 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> domain = new Ref(); Ref<Conversation> conversation = new Ref(); Timestamp date; Map<String, String> answers; } static class ConversationFeedback extends Concept { static final String _fieldOrder = "domain conversation date answers"; Ref<Domain> domain = new Ref(); Ref<Conversation> conversation = new Ref(); Timestamp date; Map<String, String> 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<UploadedSound> notificationSound = new Ref(); boolean talkToBotOnlyWithAuth = false; boolean enableWorkerChat = false; Ref<Language> 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 Object mainBot; static Object getMainBot() { return mainBot; } static String htmlEncode2(String s) { return htmlencode_noQuotes(s); } static String htmlEncode2(Object o) { return htmlEncode2(strOrEmpty(o)); } static String ukTimeZone_string() { return "Europe/London"; } static String appendSlash(String s) { return addSlash(s); } 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 Object subBot_serveRedirect(String url) { return call(getMainBot(), "serveRedirect", url); } static String htmlQuery(Map params) { return empty(params) ? "" : "?" + makePostData(params); } static String htmlQuery(Object... data) { return empty(data) ? "" : "?" + makePostData(data); } 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(BitSet bs) { return !empty(bs); } static boolean nempty(Map m) { return !empty(m); } static boolean nempty(Iterator i) { return i != null && i.hasNext(); } static boolean nempty(MultiMap mm) { return mm != null && !mm.isEmpty(); } 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 nempty(MultiSet ms) { return ms != null && !ms.isEmpty(); } static String afterLastSlash(String s) { if (s == null) return null; int i = s.lastIndexOf('/'); return i < 0 ? s : substring(s, i+1); } 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 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 <A> String joinWithComma(Collection<A> c) { return join(", ", c); } static String joinWithComma(Object... 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 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 <A, B> List<B> map(Iterable<A> l, F1<A, B> f) { return map(f, l); } static <A, B> List<B> map(F1<A, B> f, Iterable<A> l) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(callF(f, o)); } return x; } static <A, B> List<B> map(IF1<A, B> f, Iterable<A> l) { return map(l, f); } static <A, B> List<B> map(Iterable<A> l, IF1<A, B> f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static <A, B> List<B> map(IF1<A, B> f, A[] l) { return map(l, f); } static <A, B> List<B> map(A[] l, IF1<A, B> 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 <A, B, C> List<C> map(Map<A, B> map, IF2<A, B, C> f) { return map(map, (Object) f); } // new magic alias for mapLL - does it conflict? static <A, B> List<A> map(IF1<A, B> 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; } // 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<? extends Concept> l, Object... values) { int changes = 0; for (Concept c : unnullForIteration(l)) changes += cset(c, values); return changes; } static <A extends Concept> int cset(Concept.Ref<A> c, Object... values) { return cset(getVar(c), values); } static boolean cset_trueIfChanged(Concept c, Object... values) { try { return cset(c, values) != 0; } catch (Exception __e) { throw rethrow(__e); } } static Concept getConcept(long id) { return db_mainConcepts().getConcept(id); } static Concept getConcept(Concepts concepts, long id) { return concepts.getConcept(id); } static <A extends Concept> A getConcept(Class<A> cc, long id) { return getConcept(db_mainConcepts(), cc, id); } static <A extends Concept> A getConcept(Concepts concepts, Class<A> 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 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 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 <A, B, C> LinkedHashMap<B, C> mapToOrderedMap(IF1<A, Pair<B, C>> f, Iterable<A> l) { LinkedHashMap<B, C> map = new LinkedHashMap(); for (A o : unnullForIteration(l)) { Pair<B, C> p = callF(f, o); map.put(p.a, p.b); } return map; } static <A, B, C> LinkedHashMap<B, C> mapToOrderedMap(Iterable<A> l, IF1<A, Pair<B, C>> f) { return mapToOrderedMap(f, l); } static boolean conceptsSortedByFieldCI_verbose = false; static <A extends Concept> Collection<A> conceptsSortedByFieldCI(Class<A> c, String field) { return conceptsSortedByFieldCI(db_mainConcepts(), c, field); } static <A extends Concept> Collection<A> conceptsSortedByFieldCI(Concepts concepts, Class<A> c, String field) { // indexed IFieldIndex<A, Object> index = concepts.getCIFieldIndex(c, field); if (index instanceof ConceptFieldIndexCI) return (Collection<A>) 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 <A, B> Pair<A, B> pair(A a, B b) { return new Pair(a, b); } static <A> Pair<A, A> pair(A a) { return new Pair(a, a); } static String hsansserif() { return hcss("body { font-family: Sans-Serif; }"); } static String p(Object contents, Object... params) { return hfulltag("p", contents, params) + "\n"; } static String p() { return p(""); } static String hpostform(Object contents, Object... params) { return tag("form", contents, concatArrays(new Object[] {"method", "POST"}, params)); } 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("<option></option>\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 long conceptID(Concept c) { return c == null ? 0 : c.id; } static long conceptID(Concept.Ref ref) { return conceptID(cDeref(ref)); } 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 String hquery(Map params) { return htmlQuery(params); } static String hquery(Object... data) { return htmlQuery(data); } static String loadJQuery() { return "<script src=\"https://code.jquery.com/jquery-1.10.2.js\"></script>"; } 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<String, String> map, String... keys) { return hiddenFields(map, keys); } static String hscriptsrc(String src) { return hjavascript_src(src); } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } 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 Class mc() { return main.class; } static long sysNow() { ping(); return System.nanoTime()/1000000; } 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 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); } } // 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 String hscript(String script) { return hjavascript(script); } // f: A -> Comparable static <A> List<A> sortByCalculatedFieldDesc(Collection<A> c, final Object f) { return sortByCalculatedFieldDesc_inPlace(cloneList(c), f); } static <A> List<A> sortByCalculatedFieldDesc(Object f, Collection<A> c) { return sortByCalculatedFieldDesc(c, f); } static <A, B> List<A> sortByCalculatedFieldDesc(Iterable<A> c, IF1<A, B> f) { List<A> l = cloneList(c); sort(l, new Comparator<A>() { public int compare(A a, A b) { return stdcompare(f.get(b), f.get(a)); } }); return l; } static <A, B> List<A> sortByCalculatedFieldDesc(IF1<A, B> f, Iterable<A> c) { return sortByCalculatedFieldDesc(c, f); } static boolean conceptsWithFieldGreaterThan_verbose = false; static <A extends Concept> Collection<A> conceptsWithFieldGreaterThan(Class<A> c, String field, Object value) { return conceptsWithFieldGreaterThan(db_mainConcepts(), c, field, value); } static <A extends Concept> Collection<A> conceptsWithFieldGreaterThan(Concepts concepts, Class<A> c, String field, Object value) { // indexed IFieldIndex<A, Object> 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 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 hsimpletableheader(String... cols) { return tag("tr", join(lambdaMap(__61 -> th(__61), cols))); } static List<String> mapToLines(Map map) { List<String> 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 <A> String mapToLines(Iterable<A> l, IF1<A, String> f) { return mapToLines((Object) f, l); } static <A> String mapToLines(IF1<A, String> f, Iterable<A> l) { return mapToLines((Object) f, l); } static <A, B> String mapToLines(Map<A, B> map, IF2<A, B, String> f) { return lines(map(map, f)); } static <A> String mapToLines(IF1<A, String> f, A data1, A... moreData) { return lines(map(f, data1, moreData)); } static <A> List<A> lastTwo(List<A> l) { return takeLast(2, l); } 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 ahref(String link, Object contents, Object... params) { return link == null ? str(contents) : href(link, contents, params); } 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"; } static String hparagraphs(Collection<String> l) { return lines(lambdaMap(__62 -> p(__62), l)); } static <A, B> List<B> lambdaMap(IF1<A, B> f, Iterable<A> l) { return map(l, f); } static <A, B> List<B> lambdaMap(IF1<A, B> f, A[] l) { return map(l, f); } static String hhtml(Object contents) { return containerTag("html", contents); } static String hhead(Object contents) { return tag("head", contents); } 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 hbody(Object contents, Object... params) { return tag("body", contents, params); } static String h3(String s, Object... params) { return tag("h3", s, params) + "\n"; } 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 b(Object contents, Object... params) { return tag("b", contents, params); } // 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 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 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 Complex div(Complex a, double b) { return new Complex(a.re/b, a.im/b); } static double div(double a, double b) { return a/b; } static double div(double a, int b) { return a/b; } static int div(int a, int b) { return a/b; } static String small(Object contents, Object... params) { return tag("small", contents, params); } static String span(Object contents, Object... params) { return hfulltag("span", contents, params); } static String span() { return span(""); } static String hbutton(String text, Object... params) { return hfulltag("button", text, params); } static String hhead_title(String title) { return hhead(htitle(title)); } 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 <A> List<A> filter(Iterable<A> c, Object pred) { if (pred instanceof F1) return filter(c, (F1<A, Boolean>) 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 <A, B extends A> List<B> filter(Iterable<B> c, F1<A, Boolean> pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static <A, B extends A> List<B> filter(F1<A, Boolean> pred, Iterable<B> c) { return filter(c, pred); } //ifclass IF1 static <A, B extends A> List<B> filter(Iterable<B> c, IF1<A, Boolean> pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static <A, B extends A> List<B> filter(B[] c, IF1<A, Boolean> pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static <A, B extends A> List<B> filter(IF1<A, Boolean> pred, Iterable<B> c) { return filter(c, pred); } //endif static <A extends Concept> List<A> list(Class<A> type) { return list(type, db_mainConcepts()); } static <A extends Concept> List<A> list(Class<A> type, Concepts cc) { return cc.list(type); } static <A extends Concept> List<A> list(Concepts concepts, Class<A> type) { return concepts.list(type); } static List<Concept> list(String type) { return db_mainConcepts().list(type); } static List<Concept> list(Concepts concepts, String type) { return concepts.list(type); } static List<Concept> list(Concepts concepts) { return asList(concepts.allConcepts()); } 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 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 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 TreeMap litcimap(Object... x) { return litCIMap(x); } static <A> Set<A> 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."); } // A database system for only slightly modified Java objects. // Recent changes: // -minimal crash recovery disabled for now (could reenable) // -idCounter.structure file no longer used // -allDynamic/safeLoad disabled, it doesn't really make any sense // -conceptsFile can be in any directory now // Functions that should always be there for child processes: static int concepts_internStringsLongerThan = 10; static ThreadLocal<Boolean> 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<A extends Concept, Val> { Collection<A> getAll(Val val); List<Val> allValues(); // returns a cloned list MultiSet<Val> allValues_multiSet(); IterableIterator<A> 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 __3 = (ConceptCreate) o; return eq(c, __3.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 __4 = (ConceptChange) o; return eq(c, __4.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 __5 = (ConceptDelete) o; return id == __5.id && eq(c, __5.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 { SortedMap<Long, Concept> concepts = synchroTreeMap(); long idCounter; transient HashMap<Class, Object> 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: phase out (we have conceptsFile field now) transient String programID; transient File conceptsFile; 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<IConceptIndex> conceptIndices; transient Map<Class<? extends Concept>, Map<String, IFieldIndex>> fieldIndices; transient Map<Class<? extends Concept>, Map<String, IFieldIndex>> ciFieldIndices; //transient L saveActions = synchroList(); transient List<Runnable> preSave; transient Object classFinder = _defaultClassFinder(); transient List onAllChanged = synchroList(); // list of runnables transient Set<IVF1> onChange = new HashSet(); transient Object saveWrapper; // VF1<Runnable>, 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;} Concepts(File conceptsFile) { this.conceptsFile = conceptsFile;} synchronized long internalID() { do { ++idCounter; } while (hasConcept(idCounter)); return idCounter; } synchronized HashMap<Class, Object> perClassData() { if (perClassData == null) perClassData = new HashMap(); return perClassData; } void initProgramID() { if (programID == null) programID = getDBProgramID(); } // load from structure Concepts load(String structure) { return load(structure, false); } Concepts load(String structure, boolean allDynamic) { clearConcepts(); Map<Long, Concept> map = unstructureMap(structure, allDynamic, classFinder); concepts.putAll(map); assignConceptsToUs(); calcIdCounter(); return this; } Concepts load() { initProgramID(); // try custom grabber Object dbGrabber = miscMapGet("dbGrabber"); if (dbGrabber != null && !isFalse(callF(dbGrabber))) return this; try { if (tryToGrab()) return this; } catch (Throwable e) { if (!exceptionMessageContains(e, "no xfullgrab")) printShortException(e); print("xfullgrab failed - loading DB of " + programID + " from disk"); } return loadFromDisk(); } Concepts loadFromDisk() { if (nempty(concepts)) clearConcepts(); // minimal crash recovery (disabled for now) //restoreLatestBackupIfConceptsFileEmpty(programID, doIt := true); long time = now(); Map<Long, Concept> _concepts = (Map<Long, Concept>) (unstructureGZFile(conceptsFile(), toIF1(classFinder))); putAll(concepts, _concepts); assignConceptsToUs(); loadTook = now()-time; done("Loaded " + n2(l(concepts), "concept"), time); calcIdCounter(); return this; } Concepts loadConcepts() { return load(); } boolean tryToGrab() { if (sameSnippetID(programID, getDBProgramID())) return false; RemoteDB db = connectToDBOpt(programID); try { if (db != null) { loadGrab(db.fullgrab()); return true; } return false; } finally { _close(db); }} Concepts loadGrab(String grab) { clearConcepts(); DynamicObject_loading.set(true); try { Map<Long, Concept> map = (Map) unstructure(grab, false, 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<Long, Object> p: mapToPairs((Map<Long, Object>) (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 lastID = lastKey(concepts); idCounter = lastID == null ? 1 : lastID+1; } File conceptsDir() { return dirOfFile(conceptsFile()); } Concepts conceptsFile(File conceptsFile) { this.conceptsFile = conceptsFile; return this; } File conceptsFile() { if (conceptsFile != null) return conceptsFile; return getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure"); } // used for locking when useFileLock is activated File lockFile() { return newFile(conceptsDir(), "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 { 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..."; }}); newFile(conceptsDir(), "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)); newFile(conceptsDir(), "concepts.structure.gz").delete(); } File conceptsFile = conceptsFile(); File backupFile = newFile(conceptsDir(), "backups/" + fileName(conceptsFile) + ".backup" + ymd() + "-" + formatInt(hours(), 2) + (newBackupEveryXMinutes >= 60 ? "" : formatInt(roundDownTo_rev(minutes(), newBackupEveryXMinutes), 2))); // TODO: get rid of this copyFile(f, backupFile); time = now()-start; if (!quietSave) print("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<structure_Data> makeStructureData; structure_Data makeStructureData() { return makeStructureData != null ? makeStructureData.get() : makeStructureData_base(); } final structure_Data makeStructureData_fallback(IF0<structure_Data> _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 " + conceptsDir(), 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) { printStackTrace(__e); } { cleanUp(fileLock); fileLock = null; } } Map<Long, String> getIDsAndNames() { Map<Long, String> map = new HashMap(); Map<Long, Concept> 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 extends Concept> A conceptOfType(Class<A> type) { IConceptCounter counter = conceptCounterForClass(type); if (counter != null) return (A) first(counter.allConcepts()); return firstOfType(allConcepts(), type); } <A extends Concept> List<A> conceptsOfType(Class<A> type) { List<A> l = conceptsOfType_noParent(type); if (parent == null) return l; return concatLists_conservative(l, parent.conceptsOfType(type)); } <A extends Concept> List<A> conceptsOfType_noParent(Class<A> type) { ping(); IConceptCounter counter = conceptCounterForClass(type); if (counter != null) return (List<A>) cloneList(counter.allConcepts()); return filterByType(allConcepts(), type); } <A extends Concept> List<A> listConcepts(Class<A> type) { return conceptsOfType(type); } <A extends Concept> List<A> list(Class<A> type) { return conceptsOfType(type); } <A extends Concept> List<A> list_noParent(Class<A> type) { return conceptsOfType_noParent(type); } // TODO: would be better to make this Cl (indices may return sets) List<Concept> list(String type) { return conceptsOfType(type); } List<Concept> conceptsOfType(String type) { return filterByDynamicType(allConcepts(), "main$" + type); } boolean hasConceptOfType(Class<? extends Concept> 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 extends Concept> A ensureHas(Class<A> 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<? extends Concept> c1, Class<? extends Concept> 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<? extends Concept> type, Object func) { for (Concept c : conceptsOfType(type)) callF(func, c); } int deleteAll(Class<? extends Concept> type) { List<Concept> l = (List) conceptsOfType(type); for (Concept c : l) c.delete(); return l(l); } // always returns a new list (callers depend on this) Collection<Concept> allConcepts() { synchronized(concepts) { return new ArrayList(values(concepts)); } } IConceptCounter conceptCounterForClass(Class<? extends Concept> 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; } <A extends Concept> int countConcepts(Class<A> c, Object... params) { int n = countConcepts_noParent(c, params); if (parent == null) return n; return n+parent.countConcepts(c, params); } <A extends Concept> int countConcepts_noParent(Class<A> 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<IConceptIndex> 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<? extends Concept> c, String field, IFieldIndex index) { if (fieldIndices == null) fieldIndices = new HashMap(); Map<String, IFieldIndex> map = fieldIndices.get(c); if (map == null) fieldIndices.put(c, map = new HashMap()); map.put(field, index); } synchronized void removeFieldIndex(Class<? extends Concept> c, String field, IFieldIndex index) { Map<String, IFieldIndex> map = mapGet(fieldIndices, c); mapRemove(map, field); } synchronized IFieldIndex getFieldIndex(Class<? extends Concept> c, String field) { if (fieldIndices == null) return null; Map<String, IFieldIndex> map = fieldIndices.get(c); return map == null ? null : map.get(field); } synchronized IFieldIndex getAnyIndexForClass(Class<? extends Concept> c) { return firstValue(fieldIndices == null ? null : fieldIndices.get(c)); } synchronized void addCIFieldIndex(Class<? extends Concept> c, String field, IFieldIndex index) { if (ciFieldIndices == null) ciFieldIndices = new HashMap(); Map<String, IFieldIndex> map = ciFieldIndices.get(c); if (map == null) ciFieldIndices.put(c, map = new HashMap()); map.put(field, index); } synchronized void removeCIFieldIndex(Class<? extends Concept> c, String field) { Map<String, IFieldIndex> map = mapGet(ciFieldIndices, c); mapRemove(map, field); } synchronized IFieldIndex getCIFieldIndex(Class<? extends Concept> c, String field) { if (ciFieldIndices == null) return null; Map<String, IFieldIndex> 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<RC> l) { for (RC c : l) xdelete(c); } List<RC> xlist() { return map("toPassRef", allConcepts()); } List<RC> 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<File, byte[]> 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> A miscMapGetOrCreate(Object key, IF0<A> 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<ConceptsChange> l){ addChangeListener(l); } void addChangeListener(IVF1<ConceptsChange> l) { syncAdd(onChange, l); } void removeChangeListener(IVF1<ConceptsChange> l) { syncRemove(onChange, l); } void addPreSave(Runnable r) { preSave = syncAddOrCreate(preSave, r); } public String toString() { return nConcepts(concepts) + " (" + conceptsDir() + ", hash: " + identityHashCode(this) + ")"; } } // end of Concepts // TODO: Move everyone over to this so we can have concepts that // don't subclass Concept. It's hard though because of Concept.Ref interface IConcept { public long _conceptID(); public Concepts concepts(); } static class Concept extends DynamicObject implements IConcept, ChangeTriggerable { transient Concepts _concepts; // Where we belong long id; long created, _modified; List<Ref> 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(); } boolean includeZeroIDInToString() { return false; } public String toString() { String s = shortDynamicClassName(this); long id = this.id; if (id != 0 || includeZeroIDInToString()) s += " " + id; return s; } 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<A extends Concept, B> extends Ref<A> { TypedRef() {} //Class<B> aType; Class<B> bType; TypedRef(Class<B> bType) { this.bType = bType;} TypedRef(Class<B> 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> C checkValue(C a) { if (bType != null && a != null) assertIsInstance(a, bType); return a; } B b() { return (B) value; } } class Ref<A extends Concept> implements IRef<A> { 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<A> 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<A> 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<A extends Concept> extends AbstractList<A> { List<Ref<A>> l = new ArrayList(); RefL() {} RefL(List<A> l) { replaceWithList(l); } public void clear() { while (!isEmpty()) removeLast(this); } public void replaceWithList(List<A> l) { clear(); for (A a : unnullForIteration(l)) add(a); } public A set(int i, A o) { Ref<A> 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<A> 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(); // 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 final public void _change(){ change(); } public void change() { _setModified(now()); _change_withoutUpdatingModifiedField(); } void _setModified(long modified) { _modified = modified; } final void _change_withoutUpdatingModifiedField() { _onChange(); if (_concepts != null) _concepts.conceptChanged(this); } // overridable void _onChange() {} 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) { } 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> 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); } public Concepts concepts() { return _concepts; } boolean isDeleted() { return id == 0; } void _doneLoading2() { Map<String, FieldMigration> map = _fieldMigrations(); if (map != null) for (Map.Entry<? extends String, ? extends FieldMigration> __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 __6 = (FieldMigration) o; return eq(newField, __6.newField); } public int hashCode() { int h = 558692372; h = boostHashCombine(h, _hashCode(newField)); return h; } public Object[] _fieldsToList() { return new Object[] {newField}; } } // value is Map<String, FieldMigration> _fieldMigrations() { return null; } // new wrapper to get a copy of the refs list // so we can eventually drop the refs field Collection<Ref> _refs() { return scanConceptForRefs(this); } 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();*/ } <A> boolean addAndChange(Collection<A> cl, A a) { if (cl == null || !cl.add(a)) return false; change(); return true; } <A> void clearAndChange(Collection<A> cl) { if (cl == null) return; cl.clear(); change(); } File conceptsDir() { var concepts = concepts(); return concepts == null ? null : concepts.conceptsDir(); } File fileInConceptsDir(String name) { var dir = conceptsDir(); return dir == null ? null : newFile(dir, name); } public long _conceptID() { return id; } } // 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); } } // end of RC // 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<BaseXRef, XRef> getIndex() { return getXRefIndex(_concepts); } } static synchronized HashMap<BaseXRef, XRef> 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 String standardTimeZone_name = "Europe/Berlin"; static String standardTimeZone() { return standardTimeZone_name; } static volatile StringBuffer local_log = new StringBuffer(); // not redirected static boolean printAlsoToSystemOut = true; 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<Object> print_byThread; // special handling by thread - prefers F1<S, Bool> static volatile Object print_allThreads; static volatile Object print_preprocess; static void print() { print(""); } static <A> A print(String s, A o) { print(combinePrintParameters(s, o)); return o; } // slightly overblown signature to return original object... static <A> 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); if (printAlsoToSystemOut) System.out.print(s); vmBus_send("printed", mc(), s); } static void print_autoRotate() { } 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<Class> 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 Object[] litobjectarray(Object... l) { return litObjectArray(l); } 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 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 <A, B> B mapGet(Map<A, B> map, A a) { return map == null || a == null ? null : map.get(a); } static <A, B> B mapGet(A a, Map<A, B> map) { return map == null || a == null ? null : map.get(a); } static String appendParamsToURL(String url, Map params) { return appendQueryToURL(url, params); } static String appendParamsToURL(String url, Object... data) { return appendQueryToURL(url, data); } 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 <A extends Throwable> A printStackTrace(A e) { // we go to system.out now - system.err is nonsense if (e != null) 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 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 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 void htmlencode_forParams_useV2() { htmlencode_forParams_useV2.set(true); } static void change() { //mainConcepts.allChanged(); // safe version for now cause function is sometimes included unnecessarily (e.g. by EGDiff) callOpt(getOptMC("mainConcepts"), "allChanged"); } 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 <A> AutoCloseable tempAdd(Collection<A> l, 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 <A> WeakReference<A> weakRef(A a) { return newWeakReference(a); } static Thread currentThread() { return Thread.currentThread(); } 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 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 <A, B> Map<A, B> mapPlus(Map<A, B> m, Object... data) { m = cloneMap(m); litmap_impl(m, data); return m; } static <A, B> Map<A, B> mapMinus(Map<A, B> map, Object... keys) { if (empty(keys)) return map; Map<A, B> m2 = cloneMap(map); for (Object key : keys) m2.remove(key); return m2; } static String aGlobalID() { return randomID(globalIDLength()); } static String aGlobalID(Random random) { return randomID(random, globalIDLength()); } 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<String, String> decodeURIParams(String query) { return decodeHQuery(query); } 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 Object serve404() { return subBot_serve404(); } static Object serve404(String msg) { return subBot_serve404(msg); } 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 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 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 File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } static boolean preferCached = false; static boolean loadSnippet_debug = false; static ThreadLocal<Boolean> loadSnippet_silent = new ThreadLocal(); static ThreadLocal<Boolean> loadSnippet_publicOnly = new ThreadLocal(); static int loadSnippet_timeout = 30000; 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<Long, String> loadSnippet; static String loadSnippet(long snippetID) { return loadSnippet != null ? loadSnippet.get(snippetID) : loadSnippet_base(snippetID); } final static String loadSnippet_fallback(IF1<Long, String> _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 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 webChatBotLogsHTML2(final String baseLink, final Map<String, String> params) { return withDBLock(new F0<String>() { public String get() { try { List<String> l = new ArrayList(); for (Conversation conv : sortByCalculatedFieldDesc(list(Conversation.class), new F1<Conversation, Object>() { 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<List<Msg>> dialogs = reversed(unnull(conv.oldDialogs)); if (l(conv.msgs) > 1) l.add(webChatBotLogsHTML_formatDialog(str(conv.id), conv.msgs)); int i = 2; for (List<Msg> 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<S> l;\r\n for (Conversation conv : sortByCalculatedFieldDesc(list Conv..."; }}); } 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 } // 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 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 LinkedHashMap litorderedmap(Object... x) { LinkedHashMap map = new LinkedHashMap(); litmap_impl(map, x); return map; } static List<String> allToString(Iterable c) { List<String> l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static List<String> allToString(Object[] c) { List<String> l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } 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 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(BitSet bs) { return bs == null || bs.isEmpty(); } 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 boolean empty(AppendableChain c) { return c == null; } 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<String> toks, Matches matches) { return match3(pat, toks, matches); } 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 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 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 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 String hstylesheetsrc(String src) { return tag("link", "", "rel" , "stylesheet", "href" , src); } static int parseInt(String s) { return emptyString(s) ? 0 : Integer.parseInt(s); } static int parseInt(char c) { return Integer.parseInt(str(c)); } 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 <A> List<A> cloneSubList(List<A> l, int startIndex, int endIndex) { return newSubList(l, startIndex, endIndex); } static <A> List<A> cloneSubList(List<A> l, int startIndex) { return newSubList(l, startIndex); } static <A> ArrayList<A> cloneList(Iterable<A> l) { return l instanceof Collection ? cloneList((Collection) l) : asList(l); } static <A> ArrayList<A> cloneList(Collection<A> l) { if (l == null) return new ArrayList(); synchronized(collectionMutex(l)) { return new ArrayList<A>(l); } } static Object serveJSON_breakAtLevels(int levels, Object... data) { return serveText(jsonEncode_breakAtLevels(levels, jsonPrepareData(data))); } 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 hcss(Object contents) { if (contents instanceof String && isRelativeOrAbsoluteURL((String) contents)) return hstylesheetsrc((String) contents); else return htag("style", contents); } static String hexColorToCSSRGB(String hex) { return empty(hex) ? null : "rgb(" + joinWithComma(rgbToIntList(new RGB(hex))) + ")"; } static String jsQuote(String s) { return javascriptQuote(s); } static String stringIf(boolean b, String s) { return stringIfTrue(b, s); } static String stringIf(String s, boolean b) { return stringIf(b, s); } static String str(Object o) { return o == null ? "null" : o.toString(); } static String str(char[] c) { return new String(c); } static String str(char[] c, int offset, int count) { return new String(c, offset, count); } static boolean eqOneOf(Object o, Object... l) { for (Object x : l) if (eq(o, x)) return true; return false; } static String loadSnippet_cached(String id) { return loadSnippet_simpleCache(id); } static String urlencode(String x) { try { return URLEncoder.encode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } static String replaceDollarVars(String s, Object... params) { if (empty(params)) return s; Map<String, Object> vars = mapKeys(__63 -> dropDollarPrefix(__63), (Map<String, Object>) litcimap(params)); return replaceDollarVars_dyn(s, var -> strOrNull(vars.get(var))); } static String replaceDollarVars(String s, IF1<String, String> f) { return replaceDollarVars_dyn(s, f); } 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 jsBool(boolean b) { return b ? "true" : "false"; } 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 String dropSlashPrefix(String s) { return dropLeadingSlash(s); } static <A extends Concept> A conceptWhere(Class<A> c, Object... params) { return findConceptWhere(c, params); } static <A extends Concept> A conceptWhere(Concepts cc, Class<A> c, Object... params) { return findConceptWhere(cc, c, params); } static String urldecode(String x) { try { return URLDecoder.decode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } 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 Map<String, String> 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 long toLong(Object o) { if (o instanceof Number) return ((Number) o).longValue(); if (o instanceof String) return parseLong((String) o); return 0; } static String hhtml_head_title_body(String title, Object bodyContents, Object... bodyParams) { return hhtml(hhead(htitle(title)) + "\n" + hbody(bodyContents, bodyParams)); } static String hmobilefix() { return "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"; } static String hmobilefix(String html) { return hAddToHead(html, hmobilefix()); } static String h2(String s, Object... params) { return tag("h2", s, params); } static String hcss_linkColorInherit() { return hcss("a { color: inherit; }"); } static <A extends Concept> Collection<A> conceptsWhere(Class<A> c, Object... params) { return findConceptsWhere(c, params); } static Collection<Concept> conceptsWhere(String c, Object... params) { return findConceptsWhere(c, params); } static <A extends Concept> Collection<A> conceptsWhere(Concepts concepts, Class<A> c, Object... params) { return findConceptsWhere(concepts, c, params); } static Object[] mapToParams(Map map) { return mapToObjectArray(map); } static <A> List<A> replace(List<A> 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 <A> List<A> replace(A a, A b, List<A> 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 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<String, Object[]> paramsByColName = (Map<String, Object[]>) (optPar("paramsByColName", __)); Object[] tableParams = (Object[]) (optPar("tableParams", __)); Object[] trParams = (Object[]) (optPar("trParams", __)); Object[] tdParams = (Object[]) (optPar("tdParams", __)); Map<String, String> replaceHeaders = (Map<String, String>) (optPar("replaceHeaders", __)); // optionally replace HTML contents for header row boolean noHeader = boolPar("noHeader", __); // prepare table List<List<String>> rows = new ArrayList(); List<String> cols = new ContentsIndexedList(); if (data instanceof List) { for (Object x : (List) data) { try { rows.add(dataToTable_makeRow(x, cols)); } catch (Throwable __e) { printStackTrace(__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<String> 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("<tr>\n"); for (String cell : padList(cols, w, "")) buf.append(" <th>" + htmlTable2_encodeCell( getOrKeep(replaceHeaders, cell), htmlEncode, useBr) + "</th>\n"); buf.append("</tr>\n"); } // data for (List<String> 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("</tr>\n"); } buf.append("</table>\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<String, String> emojiShortNameMap_cache; static NavigableMap<String, String> emojiShortNameMap() { if (emojiShortNameMap_cache == null) emojiShortNameMap_cache = emojiShortNameMap_load(); return emojiShortNameMap_cache;} static NavigableMap<String, String> emojiShortNameMap_load() { NavigableMap<String, String> 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<? extends 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("</tr>\n"); } return htag("table", buf, toObjectArray(paramsTable)); } static <A> List<A> nonNulls(Iterable<A> l) { return withoutNulls(l); } static <A> List<A> nonNulls(A[] l) { return withoutNulls(l); } static <A, B> Map<A, B> nonNulls(Map<A, B> map) { return withoutNulls(map); } static <A> List<A> ll(A... a) { ArrayList l = new ArrayList(a.length); if (a != null) for (A x : a) l.add(x); return l; } static String unnull(String s) { return s == null ? "" : s; } static <A> Collection<A> unnull(Collection<A> l) { return l == null ? emptyList() : l; } static <A> List<A> unnull(List<A> 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 <A, B> Map<A, B> unnull(Map<A, B> l) { return l == null ? emptyMap() : l; } static <A> Iterable<A> unnull(Iterable<A> i) { return i == null ? emptyList() : i; } static <A> 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 <A, B> Pair<A, B> unnull(Pair<A, B> 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; } // lists files only - who needs to list directories?! // lists in typical .zip fashion - dir1/dir2/bla.txt static List<String> listZIP(File inZip) { return listZip(inZip); } static <A extends Concept> A uniq(Class<A> c, Object... params) { return uniqueConcept(c, params); } static <A extends Concept> A uniq(Concepts cc, Class<A> c, Object... params) { return uniqueConcept(cc, c, params); } 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); } } /** 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 void touchConcept(Concept c) { if (c != null) c.change(); } static String nChanges(long n) { return n2(n, "change"); } static String nChanges(Collection l) { return nChanges(l(l)); } static String squareBracket(String s) { return "[" + s + "]"; } static String hresponstable() { return hcss_responstable(); } 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 "<script src=\"https://code.jquery.com/jquery-3.2.1.min.js\"></script>"; } static String hjs_selectize() { return "\r\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/js/standalone/selectize.min.js\" integrity=\"sha256-+C0A5Ilqmu4QcSPxrlGpaZxJ04VjsRjKu+G82kl5UJk=\" crossorigin=\"anonymous\"></script>\r\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/css/selectize.bootstrap3.min.css\" integrity=\"sha256-ze/OEYGcFbPRmvCnrSeKbRTtjG4vGLHXgOqsyLFTRjg=\" crossorigin=\"anonymous\" />\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 String joinWithVBar(Iterable<String> l) { return join(" | ", l); } static String joinWithVBar(String... l) { return joinWithVBar(asList(l)); } static boolean neqOneOf(Object o, Object... l) { for (Object x : l) if (eq(o, x)) return false; return true; } 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; } // 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 <A> String joinMap(Iterable<A> l, IF1<A, String> f) { return joinMap(f, l); } static <A> String joinMap(IF1<A, String> f, Iterable<A> l) { return join(map(f, l)); } static <A, B> String joinMap(String separator, Map<A, B> map, IF2<A, B, String> f) { return join(separator, map(map, f)); } static String htmlEncode_nlToBr_withIndents(String s) { return nlToBr_withIndents(htmlEncode(s)); } static String joinNemptiesWithVBar(String... strings) { return joinNempties(" | ", strings); } static String joinNemptiesWithVBar(Collection<String> strings) { return joinNempties(" | ", strings); } static <A> HashSet<A> litset(A... items) { return lithashset(items); } static <A extends Concept> int countConcepts(Concepts concepts, Class<A> c, Object... params) { return concepts.countConcepts(c, params); } static <A extends Concept> int countConcepts(Class<A> 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 <A extends Concept> int countConcepts(Concepts concepts, String className) { return concepts.countConcepts(className); } static int countConcepts(Concepts concepts) { return concepts.countConcepts(); } 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 <A, B> int indexOfPred(List<A> l, IF1<A, B> pred) { return indexOfPred(l, (Object) pred); } static boolean cic(Collection<String> l, String s) { return containsIgnoreCase(l, s); } static boolean cic(Collection<Symbol> 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 Object first(Object list) { return first((Iterable) list); } static <A> A first(List<A> list) { return empty(list) ? null : list.get(0); } static <A> A first(A[] bla) { return bla == null || bla.length == 0 ? null : bla[0]; } static <A, B> Pair<A, B> first(Map<A, B> map) { return mapEntryToPair(first(entrySet(map))); } static <A, B> Pair<A, B> first(MultiMap<A, B> mm) { if (mm == null) return null; var e = first(mm.data.entrySet()); if (e == null) return null; return pair(e.getKey(), first(e.getValue())); } static <A> A first(IterableIterator<A> i) { return first((Iterator<A>) i); } static <A> A first(Iterator<A> i) { return i == null || !i.hasNext() ? null : i.next(); } static <A> A first(Iterable<A> i) { if (i == null) return null; Iterator<A> 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, B> A first(Pair<A, B> p) { return p == null ? null : p.a; } static <A, B, C> A first(T3<A, B, C> t) { return t == null ? null : t.a; } static Byte first(byte[] l) { return empty(l) ? null : l[0]; } static <A> A first(A[] l, IF1<A, Boolean> pred) { return firstThat(l, pred); } static <A> A first(Iterable<A> l, IF1<A, Boolean> pred) { return firstThat(l, pred); } static <A> A first(IF1<A, Boolean> pred, Iterable<A> l) { return firstThat(pred, l); } static <A> A first(AppendableChain<A> a) { return a == null ? null : a.element; } static String hcrud_mergeTables(String table1, String table2, String connector) { List<String> tok1 = htmlTok(table1), tok2 = htmlTok(table2); List<String> tbl1 = first(findContainerTag(tok1, "table")); List<String> tbl2 = first(findContainerTag(tok2, "table")); if (tbl1 == null) return table2; if (tbl2 == null) return table1; List<List<String>> rows1 = findContainerTag(tbl1, "tr"); List<List<String>> rows2 = findContainerTag(tbl2, "tr"); print("rows1: " + l(rows1) + ", rows2: " + l(rows2)); int nRows = max(l(rows1), l(rows2)); List<String> rows = new ArrayList(); for (int i = 0; i < nRows; i++) { List<String> row1 = dropFirstTwoAndLastTwo(get(rows1, i)); List<String> row2 = dropFirstTwoAndLastTwo(get(rows2, i)); row2 = dropFirst(row2, indexOfIC(row2, "</td>")+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 String targetBlank(String link, Object contents, Object... params) { return hrefBlank(link, contents, params); } static <A> void addInFront(List<A> c, A a) { addFirst(c, a); } static void addInFront(Container c, Component... l) { containerAddFirst(c, l); } 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 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 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()))); } 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; } // e. g. <input name="thefile" type="file" size="50" maxlength="500000" accept="image/png,image/jpeg"> static String hfileupload(Object... params) { return hinputtag("", paramsPlus_noOverwrite(params, "type" , "file", "name" , "thefile")); } 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 boolean isSubclassOf(Class a, Class b) { return isSubclass(a, b); } // 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<String> entries) { return hPopDownButton(toStringArray(entries)); } static List<Concept> allBackRefs(Concept c) { IdentityHashMap<Concept, Boolean> l = new IdentityHashMap(); if (c != null && c.backRefs != null) for (Concept.Ref r : cloneList(c.backRefs)) l.put(r.concept(), true); return keysList(l); } static <A extends Concept> List<A> sortedByConceptID(Collection<A> c) { return sortedByCalculatedField(__64 -> conceptID(__64), c); } 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 String span_title(String title, Object contents) { return spanTitle(title, contents); } static <A> List<A> takeFirst(List<A> l, int n) { return l(l) <= n ? l : newSubListOrSame(l, 0, n); } static <A> List<A> takeFirst(int n, List<A> 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 <A> List<A> takeFirst(int n, Iterator<A> 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 <A> List<A> takeFirst(int n, Iterable<A> i) { if (i == null) return null; return i == null ? null : takeFirst(n, i.iterator()); } static <A> List<A> takeFirst(int n, IterableIterator<A> i) { return takeFirst(n, (Iterator<A>) i); } static int[] takeFirst(int n, int[] a) { return takeFirstOfIntArray(n, a); } static short[] takeFirst(int n, short[] a) { return takeFirstOfShortArray(n, a); } static byte[] takeFirst(int n, byte[] a) { return takeFirstOfByteArray(n, a); } static byte[] takeFirst(byte[] a, int n) { return takeFirstOfByteArray(n, a); } static double[] takeFirst(int n, double[] a) { return takeFirstOfDoubleArray(n, a); } static double[] takeFirst(double[] a, int n) { return takeFirstOfDoubleArray(n, a); } static String addParamsToURL(String url, Map params) { return appendQueryToURL(url, params); } static String addParamsToURL(String url, Object... data) { return appendQueryToURL(url, data); } 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 <A> A firstThat(Iterable<A> l, IF1<A, Boolean> pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static <A> A firstThat(A[] l, IF1<A, Boolean> pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static <A> A firstThat(IF1<A, Boolean> pred, Iterable<A> l) { return firstThat(l, pred); } static <A> A firstThat(IF1<A, Boolean> pred, A[] l) { return firstThat(l, pred); } 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 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 <A> Set<A> joinSets(Collection<A>... l) { Set<A> set = similarEmptySet(first(l)); for (Collection<A> o : l) if (o != null) set.addAll(o); return set; } static <A, B extends A> void addAll(Collection<A> c, Iterable<B> b) { if (c != null && b != null) for (A a : b) c.add(a); } static <A, B extends A> boolean addAll(Collection<A> c, Collection<B> b) { return c != null && b != null && c.addAll(b); } static <A, B extends A> boolean addAll(Collection<A> c, B... b) { return c != null && b != null && c.addAll(Arrays.asList(b)); } static <A, B> Map<A, B> addAll(Map<A, B> a, Map<? extends A,? extends B> b) { if (a != null && b != null) a.putAll(b); return a; } static <A extends Container> A addAll(A c, Collection<? extends Component> components) { return addComponents(c, components); } static <A extends Container> A addAll(A c, Component... components) { return addComponents(c, components); } 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 String shortName(Object o) { return shortClassName(o); } static <A> A replaceIfEquals(A a, A b, A c) { return replaceIfEqual(a, b, c); } static String dropPrefix(String prefix, String s) { return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s; } static String className(Object o) { return getClassName(o); } static Type type_LS() { return new ParameterizedTypeImpl(null, List.class, String.class); } static List<String> tlft(String s) { return toLinesFullTrim(s); } static List<String> tlft(File f) { return toLinesFullTrim(f); } static String lines_rtrim(Collection lines) { return rtrim_fromLines(lines); } 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(__65 -> htmlEncode2(__65), list), params); } 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 String htmlEncode2_nlToBr(String s) { return htmlEncode_nlToBr(s); } static <A, B> void replaceMap(Map<A, B> dest, Map<A, B> 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 <A, B> String renderColonProperties(List<Pair<A, B>> l) { return formatColonProperties(l); } static String renderColonProperties(Object... params) { return renderColonProperties(litorderedmap(params)); } static String ol(String... list) { return ol(asList(list)); } static String ol(List<String> 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 <A, B> Map<A, B> cloneMap(Map<A, B> 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 <A, B> List<B> cloneMap(Iterable<A> l, IF1<A, B> f) { List x = emptyList(l); if (l != null) for (A o : cloneList(l)) x.add(f.get(o)); return x; } static <A> List<A> itemPlus(A a, Collection<A> l) { return itemPlusList(a, l); } static <A, B> Set<A> keys(Map<A, B> map) { return map == null ? new HashSet() : map.keySet(); } // convenience shortcut for keys_gen static Set keys(Object map) { return keys((Map) map); } static <A> Set<A> keys(MultiSet<A> ms) { return ms.keySet(); } static <A, B> Set<A> keys(MultiMap<A, B> mm) { return mm.keySet(); } static <A, B> Set<A> keys(MultiSetMap<A, B> mm) { return mm.keySet(); } static <A> String pnlToStringWithEmptyLines_rtrim(Iterable<A> l) { return rtrim(pnlToStringWithEmptyLines(l)); } static boolean isURL(String s) { return startsWithOneOf(s, "http://", "https://", "file:"); } 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 hcrud_imagePreviewStyle() { return "max-width: 200px; max-height: 100px; width: auto; height: auto"; } static String himgsnippet(String imageID, Object... params) { return hsnippetimg(imageID, params); } static String toK_str(long l) { return str_toK(l); } static long fileSize(String path) { return getFileSize(path); } static long fileSize(File f) { return getFileSize(f); } 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<numFullGroups; i++) { int ch0 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); int ch1 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); int ch2 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); int ch3 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 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 <A extends Concept> A uniqCI(Class<A> c, Object... params) { return uniqueConcept(db_mainConcepts(), c, params); } static <A extends Concept> A uniqCI(Concepts cc, Class<A> 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 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> A withDBLock(F0<A> 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> A withDBLock(Concepts concepts, F0<A> r) { return (A) withDBLock(concepts, (Object) r); } static <A> A withDBLock(Concept concept, IF0<A> r) { return (A) withDBLock(concept._concepts, r); } static <A extends Concept> List<A> cdelete(Class<A> c, Object... params) { return deleteConcepts(c, params); } static void cdelete(Concept c) { deleteConcept(c); } static <A extends Concept> void cdelete(Collection<A> c) { for (A a : cloneList(c)) cdelete(a); } 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 <A, B> List<B> lmap(IF1<A, B> f, Iterable<A> l) { return lambdaMap(f, l); } static <A, B> List<B> lmap(IF1<A, B> f, A[] l) { return lambdaMap(f, l); } static String htmlEncode2_gen(Object o) { return htmlEncode2(strOrEmpty(o)); } static <A> List<A> reversed(Iterable<A> l) { return reversedList(l); } static <A> List<A> reversed(A[] l) { return reversedList(asList(l)); } static String reversed(String s) { return reversedString(s); } static <A extends Concept> A conceptWhereCI(Class<A> c, Object... params) { return findConceptWhereCI(c, params); } static Concept conceptWhereCI(String c, Object... params) { return findConceptWhereCI(db_mainConcepts(), c, params); } static <A extends Concept> A conceptWhereCI(Concepts concepts, Class<A> c, Object... params) { return findConceptWhereCI(concepts, c, params); } static Concept conceptWhereCI(Concepts concepts, String c, Object... params) { return findConceptWhereCI(concepts, c, params); } static TreeSet<String> litciset(String... items) { TreeSet<String> set = caseInsensitiveSet(); for (String a : items) set.add(a); return set; } static TreeSet<Symbol> litciset(Symbol... items) { TreeSet<Symbol> 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 int countPred(Iterable c, Object pred) { return nfilter(c, pred); } static int countPred(Object pred, Iterable c) { return nfilter(pred, c); } static <A> int countPred(Iterable<A> c, IF1<A, Boolean> 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 String loadTextFileOrCreateWithRandomID(File f) { String id = trim(loadTextFile(f)); if (empty(id)) saveTextFile(f, id = aGlobalID()); return id; } static File secretProgramFile(String progID, String fileName) { return new File(getSecretProgramDir(progID), fileName); } static File secretProgramFile(String fileName) { return secretProgramFile(getProgramID(), fileName); } static String dropParamFromURL(String url, String key) { Map<String, String> params = paramsFromURL(url); if (!containsKey(params, key)) return url; params.remove(key); return addAnchorToURL( appendParamsToURL(urlWithoutQueryAndAnchor(url), params), anchorFromURL(url)); } static String htitle(String title) { return hfulltag("title", htmlencode_noQuotes(title)); } static String hfullcenter(Object contents, Object... __) { return tag("table", tr(td(contents, "align" , "center")), paramsPlus(__, "width" , "100%", "height" , "100%")); } 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 pUnlessEmpty(String s, Object... __) { return empty(s) ? "" : p(s, __); } static String joinNemptiesWithSpace(String... strings) { return joinNempties(" ", strings); } static String joinNemptiesWithSpace(Collection<String> strings) { return joinNempties(" ", strings); } static String hbuttonLink(String url, String text, Object... params) { return hbuttonOnClick(text, "location.href=" + jsQuote(url) + ";", params); } static String h3_htmlEncode(Object contents, Object... params) { return h3(htmlEncode2(str(contents)), params); } static ContentsIndexedList<String> ciContentsIndexedList() { return new ContentsIndexedList<String>(ciMap()); } static String unnullForIteration(String s) { return s == null ? "" : s; } static <A> Collection<A> unnullForIteration(Collection<A> l) { return l == null ? immutableEmptyList() : l; } static <A> List<A> unnullForIteration(List<A> 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 <A, B> Map<A, B> unnullForIteration(Map<A, B> l) { return l == null ? immutableEmptyMap() : l; } static <A> Iterable<A> unnullForIteration(Iterable<A> i) { return i == null ? immutableEmptyList() : i; } static <A> 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 <A, B> Pair<A, B> unnullForIteration(Pair<A, B> p) { return p != null ? p : new Pair(null, null); } static long unnullForIteration(Long l) { return l == null ? 0L : l; } static <A, B> Set<Map.Entry<A,B>> _entrySet(Map<A, B> map) { return map == null ? Collections.EMPTY_SET : map.entrySet(); } static <A, B> Map<A, B> mapPlus_inFront(Map<A, B> m, Object... data) { LinkedHashMap m2 = new LinkedHashMap(); litmap_impl(m2, data); putAll(m2, m); return m2; } static <A> int addAndReturnIndex(List<A> l, A a) { if (l == null) return -1; int idx = l.size(); l.add(a); return idx; } static <A> void listSet(List<A> l, int i, A a, A emptyElement) { if (i < 0) return; while (i >= l(l)) l.add(emptyElement); l.set(i, a); } static <A> void listSet(List<A> l, int i, A a) { listSet(l, i, a, null); } static String formatCSVFileForExcel2(List<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 <A> List<A> itemPlusList(A a, Collection<A> l) { return concatLists(ll(a), l); } static <A> Set<A> putSetElementsFirst(Collection<A> cl, Collection<A> elements) { Set<A> inputSet = asSet(cl); LinkedHashSet<A> set = new LinkedHashSet(); for (A a : unnullForIteration(elements)) if (contains(inputSet, a)) set.add(a); addAll(set, cl); // keep order return set; } static <A> Set<A> putSetElementsFirst(Collection<A> cl, A... elements) { return putSetElementsFirst(cl, asList(elements)); } static Cache<MultiMap<String, CountryDialCode>> countryDialCodesMultiMap_cache = new Cache<>(() -> countryDialCodesMultiMap_load()); static MultiMap<String, CountryDialCode> countryDialCodesMultiMap() { return countryDialCodesMultiMap_cache.get(); } static MultiMap<String, CountryDialCode> countryDialCodesMultiMap_load() { return treeMultiMapIndexByField("dialCode", countryDialCodes()); } static List<String> 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); } 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(); } // 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 <A, B, C extends Iterable<B>> List<B> concatMap(Iterable<A> l, IF1<A, C> f) { return concatMap(l, (Object) f); } static <A, B, C extends Iterable<B>> List<B> concatMap(IF1<A, C> f, Iterable<A> l) { return concatMap(l, f); } static <A, B, C extends Iterable<B>> List<B> concatMap(IF1<A, C> f, A[] l) { return concatMap((Object) f, l); } static Object rcall(String method, Object o, Object... args) { return call_withVarargs(o, method, args); } // get purpose 1: access a list/array/map (safer version of x.get(y)) static <A> A get(List<A> l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } // seems to conflict with other signatures /*static <A, B> B get(Map<A, B> map, A key) { ret map != null ? map.get(key) : null; }*/ static <A> 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 String dropPrefix_trim(String prefix, String s) { return dropPrefixTrim(prefix, s); } static boolean neq(Object a, Object b) { return !eq(a, b); } static Object serveJSON(Object... data) { return serveJSON_shallowLineBreaks(jsonPrepareData(data)); } static String formatLocalDateWithSeconds(long time) { return localDateWithSeconds(time); } static String formatLocalDateWithSeconds() { return localDateWithSeconds(); } static String imageSnippetURLOrEmptyGIF(String snippetID) { return empty(snippetID) ? smallestTransparentGIFDataURI() : snippetImageURL(snippetID); } static String makePostData(Map map) { StringBuilder buf = new StringBuilder(); for (Map.Entry<Object, Object> 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 Scorer scorerWithSuccessesAndErrors() { Scorer scorer = new Scorer(); scorer.successes = new ArrayList(); scorer.errors = new ArrayList(); return scorer; } 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 <A> List<A> concatLists(Iterable<A>... lists) { List<A> l = new ArrayList(); if (lists != null) for (Iterable<A> list : lists) addAll(l, list); return l; } static <A> List<A> concatLists(Collection<? extends Iterable<A>> lists) { List<A> l = new ArrayList(); if (lists != null) for (Iterable<A> list : lists) addAll(l, list); return l; } static <A> List<A> sortByFieldDesc(Collection<A> c, final String field) { return sortedByFieldDesc(c, field); } static <A> List<A> sortByFieldDesc(String field, Collection<A> 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 extends Concept> A lowestConceptByField(Class<A> c, String field) { return lowestConceptByField(db_mainConcepts(), c, field); } static <A extends Concept> A lowestConceptByField(Concepts concepts, Class<A> c, String field) { // indexed IFieldIndex<A, Object> 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)); } // TODO: remove the O versions static java.util.Timer doAfter(long delay, Object r) { return doLater(delay, r); } static java.util.Timer doAfter(long delay, Runnable r) { return doLater(delay, (Object) r); } static java.util.Timer doAfter(double delaySeconds, Object r) { return doLater(delaySeconds, r); } static java.util.Timer doAfter(double delaySeconds, Runnable r) { return doLater(delaySeconds, r); } static <A extends Concept> A registerConcept(A c) { return registerConcept(db_mainConcepts(), c); } static <A extends Concept> A registerConcept(Concepts cc, A c) { { if (cc != null) cc.register(c); } return c; } static void indexConceptFieldDesc(Class<? extends Concept> 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<? extends Concept> c, String field) { if (!isConceptFieldIndexed(concepts, c, field)) new ConceptFieldIndexDesc(concepts, c, field); } static <A extends Concept> ConceptFieldIndexCI<A> indexConceptFieldCI(Class<A> c, String field) { return indexConceptFieldCI(db_mainConcepts(), c, field); } static <A extends Concept> ConceptFieldIndexCI<A> indexConceptFieldCI(Concepts concepts, Class<A> c, String field) { ConceptFieldIndexCI<A> idx = getConceptFieldCIIndex(concepts, c, field); return idx != null ? idx : new ConceptFieldIndexCI(concepts, c, field); } static void indexConceptField(Class<? extends Concept> c, String field) { indexConceptField(db_mainConcepts(), c, field); } static void indexConceptField(Concepts concepts, Class<? extends Concept> 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 <A> A assertNotNull(A a) { assertTrue(a != null); return a; } static <A> A assertNotNull(String msg, A a) { assertTrue(msg, a != null); return a; } static <A> 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 void indexSingletonConcept(Concepts cc, Class<? extends Concept> c) { indexConceptField(cc, c, "_dummy"); } static void indexSingletonConcept(Class<? extends Concept> 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 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 String nlToBr(String s) { return s.replace("\n", "<br>\n"); } static String html_emojisToUnicode(String s) { return join(mapNonCodeTokens(__66 -> emojisToUnicode(__66), htmlTok(s))); } static <A> A last(List<A> l) { return empty(l) ? null : l.get(l.size()-1); } static char last(String s) { return empty(s) ? '#' : s.charAt(l(s)-1); } static byte last(byte[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } 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> A last(A[] a) { return l(a) != 0 ? a[l(a)-1] : null; } static <A> A last(Iterator<A> it) { A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static <A> A last(Collection<A> 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<A> it = iterator(l); A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static <A> A last(SortedSet<A> l) { return l == null ? null : l.last(); } static <A> A last(CompactLinkedHashSet<A> set) { return set == null ? null : set.last(); } 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 boolean fileExists(String path) { return path != null && new File(path).exists(); } static boolean fileExists(File f) { return f != null && f.exists(); } 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<String> splitAtVerticalBar(String s) { return s == null ? new ArrayList() : trimAll(asList(s.split("\\|"))); } static <A> A second(List<A> l) { return get(l, 1); } static <A> A second(Iterable<A> l) { if (l == null) return null; Iterator<A> it = iterator(l); if (!it.hasNext()) return null; it.next(); return it.hasNext() ? it.next() : null; } static <A> A second(A[] bla) { return bla == null || bla.length <= 1 ? null : bla[1]; } static <A, B> B second(Pair<A, B> p) { return p == null ? null : p.b; } static <A, B, C> B second(T3<A, B, C> t) { return t == null ? null : t.b; } static <A> A second(Producer<A> 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 <A, B> B second(Either<A, B> e) { return e == null ? null : e.bOpt(); } static String hbuttonOnClick_returnFalse(String text, String onClick, Object... params) { return hfulltag("button", text, paramsPlus(params, "onclick" , addSuffix(trim(onClick), ";") + " return false;")); } static String lines(Iterable lines) { return fromLines(lines); } static String lines(Object[] lines) { return fromLines(asList(lines)); } static List<String> lines(String s) { return toLines(s); } // convenience map call static <A> String lines(Iterable<A> l, IF1<A, String> f) { return mapToLines(l, f); } static String stringUnless(boolean b, String s) { return b ? "" : s; } static List<String> text_multipleChoiceSplit(String input, String multipleChoiceSeparator) { return trimAll(splitAt(input, dropSpaces(multipleChoiceSeparator))); } static TreeSet<String> asCISet(Iterable<String> c) { return toCaseInsensitiveSet(c); } static TreeSet<String> asCISet(String... x) { return toCaseInsensitiveSet(x); } static String joinWithBR(Iterable l) { return join("<br>\n", l); } static String joinWithBR(String... l) { return joinWithBR(asList(l)); } 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 <A> boolean contains(Producer<A> 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; } static boolean contains(Rect r, Pt p) { return rectContains(r, p); } public static <A> String join(String glue, Iterable<A> strings) { if (strings == null) return ""; if (strings instanceof Collection) { if (((Collection) strings).size() == 1) return str(first((Collection) strings)); } StringBuilder buf = new StringBuilder(); Iterator<A> i = strings.iterator(); if (i.hasNext()) { buf.append(i.next()); while (i.hasNext()) buf.append(glue).append(i.next()); } return buf.toString(); } public static String join(String glue, String... strings) { return join(glue, Arrays.asList(strings)); } public static String join(String glue, Object... strings) { return join(glue, Arrays.asList(strings)); } static <A> String join(Iterable<A> strings) { return join("", strings); } static <A> String join(Iterable<A> 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 <A> List<A> listMinusSet(Iterable<A> l, Collection<? extends A> stuff) { if (l == null) return null; if (empty(stuff)) return asList(l); Set<? extends A> set = asSet(stuff); List<A> l2 = new ArrayList(); for (A a : l) if (!set.contains(a)) l2.add(a); return l2; } static <A> List<A> listMinusSet(Iterable<A> l, Collection<A> stuff, Collection<? extends A> stuff2) { return listMinusSet(listMinusSet(l, stuff), stuff2); } 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 boolean eqicOneOf(String s, String... l) { for (String x : l) if (eqic(s, x)) return true; return false; } 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 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 jsBackLink() { return "javascript:history.go(-1)"; } 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 <A> boolean any(Object pred, Iterable<A> l) { if (l != null) for (A a : l) if (isTrue(callF(pred, a))) return true; return false; } static <A> boolean any(IF1<A, Boolean> pred, Iterable<A> l) { if (l != null) for (A a : l) if (pred.get(a)) return true; return false; } static <A> boolean any(Iterable<A> l, IF1<A, Boolean> pred) { return any(pred, l); } static <A> boolean any(A[] l, IF1<A, Boolean> pred) { if (l != null) for (A a : l) if (pred.get(a)) return true; return false; } static boolean any(Iterable<Boolean> l) { if (l != null) for (Boolean a : l) if (isTrue(a)) return true; return false; } static String ipToCountry2020_safe(String ip) { try { return or2(ipToCountry2020(ip), "?"); } catch (Throwable __e) { printStackTrace(__e); } return "?"; } static String replaceSquareBracketVars(String s, Object... params) { if (empty(params)) return s; Map<String, Object> vars = mapKeys(__67 -> deSquareBracket(__67), (Map<String, Object>) 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<String> trimAll(String... l) { return trimAll(asList(l)); } static List<String> trimAll(Collection<String> l) { List<String> l2 = new ArrayList(); if (l != null) for (String s : l) l2.add(trim(s)); return l2; } // TODO: returns empty first, but not empty last static List<String> splitAt(String s, String splitter) { if (empty(splitter)) return null; // avoid endless loop List<String> 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 String dropSpaces(String s) { return unnull(s).replace(" ", ""); } static <A> A lastThat(List<A> 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> A lastThat(Object pred, List<A> l) { return lastThat(l, pred); } static <A> A lastThat(IF1<A, Boolean> pred, List<A> l) { return lastThat((Object) pred, l); } static <A> A lastThat(List<A> l, IF1<A, Boolean> pred) { return lastThat(pred, l); } 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 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 addSlashPrefix(String s) { return addPrefix("/", s); } static boolean confirmOKCancel(final Component owner, final String msg) { return isTrue(swingAndWait(new F0<Object>() { 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 "return JOptionPane.showConfirmDialog(owner,\r\n msg, \"JavaX\", JOptionPane...."; }})); } static boolean confirmOKCancel(String msg) { return confirmOKCancel(null, msg); } 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(Object text) { return infoMessage(strOrNull(text)); } 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); } /** writes safely (to temp file, then rename) */ static File saveTextFile(String fileName, String contents) throws IOException { /*ifdef CriticalActions temp beginCriticalAction("Saving file " + fileName + " (" + l(contents) + " chars)"); endifdef*/ 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; } static File saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); return fileName; } catch (Exception __e) { throw rethrow(__e); } } static void inputText(String msg, String defaultText, IVF1<String> action) { inputText(msg, defaultText, (Object) action); } static void inputText(String msg, IVF1<String> action) { inputText(msg, (Object) action); } // legacy signatures static void inputText(String msg, Object action) { inputText(msg, "", action); } // action: voidfunc(S) static void inputText(String msg, String defaultText, Object action) { final Object threadInfo = _threadInfo(); swingLater(new Runnable() { public void run() { try { final JTextField tf = jtextfield(defaultText); String title = joinNemptiesWithVBar(msg, programNameOrNull()); 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 = joinNempti..."; }}); } // pred receives the params of opening tag as string (e.g. <if $bla> => "$bla") static String html_evaluateIfTags(String html, IF1<String, Boolean> pred) { List<String> tok = htmlTok(html); boolean anyChange = false; while (true) { ping(); List<String> ifTag = first(findContainerTag(tok, "if")); if (ifTag == null) break; anyChange = true; int iElse = indexOfIC(ifTag, "<else>"); 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 <else> (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 dropDollarPrefix(String s) { return dropPrefix("$", s); } // f takes variable without $ sign. if it returns null, variable is kept static String replaceDollarVars_dyn(String s, IF1<String, String> 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 <A> A syncGet(List<A> l, int idx) { if (l == null || idx < 0) return null; synchronized(l) { return idx < l(l) ? l.get(idx) : null; } } static <A, B> B syncGet(Map<A, B> map, A a) { if (map == null) return null; synchronized(map) { return map.get(a); } } 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 <A> IterableIterator<Pair<Integer, A>> iterateWithIndex1(Collection<A> l) { return iterateListWithIndex(l, 1); } static <A> IterableIterator<Pair<Integer, A>> iterateWithIndex1(A[] l) { return iterateListWithIndex(asVirtualList(l), 1); } static <A, B> A pairA(Pair<A, B> p) { return p == null ? null : p.a; } static <A, B> B pairB(Pair<A, B> p) { return p == null ? null : p.b; } 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 = strOrEmpty(o); return empty(s) ? "" : squareBracketed(s) + " "; } static int countWords(String s) { return numberOfWords2(s); } // tok should be the output of htmlcoarsetok static List<String> dropHTMLTags(List<String> tok) { return dropAllTags(tok); } // alternatively, call this convenient function static String dropHTMLTags(String html) { return dropAllTags(html); } 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 boolean isValidInternationalPhoneNumber(String s) { InternationalPhoneValidator v = new InternationalPhoneValidator(s); v.run(); if (v.error != null) print(v.error); return v.valid; } static <A, B> String formatColonProperties(Map<A, B> map) { return mapToLines(__68 -> joinPairWithColon(__68), mapToPairs(map)); } static <A, B> String formatColonProperties(List<Pair<A, B>> l) { return mapToLines(__69 -> joinPairWithColon(__69), l); } static <A, B> String formatColonProperties(Object... params) { return formatColonProperties(paramsToPairs(params)); } static void sendMailThroughScript(String senderInfo, String from, String to, String subject, String text) { List<String> 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 Lock appendToFile_lock = lock(); static boolean appendToFile_keepOpen = false; static HashMap<String, Writer> 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 programFile(String name) { return prepareProgramFile(name); } static File programFile(String progID, String name) { return prepareProgramFile(progID, name); } static <A> A syncNextToLast(List<A> l) { if (l == null) return null; synchronized(l) { return nextToLast(l); } } static <A> void syncRemoveLast(List<A> 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 <A> void syncReplaceCollection(Collection<A> dest, Collection<A> 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 Set asSet(Object[] array) { HashSet set = new HashSet(); for (Object o : array) if (o != null) set.add(o); return set; } static Set<String> asSet(String[] array) { TreeSet<String> set = new TreeSet(); for (String o : array) if (o != null) set.add(o); return set; } static <A> Set<A> asSet(Iterable<A> l) { if (l instanceof Set) return (Set) l; HashSet<A> 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 <A> Set<A> asSet(MultiSet<A> ms) { return ms == null ? null : ms.asSet(); } 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(__70 -> isHTMLRefreshTag(__70), 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)); } static String hcomment(String text) { return hcommentSafe(text); } // Use like this: renderVars(+x, +y) static String renderVars_struct(Object... params) { List<String> 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<String, Object> allConceptFieldsAsMap(Concept c) { Map<String, Object> map = new HashMap(); for (String field : unnullForIteration(conceptFields(c))) map.put(field, cget(c, field)); return map; } static <A, B> Collection<B> values(Map<A, B> map) { return map == null ? emptyList() : map.values(); } // convenience shortcut for values_gen static Collection values(Object map) { return values((Map) map); } static <A, B> Collection<B> values(MultiMap<A, B> mm) { return mm == null ? emptyList() : concatLists(values(mm.data)); } // Use like this: renderVars(+x, +y) static String renderVars_str(Object... params) { List<String> 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 String hreconnectingWebSockets() { return hjavascript_src_snippet(hreconnectingWebSockets_snippetID()); } 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 <A extends Concept> int conceptCount(Concepts concepts, Class<A> c, Object... params) { return countConcepts(concepts, c, params); } static <A extends Concept> int conceptCount(Class<A> c, Object... params) { return countConcepts(c, params); } static int conceptCount() { return countConcepts(); } static int conceptCount(String className) { return countConcepts(className); } static <A extends Concept> int conceptCount(Concepts concepts, String className) { return countConcepts(concepts, className); } static int conceptCount(Concepts concepts) { return countConcepts(concepts); } static File conceptsFile(String progID) { return getProgramFile(progID, conceptsFileName()); } static File conceptsFile() { return conceptsFile(dbProgramID()); } static File conceptsFile(Concepts concepts) { return concepts.conceptsFile(); } 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 hjavascript(String scriptOrURL, Object... __) { if (isRelativeOrAbsoluteURL(scriptOrURL) && !startsWithOneOf(scriptOrURL, "//", "/*")) return hjavascript_src(scriptOrURL, __); else return tag("script", scriptOrURL, paramsPlus(__, "type" , "text/javascript")); } 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 <A> boolean syncAdd(Collection<A> c, A b) { if (c == null) return false; synchronized(collectionMutex(c)) { return c.add(b); } } static <A> void syncAdd(List<A> l, int idx, A b) { if (l != null) synchronized(collectionMutex(l)) { l.add(idx, b); } } 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 int random(int n) { return random(n, defaultRandomGenerator()); } static int random(int n, Random r) { return random(r, n); } static int random(Random r, int n) { return n <= 0 ? 0 : getRandomizer(r).nextInt(n); } static double random(double max) { return random()*max; } static double random() { return defaultRandomGenerator().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> A random(List<A> l) { return oneOf(l); } static <A> A random(Collection<A> c) { if (c instanceof List) return random((List<A>) c); int i = random(l(c)); return collectionGet(c, i); } static int random(IntRange r) { return random(r.start, r.end); } static <A, B> Pair<A, B> random(Map<A, B> 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 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 himgsrc(String src, Object... params) { return tag("img", "", arrayPlus(params, "src", src)); } static boolean deleteFile(File file) { return file != null && file.delete(); } static BufferedImage loadImage2(String snippetIDOrURL) { return loadBufferedImage(snippetIDOrURL); } static BufferedImage loadImage2(File file) { return loadBufferedImage(file); } static String mp3mimeType() { return "audio/mpeg"; } static long toMS(double seconds) { return (long) (seconds*1000); } 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 String orEmptyQuotes(String s) { return or2(s, "\"\""); } static <A, B> List<Pair<A, B>> zipTwoListsToPairs_longer(List<A> l1, List<B> l2) { int n = max(l(l1), l(l2)); List<Pair<A, B>> out = emptyList(n); for (int i = 0; i < n; i++) addPair(out, _get(l1, i), _get(l2, i)); return out; } static <A> A or(A a, A b) { return a != null ? a : b; } static <A, B> B syncPut(Map<A, B> 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<String> a, String b) { return indexOfIgnoreCase(a, b); } static int indexOfIC(List<String> a, String b, int i) { return indexOfIgnoreCase(a, b, i); } static int indexOfIC(String[] a, String b) { return indexOfIC(a, b, 0); } static int indexOfIC(String[] 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> A getOrKeep(Map<A, ? extends A> map, A a) { if (map == null) return a; A v = map.get(a); return v != null ? v : a; } static Concept cnew(String name, Object... values) { return cnew(db_mainConcepts(), name, values); } static Concept cnew(Concepts concepts, String name, Object... values) { Class<? extends Concept> 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 extends Concept> A cnew(Class<A> cc, Object... values) { return cnew(db_mainConcepts(), cc, values); } static <A extends Concept> A cnew(Concepts concepts, Class<A> 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 <A, B> Map<A, B> filterKeys(Map<A, B> map, Object f) { return filterMapByFunctionOnKey(map, f); } static <A, B> Map<A, B> filterKeys(Object f, Map<A, B> map) { return filterMapByFunctionOnKey(f, map); } static <A, B> Map<A, B> filterKeys(IF1<A, Boolean> f, Map<A, B> map) { return filterMapByFunctionOnKey((Object) f, map); } static <A, B> Map<A, B> filterKeys(Map<A, B> map, IF1<A, Boolean> f) { return filterKeys(f, map); } static IF1<String, Boolean> swic$(String prefix) { return s -> swic(s, prefix); } static <A> void syncRemoveAllExceptLast(List<A> 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<String> tok = javaTokWithBrackets(s); // commas (or) - "car, vehicle" List<String> l = tok_splitAtComma(tok); if (l(l) > 1) return new MMOPattern.Or(lambdaMap(__71 -> mmo2_parsePattern(__71), l)); // plus (and) - "give + money" l = tok_splitAtPlus(tok); if (l(l) > 1) return new MMOPattern.And(lambdaMap(__72 -> mmo2_parsePattern(__72), 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<IntRange> parseBusinessHours_pcall(String s) { try { return parseBusinessHours(s); } catch (Throwable __e) { printStackTrace(__e); } return null; } // convert things like "18-8" to "18-24, 0-8" static List<IntRange> splitBusinessHoursAtMidnight(List<IntRange> 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<IntRange> ranges, int i) { return any(ranges, r -> intRangeContains(r, i)); } 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 <A> List<A> syncL() { return syncList(); } static <A> int syncLengthLevel2(Collection<? extends Collection> l) { int sum = 0; for (Collection c : cloneList(l)) sum += syncL(c); return sum; } static <A> List<A> concatLists_syncIndividual(Iterable<A>... lists) { List<A> l = new ArrayList(); if (lists != null) for (Iterable<A> list : lists) syncAddAll(l, list); return l; } static <A> List<A> concatLists_syncIndividual(Collection<? extends Iterable<A>> lists) { List<A> l = new ArrayList(); if (lists != null) for (Iterable<A> list : lists) syncAddAll(l, list); return l; } static <A> List<A> syncListPlus(Collection<A> l, A... more) { if (l == null) return asList(more); synchronized(collectionMutex(l)) { return concatLists(l, asList(more)); } } static void add(BitSet bs, int i) { bs.set(i); } static <A> boolean add(Collection<A> 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> boolean syncNempty(Collection<A> l) { return !syncEmpty(l); } static <A> void syncClear(Collection<A> c) { if (c != null) synchronized(collectionMutex(c)) { c.clear(); } } static void syncClear(Map map) { if (map != null) synchronized(collectionMutex(map)) { map.clear(); } } static void deleteConcepts(Collection conceptsOrIDs) { db_mainConcepts().deleteConcepts(asList(conceptsOrIDs)); } static <A extends Concept> List<A> deleteConcepts(Class<A> c, Object... params) { return deleteConcepts(db_mainConcepts(), c, params); } static <A extends Concept> List<A> deleteConcepts(Concepts cc, Class<A> c, Object... params) { List<A> l = asList(findConceptsWhere(cc, c, params)); deleteConcepts(l); return l; } static <A extends Concept> List<A> deleteConcepts(Class<A> c, IF1<A, Boolean> pred) { return deleteConcepts(db_mainConcepts(), c, pred); } static <A extends Concept> List<A> deleteConcepts(Concepts cc, Class<A> c, IF1<A, Boolean> pred) { var l = filter(list(cc, c), pred); deleteConcepts(l); return l; } static List<Concept> deleteConcepts(Concepts cc) { return deleteConcepts(cc, Concept.class); } static <A> A syncPopLast(List<A> l) { if (l == null) return null; synchronized(l) { return popLast(l); } } static <A> List<A> syncPopLast(int n, List<A> l) { if (l == null) return null; synchronized(l) { return popLast(n, l); } } static <A> List<A> syncShallowCloneElements(List<A> l) { return syncLambdaMap(__73 -> shallowClone(__73), l); } static <A> List<A> syncAddOrCreate(List<A> l, A a) { if (l == null) l = syncList(); l.add(a); return l; } static <A> A syncLast(List<A> l) { if (l == null) return null; synchronized(l) { return last(l); } } static DynModule dm_current_mandatory() { return assertNotNull("No module set!", dm_currentModule()); } static boolean bareDBMode_on = false; static void bareDBMode() { bareDBMode(null); // default autoSaveInterval } static void bareDBMode(Integer autoSaveInterval) { bareDBMode_on = true; conceptsAndBot(autoSaveInterval); } static ThreadLocal<Boolean> DynamicObject_loading = or((ThreadLocal) get(getClass("x30_pkg.x30_util"), "DynamicObject_loading"), new ThreadLocal()); static ThreadLocal<Boolean> dynamicObjectIsLoading_threadLocal() { return DynamicObject_loading; } static AutoCloseable tempInterceptPrintIfNotIntercepted(F1<String, Boolean> f) { return print_byThread().get() == null ? tempInterceptPrint(f) : null; } // this should be on by default now I think, but it may break // legacy code... static ThreadLocal<Boolean> 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 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 strOrEmpty(Object o) { return o == null ? "" : str(o); } static String addSlash(String s) { return empty(s) || s.endsWith("/") ? s : s + "/"; } static boolean nemptyString(String s) { return s != null && s.length() > 0; } 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 int strL(String s) { return s == null ? 0 : s.length(); } static int listL(Collection l) { return l == null ? 0 : l.size(); } 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 <A> ArrayList<A> emptyList(Class<A> c) { return new ArrayList(); } //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<Thread, Object> ping_actions = newWeakHashMap(); static ThreadLocal<Boolean> 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 Map<Class, ArrayList<Method>> callF_cache = newDangerousWeakHashMap(); static <A> A callF(F0<A> f) { return f == null ? null : f.get(); } static <A, B> B callF(F1<A, B> f, A a) { return f == null ? null : f.get(a); } static <A> A callF(IF0<A> f) { return f == null ? null : f.get(); } static <A, B> B callF(IF1<A, B> f, A a) { return f == null ? null : f.get(a); } static <A, B> B callF(A a, IF1<A, B> f) { return f == null ? null : f.get(a); } static <A, B, C> C callF(F2<A, B, C> f, A a, B b) { return f == null ? null : f.get(a, b); } static <A, B, C> C callF(IF2<A, B, C> f, A a, B b) { return f == null ? null : f.get(a, b); } static <A> void callF(VF1<A> f, A a) { if (f != null) f.get(a); } static <A> void callF(A a, IVF1<A> f) { if (f != null) f.get(a); } static <A> void callF(IVF1<A> 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) { 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<Method> methods; synchronized(callF_cache) { methods = callF_cache.get(c); if (methods == null) methods = callF_makeCache(c); } int n = l(methods); if (n == 0) { if (f instanceof String) throw fail("Legacy call: " + f); 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<Method> callF_makeCache(Class c) { ArrayList<Method> 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; } // unclear semantics as to whether return null on null static <A> ArrayList<A> asList(A[] a) { return a == null ? new ArrayList<A>() : new ArrayList<A>(Arrays.asList(a)); } static ArrayList<Integer> asList(int[] a) { if (a == null) return null; ArrayList<Integer> l = emptyList(a.length); for (int i : a) l.add(i); return l; } static ArrayList<Long> asList(long[] a) { if (a == null) return null; ArrayList<Long> l = emptyList(a.length); for (long i : a) l.add(i); return l; } static ArrayList<Float> asList(float[] a) { if (a == null) return null; ArrayList<Float> l = emptyList(a.length); for (float i : a) l.add(i); return l; } static ArrayList<Double> asList(double[] a) { if (a == null) return null; ArrayList<Double> l = emptyList(a.length); for (double i : a) l.add(i); return l; } static ArrayList<Short> asList(short[] a) { if (a == null) return null; ArrayList<Short> l = emptyList(a.length); for (short i : a) l.add(i); return l; } static <A> ArrayList<A> asList(Iterator<A> it) { ArrayList l = new ArrayList(); if (it != null) while (it.hasNext()) l.add(it.next()); return l; } // disambiguation static <A> ArrayList<A> asList(IterableIterator<A> s) { return asList((Iterator) s); } static <A> ArrayList<A> asList(Iterable<A> 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 <A> ArrayList<A> asList(Producer<A> p) { ArrayList l = new ArrayList(); A a; if (p != null) while ((a = p.next()) != null) l.add(a); return l; } static <A> ArrayList<A> asList(Enumeration<A> e) { ArrayList l = new ArrayList(); if (e != null) while (e.hasMoreElements()) l.add(e.nextElement()); return l; } static <A> List<A> asList(Pair<A, A> p) { return p == null ? null : ll(p.a, p.b); } static void warnIfOddCount(Object... list) { if (odd(l(list))) printStackTrace("Odd list size: " + list); } static <A extends Concept> Object[] expandParams(Class<A> 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(__74 -> derefRef(__74), (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 <A> A getVar(IF0<A> v) { return v == null ? null : v.get(); } static <A> A getVar(Optional<A> v) { return v == null ? null : v.orElse(null); } static boolean isInstance(Class type, Object arg) { return type.isInstance(arg); } 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 getClassName(Object o) { return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName(); } static String dropSuffix(String suffix, String s) { return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static <A, B> Map<A, B> putAll(Map<A, B> a, Map<? extends A,? extends B> b) { if (a != null && b != null) a.putAll(b); return a; } static <A, B> MultiMap<A, B> putAll(MultiMap<A, B> a, Map<? extends A,? extends B> b) { if (a != null) a.putAll((Map) b); return a; } static <A, B> Map<A, B> putAll(Map<A, B> a, Object... b) { if (a != null) litmap_impl(a, b); return a; } static <A> List<A> sortedByFieldIC(Collection<A> c, final String field) { List<A> l = new ArrayList(c); sort(l, new Comparator<A>() { public int compare(A a, A b) { return compareIC((String) getOpt(a, field), (String) getOpt(b, field)); } }); return l; } static <A> List<A> sortedByFieldIC(String field, Collection<A> c) { return sortedByFieldIC(c, field); } static String hfulltag(String tag) { return hfulltag(tag, ""); } static String hfulltag(String tag, Object contents, Object... params) { return hopeningTag(tag, params) + str(contents) + "</" + tag + ">"; } 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; } 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 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[] 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 <A> List<A> dropFirst(List<A> l) { return dropFirst(1, l); } static <A> List<A> dropFirst(int n, Iterable<A> i) { return dropFirst(n, toList(i)); } static <A> List<A> dropFirst(Iterable<A> i) { return dropFirst(toList(i)); } static <A> List<A> dropFirst(int n, List<A> l) { return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size())); } static <A> List<A> dropFirst(List<A> 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 <A> int indexOf(List<A> 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 <A> int indexOf(List<A> l, int startIndex, A a) { return indexOf(l, a, startIndex); } static <A> int indexOf(List<A> 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 <A> 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 <A> int indexOf(Iterable<A> l, A a) { if (l == null) return -1; int i = 0; for (A x : l) { if (eq(x, a)) return i; i++; } return -1; } 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 <A extends Concept> A cDeref(Concept.Ref<A> ref) { return ref == null ? null : ref.get(); } // grabs only the specified keys from the map. static String hiddenFields(Map<String, String> map, Collection<String> 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<String, String> map, String... keys) { return hiddenFields(map, asList(keys)); } static String hjavascript_src(String src, Object... __) { return hfulltag("script", "", paramsPlus(__, "src", src)); } 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<String, Set> vm_busListenersByMessage_live_cache; static Map<String, Set> vm_busListenersByMessage_live() { if (vm_busListenersByMessage_live_cache == null) vm_busListenersByMessage_live_cache = vm_busListenersByMessage_live_load(); return vm_busListenersByMessage_live_cache;} static Map<String, Set> vm_busListenersByMessage_live_load() { return vm_generalHashMap("busListenersByMessage"); } static void ping_okInCleanUp() { if (ping_pauseAll || ping_anyActions) ping_impl(true); } // 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); } } // f: A -> Comparable static <A> List<A> sortByCalculatedFieldDesc_inPlace(List<A> l, final Object f) { sort(l, new Comparator<A>() { public int compare(A b, A a) { return stdcompare((Object) callF(f, a), (Object) callF(f, b)); } }); return l; } static <A> List<A> sortByCalculatedFieldDesc_inPlace(Object f, List<A> c) { return sortByCalculatedFieldDesc_inPlace(c, f); } static <T> void sort(T[] a, Comparator<? super T> c) { if (a != null) Arrays.sort(a, c); } static <T> void sort(T[] a) { if (a != null) Arrays.sort(a); } static void sort(int[] a) { if (a != null) Arrays.sort(a); } static <T> void sort(List<T> a, Comparator<? super T> c) { if (a != null) Collections.sort(a, c); } static void sort(List a) { if (a != null) Collections.sort(a); } 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 <A> List<A> objectsWhereFieldGreaterThan(Collection<A> c, String field, Object value) { List<A> l = new ArrayList(); for (A x : unnull(c)) if (cmp(getOpt(x, field), value) > 0) l.add(x); return l; } 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 + "</" + tag + ">"; } static String th(Object contents, Object... params) { return tag("th", contents, params); } static <A> List<A> takeLast(List<A> l, int n) { return newSubList(l, l(l)-n); } static <A> List<A> takeLast(int n, List<A> 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 String href(String link, Object contents, Object... params) { if (link == null) return str(contents); return hfulltag("a", contents, arrayPlus(params, "href", link)); } static long timestampToLong(Timestamp ts) { return ts == null ? 0 : ts.date; } static int howManySecondsAgo(long timestamp) { return iround(toSeconds(now()-timestamp)); } static int iround(double d) { return (int) Math.round(d); } static int iround(Number n) { return iround(toDouble(n)); } 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 + "</" + tag + ">"; } static String hlabelFor(String id, Object contents) { String s = strOrEmpty(contents); return empty(s) ? "" : htag("label", s, "for" , id); } static String hdiv(Object contents, Object... params) { return div(contents, params); } static BigInteger bigint(String s) { return new BigInteger(s); } static BigInteger bigint(long l) { return BigInteger.valueOf(l); } 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 F0<AutoCloseable> dm_rEnter() { return dm_rEnter(dm_current()); } static F0<AutoCloseable> dm_rEnter(DynModule mod) { return mod == null ? null : new F0<AutoCloseable>() { public AutoCloseable get() { try { return mod.enter(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return 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 void _handleError(Error e) { call(javax(), "_handleError", e); } static Map<Thread, Object> vm_threadInterruptionReasonsMap() { return vm_generalWeakSubMap("Thread interruption reasons"); } static String strOr(Object o, String ifNull) { return o == null ? ifNull : str(o); } 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 TreeMap litCIMap(Object... x) { TreeMap map = caseInsensitiveMap(); litmap_impl(map, x); return map; } static <A> Set<A> synchroWeakHashSet() { return Collections.newSetFromMap((Map) newWeakHashMap()); } 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<String> 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 <A, B> List<B> getAll(Map<A, B> map, Collection<A> l) { return lookupAllOpt(map, l); } static <A, B> List<B> getAll(Collection<A> l, Map<A, B> map) { return lookupAllOpt(map, l); } static <A, B extends IF0<A>> List<A> getAll(Iterable<B> l) { return getVars(l); } static <A, B, C extends Collection<B>> List<B> allValues(Map<A, C> map) { List<B> out = new ArrayList(); for (var l : values(map)) addAll(out, l); return out; } 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 NavigableMap synchroTreeMap() { return synchroNavigableMap(new TreeMap()); } static Object _defaultClassFinder_value = defaultDefaultClassFinder(); static Object _defaultClassFinder() { return _defaultClassFinder_value; } static <A> List<A> synchroList() { return synchroList(new ArrayList<A>()); } static <A> List<A> synchroList(List<A> l) { return Collections.synchronizedList(l); } static boolean hasConcept(Class<? extends Concept> c, Object... params) { return findConceptWhere(c, params) != null; } static String getDBProgramID_id; static String getDBProgramID() { return nempty(getDBProgramID_id) ? getDBProgramID_id : programIDWithCase(); } 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 void clearConcepts() { db_mainConcepts().clearConcepts(); } static void clearConcepts(Concepts concepts) { concepts.clearConcepts(); } static Map unstructureMap(String s) { return (Map) unstructure(s); } static Map unstructureMap(String text, boolean allDynamic, Object classFinder) { return (Map) unstructure(text, allDynamic, classFinder); } static boolean isFalse(Object o) { return eq(false, o); } 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 Object unstructureGZFile(File f) { return unstructureGZFile(f, null); } static Object unstructureGZFile(File f, IF1<String, Class> classFinder) { try { if (!fileExists(f)) return null; BufferedReader reader = utf8BufferedReader(gzInputStream(f)); return unstructure_tok(javaTokC_noMLS_onReader(reader), false, classFinder); } catch (Exception __e) { throw rethrow(__e); } } static IF1 toIF1(final Object f) { if (f == null) return null; if (f instanceof IF1) return (IF1) f; if (isString(f)) { throw fail("callF_legacy"); } return new IF1() { public Object get(Object a) { return callF(f, a); } }; } 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 boolean sameSnippetID(String a, String b) { if (!isSnippetID(a) || !isSnippetID(b)) return false; return parseSnippetID(a) == parseSnippetID(b); } static RemoteDB connectToDBOpt(String dbNameOrID) { try { return new RemoteDB(dbNameOrID); } catch (Throwable __e) { return null; } } // 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<String, Class> 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); } interface unstructure_Handler { void parse(int refID, int tokIndex, unstructure_Receiver out); } static Object unstructure_tok(final Producer<String> 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<Integer, Object> refs = new HashMap(); HashMap<Integer, Object> tokrefs = new HashMap(); HashSet<String> concepts = new HashSet(); List<Runnable> stack = new ArrayList(); Map<String, String> baseClassMap = new HashMap(); HashMap<Class, Constructor> innerClassConstructors = new HashMap(); String curT; char[] unquoteBuf = new char[unstructure_unquoteBufSize]; // value is a class or a Handler final HashMap<String, Object> handlers = new HashMap(); X() { try { Class mc = (Class) (callF(_classFinder, "<main>")); if (mc != null) mcDollar = mc.getName() + "$"; } catch (Throwable __e) { printStackTrace(__e); } makeHandlers(); } void makeHandlers() { unstructure_Handler h; handlers.put("bigint", (unstructure_Handler) (refID, tokIndex, out) -> out.set(parseBigInt())); handlers.put("d", (unstructure_Handler) (refID, tokIndex, out) -> out.set(parseDouble())); handlers.put("fl", (unstructure_Handler) (refID, tokIndex, out) -> out.set(parseFloat())); handlers.put("sh", (unstructure_Handler) (refID, tokIndex, out) -> { consume(); String t = tpp(); if (t.equals("-")) { t = tpp(); out.set((short) (-parseInt(t))); return; } out.set((short) parseInt(t)); }); handlers.put("enum", (unstructure_Handler) (refID, tokIndex, out) -> { consume(); String t = tpp(); assertTrue(isJavaIdentifier(t)); String fullClassName = mcDollar + t; Class _c = findAClass(fullClassName); if (_c == null) throw fail("Enum class not found: " + fullClassName); int ordinal = parseInt(tpp()); out.set(_c.getEnumConstants()[ordinal]); }); handlers.put("false", h = (unstructure_Handler) (refID, tokIndex, out) -> { consume(); out.set(false); }); handlers.put("f", h); handlers.put("true", h = (unstructure_Handler) (refID, tokIndex, out) -> { consume(); out.set(true); }); handlers.put("t", h); handlers.put("{", (unstructure_Handler) (refID, tokIndex, out) -> parseMap(out)); handlers.put("[", (unstructure_Handler) (refID, tokIndex, out) -> { ArrayList l = new ArrayList(); if (refID >= 0) refs.put(refID, l); this.parseList(l, out); }); handlers.put("bitset", (unstructure_Handler) (refID, tokIndex, out) -> parseBitSet(out)); handlers.put("array", h = (unstructure_Handler) (refID, tokIndex, out) -> parseArray(out)); handlers.put("intarray", h); handlers.put("dblarray", h); } // end of makeHandlers - add more handlers here 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)); Object handler = handlers.get(t); if (handler instanceof unstructure_Handler) { ((unstructure_Handler) handler).parse(refID, tokIndex, out); return; } Class c = (Class) handler; 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("-")) { 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("-")) { 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("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 = handlers.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); handlers.put(className, c); } // check for existing base class if (c == null && !allDynamic) { Set<String> 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); handlers.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<String, Object> 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<String, Object> 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<String, Object> 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<Boolean> 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 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<Integer> 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 <A extends Comparable<A>> A max(A a, A b) { return cmp(a, b) >= 0 ? a : b; } static <A, B> List<Pair<A, B>> mapToPairs(Map<A, B> map) { List<Pair<A, B>> l = emptyList(l(map)); if (map != null) for (Map.Entry<A, B> e : map.entrySet()) l.add(pair(e.getKey(), e.getValue())); return l; } static String dynShortName(Object o) { return shortDynamicClassName(o); } static <A, B> A lastKey(SortedMap<A, B> map) { return empty(map) ? null : map.lastKey(); } static File conceptsDir() { return conceptsDir(db_mainConcepts()); } static File conceptsDir(Concepts cc) { return cc.conceptsDir(); } static File conceptsDir(String subName) { return conceptsDir(db_mainConcepts(), subName); } static File conceptsDir(Concepts cc, String subName) { return newFile(conceptsDir(cc), subName); } static File dirOfFile(File f) { return f == null ? null : f.getParentFile(); } 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); } // wrapper: VF1<Runnable> 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 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 String javaTokWordWrap(String s) { return javaTokWordWrap(120, s); } // TODO: complete trimming static String javaTokWordWrap(int cols, String s) { int col = 0; List<String> 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 fileName(File f) { return f == null ? null : f.getName(); } 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 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 long toK(long l) { return (l+1023)/1024; } 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 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; String shortName; List<Field> fields; Method customSerializer; IVF1<Object> 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; boolean honorFieldOrder = true; String mcDollar = actualMCDollar(); IdentityHashMap<Object, Integer> seen = new IdentityHashMap(); //new BitSet refd; HashMap<String, Integer> strings = new HashMap(); HashSet<String> concepts = new HashSet(); HashMap<Class, structure_ClassInfo> infoByClass = new HashMap(); // wrapper for _persistenceInfo field or _persistenceInfo method // by class (taking the object instance) HashMap<Class, IF1<Object, Map>> persistenceInfo = new HashMap(); int n; // token count List<Runnable> 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); String name = c.getName(); String shortName = dropPrefix("loadableUtils.utils$", dropPrefix(mcDollar, name)); if (startsWithDigit(shortName)) shortName = name; // for anonymous classes info.shortName = shortName; try { if (isSyntheticOrAnonymous(c)) { info.special = info.nullInstances = true; return info; } if (c.isEnum()) { info.special = 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 } } if (!isPersistableClass(c)) warn("Class not persistable: " + c + " (anonymous or no default constructor)"); } catch (Throwable e) { printStackTrace(e); info.nullInstances = true; } return info; } void setFields(structure_ClassInfo info, List<Field> fields) { info.fields = fields; } void writeObject(Object o, String shortName, Map<String, Object> 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<Field> 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<Class, Integer> 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("sun")) { d.append("j ").append(quote(str(o))); return; // Hm. this is not unstructure-able except for java.awt.Color } /*if (name.equals("main$Lisp")) { fail("lisp not supported right now"); }*/ if (info.special) { if (c.isEnum()) { d.append("enum "); d.append(info.shortName); d.out.append(' '); d.append(((Enum) o).ordinal()); return; } 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<Field> fields = new TreeSet<Field>(new Comparator<Field>() { 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, obj -> (Map) fieldGet(field, obj)); 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(); } Method persistenceInfoMethod = findInstanceMethod(c, "_persistenceInfo"); if (persistenceInfoMethod != null) d.persistenceInfo.put(c, obj -> (Map) invokeMethod(persistenceInfoMethod, obj)); lFields = asList(d.honorFieldOrder ? fieldObjectsInFieldOrder(c, fields) : 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 IF1<Object, Map> piGetter = d.persistenceInfo.get(c); Map persistenceInfo = piGetter == null ? null : piGetter.get(o); if (piGetter == null && o instanceof DynamicObject) persistenceInfo = (Map) getOptDynOnly(((DynamicObject) o), "_persistenceInfo"); LinkedHashMap<String, Object> 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 shortName = info.shortName; // 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"); if (((DynamicObject) o).className != null) { // TODO: this probably doesn't work with inner classes shortName = shortDynClassNameForStructure((DynamicObject) o); fv.remove("className"); } } d.writeObject(o, shortName, fv); } catch (Exception __e) { throw rethrow(__e); } } static Collection<Concept> allConcepts() { return db_mainConcepts().allConcepts(); } static Collection<Concept> allConcepts(Concepts concepts) { return concepts.allConcepts(); } 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 boolean isTransient(Field f) { return (f.getModifiers() & java.lang.reflect.Modifier.TRANSIENT) != 0; } // 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 double abs(Complex c) { return c.abs(); } static void sleepInCleanUp(long ms) { try { if (ms < 0) return; Thread.sleep(ms); } catch (Exception __e) { throw rethrow(__e); } } 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_raw(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<WeakReference> classes = (List<WeakReference>) (getOpt(c, "hotwire_classes")); if (classes != null) for (WeakReference cc : classes) { try { cleanUp(cc.get()); } catch (Throwable __e) { printStackTrace(__e); }} // interrupt all threads (experimental, they might be doing cleanup?) if (cleanUp_interruptThreads) { List<Thread> threads = registeredThreads(c); if (nempty(threads)) { print("cleanUp: Interrupting " + n2(threads, "thread") + ": " + joinWithComma(allToString(threads))); interruptThreads(threads); } } } catch (Throwable __e) { printStackTrace(__e); } setOpt_raw(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 boolean warn_on = true; static ThreadLocal<List<String>> warn_warnings = new ThreadLocal(); static void warn(String s) { if (warn_on) print("Warning: " + s); } static void warn(String s, List<String> warnings) { warn(s); if (warnings != null) warnings.add(s); addToCollection(warn_warnings.get(), s); } static <A> A firstOfType(Collection c, Class<A> type) { for (Object x : c) if (isInstanceX(type, x)) return (A) x; return null; } static List<Concept> conceptsOfType(String type) { return db_mainConcepts().conceptsOfType(type); } static <A> List<A> concatLists_conservative(List<A> a, List<A> b) { if (empty(a)) return b; if (empty(b)) return a; return concatLists(a, b); } static <A> List<A> concatLists_conservative(Collection<A> a, Collection<A> b) { if (empty(a) && b instanceof List) return ((List) b); if (empty(b) && a instanceof List) return ((List) a); return concatLists(a, b); } static <A> List<A> filterByType(Iterable c, Class<A> type) { List<A> l = new ArrayList(); if (c != null) for (Object x : c) if (isInstanceX(type, x)) l.add((A) x); return l; } static <A> List<A> filterByType(Object[] c, Class<A> type) { return filterByType(asList(c), type); } static <A> List<A> filterByType(Class<A> type, Iterable c) { return filterByType(c, type); } static <A> List<A> filterByDynamicType(Collection<A> c, String type) { List<A> 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 extends Concept> A findBackRef(Concept c, Class<A> type) { for (Concept.Ref r : c.backRefs) if (instanceOf(r.concept(), type)) return (A) r.concept(); return null; } static <A extends Concept> A findBackRef(Class<A> type, Concept c) { return findBackRef(c, type); } 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 <A, B> void mapRemove(Map<A, B> map, A key) { if (map != null && key != null) map.remove(key); } static <A, B> B firstValue(Map<A, B> map) { return first(values(map)); } static <A, B> B firstValue(MultiSetMap<A, B> map) { return map == null ? null : first(firstValue(map.data)); } static <A, B> B firstValue(MultiMap<A, B> map) { return map == null ? null : first(firstValue(map.data)); } 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 Object deref(Object o) { if (o instanceof IRef) return ((IRef) o).get(); return o; } 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 <A, B> boolean containsKey(Map<A, B> map, A key) { return map != null && map.containsKey(key); } static <A> boolean syncRemove(Collection<A> c, A b) { if (c == null) return false; synchronized(collectionMutex(c)) { return c.remove(b); } } static <A> A syncRemove(List<A> l, int idx) { if (l == null) return null; synchronized(collectionMutex(l)) { return l.remove(idx); } } static <A, B> B syncRemove(Map<A, B> map, A key) { return map == null ? null : map.remove(key); } 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 isUnstructuring(); } static <A> 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 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 boolean has(String a, String b, String c) { return false; } static boolean has(T3<String, String, String> t) { return false; } 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 boolean isEmpty(AppendableChain c) { return c == null; } 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 clear(Collection c) { if (c != null) c.clear(); } static void clear(Map map) { if (map != null) map.clear(); } static <A> List<A> addDyn_quickSync(List<A> l, A a) { if (l == null) l = new ArrayList(); syncAdd(l, a); return l; } static <A> List<A> removeDyn_quickSync(List<A> 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<Concept.Ref> scanConceptForRefs(Concept c) { Set<Concept.Ref> 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 combinePrintParameters(String s, Object o) { return (endsWithLetterOrDigit(s) ? s + ": " : s) + o; } // 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> A getThreadLocal(ThreadLocal<A> tl) { return tl == null ? null : tl.get(); } static <A> A getThreadLocal(ThreadLocal<A> tl, A defaultValue) { return or(getThreadLocal(tl), defaultValue); } static ThreadLocal<Object> print_byThread_dontCreate() { return print_byThread; } 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 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 void dm_ownTimer(Object resource, Object closerHelper) { call(dm_current_generic(), "ownTimer", resource, closerHelper); } static <A> A dm_ownTimer(A resource) { call(dm_current_generic(), "ownTimer", resource); return resource; } static <A> void dm_ownTimer(IVF1<A> closerHelper, A resource) { dm_ownTimer(resource, closerHelper); } // 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 ensureConceptClassIsIndexed(Class<? extends Concept> c) { ensureConceptClassIsIndexed(db_mainConcepts(), c); } static void ensureConceptClassIsIndexed(Concepts cc, Class<? extends Concept> c) { if (cc != null && c != null && !isConceptClassIndexed(cc, c)) indexRandomConceptField(cc, c); } static <A extends JComponent> A toolTip(A c, final Object toolTip) { return setToolTipText(c, toolTip); } static <A extends JComponent> 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..."; }}); } static Object[] litObjectArray(Object... l) { return l; } 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; } // 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 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)); } // 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 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=<hidden>"); } static String hideCredentials(Object o) { return hideCredentials(str(o)); } static Object callOpt(Object o) { return callF(o); } static Object callOpt(Object o, String method, Object... args) { return callOpt_withVarargs(o, method, args); } static Object getOptMC(String field) { return getOpt(mc(), field); } static <A> WeakReference<A> newWeakReference(A a) { return a == null ? null : new WeakReference(a); } static int iceil(double d) { return (int) Math.ceil(d); } // 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 <A> int smartIndexOf(List<A> l, A sub) { return smartIndexOf(l, sub, 0); } static <A> int smartIndexOf(List<A> l, int start, A sub) { return smartIndexOf(l, sub, start); } static <A> int smartIndexOf(List<A> l, A sub, int start) { int i = indexOf(l, sub, start); return i < 0 ? l(l) : i; } 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; } static int globalIDLength() { return 16; } static ThreadLocal<Object> print_byThread() { synchronized(print_byThread_lock) { if (print_byThread == null) print_byThread = new ThreadLocal(); } return print_byThread; } static void print_setPrefixForThread(final String prefix) { interceptPrintInThisThread(empty(prefix) ? null : new _PrintIndent(prefix)); } static Map<String, String> decodeHQuery(String query) { Map<String, String> 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 Object subBot_serve404() { return call(getMainBot(), "serve404"); } static Object subBot_serve404(String msg) { return call(getMainBot(), "serve404", msg); } 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 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); } } 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 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 IResourceLoader vm_getResourceLoader() { return proxy(IResourceLoader.class, vm_generalMap_get("_officialResourceLoader")); } static String fsI(String id) { return formatSnippetID(id); } static String fsI(long id) { return formatSnippetID(id); } 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 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 String standardCredentials() { String user = standardCredentialsUser(); String pass = standardCredentialsPass(); if (nempty(user) && nempty(pass)) return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass); return ""; } 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 getGlobalCache() { File file = new File(javaxCachesDir(), "Binary Snippets"); file.mkdirs(); return file; } static <A> A setThreadLocal(ThreadLocal<A> tl, A value) { if (tl == null) return null; A old = tl.get(); tl.set(value); return old; } static int loadPage_defaultTimeout = 60000; static ThreadLocal<String> 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<Boolean> loadPage_silent = new ThreadLocal(); static volatile int loadPage_forcedTimeout; // ms static ThreadLocal<Integer> loadPage_forcedTimeout_byThread = new ThreadLocal(); // ms static ThreadLocal<Map<String, List<String>>> loadPage_responseHeaders = new ThreadLocal(); static ThreadLocal<Map<String, String>> loadPage_extraHeaders = new ThreadLocal(); static ThreadLocal<Long> 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<String, String> 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 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 webChatBotLogsHTML() { return withDBLock(new F0<String>() { public String get() { try { List<String> l = new ArrayList(); for (Conversation conv : sortByCalculatedFieldDesc(list(Conversation.class), new F1<Conversation, Object>() { 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<List<Msg>> dialogs = reversed(unnull(conv.oldDialogs)); l.add(webChatBotLogsHTML_formatDialog(str(conv.id + "/" + (l(dialogs)+1)), conv.msgs)); int i = l(dialogs); for (List<Msg> 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<S> l;\r\n for (Conversation conv : sortByCalculatedFieldDesc(list Conv..."; }}); } static String webChatBotLogsHTML_formatDialog(String id, List<Msg> msgs) { long startTime = collectMinLong(msgs, "time"); long endTime = collectMaxLong(msgs, "time"); List<String> 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); } // step = e.g. 100 // value = 0 to count static String pageNav2(String baseLink, int count, int value, int step, String nVar, Object... __) { List<String> 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 <A> List<A> subList(List<A> l, int startIndex) { return subList(l, startIndex, l(l)); } static <A> List<A> subList(int startIndex, List<A> l) { return subList(l, startIndex); } static <A> List<A> subList(int startIndex, int endIndex, List<A> l) { return subList(l, startIndex, endIndex); } static <A> List<A> subList(List<A> 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 <A> List<A> subList(List<A> l, IntRange r) { return subList(l, r.start, r.end); } static Concepts newConceptsWithClassFinder(String progID) { Concepts cc = new Concepts(progID); cc.classFinder = _defaultClassFinder(); return cc; } static Concepts newConceptsWithClassFinder(File conceptsFile) { Concepts cc = new Concepts(assertNotNull(conceptsFile)); cc.classFinder = _defaultClassFinder(); return cc; } static Concepts newConceptsWithClassFinder(File conceptsFile, IF1<String, Class> classFinder) { Concepts cc = new Concepts(assertNotNull(conceptsFile)); cc.classFinder = classFinder; return cc; } static byte[] toUtf8(String s) { try { return s.getBytes(utf8charset()); } catch (Exception __e) { throw rethrow(__e); } } static String spaces(int n) { return rep(' ', n); } 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 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 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<String> toks, Matches matches) { List<String> tokpat = parse3_cachedPattern(pat); return match3(tokpat, toks, matches); } static boolean match3(List<String> tokpat, List<String> 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 String asString(Object o) { return o == null ? null : o.toString(); } 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 boolean emptyString(String s) { return s == null || s.length() == 0; } static int boolToInt(boolean b) { return b ? 1 : 0; } static <A> List<A> newSubList(List<A> l, int startIndex, int endIndex) { return cloneList(subList(l, startIndex, endIndex)); } static <A> List<A> newSubList(List<A> l, int startIndex) { return cloneList(subList(l, startIndex)); } // TODO: JDK 17!! ?? No! Yes? Yes!! static Object collectionMutex(List l) { return l; } static Object collectionMutex(Object o) { if (o instanceof List) return o; // TODO: actually use our own maps so we can get the mutex properly String c = className(o); return o; } static Object jsonPrepareData(Object... data) { if (l(data) == 1) return data[0]; return litorderedmap(data); } static boolean isRelativeOrAbsoluteURL(String s) { return isAbsoluteURL(s) || isRelativeURL(s); } static List<Integer> rgbToIntList(RGB rgb) { return rgb == null ? null : ll(rgb.redInt(), rgb.greenInt(), rgb.blueInt()); } static String javascriptQuote(String s) { return quote(s); // use regular Java quoting } static String stringIfTrue(boolean b, String s) { return b ? s : ""; } static ExpiringMap2<String, String> 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); } } 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 <A, B, C> Map<B, C> mapKeys(Map<A, C> map, IF1<A, B> func) { return mapKeys(map, (Object) func); } static <A, B, C> Map<B, C> mapKeys(IF1<A, B> func, Map<A, C> map) { return mapKeys(map, func); } static String strOrNull(Object o) { return o == null ? null : str(o); } static long psI(String snippetID) { return parseSnippetID(snippetID); } // tag = tag name // newTag = full new contents including outer tag // replaces only one occurrence static String hreplacetag(String html, String tag, String newTag) { List<String> tok = htmlcoarsetok(html); List<List<String>> tags = findContainerTag(tok, tag); if (empty(tags)) return html; List<String> theTag = first(tags); List<String> 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 dropLeadingSlash(String s) { return dropPrefix("/", s); } // TODO: if field is a Ref<>, you can thoretically use findBackRefs static <A extends Concept> A findConceptWhere(Class<A> c, Object... params) { return findConceptWhere(db_mainConcepts(), c, params); } static <A extends Concept> A findConceptWhere(Concepts concepts, Class<A> c, Object... params) { ping(); params = expandParams(c, params); // indexed if (concepts.fieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex<A, Object> 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 String fileExtension(File f) { if (f == null) return null; return fileExtension(f.getName()); } static String fileExtension(String s) { return substring(s, smartLastIndexOf(s, '.')); } // if <head> block exists: put tag after <head> // if not: add tag in front of the whole HTML static String hAddToHead(String html, String tag) { List<String> tok = htmlTok(html); List<String> head = first(findContainerTag(tok, "head")); if (head == null) return tag + html; head.set(2, addLineBreak(tag) + head.get(2)); return join(tok); } static <A extends Concept> Collection<A> findConceptsWhere(Class<A> c, Object... params) { return findConceptsWhere(db_mainConcepts(), c, params); } static Collection<Concept> findConceptsWhere(String c, Object... params) { return findConceptsWhere(db_mainConcepts(), c, params); } static <A extends Concept> Collection<A> findConceptsWhere(Concepts concepts, Class<A> c, Object... params) { ping(); params = expandParams(c, params); // indexed if (concepts.fieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex<A, Object> index = concepts.getFieldIndex(c, (String) params[i]); if (index != null) { Collection<A> rawList = index.getAll(params[i+1]); params = dropEntryFromParams(params, i); if (params == null) return rawList; List<A> 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<Concept> findConceptsWhere(Concepts concepts, String c, Object... params) { return filterConcepts(concepts.list(c), params); } 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 <A> Object[] mapToObjectArray(Collection<A> l, IF1<A, Object> f) { return mapToObjectArray(f, l); } static <A> Object[] mapToObjectArray(A[] l, IF1<A, Object> f) { return mapToObjectArray(f, l); } static <A> Object[] mapToObjectArray(IF1<A, Object> 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 <A> Object[] mapToObjectArray(IF1<A, Object> f, Collection<A> 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 String hhmm() { return hhmm(now()); } static String hhmm(long time) { return new SimpleDateFormat("HHmm").format(time); } static Object serveWithContentType(String text, String contentType) { return serveByteArray(toUtf8(text), contentType); } static <A> A optPar(ThreadLocal<A> tl, A defaultValue) { A a = tl.get(); if (a != null) { tl.set(null); return a; } return defaultValue; } static <A> A optPar(ThreadLocal<A> 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> A optPar(Object[] params, String name, A defaultValue) { return optParam(params, name, defaultValue); } static <A> A optPar(String name, Object[] params, A defaultValue) { return optParam(params, name, defaultValue); } static String htmlEncode(String s) { return htmlencode(s); } static boolean boolPar(ThreadLocal<Boolean> 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 ThreadLocal<Boolean> 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<String> 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 RGBImage) return o; // huh? if (o instanceof Boolean) return o; return dataToTable_useStruct.get() ? structureOrTextForUser(o) : strOrNull(o); } static <A> ArrayList<A> litlist(A... a) { ArrayList l = new ArrayList(a.length); for (A x : a) l.add(x); return l; } 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 <A> List<A> padList(List<A> l, int w) { return padList(l, w, null); } static <A> List<A> padList(List<A> l, int w, A a) { if (l(l) >= w) return l; List<A> x = cloneList(l); while (l(x) < w) x.add(a); return x; } static <A> TreeMap<String, A> ciMap() { return caseInsensitiveMap(); } static List<Map> 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 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); } // 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()]); } static String htd(Object contents, Object... params) { return htag("td", contents, params); } static <A> List<A> withoutNulls(Iterable<A> l) { if (l instanceof List) if (!containsNulls((List) l)) return ((List) l); List<A> l2 = new ArrayList(); for (A a : l) if (a != null) l2.add(a); return l2; } static <A, B> Map<A, B> withoutNulls(Map<A, B> map) { Map<A, B> 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 <A> List<A> withoutNulls(A[] l) { List<A> l2 = new ArrayList(); if (l != null) for (A a : l) if (a != null) l2.add(a); return l2; } 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 Map emptyMap() { return new HashMap(); } 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; } // lists files only - who needs to list directories?! // lists in typical .zip fashion - dir1/dir2/bla.txt static List<String> listZip(File inZip) { return listZipFile(inZip); } static List<String> listZip(String inZip) { return listZipFile(inZip); } static <A extends Concept> A uniqueConcept(Class<A> c, Object... params) { return uniqueConcept(db_mainConcepts(), c, params); } static <A extends Concept> A uniqueConcept(Concepts cc, Class<A> 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 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 boolean isFile(File f) { return f != null && f.isFile(); } static boolean isFile(String path) { return isFile(newFile(path)); } 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 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 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 joinNempties(String sep, Object... strings) { return joinStrings(sep, strings); } static String joinNempties(String sep, Iterable strings) { return joinStrings(sep, strings); } static <A> HashSet<A> lithashset(A... items) { HashSet<A> set = new HashSet(); for (A a : items) set.add(a); return set; } static boolean checkCondition(Object condition, Object... args) { return isTrue(callF(condition, args)); } static <A> boolean checkCondition(IF1<A, Boolean> condition, A arg) { return isTrue(callF(condition, arg)); } static boolean containsIgnoreCase(Collection<String> 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 <A, B> Pair<A, B> mapEntryToPair(Map.Entry<A, B> e) { return e == null ? null : pair(e.getKey(), e.getValue()); } static <A, B> Set<Map.Entry<A,B>> entrySet(Map<A, B> map) { return _entrySet(map); } static List<String> 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<List<String>> findContainerTag(List<String> tok, String tag) { List<List<String>> 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<List<String>> findContainerTag(String html, String tag) { return findContainerTag(htmlTok(html), tag); } static <A> List<A> dropFirstTwoAndLastTwo(List<A> l) { return dropFirstAndLast(2, l); } static void replaceTokens(List<String> tok, IntRange r, String s) { replaceTokens(tok, r.start, r.end, s); } static void replaceTokens(List<String> tok, int i, int j, String s) { clearAllTokens(tok, i+1, j); tok.set(i, s); } static void replaceTokens(List<String> tok, String s) { clearAllTokens(tok, 1, l(tok)); tok.set(0, s); } 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 <A> void addFirst(List<A> c, A a) { if (c != null) c.add(0, a); } static void containerAddFirst(Container c, Component... l) { if (c != null) { swing(() -> { for (int i = 0; i < l(l); i++) c.add(l[i], i); revalidate(c); }); } } static Object[] assertEvenLength(Object[] a) { assertTrue(even(l(a))); return a; } static LinkedHashMap paramsToOrderedMap(Object... params) { return asLinkedHashMap(paramsToMap(params)); } 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 String hstyle(Object contents) { return hcss(contents); } static String unicode_smallDownPointingTriangle() { return charToString(0x25BE); } static String[] toStringArray(Collection<String> c) { String[] a = new String[l(c)]; Iterator<String> 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<String>) o); else throw fail("Not a collection or array: " + getClassName(o)); } static <A, B> List<A> keysList(Map<A, B> map) { return cloneListSynchronizingOn(keys(map), map); } static <A> List<A> keysList(MultiSet<A> ms) { return ms == null ? null : keysList(ms.map); } // f: A -> Comparable static <A> List<A> sortedByCalculatedField(Iterable<A> c, Object f) { return sortByCalculatedField(c, f); } static <A> List<A> sortedByCalculatedField(Object f, Iterable<A> c) { return sortedByCalculatedField(c, f); } static <A, B> List<A> sortedByCalculatedField(IF1<A, B> f, Iterable<A> c) { return sortedByCalculatedField(c, f); } static <A, B> List<A> sortedByCalculatedField(Iterable<A> c, IF1<A, B> f) { List<A> l = cloneList(c); sort(l, new Comparator<A>() { public int compare(A a, A b) { return stdcompare(f.get(a), f.get(b)); } }); return l; } static <A> int iteratorCount_int_close(Iterator<A> 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 String spanTitle(String title, Object contents) { return empty(title) ? str(contents) : span(contents, "title", title); } static <A> List<A> newSubListOrSame(List<A> l, int startIndex) { return newSubListOrSame(l, startIndex, l(l)); } static <A> List<A> newSubListOrSame(List<A> 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 <A> List<A> newSubListOrSame(List<A> l, IntRange r) { return newSubListOrSame(l, r.start, r.end); } 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 int[] takeFirstOfIntArray(int[] b, int n) { return subIntArray(b, 0, n); } static int[] takeFirstOfIntArray(int n, int[] b) { return takeFirstOfIntArray(b, n); } static short[] takeFirstOfShortArray(short[] b, int n) { return subShortArray(b, 0, n); } static short[] takeFirstOfShortArray(int n, short[] b) { return takeFirstOfShortArray(b, n); } static byte[] takeFirstOfByteArray(byte[] b, int n) { return subByteArray(b, 0, n); } static byte[] takeFirstOfByteArray(int n, byte[] b) { return takeFirstOfByteArray(b, n); } static double[] takeFirstOfDoubleArray(double[] b, int n) { return subDoubleArray(b, 0, n); } static double[] takeFirstOfDoubleArray(int n, double[] b) { return takeFirstOfDoubleArray(b, n); } static ClassLoader classLoaderForObject(Object o) { if (o instanceof ClassLoader) return ((ClassLoader) o); if (o == null) return null; return _getClass(o).getClassLoader(); } // Note: This is actually broken. Inner classes must stay with a $ separator static String classNameToVM(String name) { return name.replace(".", "$"); } 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 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 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 <A extends Container> A addComponents(A c, Collection<? extends Component> components) { if (nempty(components)) { swing(() -> { for (Component comp : components) c.add(comp); revalidate(c); }); } return c; } static <A extends Container> A addComponents(A c, Component... components) { return addComponents(c, asList(components)); } static <A> A replaceIfEqual(A a, A b, A c) { return eq(a, b) ? c : a; } static List<String> toLinesFullTrim(String s) { List<String> l = new ArrayList(); for (String line : toLines(s)) if (nempty(line = trim(line))) l.add(line); return l; } static List<String> toLinesFullTrim(File f) { List<String> l = new ArrayList(); for (String line : linesFromFile(f)) if (nempty(line = trim(line))) l.add(line); return l; } 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 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 String htmlEncode_nlToBr(String s) { return nlToBr(htmlEncode2(s)); } 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 <A> String pnlToStringWithEmptyLines(Iterable<A> l) { return pnlToString(map(l, o -> o + "\n")); } 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; } // one array plus more elements static Object[] arrayPlus(Object[] a1, Object... a2) { return concatArrays(a1, a2); } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } static String snippetImageLink(String snippetID) { return snippetImageURL(snippetID); } 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 hsnippetimg(String imageID, Object... params) { return himg(snippetImageLink(imageID), params); } 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 AutoCloseable tempDBLock(Concepts concepts) { return tempLock(concepts.lock); // NO null propagation this time } static AutoCloseable tempDBLock() { return tempDBLock(db_mainConcepts()); } static <A extends Concept> A findConceptWhereCI(Class<A> c, Object... params) { return findConceptWhereCI(db_mainConcepts(), c, params); } static <A extends Concept> A findConceptWhereCI(Concepts concepts, Class<A> c, Object... params) { params = expandParams(c, params); // find smallest index to use Lowest<IF0<Collection<A>>> bestIndex = new Lowest(); if (concepts.ciFieldIndices != null) for (int i = 0; i < l(params); i += 2) { ping(); Object val = params[i+1]; IFieldIndex<A, Object> index = concepts.getCIFieldIndex(c, (String) params[i]); if (index != null) { Collection<A> 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<A> 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 extends Concept> A unlisted(Class<A> 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<? extends Concept> cc = findClass(name); concepts_unlisted.set(true); try { return cc != null ? nuObject(cc) : new Concept(name); } finally { concepts_unlisted.set(null); } } static int csetAll(Concept c, Object... values) { return cset(c, values); } static int csetAll(Iterable<? extends Concept> l, Object... values) { int n = 0; for (Concept c : unnullForIteration(l)) n += cset(c, values); return n; } static int csetAll(Concept c, Map<String, Object> values) { int n = 0; for (Map.Entry<? extends String, ? extends Object> __0 : _entrySet( values)) { String field = __0.getKey(); Object value = __0.getValue(); n += cset(c, field, value); } return n; } 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 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 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 <A> List<A> reversedList(Iterable<A> l) { List<A> x = cloneList(l); Collections.reverse(x); return x; } static String reversedString(String s) { return reverseString(s); } static TreeSet<String> caseInsensitiveSet() { return caseInsensitiveSet_treeSet(); } static TreeSet<String> caseInsensitiveSet(Collection<String> c) { return caseInsensitiveSet_treeSet(c); } static <A> TreeSet<A> treeSet() { return new TreeSet(); } static <A> int nfilter(Iterable<A> c, IF1<A, Boolean> pred) { return nfilter(pred, c); } static <A> int nfilter(IF1<A, Boolean> pred, Iterable<A> 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 File getSecretProgramDir() { return getSecretProgramDir(actualProgramID()); } static File getSecretProgramDir(String snippetID) { if (empty(snippetID)) return javaxSecretDir(); return newFile(javaxSecretDir(), formatSnippetID(snippetID)); } static Map<String, String> paramsFromURL(String url) { return decodeHQuery(getQueryFromURL(url)); } static String addAnchorToURL(String url, String anchor) { int i = smartIndexOf(url, '#'); url = takeFirst(url, i); if (nempty(anchor)) url += "#" + anchor; return url; } static String urlWithoutQueryAndAnchor(String url) { return takeFirst(url, min(smartIndexOf(url, "?"), smartIndexOf(url, "#"))); } static String anchorFromURL(String url) { return substring(url, smartIndexOf(url, "#")); } // 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 hbuttonOnClick(String text, String onClick, Object... params) { return hfulltag("button", text, paramsPlus(params, "onClick" , onClick)); } static <A> List<A> immutableEmptyList() { return Collections.emptyList(); } static short[] emptyShortArray = new short[0]; static short[] emptyShortArray() { return emptyShortArray; } static <A, B> Map<A, B> immutableEmptyMap() { return Collections.emptyMap(); } static String windowsLineBreaks(String s) { return s == null ? null : s.replaceAll("(?<!\r)\n", "\r\n"); } static String mapToLines_rtrim(Object f, Map map) { return lines_rtrim(map(map, f)); } static String mapToLines_rtrim(Object f, Iterable l) { return lines_rtrim(map(f, l)); } static <A> String mapToLines_rtrim(Iterable<A> l, IF1<A, String> 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<List<CountryDialCode>> countryDialCodes_cache = new Cache<>(() -> countryDialCodes_load()); static List<CountryDialCode> countryDialCodes() { return countryDialCodes_cache.get(); } static List<CountryDialCode> countryDialCodes_load() { return sortedByField("dialCode", filter(c -> nempty(c.dialCode), map((List<Map<String, String>>) loadJSONFile(loadLibrary("#1400429")), m -> new CountryDialCode(m.get("name"), m.get("code"), m.get("dial_code"))))); } static List<String> nempties(Collection<String> c) { return filterNempty(c); } static List<String> 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 <A> List<A> sorted(Collection<A> c, Object comparator) { List<A> l = cloneList(c); sort(l, makeComparator(comparator)); return l; } static <A> List<A> sorted(Collection<A> c) { List<A> l = cloneList(c); sort(l); return l; } static <A> List<A> sorted(Comparator<A> comparator, Collection<A> c) { List<A> l = cloneList(c); sort(l, comparator); return l; } 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 Object call_withVarargs(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); Method me = cache.findStaticMethod(methodName, args); if (me != null) return invokeMethod(me, null, args); // try varargs List<Method> methods = cache.cache.get(methodName); 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() + "." + methodName + "(" + joinWithComma(classNames(args)) + ") not found"); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(methodName, args); if (me != null) return invokeMethod(me, o, args); // try varargs List<Method> methods = cache.cache.get(methodName); 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() + "." + methodName + "(" + joinWithComma(classNames(args)) + ") not found"); } } catch (Exception __e) { throw rethrow(__e); } } 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 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 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); } static String dropPrefixTrim(String prefix, String s) { return trim(dropPrefix(prefix, s)); } static Object serveJSON_shallowLineBreaks(Object data) { return serveText(jsonEncode_shallowLineBreaks(data)); } 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 smallestTransparentGIFDataURI() { return "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; } 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<Object, Object> castMapToMapO(Map map) { return map; } static <A> List<A> sortedByFieldDesc(Collection<A> c, String field) { List<A> l = new ArrayList(c); sort(l, descFieldComparator(field)); return l; } static <A> List<A> sortedByFieldDesc(String field, Collection<A> c) { return sortedByFieldDesc(c, field); } static <A> A lowestByField(Iterable<A> 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> A lowestByField(String field, Iterable<A> 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 boolean isConceptFieldIndexed(Class<? extends Concept> c, String field) { return isConceptFieldIndexed(db_mainConcepts(), c, field); } static boolean isConceptFieldIndexed(Concepts concepts, Class<? extends Concept> c, String field) { return concepts.getFieldIndex(c, field) != null; } static <A extends Concept> ConceptFieldIndexCI<A> getConceptFieldCIIndex(Class<A> c, String field) { return getConceptFieldCIIndex(db_mainConcepts(), c, field); } static <A extends Concept> ConceptFieldIndexCI<A> getConceptFieldCIIndex(Concepts concepts, Class<A> c, String field) { return (ConceptFieldIndexCI) concepts.getCIFieldIndex(c, field); } 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(); cc.programID = getDBProgramID(); 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) { printStackTrace(__e); }} } 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 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<String> mapNonCodeTokens(List<String> tok, IF1<String, String> f) { return mapNonCodeTokens(tok, (Object) f); } static List<String> mapNonCodeTokens(IF1<String, String> f, List<String> tok) { return mapNonCodeTokens(tok, f); } static String emojisToUnicode(String s) { return regexReplace(s, ":(\\w+):", matcher -> lookupOrKeep(emojiShortNameMap(), matcher.group())); } static <A> Iterator<A> iterator(Iterable<A> c) { return c == null ? emptyIterator() : c.iterator(); } 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); } 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 String domain(String url) { return hostNameFromURL(url); } static String unicodeFromCodePoint(int codePoint) { return codePointToString(codePoint); } 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<String, CharSequence> htmldecode_lookupMap_cache; static HashMap<String, CharSequence> htmldecode_lookupMap() { if (htmldecode_lookupMap_cache == null) htmldecode_lookupMap_cache = htmldecode_lookupMap_load(); return htmldecode_lookupMap_cache;} static HashMap<String, CharSequence> htmldecode_lookupMap_load() { var map = new HashMap<String, CharSequence>(); for (CharSequence[] seq : htmldecode_escapes()) map.put(seq[1].toString(), seq[0]); return map; } // tok should be the output of htmlcoarsetok static List<String> dropAllTags(List<String> tok) { List<String> 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 char charAt(String s, int i) { return s != null && i >= 0 && i < s.length() ? s.charAt(i) : '\0'; } static String addSuffix(String s, String suffix) { return s == null || s.endsWith(suffix) ? s : s + suffix; } // usually L<S> 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<String> toLines(File f) { return linesFromFile(f); } static List<String> toLines(String s) { List<String> lines = new ArrayList<String>(); 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 TreeSet<String> toCaseInsensitiveSet(Iterable<String> c) { if (isCISet(c)) return (TreeSet) c; TreeSet<String> set = caseInsensitiveSet(); addAll(set, c); return set; } static TreeSet<String> toCaseInsensitiveSet(String... x) { TreeSet<String> set = caseInsensitiveSet(); addAll(set, x); return set; } 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, Rectangle b) { return rectContains(a, toRect(b)); } static boolean rectContains(Rect a, int x, int y) { return a != null && a.contains(x, y); } static boolean rectContains(Rect a, Pt p) { return a != null && p != null && a.contains(p); } 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 intToHex_flexLength(int i) { return Integer.toHexString(i); } static String _userHome; static String userHome() { if (_userHome == null) return actualUserHome(); return _userHome; } static File userHome(String path) { return new File(userDir(), path); } static String ipToCountry2020_dataSnippetID = "#1400400"; static Map<Long, String> 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<String> 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; } // 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<Matcher, String> f) { return regexReplaceIC(s, pat, f); } static String programID() { return getProgramID(); } static String programID(Object o) { return getProgramID(o); } static String formatSnippetIDOpt(String s) { return isSnippetID(s) ? formatSnippetID(s) : s; } static volatile String caseID_caseID; static String caseID() { return caseID_caseID; } static void caseID(String id) { caseID_caseID = id; } 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 JTextField jTextField() { return jTextField(""); } static JTextField jTextField(final String text) { return swing(new F0<JTextField>() { 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<Container, Dimension> layouter, final String title, final Object... parts) { return showFormTitled_customArrangement(false, new F1<List<JComponent>, JPanel>() { public JPanel get(List<JComponent> 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<Container, Dimension> formLayouter1() { final int yplus = formLayouter1_yPlus, fixer2 = formLayouter1_fixer2; return new F1<Container, Dimension>() { public Dimension get(Container parent) { try { List<Component> 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<Component> 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 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<Object>() { public Object get() { try { JWindow window = makeWindow(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 "JWindow window = makeWindow(infoMessage_makePanel(text));\r\n window.setSize..."; }}); } static JWindow infoMessage(Throwable e) { //showConsole(); printStackTrace(e); return infoMessage(exceptionToStringShort(e)); } 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 List<VF1<Map>> _threadInfo_makers = synchroList(); static Object _threadInfo() { if (empty(_threadInfo_makers)) return null; HashMap map = new HashMap(); pcallFAll(_threadInfo_makers, map); return map; } 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 String programNameOrNull() { return replaceIfEqual(programName(), "?", null); } 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<JPanel>() { public JPanel get() { try { final Var<JFrame> frame = new Var(); JPanel panel = showForm_makePanel(false, _parts); showForm_makeFrame(title, panel); frame.set(getFrame(panel)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final new Var<JFrame> frame;\r\n JPanel panel = showForm_makePanel(false, _p..."; }}); } static JPanel showForm_makePanel(Boolean internalFrame, Object... _parts) { List<JComponent> out = showForm_arrange1(showForm_makeComponents(internalFrame, _parts)); return vstackWithSpacing(out, showForm_defaultGap); } 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 extends JComponent> A renameSubmitButton(A form, String newName) { renameButton(form, showFormSubmitButtonName(), newName); return form; } static <A extends JComponent> 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<String> tok) { clearAllTokens(tok); } static void clearTokens(List<String> tok, int i, int j) { clearAllTokens(tok, i, j); } static void clearTokens(List<String> tok, IntRange r) { clearAllTokens(tok, r.start, r.end); } static void clearToken(List<String> tok, int i) { tok.set(i, ""); } static <A> IterableIterator<Pair<Integer, A>> iterateListWithIndex(Iterable<A> l) { return iterateListWithIndex(l, 0); } static <A> IterableIterator<Pair<Integer, A>> iterateListWithIndex(Iterable<A> 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 <A> List<A> asVirtualList(A[] a) { return wrapArrayAsList(a); } static String unparseCSSParameter(Map<String, String> map) { return joinWithSemicolon(map(map, new F2<String, String, String>() { public String get(String key, String value) { try { return key + ": " + value; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "key + \": \" + value"; }})); } static <A, B> Map<A, B> mapPlus_noOverwrite(Map<A, B> m, Object... data) { m = cloneMap(m); litmap_impl_noOverwrite(m, data); return m; } static Map<String, String> parseCSSParameter(String s) { Map<String, String> map = new LinkedHashMap(); for (String x : trimAll(splitAt(s, ";"))) { List<String> y = trimAll(splitAtColon(x)); if (l(y) == 2) map.put(first(y), second(y)); } return map; } 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 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> List<Pair<A, A>> paramsToPairs(A... params) { if (l(params) == 1 && params[0] instanceof Map) return mapToPairs((Map) params[0]); List<Pair<A, A>> l = emptyList(l(params)/2); for (int i = 0; i+1 < l(params); i += 2) l.add(pair(params[i], params[i+1])); return l; } static <A> A printIndent(A o) { print(indentx(str(o))); return o; } static <A> 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 String postPage(String url, Object... params) { return doPost(litmap(params), url); } 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() { AutoCloseable result = null; result = tempSetTL(ping_isCleanUpThread, true); return result; } static void closeAllWriters(Collection<? extends Writer> l) { for (Writer w : unnull(l)) { try { w.close(); } catch (Throwable __e) { printStackTrace(__e); }} } static File prepareProgramFile(String name) { return mkdirsForFile(getProgramFile(name)); } static File prepareProgramFile(String progID, String name) { return mkdirsForFile(getProgramFile(progID, name)); } static <A> A nextToLast(List<A> 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<String, String> params = ciTagParams(tag); return eqic(mapGet(params, "http-equiv"), "refresh"); } // tok = parsed with htmlTok static List<String> findAllTagsNamed(List<String> tok, String tag) { List<String> 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<String> findAllTagsNamed(String tag, List<String> tok) { return findAllTagsNamed(tok, tag); } // Note the reversed parameters static List<String> findAllTagsNamed(String tag, String html) { return findAllTagsNamed(htmlTok(html), tag); } static String hcommentSafe(String text) { return hcomment_unescaped(contains(text, "-->") ? htmlencode(text) : text); } static String struct(Object o) { return structure(o); } static String struct(Object o, structure_Data data) { return structure(o, data); } static String[] conceptFields_drop = {"className", "fieldValues", "id", "created", "_modified", "refs", "backRefs", "_concepts"}; static Set<String> conceptFields(Concept c) { return setMinus(mergeSets(allNonStaticNonTransientFields(c), keys(c.fieldValues)), conceptFields_drop); } static Set<String> conceptFields(Class<? extends Concept> c) { return setMinus(allNonStaticNonTransientFields(c), conceptFields_drop); } 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 conceptsFileName() { return "concepts.structure.gz"; } static Random defaultRandomGenerator() { { Random r = customRandomizerForThisThread(); if (r != null) return r; } return ThreadLocalRandom.current(); } static Random getRandomizer(Random r) { return r != null ? r : defaultRandomGenerator(); } static <A> A oneOf(List<A> l) { if (empty(l)) return null; int n = l.size(); return n == 1 ? first(l) : l.get(defaultRandomizer().nextInt(n)); } static char oneOf(String s) { return empty(s) ? '?' : s.charAt(random(l(s))); } static String oneOf(String... l) { return oneOf(asList(l)); } static <A> A collectionGet(Collection<A> c, int idx) { if (c == null || idx < 0 || idx >= l(c)) return null; if (c instanceof List) return listGet((List<A>) c, idx); Iterator<A> 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 <A, B> Pair<A, B> entryToPair(Map.Entry<A, B> e) { return mapEntryToPair(e); } static <A, B> Set<Map.Entry<A,B>> entries(Map<A, B> map) { return _entrySet(map); } static String roundBracket(String s) { return "(" + s + ")"; } static String roundBracket(Object s) { return roundBracket(str(s)); } static String newLinesToSpaces_trim(String s) { return s == null ? null : trim(s).replaceAll("\\s*\r?\n\\s*", " "); } 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 (isSnippetID(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; } else return loadBufferedImage(new File(snippetIDOrURLOrFile)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedImage loadBufferedImage(File file) { return loadBufferedImageFile(file); } static String rep(int n, char c) { return repeat(c, n); } static String rep(char c, int n) { return repeat(c, n); } static <A> List<A> rep(A a, int n) { return repeat(a, n); } static <A> List<A> rep(int n, A a) { return repeat(n, a); } 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 <A, B> void addPair(Collection<Pair<A, B>> c, A a, B b) { if (c != null) c.add(pair(a, b)); } static <A> A _get(List<A> 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> A _get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } 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(); } // works on lists and strings and null static int indexOfIgnoreCase(List<String> a, String b) { return indexOfIgnoreCase(a, b, 0); } static int indexOfIgnoreCase(List<String> 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(a, b, 0); } static int indexOfIgnoreCase(String[] a, String b, int i) { int n = a == null ? 0 : a.length; for (; i < n; i++) if (eqic(a[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 String structureForUser(Object o) { return beautifyStructure(struct_noStringSharing(o)); } static HashMap<String, Class> 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> A nuObject(Class<A> c, Object... args) { try { if (args == null || 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 <A, B> Map<A, B> filterMapByFunctionOnKey(Map<A, B> map, Object f) { Map<A, B> m2 = similarEmptyMap(map); for (A a : keys(map)) { if (isTrue(callF(f, a))) m2.put(a, map.get(a)); } return m2; } static <A, B> Map<A, B> filterMapByFunctionOnKey(Object f, Map<A, B> map) { return filterMapByFunctionOnKey(map, f); } 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 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 String tok_deRoundBracket(String s) { return tok_isRoundBracketed(s) ? join(dropFirstThreeAndLastThree(javaTok(s))) : s; } static List<String> javaTokWithBrackets(String s) { return javaTokPlusBrackets(s); } static List<String> tok_splitAtComma(String s) { return tok_splitAtComma(javaTok(s)); } static List<String> tok_splitAtComma(List<String> tok) { List<String> 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<String> tok_splitAtPlus(String s) { return tok_splitAtPlus(javaTokWithAllBrackets_cached(s)); } static List<String> tok_splitAtPlus(List<String> tok) { return splitAtTokens(tok, "+"); } 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 <A> A[] dropLast(A[] a) { return dropLast(a, 1); } static <A> 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 <A> List<A> dropLast(List<A> l) { return subList(l, 0, l(l)-1); } static <A> List<A> dropLast(int n, List<A> l) { return subList(l, 0, l(l)-n); } static <A> List<A> dropLast(Iterable<A> 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 List<String> tok_splitAtAsterisk(String s) { return tok_splitAtAsterisk(javaTokWithAllBrackets_cached(s)); } static List<String> tok_splitAtAsterisk(List<String> tok) { return splitAtTokens(tok, "*"); } static double parseDouble(String s) { return empty(s) ? 0.0 : Double.parseDouble(s); } 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 } // 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 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<IntRange> parseBusinessHours(String s) { List<String> parts = nempties(tok_splitAtComma(s)); return map(parts, part -> { List<String> 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 IntRange intRange(int start, int end) { return new IntRange(start, end); } 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 boolean intRangeContains(IntRange r, int i) { return r != null && i >= r.start && i < r.end; } static <A> List<A> syncList() { return synchroList(); } static <A> List<A> syncList(List<A> l) { return synchroList(l); } static <A> boolean syncAddAll(Collection<A> c, Iterable<A> b) { if (c == null || b == null) return false; synchronized(c) { return c.addAll(asCollection(b)); } } static void addToContainer(Container a, Component... b) { if (a == null) return; { swing(() -> { for (Component c : unnullForIteration(b)) if (c != null) a.add(c); }); } } static <A> boolean syncEmpty(Collection<A> l) { if (l == null) return true; synchronized(l) { return l.isEmpty(); } } static <A> A popLast(List<A> l) { return liftLast(l); } static <A> List<A> popLast(int n, List<A> l) { return liftLast(n, l); } static <A, B> List<B> syncLambdaMap(IF1<A, B> f, Collection<A> l) { if (l == null) return emptyList(); synchronized(collectionMutex(l)) { return map(l, f); } } static <A> A shallowClone(A o) { return (A) shallowClone_impl(o); } static <A> 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 DynModule dm_currentModule() { return optCast(DynModule.class, dm_current_generic()); } 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); } // f can return false to suppress regular printing // call print_raw within f to actually print something static AutoCloseable tempInterceptPrint(F1<String, Boolean> f) { return tempSetThreadLocal(print_byThread(), f); } 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 lCharSequence(CharSequence s) { return s == null ? 0 : s.length(); } static <A, B> Map<A, B> newWeakHashMap() { return _registerWeakMap(synchroMap(new WeakHashMap())); } 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 <A, B> Map<A, B> newDangerousWeakHashMap() { return _registerDangerousWeakMap(synchroMap(new WeakHashMap())); } // initFunction: voidfunc(Map) - is called initially, and after clearing the map static <A, B> Map<A, B> newDangerousWeakHashMap(Object initFunction) { return _registerDangerousWeakMap(synchroMap(new WeakHashMap()), initFunction); } 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 != l(args)) { 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 String singleFieldName(Class c) { Set<String> l = listFields(c); if (l(l) != 1) throw fail("No single field found in " + c + " (have " + n(l(l), "fields") + ")"); return first(l); } static Field setOpt_findField(Class c, String field) { HashMap<String, Field> 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<String, Field> 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. TODO: huh? 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 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 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 a != null && b != null && b.isAssignableFrom(a); // << always hated that method, let's replace it! } static Object derefRef(Object o) { if (o instanceof Concept.Ref) o = ((Concept.Ref) o).get(); return o; } static <A extends Concept> A derefRef(Concept.Ref<A> r) { return r == null ? null : r.get(); } static int compareIC(String s1, String s2) { return compareIgnoreCase_jdk(s1, s2); } 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 <A> A[] itemPlusArray(A a, A[] l) { return singlePlusArray(a, l); } 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 <A> ArrayList<A> toList(A[] a) { return asList(a); } static ArrayList<Integer> toList(int[] a) { return asList(a); } static <A> ArrayList<A> toList(Set<A> s) { return asList(s); } static <A> ArrayList<A> toList(Iterable<A> s) { return asList(s); } 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(vm_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(vm_generalMap()) { Map m = (Map) (vm_generalMap_get(name)); if (m == null) vm_generalMap_put(name, m = syncHashMap()); return m; } } 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 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 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 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 boolean neqic(String a, String b) { return !eqic(a, b); } static boolean neqic(char a, char b) { return !eqic(a, b); } 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 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 DynModule dm_current() { return dm_currentModule(); } static Class javax() { return getJavaX(); } static Map vm_generalWeakSubMap(Object name) { synchronized(vm_generalMap()) { Map map = (Map) (vm_generalMap_get(name)); if (map == null) vm_generalMap_put(name, map = newWeakMap()); return map; } } static <A> TreeMap<String, A> caseInsensitiveMap() { return new TreeMap(caseInsensitiveComparator()); } 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 Object dm_current_generic() { return getWeakRef(dm_current_generic_tl().get()); } static List<String> dm_modulesWithLibID(final String moduleLibID) { return filter(dm_moduleIDs() , new F1<String, Boolean>() { 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)"; }}); } // Use like this: printVars(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(, +x); static void printVars(Object... params) { printVars_str(params); } 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<String> 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 <A, B> List<B> lookupAllOpt(Map<A, B> map, Collection<A> l) { List<B> out = new ArrayList(); if (l != null) for (A a : l) addIfNotNull(out, map.get(a)); return out; } static <A, B> List<B> lookupAllOpt(Collection<A> l, Map<A, B> map) { return lookupAllOpt(map, l); } static <A, B extends IF0<A>> List<A> getVars(Iterable<B> l) { return lambdaMap(__75 -> getVar(__75), l); } static String dropNumberPrefix(String s) { return dropFirst(s, indexOfNonDigit(s)); } static <A, B> NavigableMap<A, B> synchroNavigableMap(NavigableMap<A, B> map) { return Collections.synchronizedNavigableMap(map); } static Object defaultDefaultClassFinder() { return new F1<String, Class>() { public Class get(String name) { // Fix some buggy concepts files out there name = replacePrefix("main$main$", "main$", name); Class c = get2(name); return c; } Class get2(String name) { // special invocation to find main class irrelevant of name if (eq(name, "<main>")) 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 String programIDWithCase() { return nempty(caseID()) ? programID() + "/" + quoteUnlessIdentifierOrInteger(caseID()) : programID(); } 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<Boolean> 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 String getInnerMessage(Throwable e) { if (e == null) return null; return getInnerException(e).getMessage(); } 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; } static BufferedReader utf8BufferedReader(InputStream in) { return utf8bufferedReader(in); } static BufferedReader utf8BufferedReader(File f) { return utf8bufferedReader(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); } } static Producer<String> javaTokC_noMLS_onReader(final BufferedReader r) { final class X implements Producer<String> { 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 boolean isString(Object o) { return o instanceof String; } static Producer<String> javaTokC_noMLS_iterator(final String s) { return javaTokC_noMLS_iterator(s, 0); } static Producer<String> javaTokC_noMLS_iterator(final String s, final int startIndex) { return new Producer<String>() { 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 String actualMCDollar() { return actualMC().getName() + "$"; } static BigInteger parseBigInt(String s) { return new BigInteger(s); } static float parseFloat(String s) { return Float.parseFloat(s); } 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 HashMap<String, Class> 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 boolean isLongConstant(String s) { if (!s.endsWith("L")) return false; s = s.substring(0, l(s)-1); return isInteger(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 List parseList(String s) { return (List) safeUnstructure(s); } static <A> List<A> synchroLinkedList() { return synchroList(new LinkedList<A>()); } static <A, B> SortedMap<A, B> synchroSortedMap(SortedMap<A, B> map) { return Collections.synchronizedSortedMap(map); } static Map synchroMap() { return synchroHashMap(); } static <A, B> Map<A, B> synchroMap(Map<A, B> map) { return Collections.synchronizedMap(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 <A> Constructor nuStubInnerObject_findConstructor(Class<A> c) { return nuStubInnerObject_findConstructor(c, null); } static <A> Constructor nuStubInnerObject_findConstructor(Class<A> 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<Class, Constructor> nuEmptyObject_cache = newDangerousWeakHashMap(); static <A> A nuEmptyObject(Class<A> 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<String, Object> fields) { if (fields == null || o == null) return; HashMap<String, Field> fieldMap = instanceFieldsMap(o); for (Map.Entry<String, Object> 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) { printStackTrace(__e); }} } static void setOptAll_pcall(Object o, Map<String, Object> 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 setDynObjectValue(DynamicObject o, String field, Object value) { dynamicObject_setRawFieldValue(o, field, value); } static void pcallOpt_noArgs(Object o, String method) { try { callOpt_noArgs(o, method); } catch (Throwable __e) { printStackTrace(__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<Integer> 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<Double> 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<String> ciSet() { return caseInsensitiveSet(); } // DIFFERENCES to jfind: always ignores case, doesn't recognize <id> 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<String> toks) { return jmatch(pat, toks, null); } static boolean jmatch(String pat, List<String> toks, Matches matches) { List<String> 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 File getProgramDir() { return programDir(); } static File getProgramDir(String snippetID) { return programDir(snippetID); } 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"); } static double toM_double(long l) { return l/(1024*1024.0); } // TODO: extended multi-line strings static int javaTok_n, javaTok_elements; static boolean javaTok_opt = false; static List<String> javaTok(String s) { ++javaTok_n; ArrayList<String> 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<String> javaTok(List<String> tok) { return javaTokWithExisting(join(tok), tok); } static boolean containsNewLine(String s) { return contains(s, '\n'); // screw \r, nobody needs it } 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); } // 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 Map<String, String> singular_specials = litmap( "children", "child", "images", "image", "chess", "chess"); static Set<String> 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 Set<String> getPlural_specials = litciset("sheep", "fish"); static String getPlural(String s) { if (contains(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 <A> A assertEquals(Object x, A y) { return assertEquals("", x, y); } static <A> 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 List<String> javaTokC(String s) { if (s == null) return null; int l = s.length(); ArrayList<String> 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 boolean startsWithDigit(String s) { return nempty(s) && isDigit(s.charAt(0)); } 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 boolean isPersistableClass(Class c) { String name = c.getName(); if (isAnonymousClassName(name)) return false; if (isBoxedType(c)) return true; if (isArrayType(c)) return true; if (c == Class.class || c == String.class || c == File.class || c == Color.class) return true; if (name.startsWith("java.util.Collections$Synchronized")) return true; if (hasThisDollarFields(c)) return hasSingleArgumentConstructor(c); else return getDefaultConstructor(c) != null; } 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<String> l) { return l instanceof TreeSet && className(((TreeSet) l).comparator()).contains("CIComp"); } static boolean isJavaXClassName(String s) { return startsWithOneOf(s, "main$", "loadableUtils."); } static <A> List<A> unwrapSynchronizedList(List<A> 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 <A, B> Map<A, B> unwrapSynchronizedMap(Map<A, B> map) { if (eqOneOf(shortClassName(map), "SynchronizedMap", "SynchronizedSortedMap", "SynchronizedNavigableMap")) return (Map) get_raw(map, "m"); return map; } 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 String boolArrayToHex(boolean[] a) { return bytesToHex(boolArrayToBytes(a)); } static Pair<Class, Integer> arrayTypeAndDimensions(Object o) { return arrayTypeAndDimensions(_getClass(o)); } static Pair<Class, Integer> 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 Map<Class, Field[]> 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 Object fieldGet(Field f, Object o) { try { return f == null ? null : f.get(o); } catch (Exception __e) { throw rethrow(__e); } } static Method findInstanceMethod(Class c, String method, Object... args) { while (c != null) { for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method) && findMethod_checkArgs(m, args, false)) return m; c = c.getSuperclass(); } return null; } static Set<Field> fieldObjectsInFieldOrder(Class c, Set<Field> fields) { try { var byName = mapToKey(f -> f.getName(), fields); LinkedHashSet<Field> out = new LinkedHashSet(); for (String name : unnullForIteration(getFieldOrder(c))) { Field f = byName.get(name); if (f != null) { byName.remove(name); out.add(f); } } addAll(out, fields); return out; } catch (Throwable __0) { printStackTrace(__0); return fields; } } static Object pcallF(Object f, Object... args) { return pcallFunction(f, args); } static <A> A pcallF(F0<A> f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A, B> B pcallF(F1<A, B> f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A> void pcallF(VF1<A> f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { printStackTrace(__e); } } static Object pcallF(Runnable r) { try { { if (r != null) r.run(); } } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A> A pcallF(IF0<A> f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A, B> B pcallF(IF1<A, B> f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } 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 void close_pcall(AutoCloseable c) { if (c != null) { try { c.close(); } catch (Throwable __e) { printStackTrace(__e); }} } static void preCleanUp(Object c) { if (c instanceof Collection) { for (Object o : ((Collection) c)) preCleanUp(o); return; } callOpt(c, "licensed_off"); setOpt_raw(c, "ping_anyActions" , true); // so ping notices setOpt_raw(c, "cleaningUp_flag" , true); } // TODO: optimize (use getOpt_cache) 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 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 pcallOpt(Object o, String method, Object... args) { try { return callOpt(o, method, args); } catch (Throwable __e) { printStackTrace(__e); } return null; } static List<Thread> registeredThreads(Object o) { Map<Thread, Boolean> map = (Map<Thread, Boolean>) (getOpt(o, "_registerThread_threads")); if (map == null) return ll(); map.size(); // force clean-up synchronized(map) { return asList(keys(map)); } } static List<Thread> registeredThreads() { _registerThread_threads.size(); // force clean-up return asList(keys(_registerThread_threads)); } static void interruptThreads(Collection<Thread> 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 <A> boolean addToCollection(Collection<A> c, A a) { return c != null && c.add(a); } static boolean isInstanceX(Class type, Object arg) { if (type == boolean.class) return arg instanceof Boolean; if (type == int.class) return arg instanceof Integer; if (type == long.class) return arg instanceof Long; if (type == float.class) return arg instanceof Float; if (type == short.class) return arg instanceof Short; if (type == char.class) return arg instanceof Character; if (type == byte.class) return arg instanceof Byte; if (type == double.class) return arg instanceof Double; return type.isInstance(arg); } static String dynamicClassName(Object o) { if (o instanceof DynamicObject && ((DynamicObject) o).className != null) return "main$" + ((DynamicObject) o).className; return className(o); } // 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); } 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 Class __javax; static Class getJavaX() { try { return __javax; } catch (Exception __e) { throw rethrow(__e); } } static void __setJavaX(Class j) { __javax = j; _onJavaXSet(); } static boolean isUnstructuring() { return isTrue(getTL(dynamicObjectIsLoading_threadLocal())); } 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; } } // o is either a map already (string->object) or an arbitrary object, // in which case its fields are converted into a map. static Map<String, Object> objectToMap(Object o) { try { if (o instanceof Map) return (Map) o; TreeMap<String, Object> 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<Map<String, Object>> objectToMap(Iterable l) { if (l == null) return null; List x = new ArrayList(); for (Object o : l) x.add(objectToMap(o)); return x; } 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> A vmBus_timerStarted(A timer) { vmBus_send("timerStarted", timer, costCenter()); return timer; } static <A extends Concept> boolean isConceptClassIndexed(Class<A> c) { return isConceptClassIndexed(db_mainConcepts(), c); } static <A extends Concept> boolean isConceptClassIndexed(Concepts concepts, Class<A> c) { return concepts.conceptCounterForClass(c) != null; } static void indexRandomConceptField(Class<? extends Concept> c) { indexRandomConceptField(db_mainConcepts(), c); } static void indexRandomConceptField(Concepts cc, Class<? extends Concept> 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 <A extends JComponent> A setToolTipText(final A c, final Object toolTip) { if (c == null) return null; { swing(() -> { String s = nullIfEmpty(str(toolTip)); if (neq(s, c.getToolTipText())) c.setToolTipText(s); }); } return c; } static <A extends JComponent> A setToolTipText(Object toolTip, A c) { return setToolTipText(c, toolTip); } static JButton jbutton(String text, Runnable action) { return newButton(text, action); } 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); } static String unicode_downPointingTriangle() { return charToString(0x25BC); } // now also takes a map as single array entry static <A> 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<String> lv = (LiveValue<String>) 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!;\r\n if (isCurlyBracketed(s)) {\r\n setEnabled(mi,..."; }}); 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 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 <A> A heldInstance(Class<A> c) { List<Object> 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 int getPreferredWidth(Component c) { return preferredWidth(c); } 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; if (o instanceof String) throw fail("callF_legacy"); return new Runnable() { public void run() { try { callF(o) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(o)"; }}; } static Map<Thread, Boolean> _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 String stackTraceToString(StackTraceElement[] st) { return lines(st); } static String stackTraceToString(Throwable e) { return getStackTrace_noRecord(e); } static PersistableThrowable persistableThrowable(Throwable e) { return e == null ? null : new PersistableThrowable(e); } static boolean isAGIBlueDomain(String domain) { return domainIsUnder(domain, theAGIBlueDomain()); } static String hostNameFromURL(String url) { try { return empty(url) ? null : new URL(url).getHost(); } catch (Exception __e) { throw rethrow(__e); } } 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<Method> 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 <A, B> void mapPut(Map<A, B> map, A key, B value) { if (map != null && key != null && value != null) map.put(key, value); } static <A, B> void mapPut(Map<A, B> map, Pair<A, B> p) { if (map != null && p != null) map.put(p.a, p.b); } // f can return false to suppress regular printing // call print_raw within f to actually print something // f preferrably is F1<S, Bool> static Object interceptPrintInThisThread(Object f) { Object old = print_byThread().get(); print_byThread().set(f); return old; } static List<String> splitAtAmpersand(String s) { return s == null ? emptyList() : asList(s.split("&")); } static Object mainBot() { return getMainBot(); } 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 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 File DiskSnippetCache_getLibrary(String snippetID) { try { return DiskSnippetCache_getLibrary(psI(snippetID)); } catch (Exception __e) { throw rethrow(__e); } } 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<URL> urlsTried = new ArrayList(); List<Throwable> 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 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 File localSnippetFile(long snippetID) { return localSnippetsDir(snippetID + ".text"); } static File localSnippetFile(String snippetID) { return localSnippetFile(parseSnippetID(snippetID)); } static <A> A proxy(Class<A> 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> A proxy(Object target, Class<A> intrface) { return proxy(intrface, target); } static Object vm_generalMap_get(Object key) { return vm_generalMap().get(key); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } 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 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")))); } static ThreadLocal<VF1<File>> checkFileNotTooBigToRead_tl = new ThreadLocal(); static void checkFileNotTooBigToRead(File f) { callF(checkFileNotTooBigToRead_tl.get(), f); } 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 boolean networkAllowanceTest(String url) { return isAllowed("networkAllowanceTest", url); } static void sleepSeconds(double s) { if (s > 0) sleep(round(s*1000)); } static <A> A printWithTime(A a) { return printWithTime("", a); } static <A> A printWithTime(String s, A a) { print(hmsWithColons() + ": " + s, a); return a; } static <A> A getAndClearThreadLocal(ThreadLocal<A> tl) { A a = tl.get(); tl.set(null); return a; } 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 Map vm_generalSubMap(Object name) { synchronized(vm_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 { return con.getInputStream(); } static GZIPInputStream newGZIPInputStream(File f) { return gzInputStream(f); } static GZIPInputStream newGZIPInputStream(InputStream in) { return gzInputStream(in); } 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 Matcher regexpMatcher(java.util.regex.Pattern pat, String s) { return 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 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 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 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 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, (long) 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 Object leftArrow(String script) { GazelleV_LeftArrowScriptParser parser = new GazelleV_LeftArrowScriptParser(); parser.allowTheWorld(); return parser.parse(script).get(); } static String unicode_leftPointingTriangle() { return unicodeFromCodePoint(0x25C2); } static String unicode_rightPointingTriangle() { return charToString(0x25B8); } 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 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 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<String, List<String>> parse3_cachedInput_cache = synchronizedMRUCache(1000); static List<String> parse3_cachedInput(String s) { List<String> tok = parse3_cachedInput_cache.get(s); if (tok == null) parse3_cachedInput_cache.put(s, tok = parse3(s)); return tok; } static Map<String, List<String>> parse3_cachedPattern_cache = synchronizedMRUCache(1000); static synchronized List<String> parse3_cachedPattern(String s) { List<String> tok = parse3_cachedPattern_cache.get(s); if (tok == null) parse3_cachedPattern_cache.put(s, tok = parse3(s)); return tok; } // match2 matches multiple "*" (matches a single token) wildcards and zero or one "..." wildcards (matches multiple tokens) static String[] match2(List<String> pat, List<String> tok) { // standard case (no ...) int i = pat.indexOf("..."); if (i < 0) return match2_match(pat, tok); pat = new ArrayList<String>(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<String> pat, List<String> tok) { List<String> result = new ArrayList<String>(); 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 String formatWithThousandsSeparator(long l) { return NumberFormat.getInstance(new Locale("en_US")).format(l); } static boolean isAbsoluteURL(String s) { return isURL(s); } // does not detect relative urls starting with a letter static boolean isRelativeURL(String s) { return startsWithOneOf(s, "/", "./", "../"); } 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(); } // TODO: process CDATA, scripts static List<String> htmlcoarsetok(String s) { List<String> 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("<!--")) { // HTML comment j = j+3; do ++j; while (j < l && !s.substring(j, Math.min(j+3, 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 List<String> replaceSubList(List<String> l, List<String> x, List<String> y) { return replaceSublist(l, x, y); } static <A> List<A> replaceSubList(List<A> l, int fromIndex, int toIndex, List<A> 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 <A> int smartLastIndexOf(List<A> l, A sub) { int i = lastIndexOf(l, sub); return i < 0 ? l(l) : i; } static String addLineBreak(String s) { return addSuffix(s, "\n"); } 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 <A extends Concept> List<A> filterConcepts(List<A> list, Object... params) { if (empty(params)) return list; List<A> l = new ArrayList(); for (A x : list) if (checkConceptFields(x, params)) l.add(x); return l; } static Object serveByteArray(byte[] data, String mimeType) { return subBot_serveByteArray(data, mimeType); } static <A> A optParam(ThreadLocal<A> tl, A defaultValue) { return optPar(tl, defaultValue); } static <A> A optParam(ThreadLocal<A> 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> 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 boolean boolOptParam(ThreadLocal<Boolean> 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 <A> ThreadLocal<A> threadLocalWithDefault(A defaultValue) { return new ThreadLocal<A>() { public A initialValue() { return defaultValue; } }; } static <A, B> List<A> keysWithoutHidden(Map<A, B> map) { return filter(keys(map) , new F1<Object, Boolean>() { 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 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 Font typeWriterFont() { return typeWriterFont(iround(14*getSwingFontScale())); } static Font typeWriterFont(int size) { return new Font("Courier", Font.PLAIN, size); } static void consoleFont(Font font) { callOpt(getConsoleTextArea_gen(), "setFont", font); } static void consoleInputFont(final Font f) { { swing(() -> { JTextField input = consoleInputField(); if (input != null) { input.setFont(f); revalidateFrame(input); } }); } } static Object[] unrollParams(Object[] params) { if (l(params) == 1 && params[0] instanceof Map) return mapToParams((Map) params[0]); return params; } static String htmlQuote(String s) { return "\"" + htmlencode_forParams(s) + "\""; } static boolean containsNulls(Collection c) { return contains(c, null); } static WeakHasherMap<Symbol, Boolean> symbol_map = new WeakHasherMap(new Hasher<Symbol>() { 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); } // lists files only - who needs to list directories?! // lists in typical .zip fashion - dir1/dir2/bla.txt static List<String> listZipFile(File inZip) { try { ZipFile zipFile = new ZipFile(inZip); try { return listZipFile(zipFile); } finally { zipFile.close(); } } catch (Exception __e) { throw rethrow(__e); } } static List<String> listZipFile(ZipFile zipFile) { try { List<String> 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<String> listZipFile(String name) { if (isSnippetID(name)) return listZipFile(loadLibrary(name)); return listZipFile(getProgramFile(name)); } static void _registerIO(Object object, String path, boolean opened) { } 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 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 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 <A> List<A> dropFirstAndLast(int n, List<A> l) { return cloneSubList(l, n, l(l)-n); } static <A> List<A> dropFirstAndLast(int m, int n, List<A> l) { return cloneSubList(l, m, l(l)-n); } static <A> List<A> dropFirstAndLast(List<A> l) { return dropFirstAndLast(1, l); } static String dropFirstAndLast(String s) { return substring(s, 1, l(s)-1); } static void clearAllTokens(List<String> tok) { for (int i = 0; i < tok.size(); i++) tok.set(i, ""); } static void clearAllTokens(List<String> tok, int i, int j) { for (; i < j; i++) tok.set(i, ""); } static Object swing(Object f) { return swingAndWait(f); } static void swing(Runnable f) { swingAndWait(f); } static <A> A swing(F0<A> f) { return (A) swingAndWait(f); } static <A> A swing(IF0<A> f) { return (A) swingAndWait(f); } static <A extends Component> A revalidate(final A c) { if (c == null || !c.isShowing()) return c; { swing(() -> { // magic combo to actually relayout and repaint c.revalidate(); c.repaint(); }); } return c; } static void revalidate(JFrame f) { revalidate((Component) f); } static void revalidate(JInternalFrame f) { revalidate((Component) f); } static <A, B> LinkedHashMap<A, B> asLinkedHashMap(Map<A, B> map) { if (map instanceof LinkedHashMap) return (LinkedHashMap) map; LinkedHashMap<A, B> m = new LinkedHashMap(); if (map != null) synchronized(collectionMutex(map)) { m.putAll(map); } return m; } static <A, B> void mapPut_noOverwrite(Map<A, B> map, A key, B value) { if (map != null && key != null && value != null && !map.containsKey(key)) map.put(key, value); } static String charToString(char c) { return String.valueOf(c); } static String charToString(int c) { return String.valueOf((char) c); } static <A> ArrayList<A> cloneListSynchronizingOn(Collection<A> l, Object mutex) { if (l == null) return new ArrayList(); synchronized(mutex) { return new ArrayList<A>(l); } } // f: A -> Comparable static <A> List<A> sortByCalculatedField(Iterable<A> c, final Object f) { List<A> l = cloneList(c); sort(l, new Comparator<A>() { public int compare(A a, A b) { return stdcompare(callF(f, a), callF(f, b)); } }); return l; } 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 short[] subShortArray(short[] 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 short[0]; short[] x = new short[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static byte[] subByteArray(byte[] b, int start) { return subByteArray(b, start, l(b)); } static byte[] subByteArray(byte[] 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 byte[0]; byte[] x = new byte[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static byte[] subByteArray(byte[] b, IntRange r) { return r == null ? null : subByteArray(b, r.start, r.end); } static double[] subDoubleArray(double[] b, int start) { return subDoubleArray(b, start, l(b)); } static double[] subDoubleArray(double[] 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 double[0]; double[] x = new double[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } 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 <A> int lastIndexOf(List<A> 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 <A> int lastIndexOf(List<A> 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; } static CloseableIterableIterator<String> linesFromFile(File f) { return linesFromFile(f, null); } static CloseableIterableIterator<String> 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<String> linesFromFile(String path) { return linesFromFile(path, null); } static CloseableIterableIterator<String> linesFromFile(String path, IResourceHolder resourceHolder) { return linesFromFile(newFile(path), resourceHolder); } static SimpleDateFormat simpleDateFormat_local(String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(localTimeZone()); return sdf; } static <A> String pnlToString(String prefix, Iterable<A> 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 <A> String pnlToString(final Iterable<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 <A> 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 <A, B> String pnlToString(final Map<A, B> map) { return hijackPrint(new Runnable() { public void run() { try { pnl(map) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pnl(map)"; }}); } static <A, B> String pnlToString(MultiMap<A, B> map) { return pnlToString(multiMapToMap(map)); } 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; } // 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 <A extends Concept> List<A> findBackRefs(Collection<? extends Concept> concepts, Class<A> type) { IdentityHashMap<A, Boolean> 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 <A extends Concept> List<A> findBackRefs(Concept c, Class<A> type) { IdentityHashMap<A, Boolean> 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 <A extends Concept> List<A> findBackRefs(Class<A> type, Concept c) { return findBackRefs(c, type); } static Collection<Concept> findBackRefs(Concept c) { return findBackRefs(c, Concept.class); } 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 String joinNemptiesWithSemicolon(String... strings) { return joinNempties("; ", strings); } static String joinNemptiesWithSemicolon(Collection<String> strings) { return joinNempties("; ", strings); } static String reverseString(String s) { return empty(s) ? s : new StringBuilder(s).reverse().toString(); } static TreeSet<String> caseInsensitiveSet_treeSet() { return new TreeSet(caseInsensitiveComparator()); } static TreeSet<String> caseInsensitiveSet_treeSet(Collection<String> c) { return toCaseInsensitiveSet_treeSet(c); } static String actualProgramID() { //try answer getProgramIDOrNull(realMC()); return programID(); } 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 getQueryFromURL(String url) { return dropAfterSubstring(substring(url, smartIndexOf(url, "?")+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 <A, B> MultiMap<A, B> treeMultiMap() { return new MultiMap(true); } static <A, B> MultiMap<A, B> treeMultiMap(Comparator<A> comparator) { return new MultiMap(new TreeMap<A, List<B>>(comparator)); } static <A> List<A> sortedByField(Collection<A> c, final String field) { List<A> l = new ArrayList(c); sort(l, new Comparator<A>() { public int compare(A a, A b) { return cmp(getOpt(a, field), getOpt(b, field)); } }); return l; } static <A> List<A> sortedByField(String field, Collection<A> c) { return sortedByField(c, field); } static Object loadJSONFile(File f) { return jsonDecode(loadTextFile(f)); } static List<String> filterNempty(Collection<String> c) { List<String> l = new ArrayList(); for (String x : unnull(c)) if (nempty(x)) l.add(x); return l; } static <A> A objectWhere(Collection<A> c, Object... data) { return findWhere(c, data); } static String upper(String s) { return s == null ? null : s.toUpperCase(); } static char upper(char c) { return Character.toUpperCase(c); } 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 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 final Map<Class, _MethodCache> 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(Executable 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); // TODO: optimize int nVarArgs = nArgs-n; Object varArgs = Array.newInstance(varArgType, nVarArgs); for (int i = 0; i < nVarArgs; i++) Array.set(varArgs, i, args[n+i]); newArgs[n] = varArgs; return newArgs; } static List<String> classNames(Collection l) { return getClassNames(l); } static List<String> classNames(Object[] l) { return getClassNames(Arrays.asList(l)); } 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 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 <A> Comparator<A> descFieldComparator(final String field) { return new Comparator<A>() { public int compare(A a, A b) { return cmp(getOpt(b, field), getOpt(a, field)); } }; } 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 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) { printStackTrace(__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<File> files = new ArrayList(); Map<File, Double> 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); } // 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<Matcher, String> f) { return regexReplace(s, pat, (Object) f); } static <A> A lookupOrKeep(Map<A, A> 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 String codePointToString(int codePoint) { return new String(Character.toChars(codePoint)); } 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 boolean isCISet(Iterable<String> l) { return l instanceof TreeSet && ((TreeSet) l).comparator() == caseInsensitiveComparator(); } 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(DoubleRect r) { if (r == null) return null; int x = iround(r.x), y = iround(r.y); return new Rect(x, y, iround(r.x2())-x, iround(r.y2())-y); } static Rect toRect(Rect r) { return r; } static SimpleDateFormat simpleDateFormat_timeZone(String format, String timeZone) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(timeZone(timeZone)); return sdf; } 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 <A, B> Map<A, B> 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 <A, B> B mapGetOrCreate(Map<A, B> map, A key, Class<? extends B> c) { return getOrCreate(map, key, c); } // f : func -> B static <A, B> B mapGetOrCreate(Map<A, B> map, A key, Object f) { return getOrCreate(map, key, f); } static <A, B> B mapGetOrCreate(Map<A, B> map, A key, IF0<B> f) { return getOrCreate(map, key, (Object) f); } static <A, B> B mapGetOrCreate(Class<? extends B> c, Map<A, B> 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<LongRange, String> binarySearchForLineInTextFile(File file, IF1<String, Integer> nav) { long length = l(file); int bufSize = 1024; RandomAccessFile raf = randomAccessFileForReading(file); try { long min = 0, max = length; int direction = 0; Pair<LongRange, String> 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<String> tok_splitAtComma_unquote(String s) { List<String> tok = javaTok(s); List<String> 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; } // 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 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);"; }}; } static JTextField standardTextFieldPopupMenu(final JTextField tf) { final WeakReference<JTextField> 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 <A extends JTextComponent> A jenableUndoRedo(final A textcomp) { { swing(() -> { 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"); }); } return textcomp; } static JPanel showFormTitled_customArrangement(final boolean internalFrame, final F1<List<JComponent>, JPanel> arrange, final String title, final Object... _parts) { return swing(new F0<JPanel>() { public JPanel get() { try { List<JComponent> 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<JComponent> 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<? extends Component> components) { return addAllComponents(customLayoutPanel(layouter), components); } static List<Component> getComponents(final Component c) { return !(c instanceof Container) ? emptyList() : asList(swing(new F0<Component[]>() { public Component[] get() { try { return ((Container) c).getComponents(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return ((Container) c).getComponents();"; }})); } static boolean containsATextArea(Component c) { return childOfType(c, JTextArea.class) != null; } static int totalPreferredHeight(List<Component> l) { int h = 0; for (Component c : unnull(l)) h += c.getPreferredSize().height; return h; } static <A> List<A> listWithoutIndicesInBitSet(List<A> l, BitSet bs) { List<A> out = new ArrayList(); for (int i = 0; i < l(l); i++) if (!bs.get(i)) out.add(l.get(i)); return out; } static double doubleRatio(double x, double y) { return y == 0 ? 0 : x/y; } static double doubleRatio(Seconds x, Seconds y) { return doubleRatio(x.get(), y.get()); } 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 String getText(final AbstractButton c) { return c == null ? "" : (String) swingAndWait(new F0<Object>() { public Object get() { try { return c.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getText();"; }}); } static String getText(final JTextComponent c) { return c == null ? "" : (String) swingAndWait(new F0<Object>() { public Object get() { try { return c.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getText();"; }}); } static String getText(final JLabel l) { return l == null ? "" : (String) swingAndWait(new F0<Object>() { public Object get() { try { return l.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return 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 <A> 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 JWindow makeWindow(final Component c) { return swing(new F0<JWindow>() { public JWindow get() { try { JWindow w = new JWindow(); w.add(wrap(c)); return w; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "new JWindow w;\r\n w.add(wrap(c));\r\n ret 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 extends Component> A moveToTopRightCorner(A a) { return moveToTopRightCorner(moveToTopRightCorner_inset, moveToTopRightCorner_inset, a); } static <A extends Component> A moveToTopRightCorner(int insetX, int insetY, A a) { { swing(() -> { Window w = getWindow(a); if (w != null) { var bounds = preferredScreenBounds(); w.setLocation(bounds.x2()-w.getWidth()-insetX, bounds.y1()+insetY); } }); } return a; } static boolean vmBus_noObjections(String msg, Object... args) { return !vmBus_anyFalse(msg, args); } static <A extends Window> 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 extends Window> A disposeWindowAfter(A w, double seconds) { return disposeWindowAfter(toMS_int(seconds), w); } static <A extends Window> A disposeWindowAfter(double seconds, A w) { return disposeWindowAfter(w, seconds); } 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 { pcallF(runnable); } finally { _close(__1); }} catch (Throwable __e) { messageBox(__e); }}}; } static String programName() { return getProgramName(); } static JDesktopPane mainDesktopPane_value; static JDesktopPane mainDesktopPane() { return mainDesktopPane_value; } 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 IVF2<String, JPanel> showForm_makeFrame; static void showForm_makeFrame(String title, JPanel panel) { if (showForm_makeFrame != null) showForm_makeFrame.get(title, panel); else showForm_makeFrame_base(title, panel); } final static void showForm_makeFrame_fallback(IVF2<String, JPanel> _f, String title, JPanel panel) { if (_f != null) _f.get(title, panel); else showForm_makeFrame_base(title, panel); } static void showForm_makeFrame_base(String title, JPanel panel) { handleEscapeKey(minFrameWidth(showPackedFrame(title, withMargin(panel)), 400)); } static JFrame getFrame(final Object _o) { return swing(new F0<JFrame>() { 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 List<JComponent> showForm_arrange1(List<List<JComponent>> l) { int minW = showForm_leftWidth(l); List<JComponent> out = new ArrayList(); for (List<JComponent> row : l) out.add(westAndCenter(withRightMargin(showForm_gapBetweenColumns, jMinWidthAtLeast(minW, first(row))), second(row))); return out; } static List<List<JComponent>> showForm_makeComponents(final Boolean internalFrame, Object... _parts) { List<List<JComponent>> 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(__76 -> wrapForSmartAdd_jComponent(__76), 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(__77 -> wrapForSmartAdd_jComponent(__77), 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<JComponent> 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<JPanel>() { 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+1)/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 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"; } 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 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); } // f: func -> A | endMarker static <A> IterableIterator<A> 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 <A> IterableIterator iff(IF0 f) { return iteratorFromFunction_withEndMarker(f); } static Object endMarker() { return iteratorFromFunction_endMarker; } static <A> List<A> wrapArrayAsList(A[] a) { return a == null ? null : Arrays.asList(a); } static <A> String joinWithSemicolon(Iterable<A> 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<String> splitAtColon(String s) { return empty(s) ? emptyList() : asList(s.split(":")); } static String stringOptPar(Object[] params, String name) { return (String) optPar(params, name); } static List<String> words2(String s) { List<String> 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 ThreadLocal<Boolean> doPost_silently = new ThreadLocal(); static ThreadLocal<Long> doPost_timeout = new ThreadLocal(); static ThreadLocal<Map<String, String>> 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<String, String> 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 <A> AutoCloseable tempSetTL(ThreadLocal<A> tl, A a) { return tempSetThreadLocal(tl, a); } static boolean rtagIs(String tag, String token) { return tagIs(token, tag); } static Map<String, String> ciTagParams(String tag) { return asCIMap(tagParams(tag)); } static String hcomment_unescaped(String text) { return "<!-- " + text + " -->"; } static <A> Set<A> setMinus(Set<A> set, Object... stuff) { Set s2 = cloneSet(set); for (Object o : stuff) s2.remove(o); return s2; } static <A> Set<A> mergeSets(Collection<A>... l) { return joinSets(l); } static Set<String> allNonStaticNonTransientFields(Object o) { TreeSet<String> 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 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 Random customRandomizerForThisThread() { return customRandomizerForThisThread_tl().get(); } static Random defaultRandomizer() { return defaultRandomGenerator(); } static <A> A listGet(List<A> l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } static BufferedImage imageIO_readURL(String url) { try { return ImageIO.read(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } 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 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 <A> List<A> repeat(A a, int n) { n = Math.max(n, 0); List<A> l = new ArrayList(n); for (int i = 0; i < n; i++) l.add(a); return l; } static <A> List<A> repeat(int n, A a) { return repeat(a, n); } 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 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 String beautifyStructure(String s) { List<String> 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 Map<String, Class> 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<Class, Constructor> nuObjectWithoutArguments_cache = newDangerousWeakHashMap(); static Object nuObjectWithoutArguments(String className) { try { return nuObjectWithoutArguments(classForName(className)); } catch (Exception __e) { throw rethrow(__e); } } static <A> A nuObjectWithoutArguments(Class<A> 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<Class> getClasses(Object[] array) { List<Class> l = emptyList(l(array)); for (Object o : array) l.add(_getClass(o)); return l; } static boolean startsWithIgnoreCase(String a, String b) { return regionMatchesIC(a, 0, b, 0, b.length()); } static boolean tok_isRoundBracketed(String s) { List<String> tok = tok_combineRoundBrackets_keep(javaTok(s)); return l(tok) == 3 && startsWithAndEndsWith(tok.get(1), "(", ")"); } static <A> List<A> dropFirstThreeAndLastThree(List<A> l) { return dropFirstAndLast(3, l); } static List<String> javaTokPlusBrackets(String s) { return tok_combineRoundOrCurlyBrackets_keep(javaTok(s)); } static String joinSubList(List<String> l, int i, int j) { return join(subList(l, i, j)); } static String joinSubList(List<String> l, int i) { return join(subList(l, i)); } static String joinSubList(List<String> l, IntRange r) { return r == null ? null : joinSubList(l, r.start, r.end); } static Map<String, List<String>> javaTokWithAllBrackets_cached_cache = synchronizedMRUCache(defaultTokenizerCacheSize()); static List<String> javaTokWithAllBrackets_cached(String s) { List<String> tok = javaTokWithAllBrackets_cached_cache.get(s); if (tok == null) javaTokWithAllBrackets_cached_cache.put(s, tok = javaTokWithAllBrackets(s)); return tok; } static List<String> splitAtTokens(String s, List<String> tokens) { return splitAtTokens(javaTok(s), tokens); } static List<String> splitAtTokens(List<String> tok, List<String> tokens) { List<String> 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<String> splitAtTokens(List<String> tok, String... tokens) { List<String> 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 char lastChar(String s) { return empty(s) ? '\0' : s.charAt(l(s)-1); } static <A> A[] arrayOfSameType(A[] a, int n) { return newObjectArrayOfSameType(a, n); } 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 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 <A> boolean all(Iterable<A> l, IF1<A, Boolean> f) { if (l != null) for (A a : l) if (!f.get(a)) return false; return true; } static <A> boolean all(IF1<A, Boolean> f, Iterable<A> 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<String> toks, Matches matches) { List<String> 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<String> tokPat = parse3_cachedPattern(pat); List<String> 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<String> splitAtMinus(String s) { return trimAll(asList(s.split("\\-"))); } static Integer parseHourAndOptionalMinutesToMinutes(String s) { List<String> 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 <A> Collection<A> asCollection(Iterable<A> l) { return l == null ? null : l instanceof Collection ? ((Collection) l) : asList(l); } static <A> A liftLast(List<A> l) { if (empty(l)) return null; int i = l(l)-1; A a = l.get(i); l.remove(i); return a; } static <A> List<A> liftLast(int n, List<A> l) { int i = l(l)-n; List<A> part = cloneSubList(l, i); removeSubList(l, i); return part; } static <A> A copyFields(Object x, A y, String... fields) { if (empty(fields)) { // assume we should copy all fields Map<String, Object> 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> A copyFields(Object x, A y, Collection<String> fields) { return copyFields(x, y, asStringArray(fields)); } static <A> A optCast(Class<A> c, Object o) { return isInstance(c, o) ? (A) o : null; } //static final Map<Class, HashMap<S, Field>> 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<Class, HashMap<String, Field>> getOpt_cache = _registerDangerousWeakMap(synchroMap(new getOpt_Map())); //static final Map<Class, HashMap<S, Field>> 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<String, Field> getOpt_getFieldMap(Object o) { Class c = _getClass(o); HashMap<String, Field> 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<String, Field> 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<String, Field> getOpt_makeCache(Class c) { HashMap<String, Field> 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<List<Object>> 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); } }; } // requires ugly casting when used (O -> A) static Object iteratorFromFunction_endMarker = new Object(); // f: func -> A | endMarker static <A> IterableIterator<A> iteratorFromFunction_withEndMarker(final Object f) { class IFF extends IterableIterator<A> { 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 <A> IterableIterator<A> iteratorFromFunction_withEndMarker(final F0 f) { return iteratorFromFunction_withEndMarker_f0(f); } static <A> AutoCloseable tempSetThreadLocal(final ThreadLocal<A> 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 List _registerWeakMap_preList; static <A> 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 x30_pkg.x30_util.BetterThreadLocal<Runnable> newPing_actionTL; static x30_pkg.x30_util.BetterThreadLocal<Runnable> newPing_actionTL() { if (newPing_actionTL == null) newPing_actionTL = vm_generalMap_getOrCreate("newPing_actionTL", () -> { Runnable value = (Runnable) (callF_gen(vm_generalMap_get("newPing_valueForNewThread"))); var tl = new x30_pkg.x30_util.BetterThreadLocal<Runnable>(); tl.set(value); return tl; }); return newPing_actionTL; } static List<Pair> _registerDangerousWeakMap_preList; static <A> A _registerDangerousWeakMap(A map) { return _registerDangerousWeakMap(map, null); } static <A> A _registerDangerousWeakMap(A map, Object init) { callF(init, map); if (init instanceof String) { final String f = (String) init; init = new VF1<Map>() { 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 Throwable getExceptionCause(Throwable e) { Throwable c = e.getCause(); return c != null ? c : e; } static String joinWithSpace(Iterable c) { return join(" ", c); } static String joinWithSpace(String... c) { return join(" ", c); } // This is for main classes that are all static. // (We don't go to base classes.) static Set<String> listFields(Object c) { TreeSet<String> fields = new TreeSet(); for (Field f : _getClass(c).getDeclaredFields()) fields.add(f.getName()); return fields; } static <A extends DynamicObject> 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 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 <A, B> LinkedHashMap<A, B> syncMapPut2_createLinkedHashMap(LinkedHashMap<A, B> 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 <A, B, C extends Map<A, B>> 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 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 <A> 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 Object callFunction(Object f, Object... args) { return callF(f, args); } static Throwable _storeException_value; static void _storeException(Throwable e) { _storeException_value = e; } 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 Object vm_generalMap_put(Object key, Object value) { return mapPutOrRemove(vm_generalMap(), key, value); } static <A> Set<A> syncIdentityHashSet() { return (Set) synchronizedSet(identityHashSet()); } static Map syncHashMap() { return synchroHashMap(); } static <A, B> Map<A, B> newWeakMap() { return newWeakHashMap(); } static Comparator<String> caseInsensitiveComparator() { return betterCIComparator(); } static boolean isIdentifier(String s) { return isJavaIdentifier(s); } 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_callOS(String functionName, Object... args) { return call(dm_os(), functionName, args); } static Object dm_resolveModule(Object moduleOrStem) { return dm_callOS("resolveModule", moduleOrStem); } static <A> A getWeakRef(Reference<A> ref) { return ref == null ? null : ref.get(); } static x30_pkg.x30_util.BetterThreadLocal<WeakReference> dm_current_generic_tl; static x30_pkg.x30_util.BetterThreadLocal<WeakReference> 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<String> dm_moduleIDs() { return dm_listModuleIDs(); } 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 AlphanumComparator alphaNumComparator_instance; static Comparator<String> alphaNumComparator() { if (alphaNumComparator_instance == null) alphaNumComparator_instance = new AlphanumComparator(); return alphaNumComparator_instance; } static boolean shortClassNameIs(Object o, String name) { return eq(shortClassName(o), name); } static boolean shortClassNameIs(String name, Object o) { return shortClassNameIs(o, name); } static <A> boolean addIfNotNull(Collection<A> l, A a) { return a != null && l != null & l.add(a); } static <A> void addIfNotNull(MultiSet<A> ms, A a) { if (a != null && ms != null) ms.add(a); } 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 String replacePrefix(String prefix, String replacement, String s) { if (!startsWith(s, prefix)) return s; return replacement + substring(s, l(prefix)); } static Object get2(Object o, String field1, String field2) { return get(get(o, field1), field2); } static boolean startsWithAny(String a, Collection<String> 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<String> 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 quoteUnlessIdentifierOrInteger(String s) { return quoteIfNotIdentifierOrInteger(s); } 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)); } 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 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 <A> A _registerIOWrap(A wrapper, Object wrapped) { return wrapper; } static String quickSubstring(String s, int i, int j) { if (i >= j) return ""; return s.substring(i, j); } static Class actualMC() { return or((Class) realMC(), mc()); } static Object safeUnstructure(String s) { return unstructure(s, true); } static Object safeUnstructure(File f) { return safeUnstructureGZFile(f); } static Map synchroHashMap() { return synchronizedMap(new HashMap()); } 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<String, Field> instanceFieldsMap(Object o) { return (HashMap) getOpt_getFieldMap(o); } static Map<Class, Field[]> 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<Field> 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<Class, HashMap<String, Method>> 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<String, Method> 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<String, Method> callOpt_noArgs_makeCache(Class c) { HashMap<String, Method> 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 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<String> javaTokWithExisting(String s, List<String> existing) { ++javaTok_n; int nExisting = javaTok_opt && existing != null ? existing.size() : 0; ArrayList<String> 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 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 String afterLastSpace(String s) { return s == null ? null : substring(s, s.lastIndexOf(' ')+1); } 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 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 ThreadLocal<Boolean> assertVerbose_value = new ThreadLocal(); static void assertVerbose(boolean b) { assertVerbose_value.set(b); } static boolean assertVerbose() { return isTrue(assertVerbose_value.get()); } static <A> A assertEqualsVerbose(Object x, A y) { assertEqualsVerbose((String) null, x, y); return y; } // x = expected, y = actual static <A> 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 <A, B> Map<A, B> nullIfEmpty(Map<A, B> map) { return isEmpty(map) ? null : map; } static <A> List<A> nullIfEmpty(List<A> l) { return isEmpty(l) ? null : l; } static boolean isDigit(char c) { return Character.isDigit(c); } 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; } static boolean isBoxedType(Class type) { return type == Boolean.class || type == Integer.class || type == Long.class || type == Float.class || type == Short.class || type == Character.class || type == Byte.class || type == Double.class; } static boolean isArrayType(Class type) { return type != null && type.isArray(); } static boolean hasThisDollarFields(Object o) { Matches m = new Matches(); for (var f : allFieldObjects_dontMakeAccessible(o)) if (startsWith(f.getName(), "this$", m) && isInteger(m.rest())) return true; return false; } static boolean hasSingleArgumentConstructor(Class c) { if (c != null) for (Constructor m : c.getDeclaredConstructors()) if (l(m.getParameterTypes()) == 1) return true; return false; } static Constructor getDefaultConstructor(Class c) { if (c != null) for (Constructor m : c.getDeclaredConstructors()) if (empty(m.getParameterTypes())) return m; return null; } 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 <A, B, C> List<Pair<A, C>> mapPairB(final Object f, Iterable<Pair<A, B>> l) { return map(l, new F1<Pair<A, B>, Pair<A, C>>() { public Pair<A, C> get(Pair<A, B> 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 <A, B, C> List<Pair<A, C>> mapPairB(final F1<B, C> f, Iterable<Pair<A, B>> l) { return mapPairB((Object) f, l); } static <A, B, C> List<Pair<A, C>> mapPairB(final IF1<B, C> f, Iterable<Pair<A, B>> l) { return mapPairB((Object) f, l); } static <A, B, C> List<Pair<A, C>> mapPairB(Iterable<Pair<A, B>> l, IF1<B, C> f) { return mapPairB((Object) f, l); } static <A, B, C> Pair<A, C> mapPairB(IF1<B, C> f, Pair<A, B> p) { return pairMapB(f, p); } static <A, B, C> Pair<A, C> mapPairB(Pair<A, B> p, IF1<B, C> f) { return pairMapB(f, p); } 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 <A, B> Map<B, A> mapToKey(Iterable<A> l, IF1<A, B> f) { return mapToKeys(l, f); } static <A, B> Map<B, A> mapToKey(IF1<A, B> f, Iterable<A> l) { return mapToKeys(f, l); } static Map<Class, List<String>> getFieldOrder_cache = weakMap(); static List<String> getFieldOrder(Object o) { return getFieldOrder(_getClass(o)); } static List<String> getFieldOrder(Class c) { if (c == null) return null; return getOrCreate(getFieldOrder_cache, c, () -> splitAtSpace(toStringOpt(getOpt(c, "_fieldOrder")))); } static Object pcallFunction(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable __e) { printStackTrace(__e); } return null; } static List<VF1<Map>> _threadInheritInfo_retrievers = synchroList(); static void _threadInheritInfo(Object info) { if (info == null) return; pcallFAll(_threadInheritInfo_retrievers, (Map) info); } static AutoCloseable tempActivity(Object r) { return null; } static long fixTimestamp(long timestamp) { return timestamp > now() ? 0 : timestamp; } static List<String> methodsStartingWith(Object o, final String prefix) { return filter(allMethodNames(o), new F1<String, Object>() { public Object get(String s) { try { return startsWith(s, prefix); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "startsWith(s, prefix)"; }}); } static volatile PersistableThrowable _handleException_lastException; static List _handleException_onException = synchroList(ll((IVF1<Throwable>) (__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 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) { printStackTrace(__e); }} } static boolean isJavaXClassLoader(ClassLoader cl) { return startsWithOneOf(className(cl), "main$JavaXClassLoader", "x30$JavaXClassLoader"); } static void setOptAll(Object o, Map<String, Object> 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 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); } } static void _onJavaXSet() {} static Object getTL(Object o, String name) { return getThreadLocal(o, name); } static <A> A getTL(ThreadLocal<A> tl) { return getThreadLocal(tl); } static <A> A getTL(ThreadLocal<A> tl, A defaultValue) { return getThreadLocal(tl, defaultValue); } static boolean isTrueOrYes(Object o) { return isTrueOpt(o) || o instanceof String && (eqicOneOf(((String) o), "1", "t", "true") || isYes(((String) o))); } static Object costCenter() { return mc(); } static ThreadLocal<Long> 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<Long> 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<JButton>() { public JButton get() { try { String text2 = dropPrefix("[disabled] ", text); JButton btn = basicJButton(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 JButton btn = basicJButton(te..."; }}); } static <A> A swingNu(final Class<A> c, final Object... args) { return swingConstruct(c, args); } static Object[] asArray(List l) { return toObjectArray(l); } static <A> A[] asArray(Class<A> 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 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<JMenuItem>() { public JMenuItem get() { try { Pair<String, Integer> 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<S, Int> p = jmenu_autoMnemonic(dropPrefix(\"[disabled] \", text));\r\n JM..."; }}); } static <A extends JComponent> 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.removeChangeListener(listener) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "lv.removeChangeListener(listener)"; }}); return component; } static boolean isCurlyBracketed(String s) { return isCurlyBraced(s); } static <A extends JComponent> A setEnabled(A c, boolean enable) { if (c != null) { swing(() -> { c.setEnabled(enable); }); } return c; } static <A extends JComponent> A setEnabled(boolean enable, A c) { return setEnabled(c, enable); } static void setEnabled(boolean enable, JComponent... l) { for (var c : unnullForIteration(l)) setEnabled(c, enable); } static String unCurlyBracket(String s) { return tok_unCurlyBracket(s); } static boolean setText_opt = true; // optimize by calling getText first static <A extends JTextComponent> A setText(A c, Object text) { setText((JComponent) c, text); return c; } static <A extends JComboBox> A setText(final A c, Object text) { // only for editable combo boxes at this point final String s = strUnnull(text); { swing(() -> { 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 extends JComponent> A setText(final A c, Object text) { if (c == null) return null; final String s = strUnnull(text); { swing(() -> { if (!setText_opt || neq(callOpt(c, "getText"), s)) call(c, "setText", s); }); } return c; } static boolean isInstanceOf(Object o, Class type) { return type.isInstance(o); } static int preferredWidth(Component c) { return c == null ? 0 : getPreferredSize(c).width; } 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 domainIsUnder(String domain, String mainDomain) { return eqic(domain, mainDomain) || ewic(domain, "." + mainDomain); } static String theAGIBlueDomain() { return "agi.blue"; } 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<Map<String, List<String>>> loadBinaryPage_responseHeaders = new ThreadLocal(); static ThreadLocal<Map<String, String>> 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<String, String> 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); } } 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 <B, A extends B> A addAndReturn(Collection<B> 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 File localSnippetsDir() { return javaxDataDir("Personal Programs"); } static File localSnippetsDir(String sub) { return newFile(localSnippetsDir(), sub); } 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<File> files) { if (files != null) for (File f : files) if (fileExists(f)) return f; return null; } 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 long round(double d) { return Math.round(d); } static String round(String s) { return roundBracket(s); } static Complex round(Complex c) { return new Complex(round(c.re), round(c.im)); } static String hmsWithColons() { return hmsWithColons(now()); } static String hmsWithColons(long time) { return new SimpleDateFormat("HH:mm:ss").format(time); } static String getComputerID_quick() { return computerID(); } static Map<String, java.util.regex.Pattern> 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 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 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 <A, B> Map<A, B> synchronizedMRUCache(int maxSize) { return synchroMap(new MRUCache(maxSize)); } static List<String> parse3(String s) { return dropPunctuation(javaTokPlusPeriod(s)); } static boolean equalsIgnoreCase(String a, String b) { return eqic(a, b); } static boolean equalsIgnoreCase(char a, char b) { return eqic(a, b); } static boolean isLetter(char c) { return Character.isLetter(c); } // syntax 1: replace all occurrences of x in l with y static <A> List<A> replaceSublist(List<A> l, List<A> x, List<A> 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 <A> List<A> replaceSublist(List<A> l, int fromIndex, int toIndex, List<A> 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 <A> List<A> replaceSublist(List<A> l, IntRange r, List<A> y) { return replaceSublist(l, r.start, r.end, y); } static Object subBot_serveByteArray(byte[] data, String mimeType) { return call(mainBot(), "serveByteArray", data, mimeType); } static boolean isStringStartingWith(Object o, String prefix) { return o instanceof String && ((String) o).startsWith(prefix); } static Object jsonDecode(String text) { return new jsonDecode_Y(text).parse(); } // the miraculous class Y static class jsonDecode_Y { String text; List<String> tok; boolean useOrderedMaps = false; int i = 1; jsonDecode_Y(String text) { this.text = text; tok = jsonTok(text); } transient IF1<String, RuntimeException> fail; RuntimeException fail(String msg) { return fail != null ? fail.get(msg) : fail_base(msg); } final RuntimeException fail_fallback(IF1<String, RuntimeException> _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 float getSwingFontScale() { return or((Float) vm_generalMap_get("swingFontScale_value"), 1f); } 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 CloseableIterableIterator emptyCloseableIterableIterator_instance = new CloseableIterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static <A> CloseableIterableIterator<A> emptyCloseableIterableIterator() { return emptyCloseableIterableIterator_instance; } static CloseableIterableIterator<String> linesFromReader(Reader r) { return linesFromReader(r, null); } static CloseableIterableIterator<String> linesFromReader(Reader r, IResourceHolder resourceHolder) { final BufferedReader br = bufferedReader(r); return holdResource(resourceHolder, iteratorFromFunction_f0_autoCloseable(new F0<String>() { public String get() { try { return readLineFromReaderWithClose(br); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return readLineFromReaderWithClose(br);"; }}, _wrapIOCloseable(r))); } static TimeZone localTimeZone() { return getTimeZone(standardTimeZone()); // TimeZone.getDefault()? } static String hijackPrint(Runnable r) { StringBuilder buf = new StringBuilder(); Object old = interceptPrintInThisThread(new F1<String, Boolean>() { public Boolean get(String s) { buf.append(s); return false; } }); try { { if (r != null) r.run(); } return str(buf); } finally { interceptPrintInThisThread(old); } } static <A extends Iterable> A pnl(A l) { return pnl("", l); } static <A extends Iterable> A pnl(String prefix, A l) { printNumberedLines(prefix, l); return l; } static <A> A[] pnl(A[] l) { return pnl("", l); } static <A> A[] pnl(String prefix, A[] l) { printNumberedLines(prefix, l); return l; } static <A extends Map> A pnl(A map) { printNumberedLines(map); return map; } static <A extends Map> A pnl(String prefix, A map) { printNumberedLines(prefix, map); return map; } static String pnl(String s) { printNumberedLines(lines(s)); return s; } static <A> MultiSet<A> pnl(MultiSet<A> ms) { pnl(ms == null ? null : ms.asMap()); return ms; } static <A, B> MultiMap<A, B> pnl(MultiMap<A, B> mm) { pnl(mm == null ? null : mm.asMap()); return mm; } static <A, B> Map<A, List<B>> multiMapToMap(MultiMap<A, B> m) { return m == null ? null : m.data; } 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<numFullGroups; i++) { int byte0 = a[inCursor++] & 0xff; int byte1 = a[inCursor++] & 0xff; int byte2 = a[inCursor++] & 0xff; result.append(intToAlpha[byte0 >> 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 boolean eqicOrEq(Object a, Object b) { return a instanceof String && b instanceof String ? eqic((String) a, (String) b) : eq(a, b); } static TreeSet<String> toCaseInsensitiveSet_treeSet(Iterable<String> c) { if (isCISet(c)) return (TreeSet) c; TreeSet<String> set = caseInsensitiveSet_treeSet(); addAll(set, c); return set; } static TreeSet<String> toCaseInsensitiveSet_treeSet(String... x) { TreeSet<String> set = caseInsensitiveSet_treeSet(); addAll(set, x); return set; } static String dropAfterSubstring(String s, String b) { return substring(s, 0, smartIndexOf(s, b)); } 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 <A> A findWhere(Collection<A> c, Object... data) { if (c != null) for (A x : c) if (checkFields(x, data)) return x; return null; } 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, int destPos, int n) { arraycopy(src, srcPos, src, destPos, n); } static void arraycopy(Object src, int srcPos, Object dest, int destPos, int n) { if (n != 0) System.arraycopy(src, srcPos, dest, destPos, n); } static List<String> getClassNames(Collection l) { List<String> out = new ArrayList(); if (l != null) for (Object o : l) out.add(o == null ? null : getClassName(o)); return out; } 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)); } static boolean possibleMD5(String s) { return isMD5(s); } 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 String dbBotName(String progIDWithCase) { return fsI_flex(progIDWithCase) + " Concepts"; } static Android3 methodsBot2(String name, final Object receiver, final List<String> exposedMethods) { return methodsBot2(name, receiver, exposedMethods, null); } static Android3 methodsBot2(String name, final Object receiver, final List<String> exposedMethods, final Lock lock) { Android3 android = new Android3(); android.greeting = name; android.console = false; android.responder = new Responder() { String answer(String s, List<String> history) { return exposeMethods2(receiver, s, exposedMethods, lock); } }; return makeBot(android); } static List<String> db_standardExposedMethods_list = ll("xlist", "xnew", "xset", "xdelete", "xget", "xclass", "xfullgrab", "xshutdown", "xchangeCount", "xcount"); static List<String> db_standardExposedMethods() { return db_standardExposedMethods_list; } static List<File> listFilesNotDirs(String dir) { return listFilesOnly(dir); } static List<File> listFilesNotDirs(File... dirs) { return listFilesOnly(dirs); } 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 <A> List<A> sortByMap_inPlace(List<A> l, Map<A, ?> 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 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 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); } // allows null keys but not null values static <A, B> B getOrCreate(Map<A, B> map, A key, Class<? extends B> 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 <A, B> B getOrCreate(Map<A, B> 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 <A, B> B getOrCreate(IF0<B> f, Map<A, B> map, A key) { return getOrCreate(map, key, f); } static <A, B> B getOrCreate(Map<A, B> map, A key, IF0<B> f) { B b = map.get(key); if (b == null) map.put(key, b = f.get()); return b; } static <A, B> B getOrCreate(Class<? extends B> c, Map<A, B> map, A key) { return getOrCreate(map, key, c); } 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 String fromUtf8(byte[] bytes) { try { return bytes == null ? null : new String(bytes, utf8charset()); } 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> A assertLessThan(A a, A b) { assertTrue(cmp(b, a) < 0); return b; } static <A> A assertBiggerThan(A a, A b) { assertTrue(cmp(b, a) > 0); return b; } static String trimJoinSubList(List<String> l, int i, int j) { return trim(join(subList(l, i, j))); } static String trimJoinSubList(List<String> l, int i) { return trim(join(subList(l, i))); } 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 Runnable asRunnable(Object o) { return toRunnable(o); } static void _inheritThreadInfo(Object info) { _threadInheritInfo(info); } static <A extends JComponent> A componentPopupMenuItem(A c, final String name, final Object action) { componentPopupMenu(c, new VF1<JPopupMenu>() { 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<JPopupMenu>() { public void get(JPopupMenu menu) { try { addMenuItem(menu, menuItem); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, 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 Set vm_generalWeakSet(Object name) { synchronized(vm_generalMap()) { Set set = (Set) (vm_generalMap_get(name)); if (set == null) vm_generalMap_put(name, set = newWeakHashSet()); return set; } } static AbstractAction abstractAction(String name, final Object runnable) { return new AbstractAction(name) { public void actionPerformed(ActionEvent evt) { pcallF(runnable); } }; } static JPanel jpanel(LayoutManager layout) { return swingNu(JPanel.class, layout); } static JPanel jpanel() { return swingNu(JPanel.class); } // 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 extends Container> A addAllComponents(final A c, final List<? extends Component> components) { if (nempty(components)) { swing(() -> { for (Component x : components) c.add(x); revalidate(c); }); } return c; } static <A> A childOfType(Component c, Class<A> theClass) { return first(childrenOfType(c, theClass)); } static <A> A childOfType(Class<A> 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 boolean isEditableComboBox(final JComboBox cb) { return cb != null && swing(new F0<Boolean>() { public Boolean get() { try { return cb.isEditable(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return cb.isEditable();"; }}); } 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; } // 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 if (swingable instanceof Swingable) c = componentToJComponent(((Swingable) swingable).visualize()); 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 extends JComponent> A onClick(final A c, final Object runnable) { if (c != null) { swing(() -> { c.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { callF(runnable, e); } }); }); } 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(() -> { window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING)); // call listeners myFrames_list.remove(window); window.dispose(); }); } } 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 JScrollPane jscroll(Component c) { return swing(() -> { return c instanceof JScrollPane ? ((JScrollPane) c) : 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<JPanel>() { public JPanel get() { try { JPanel p = marginPanel(); 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 = marginPanel();\r\n p.setBorder(BorderFactory.createEmptyBorder(to..."; }}); } 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 Rect preferredScreenBounds() { return screenBounds_safe(preferredScreen()); } static boolean vmBus_anyFalse(String msg, Object... args) { return contains(vmBus_queryAll(msg, args), false); } static int toMS_int(double seconds) { return toInt_checked((long) (seconds*1000)); } static void messageBox(final String msg) { if (headless()) print(msg); else { swing(() -> { JOptionPane.showMessageDialog(null, msg, "JavaX", JOptionPane.INFORMATION_MESSAGE); }); } } static void messageBox(Throwable e) { //showConsole(); printStackTrace(e); messageBox(hideCredentials(innerException2(e))); } 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 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 List<AbstractButton> buttonsInGroup(ButtonGroup g) { if (g == null) return ll(); return asList(g.getElements()); } static int showForm_leftWidth_safetyMargin = 10; static int showForm_leftWidth(List<List<JComponent>> l) { forEachLevel2(l, x -> vmBus_send("updateLayoutNow", x)); int minW = 0; for (List<JComponent> 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<JPanel>() { 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<JPanel>() { public JPanel get() { try { JPanel p = marginPanel(); 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 = marginPanel();\r\n p.setBorder(BorderFactory.createEmptyBorder(0,..."; }}); } static <A extends Component> A jMinWidthAtLeast(int w, final A c) { if (c == null) return null; return swing(new F0<A>() { 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 <A, B> List<B> mapLL(IF1<A, B> f, A... data) { return map(f, ll(data)); } static JComponent wrapForSmartAdd_jComponent(Object o) { return componentToJComponent(wrapForSmartAdd(o)); } static Map<String, String> 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") ); } 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(() -> { vmBus_send("disposingInternalFrame", f); f.dispose(); // now illegal... setOpt(f, lastFocusOwner := null); // Help GC }); } } static void disposeFrame(final Component c) { disposeWindow(c); } static void onEnterInAllTextFields(JComponent c, Runnable action) { if (action == null) return; for (Component tf : allChildren(c)) onEnterIfTextField(tf, action); } static void onEnterInAllTextFields(List c, Runnable action) { for (Object o : unnull(c)) if (o instanceof JComponent) onEnterInAllTextFields(((JComponent) o), action); } static void clickButton(final JButton b) { if (b != null) { swing(() -> { if (b.isEnabled()) b.doClick(); }); } } static <A extends JComponent> A makeBold(final A c) { if (c != null) { swing(() -> { 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 <A> List<A> childrenOfType(Component c, Class<A> theClass) { List<A> l = new ArrayList(); scanForComponents(c, theClass, l); return l; } static <A> List<A> childrenOfType(Class<A> 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 boolean isSpace(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } static <A, B> void putIfNotThere(Map<A, B> map, A key, B value) { if (map != null && key != null && value != null && !map.containsKey(key)) map.put(key, value); } static boolean isLetterOrDigit(char c) { return Character.isLetterOrDigit(c); } 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 + replace(unnull(s), "\n", "\n" + indent); } static String indent(String s) { return indent(indent_default, s); } static List<String> indent(String indent, List<String> lines) { List<String> l = new ArrayList(); if (lines != null) for (String s : lines) l.add(indent + s); return l; } static int lUtf8(String s) { return l(utf8(s)); } 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 <A> AutoCloseable tempSetThreadLocalIfNecessary(ThreadLocal<A> 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 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 <B> TreeMap<String, B> asCIMap(Map<String, B> map) { return asCaseInsensitiveMap(map); } static Map<String, String> tagParams(String tag) { return getHtmlTagParameters(tag); } // TODO: does not detect set type (hash/tree) when it's synchronized static <A> Set<A> cloneSet(Collection<A> set) { if (set == null) return new HashSet(); synchronized(collectionMutex(set)) { Set<A> s = similarEmptySet(set); s.addAll(set); return s; } } static ThreadLocal<Random> customRandomizerForThisThread_tl = new ThreadLocal(); static ThreadLocal<Random> customRandomizerForThisThread_tl() { return customRandomizerForThisThread_tl; } static List<String> javaTokForStructure(String s) { return javaTok_noMLS(s); } static String structure_addTokenMarkers(String s) { return join(structure_addTokenMarkers(javaTokForStructure(s))); } static List<String> structure_addTokenMarkers(List<String> tok) { // find references TreeSet<Integer> 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 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<String> 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<String> tok, String in, String out) { return jreplace(tok, in, out, false, true, null); } static boolean jreplace(List<String> tok, String in, String out, Object condition) { return jreplace(tok, in, out, false, true, condition); } static boolean jreplace(List<String> tok, String in, String out, IF2<List<String>, Integer, Boolean> condition) { return jreplace(tok, in, out, (Object) condition); } static boolean jreplace(List<String> 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<String> 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 boolean regionMatchesIC(String a, int offsetA, String b, int offsetB, int len) { return a != null && a.regionMatches(true, offsetA, b, offsetB, len); } static List<String> tok_combineRoundBrackets_keep(List<String> tok) { List<String> 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<String> tok_combineRoundOrCurlyBrackets_keep(List<String> tok) { List<String> 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<String> javaTokWithAllBrackets(String s) { return javaTokPlusBrackets2(s); } static <A> int indexOfSubList(List<A> x, List<A> y) { return indexOfSubList(x, y, 0); } static <A> int indexOfSubList(List<A> x, List<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.get(j))) continue outer; return i; } return -1; } static <A> int indexOfSubList(List<A> 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 String trimJoin(List<String> s) { return trim(join(s)); } static <A> A[] newObjectArrayOfSameType(A[] a) { return newObjectArrayOfSameType(a, a.length); } static <A> A[] newObjectArrayOfSameType(A[] a, int n) { return (A[]) Array.newInstance(a.getClass().getComponentType(), n); } 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<String> toks, Matches matches) { if (toks == null) return false; List<String> 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<String> 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<String> pat, List<String> 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<String> pat, List<String> tok, int idx) { if (idx+pat.size() > tok.size()) return null; List<String> 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 String[] asStringArray(Collection<String> c) { return toStringArray(c); } static String[] asStringArray(Object o) { return toStringArray(o); } static Map<JFrame, Boolean> myFrames_list = weakHashMap(); static List<JFrame> myFrames() { return swing(new F0<List<JFrame>>() { public List<JFrame> get() { try { return keysList(myFrames_list); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return keysList(myFrames_list);"; }}); } static Map<Component, String> componentID_map = weakHashMap(); static String componentID(Component c) { return c == null ? null : componentID_map.get(c); } static <A, B> void put(Map<A, B> map, A a, B b) { if (map != null) map.put(a, b); } static <A> void put(List<A> l, int i, A a) { if (l != null && i >= 0 && i < l(l)) l.set(i, a); } static <A, B> B syncMapGet2(Map<A, B> map, A a) { if (map == null) return null; synchronized(collectionMutex(map)) { return map.get(a); } } static <A, B> B syncMapGet2(A a, Map<A, B> map) { return syncMapGet2(map, a); } static Set<String> reflection_classesNotToScan_value = litset( "jdk.internal.loader.URLClassPath" ); static Set<String> reflection_classesNotToScan() { return reflection_classesNotToScan_value; } static <A> void listThreadLocalAdd(ThreadLocal<List<A>> tl, A a) { List<A> l = tl.get(); if (l == null) tl.set(l = new ArrayList()); l.add(a); } static <A> A listThreadLocalPopLast(ThreadLocal<List<A>> tl) { List<A> l = tl.get(); if (l == null) return null; A a = popLast(l); if (empty(l)) tl.set(null); return a; } static <A> IterableIterator<A> iteratorFromFunction_withEndMarker_f0(final F0<A> f) { class IFF2 extends IterableIterator<A> { 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 <A extends Throwable> A printException(A e) { printStackTrace(e); return e; } static <A> A vm_generalMap_getOrCreate(Object key, F0<A> create) { return vm_generalMap_getOrCreate(key, f0ToIF0(create)); } static <A> A vm_generalMap_getOrCreate(Object key, IF0<A> 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; } } static <A> A callF_gen(F0<A> f) { return f == null ? null : f.get(); } static <A, B> B callF_gen(F1<A, B> f, A a) { return f == null ? null : f.get(a); } static <A> A callF_gen(IF0<A> f) { return f == null ? null : f.get(); } static <A, B> B callF_gen(IF1<A, B> f, A a) { return f == null ? null : f.get(a); } static <A, B> B callF_gen(A a, IF1<A, B> f) { return f == null ? null : f.get(a); } static <A, B, C> C callF_gen(F2<A, B, C> f, A a, B b) { return f == null ? null : f.get(a, b); } static <A, B, C> C callF_gen(IF2<A, B, C> f, A a, B b) { return f == null ? null : f.get(a, b); } static <A> void callF_gen(VF1<A> f, A a) { { if (f != null) f.get(a); } } static <A> void callF_gen(A a, IVF1<A> f) { { if (f != null) f.get(a); } } static <A> void callF_gen(IVF1<A> f, A a) { { if (f != null) f.get(a); } } static Object callF_gen(Runnable r) { { if (r != null) r.run(); } return null; } static Object callF_gen(Object f, Object... args) { return callF(f, args); } static HashMap<String, List<Method>> 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<Method> 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 void metaMapPut(IMeta o, Object key, Object value) { { if (o != null) o.metaPut(key, value); } } static void metaMapPut(Object o, Object key, Object value) { var meta = initIMeta(o); { if (meta != null) meta.metaPut(key, value); } } static void upgradeJavaXAndRestart() { run("#1001639"); restart(); sleep(); } static <A, B> B mapPutOrRemove(Map<A, B> 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 <A> Set<A> synchronizedSet() { return synchroHashSet(); } static <A> Set<A> synchronizedSet(Set<A> set) { return Collections.synchronizedSet(set); } static <A> Set<A> identityHashSet() { return Collections.newSetFromMap(new IdentityHashMap()); } 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<String> { 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 Object vmBus_query(String msg, Object... args) { Object arg = vmBus_wrapArgs(args); { var __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 dm_os() { { var __1= vm_generalMap_get("stefansOS"); if (__1 != null) return __1; } return creator(); } static List<String> dm_listModuleIDs() { return map(__78 -> dm_moduleID(__78), dm_listStems()); } static Object dm_getStemByID(Object id) { return dm_callOS("getModuleByID", str(id)); } static String mcName() { return mc().getName(); } static String quoteIfNotIdentifierOrInteger(String s) { if (s == null) return null; return isJavaIdentifier(s) || isInteger(s) ? s : quote(s); } static Throwable getException(Runnable r) { try { callF(r); return null; } catch (Throwable e) { return e; } } 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 Object realMC() { return getThreadLocal(realMC_tl()); } 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 Map synchronizedMap() { return synchroMap(); } static <A, B> Map<A, B> synchronizedMap(Map<A, B> map) { return synchroMap(map); } 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> A[] toArray(Class<A> type, Iterable<A> c) { return toArray(c, type); } static <A> A[] toArray(Class<A> type, Collection<A> c) { return toArray(c, type); } static <A> A[] toArray(Collection<A> c, Class<A> type) { A[] a = arrayOfType(l(c), type); if (a.length == 0) return a; asList(c).toArray(a); return a; } static <A> A[] toArray(Iterable<A> c, Class<A> 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> 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 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 String appendColonIfNempty(String s) { return empty(s) ? "" : s + ": "; } static List<Field> allFieldObjects_dontMakeAccessible(Object o) { List<Field> fields = new ArrayList(); Class _c = _getClass(o); do { addAll(fields, _c.getDeclaredFields()); _c = _c.getSuperclass(); } while (_c != null); return fields; } static Pair pairMapB(Object f, Pair p) { return p == null ? null : pair(p.a, callF(f, p.b)); } static <A, B, C> Pair<A, C> pairMapB(IF1<B, C> f, Pair<A, B> p) { return p == null ? null : pair(p.a, f.get(p.b)); } static Pair pairMapB(Pair p, Object f) { return pairMap(f, p); } 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<Method> 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<Method> 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, B> Map<B, A> mapToKeys(Iterable<A> l, IF1<A, B> f) { if (l == null) return null; HashMap<B, A> map = new HashMap(); for (A a : l) map.put(f.get(a), a); return map; } static <A, B> Map<B, A> mapToKeys(IF1<A, B> f, A[] l) { return mapToKeys(f, asList(l)); } static <A, B> Map<B, A> mapToKeys(IF1<A, B> f, Iterable<A> l) { return mapToKeys(l, f); } static <A, B> Map<A, B> weakMap() { return newWeakHashMap(); } static List<String> splitAtSpace(String s) { return empty(s) ? emptyList() : asList(s.split("\\s+")); } static String toStringOpt(Object o) { return o instanceof String ? ((String) o) : null; } static List<String> allMethodNames(Object o) { Class c = _getClass(o); TreeSet<String> names = new TreeSet(); while (c != null) { for (Method m : c.getDeclaredMethods()) names.add(m.getName()); c = c.getSuperclass(); } return asList(names); } 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 Throwable innerException(Throwable e) { return getInnerException(e); } 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<String> 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 JButton basicJButton(String text) { return swing(() -> new JButton(text)); } static <A> A swingConstruct(final Class<A> c, final Object... args) { return swing(new F0<A>() { public A get() { try { return nuObject(c, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return nuObject(c, args);"; }}); } static boolean hasMethod(Object o, String method, Object... args) { return findMethod_cached(o, method, args) != null; } static JMenuItem jMenuItem(final String text) { return jmenuItem(text); } static JMenuItem jMenuItem(String text, Object r) { return jmenuItem(text, r); } static Pair<String, Integer> 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(() -> { 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 <A extends JComponent> A bindToComponent(final A component, final Runnable onShow, final Runnable onUnShow) { { swing(() -> { 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); } }); } return component; } static <A extends JComponent> A bindToComponent(A component, Runnable onShow) { return bindToComponent(component, onShow, null); } static boolean isCurlyBraced(String s) { List<String> 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 String strUnnull(Object o) { return o == null ? "" : str(o); } static String jlabel_textAsHTML_center_ifNeeded(String text) { if (swic(text, "<html>") && ewic(text, "</html>")) return text; if (!containsNewLines(text)) return text; return jlabel_textAsHTML_center(text); } static Dimension getPreferredSize(final Component c) { return c == null ? null : swing(new F0<Dimension>() { public Dimension get() { try { return c.getPreferredSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getPreferredSize();"; }}); } static <A> A println(A a) { return print(a); } 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 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 <A, B> Map<A, B> syncMRUCache(int size) { return synchroMap(new MRUCache(size)); } static List<String> dropPunctuation_keep = ll("*", "<", ">"); static List<String> dropPunctuation(List<String> tok) { tok = new ArrayList<String>(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))); } // 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<String> javaTokPlusPeriod(String s) { List<String> tok = new ArrayList<String>(); 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 <A, B extends A> void copyListPart(List<B> a, int i1, List<A> 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 List<String> jsonTok(String s) { List<String> 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 Number boxedIntOrLong(long l) { return l != (int) l ? boxed(l) : boxed((int) l); } static <A extends AutoCloseable> A holdResource(IResourceHolder holder, A a) { { if (holder != null) holder.add(a); } return a; } static <A> CloseableIterableIterator<A> iteratorFromFunction_f0_autoCloseable(final F0<A> f, final AutoCloseable closeable) { class IFF2 extends CloseableIterableIterator<A> { 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 TimeZone getTimeZone(String name) { return TimeZone.getTimeZone(name); } /*static <A> L<A> printNumberedLines(L<A> l) { printNumberedLines((Collection<A>) l); ret l; } static <A> L<A> printNumberedLines(S prefix, L<A> l) { printNumberedLines(prefix, (Collection<A>) l); ret l; }*/ static void printNumberedLines(Map map) { printNumberedLines(mapToLines(map)); } static void printNumberedLines(String prefix, Map map) { printNumberedLines(prefix, mapToLines(map)); } static <A extends Iterable> A printNumberedLines(A l) { int i = 0; if (l != null) for (Object a : cloneList(l)) print((++i) + ". " + str(a)); return l; } static <A extends Iterable> 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 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 boolean isMD5(String s) { return l(s) == 32 && isLowerHexString(s); } static Map<String, Integer> 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<ProgramScan.Program> 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<Number, String> 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 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 WeakReference<Object> creator_class; static Object creator() { return creator_class == null ? null : creator_class.get(); } static String fsI_flex(String s) { return startsWithDigit(s) ? "#" + s : s; } static boolean exposeMethods2_debug = false; static String exposeMethods2(Object receiver, String s, List<String> methodNames) { return exposeMethods2(receiver, s, methodNames, null); } static String exposeMethods2(Object receiver, String s, List<String> 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 List<File> listFilesOnly(String dir) { return listFilesOnly(new File(dir)); } static List<File> listFilesOnly(File... dirs) { return concatMap(dir -> listFilesWithSuffix("", dir), dirs); } static <A, B> Comparator<A> mapComparator(final Map<A, B> map) { return new Comparator<A>() { public int compare(A a, A b) { return cmp(map.get(a), map.get(b)); } }; } 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 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 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 Map<String, java.util.regex.Pattern> 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 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 ThreadLocal<MouseEvent> componentPopupMenu_mouseEvent; static void componentPopupMenu_init() { { swing(() -> { if (componentPopupMenu_mouseEvent == null) componentPopupMenu_mouseEvent = (ThreadLocal<MouseEvent>) vm_generalMap_get("mouseEvent"); if (componentPopupMenu_mouseEvent == null) vm_generalMap_put("componentPopupMenu_mouseEvent" , componentPopupMenu_mouseEvent = new ThreadLocal()); }); } } // menuMaker = voidfunc(JPopupMenu) static void componentPopupMenu(final JComponent component, final Object menuMaker) { if (component == null || menuMaker == null) return; { swing(() -> { Object adapter = componentPopupMenu_initForComponent(component); ((List) _get(adapter, "maker")).add(menuMaker); }); } } static Object componentPopupMenu_initForComponent(final JComponent component) { return component == null ? null : swing(new F0<Object>() { 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) { new PopupMenuMaker(e, menu -> populate(menu, e)) .allowScrolling(allowScrolling).run(); } } static void addMenuItem(JPopupMenu menu, String text, Object action) { menu.add(jmenuItem(text, action)); } static void addMenuItem(JPopupMenu menu, JMenuItem menuItem) { if (menu != null && menuItem != null) 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 <A> Set<A> newWeakHashSet() { return synchroWeakHashSet(); } static File standardLogFile() { return getProgramFile("log"); } static void logQuoted(String logFile, String line) { logQuoted(getProgramFile(logFile), line); } static void logQuoted(File logFile, String line) { appendToFile(logFile, quote(line) + "\n"); } static <A extends Component> 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(() -> { 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)); }); } return ta; } static JTextArea jtextarea() { return jTextArea(); } static JTextArea jtextarea(String text) { return jTextArea(text); } static JTextField onEnter(JTextField tf, JButton btn) { if (btn != null) onEnter(tf, new Runnable() { public void run() { try { clickButton(btn) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "clickButton(btn)"; }}); return tf; } static JTextField onEnter(JTextField tf, 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, Runnable action) { { swing(() -> { 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);"; }})); } }); } 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(Runnable action, JTextField tf) { return onEnter(tf, action); } static JPanel marginPanel() { return jtransparent(borderLayoutPanel()); } static Rect screenBounds_safe(int iScreen) { return screenBounds(min(iScreen, screenCount()-1)); } static IF0<Integer> preferredScreen; static int preferredScreen() { return preferredScreen != null ? preferredScreen.get() : preferredScreen_base(); } final static int preferredScreen_fallback(IF0<Integer> _f) { return _f != null ? _f.get() : preferredScreen_base(); } static int preferredScreen_base() { return 0; } 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 int toInt_checked(long l) { if (l != (int) l) throw fail("Too large for int: " + l); return (int) l; } static boolean headless() { return isHeadless(); } static Throwable innerException2(Throwable e) { if (e == null) return null; while (empty(e.getMessage()) && e.getCause() != null) e = e.getCause(); return e; } static Lock downloadLock_lock = fairLock(); static Lock downloadLock() { return downloadLock_lock; } static String getSnippetTitleOpt(String s) { try { return isSnippetID(s) ? getSnippetTitle(s) : s; } catch (Throwable __e) { printStackTrace(__e); } return s; } static ThreadLocal<Boolean> addInternalFrame_dontSelect = new ThreadLocal(); static ThreadLocal<Integer> addInternalFrame_layer = new ThreadLocal(); static ThreadLocal<Boolean> 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<JInternalFrame>() { 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(() -> { if (frame != null && frame.getWidth() < w) frame.setSize(w, frame.getHeight()); }); } return frame; } static JInternalFrame minInternalFrameWidth(int w, JInternalFrame frame) { return minInternalFrameWidth(frame, w); } static <A extends Component> A packInternalFrameVertically(A c) { return packInternalFrameVertically(-1, c); } static <A extends Component> 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(() -> { win.pack(); win.setSize(w, win.getHeight()); fixInternalFrame(win); }); } return c; } static JInternalFrame centerInternalFrame(final JInternalFrame f) { { swing(() -> { 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); } }); } return f; } static JInternalFrame centerInternalFrame(final int w, final int h, final JInternalFrame f) { { swing(() -> { f.setSize(w, h); }); } return centerInternalFrame(f); } static int packFrame_minw = 150, packFrame_minh = 50; static <A extends Component> A packFrame(final A c) { { swing(() -> { 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))); } }); } 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(() -> { if (frameTooSmall(f)) frameStandardSize(f); if (!f.isVisible()) f.setVisible(true); // XXX if (f.getState() == Frame.ICONIFIED) f.setState(Frame.NORMAL); }); } 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 <A, B extends Collection<A>> void forEachLevel2(Iterable<B> l, IVF1<A> f) { if (l != null) for (B b : l) forEach(b, f); } static <A, B extends Collection<A>> void forEachLevel2(IVF1<A> f, Iterable<B> l) { forEachLevel2(f, l); } static Dimension getMinimumSize(final Component c) { return c == null ? null : swing(new F0<Dimension>() { public Dimension get() { try { return c.getMinimumSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getMinimumSize();"; }}); } static <A extends Component> A jPreferWidth(int w, A c) { { swing(() -> { 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 boolean containsSpace(String s) { return containsSpaces(s); } static String firstToUpper(String s) { if (empty(s)) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } static <A> List<A> replaceElementsUsingMap(Iterable<A> l, final Map<A, A> map) { return map(l, new F1<A, A>() { 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<String> splitCamelCase(String s) { return ai_splitCamelCase(s); } static JInternalFrame getInternalFrame(final Object _o) { return _o == null ? null : swing(new F0<JInternalFrame>() { 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 List<Component> allChildren(Component c) { return childrenOfType(c, Component.class); } static void onEnterIfTextField(Component c, Runnable 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 <A> void scanForComponents(final Component c, final Class<A> theClass, final List<A> l) { if (theClass.isInstance(c)) l.add((A) c); if (c instanceof Container) { swing(() -> { for (Component comp : ((Container) c).getComponents()) scanForComponents(comp, theClass, l); }); } } static <B> TreeMap<String, B> asCaseInsensitiveMap(Map<String, B> map) { if (isCIMap(map)) return (TreeMap) map; TreeMap<String, B> m = ciMap(); putAll(m, map); return m; } static boolean getHtmlTagParameters_debug = false; static Map<String, String> getHtmlTagParameters(String tag) { if (empty(tag)) return null; List<String> 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<String, String> 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<String> javaTok_noMLS(String s) { ArrayList<String> 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 Map<String, String[]> 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<String> tok, String... tokens) { return findCodeTokens(tok, 1, false, tokens); } static int findCodeTokens(List<String> tok, boolean ignoreCase, String... tokens) { return findCodeTokens(tok, 1, ignoreCase, tokens); } static int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String... tokens) { return findCodeTokens(tok, startIdx, ignoreCase, tokens, null); } static HashSet<String> findCodeTokens_specials = lithashset("*", "<quoted>", "<id>", "<int>", "\\*"); static int findCodeTokens_bails, findCodeTokens_nonbails; static interface findCodeTokens_Matcher { boolean get(String token); } static int findCodeTokens(List<String> 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("<quoted>")) matcher = t -> isQuoted(t); else if (p.equals("<id>")) matcher = t -> isIdentifier(t); else if (p.equals("<int>")) 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<String> tokref) { if (!contains(s, '$')) return s; List<String> 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 List<String> reTok(List<String> tok) { replaceCollection(tok, javaTok(tok)); return tok; } static List<String> reTok(List<String> tok, int i) { return reTok(tok, i, i+1); } static List<String> reTok(List<String> 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<String> t = javaTok(joinSubList(tok, i, j)); replaceListPart(tok, i, j, t); // fallback to safety // reTok(tok); return tok; } static List<String> reTok(List<String> tok, IntRange r) { if (r != null) reTok(tok, r.start, r.end); return tok; } // 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<String> 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<String> javaTokPlusBrackets2(String s) { return tok_combineRoundCurlySquareBrackets_keep(javaTok(s)); } static boolean startsWithLetterOrDigit(String s) { return nempty(s) && Character.isLetterOrDigit(s.charAt(0)); } static String regexpQuote(String s) { return s.length() == 0 ? "" : Pattern.quote(s); } 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 <A, B> Map<A, B> weakHashMap() { return newWeakHashMap(); } static <A> IF0<A> f0ToIF0(F0<A> f) { return f == null ? null : () -> f.get(); } static <A, B> void multiMapPut(Map<A, List<B>> map, A a, B b) { List<B> l = map.get(a); if (l == null) map.put(a, l = new ArrayList()); l.add(b); } static <A, B> void multiMapPut(MultiMap<A, B> mm, A key, B value) { if (mm != null && key != null && value != null) mm.put(key, value); } static IMeta initIMeta(Object o) { if (o == null) return null; if (o instanceof IMeta) return ((IMeta) o); if (o instanceof JComponent) return initMetaOfJComponent((JComponent) o); if (o instanceof BufferedImage) return optCast(IMeta.class, ((BufferedImage) o).getProperty("meta")); return null; } 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 <A> Set<A> synchroHashSet() { return synchronizedSet(new HashSet<A>()); } static Object pcallFAll_returnFirstNotNull(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) { var __1= pcallF(f, args); if (__1 != null) return __1; } return null; } static Object pcallFAll_returnFirstNotNull(Iterator it, Object... args) { while (it.hasNext()) { var __2= pcallF(it.next(), args); if (__2 != null) return __2; } return null; } static List dm_listStems() { return (List) dm_callOS("onModules"); } static ThreadLocal realMC_tl_tl = new ThreadLocal(); static ThreadLocal realMC_tl() { return realMC_tl_tl; } static char stringToChar(String s) { if (l(s) != 1) throw fail("bad stringToChar: " + s); return firstChar(s); } static <A> A[] arrayOfType(Class<A> type, int n) { return makeArray(type, n); } static <A> A[] arrayOfType(int n, Class<A> type) { return arrayOfType(type, n); } static Pair pairMap(Object f, Pair p) { return p == null ? null : pair(callF(f, p.a), callF(f, p.b)); } static <A> Pair<A, A> pairMap(IF1<A, A> f, Pair<A, A> 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 String getStackTrace2(Throwable e) { return hideCredentials(getStackTrace(unwrapTrivialExceptionWraps(e)) + replacePrefix("java.lang.RuntimeException: ", "FAIL: ", hideCredentials(str(innerException2(e)))) + "\n"); } 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<String> toLowerCase(List<String> strings) { List<String> 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(__79 -> isLetterOrDigit(__79), s); else return "" + first(s); } static List<String> tok_combineCurlyBrackets_keep(List<String> tok) { List<String> 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 boolean containsNewLines(String s) { return containsNewLine(s); } static String jlabel_textAsHTML_center(String text) { return "<html><div style=\"text-align: center;\">" + replace(htmlencode2(text), "\n", "<br>") + "</div></html>"; } static File muricaPasswordFile() { return new File(javaxSecretDir(), "murica/muricaPasswordFile"); } static File computerIDFile() { return javaxDataDir("Basic Info/computer-id.txt"); } static List<String> nlTok(String s) { return javaTokPlusPeriod(s); } static Integer boxed(int i) { return i; } static Long boxed(long l) { return l; } static <A> List<A> wrapAsList(A[] a) { return wrapArrayAsList(a); } 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 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<Map<String, DialogIO>> talkTo_byThread = new ThreadLocal(); static DialogIO talkTo(String ip, int port) { try { String full = ip + ":" + port; Map<String, DialogIO> 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<ProgramScan.Program> quickBotScan() { return ProgramScan.quickBotScan(); } static List<ProgramScan.Program> quickBotScan(int[] preferredPorts) { return ProgramScan.quickBotScan(preferredPorts); } static List<ProgramScan.Program> quickBotScan(String searchPattern) { List<ProgramScan.Program> l = new ArrayList<ProgramScan.Program>(); 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 String format(String pat, Object... args) { return format3(pat, args); } 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 Object[] asObjectArray(Collection l) { return toObjectArray(l); } 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) { printStackTrace(__e); }} } public String toString() { return "Bot: " + greeting + " [vport " + vport + "]"; } } static abstract class Responder { abstract String answer(String s, List<String> 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<String> 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<String> 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<S>(); } 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<String> 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<String> 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<String> 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<String> 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<String> 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<DialogIO> makeAndroid3_io = new ThreadLocal(); static Android3 makeAndroid3() { return makeAndroid3(getProgramTitle() + "."); } static String makeResponder_callAnswerMethod(Object bot, String s, List<String> 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<String> 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<String> history) { return makeResponder_callAnswerMethod(bot, s, history); } }; } static String defaultBotName() { return getProgramTitle() + "."; } static List<File> listFilesWithSuffix(File dir, String suffix) { List<File> l = new ArrayList(); for (File f : listFiles(dir)) if (!f.isDirectory() && (empty(suffix) || endsWithIgnoreCase(f.getName(), suffix))) l.add(f); return l; } static List<File> listFilesWithSuffix(String suffix, File dir) { return listFilesWithSuffix(dir, suffix); } static Object callJavaX(String method, Object... args) { return callOpt(getJavaX(), method, args); } 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 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<Boolean>() { public Boolean get() { try { return f.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return f.isSelected();"; }}); } 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(() -> { 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(() -> { mb.add(menuItem); revalidate(mb); }); } } static JTextArea jTextArea() { return jTextArea(""); } static JTextArea jTextArea(final String text) { return jTextAreaWithUndo(text); } 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 <A extends JComponent> A jtransparent(final A a) { { swing(() -> { a.setOpaque(false); }); } return a; } static JPanel borderLayoutPanel() { return jpanel(new BorderLayout()); } static Rect screenBounds(GraphicsDevice screen) { return screen == null ? null : toRect(screen.getDefaultConfiguration().getBounds()); } static Rect screenBounds(int iScreen) { return screenBounds(get(screenDevices(), iScreen)); } static int screenCount() { return l(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()); } 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 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<JInternalFrame>() { 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(() -> { 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); }); } } static <A extends Component> A setBounds(final int x, final int y, final int w, final int h, final A a) { if (a != null) { swing(() -> { a.setBounds(x, y, w, h); }); } return a; } static <A extends Component> A setBounds(A a, Rect r) { if (a != null && r != null) { swing(() -> { a.setBounds(toRectangle(r)); }); } return a; } static <A extends Component> A setBounds(A a, Rectangle r) { if (a != null && r != null) { swing(() -> { a.setBounds(r); }); } return a; } static <A extends Component> A setBounds(Rect r, A a) { return setBounds(a, r); } static <A extends Component> 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<JInternalFrame>() { 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 String makeFrame_defaultIcon; static boolean makeFrame_hideConsole = false; static ThreadLocal<VF1<JFrame>> 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<JFrame> post = optParam(makeFrame_post); return swing(new F0<JFrame>() { 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 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(() -> { JFrame frame = getFrame(c); if (frame == null) return; frame.getContentPane().removeAll(); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(wrap(contents)); revalidate(frame); }); } static <A> void forEach(Iterable<A> l, IVF1<A> f) { if (f != null && l != null) for (A a : l) callF(f, a); } static <A> void forEach(IVF1<A> f, Iterable<A> l) { forEach(l, f); } static <A> void forEach(A[] l, IVF1<A> f) { if (f != null && l != null) for (A a : l) callF(f, a); } static <A> void forEach(IVF1<A> f, A[] l) { forEach(l, f); } static <A, B> void forEach(Map<A, B> map, IVF2<A, B> f) { for (Map.Entry<? extends A, ? extends B> __0 : _entrySet( map)) { A a = __0.getKey(); B b = __0.getValue(); f.get(a, b); } } static boolean containsSpaces(String s) { return indexOf(s, ' ') >= 0; } static List<String> ai_splitCamelCase(String s) { int j = 0; List<String> 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 boolean isCIMap(Map m) { return m instanceof TreeMap && ((TreeMap) m).comparator() == caseInsensitiveComparator(); } static List<String> codeTokens(List<String> tok) { return codeTokensOnly(tok); } static List<String> tok_joinMinusIdentifiers(List<String> 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<String> htmlFineTok(String s) { List<String> 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 <A> A printStruct(String prefix, A a) { printStructure(prefix, a); return a; } static <A> A printStruct(A a) { printStructure(a); return a; } 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 String[] codeTokensAsStringArray(List<String> 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 int jfind(String s, String in) { return jfind(javaTok(s), in); } static int jfind(List<String> tok, String in) { return jfind(tok, 1, in); } static int jfind(List<String> tok, int startIdx, String in) { return jfind(tok, startIdx, in, (ITokCondition) null); } static int jfind(List<String> tok, String in, Object condition) { return jfind(tok, 1, in, condition); } static int jfind(List<String> tok, String in, IIntPred condition) { return jfind(tok, 1, in, condition); } static int jfind(List<String> tok, int startIndex, String in, IIntPred condition) { return jfind(tok, startIndex, in, tokCondition(condition)); } static int jfind(List<String> tok, String in, ITokCondition condition) { return jfind(tok, 1, in, condition); } static int jfind(List<String> tok, int startIndex, String in, ITokCondition condition) { return jfind(tok, startIndex, in, (Object) condition); } static int jfind(List<String> 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<String> tok, List<String> tokin) { return jfind(tok, 1, tokin); } static int jfind(List<String> tok, int startIdx, List<String> tokin) { return jfind(tok, startIdx, tokin, null); } static int jfind(List<String> tok, int startIdx, String[] tokinC, Object condition) { return findCodeTokens(tok, startIdx, false, tokinC, condition); } static int jfind(List<String> tok, int startIdx, List<String> tokin, Object condition) { return jfind(tok, startIdx, codeTokensAsStringArray(tokin), condition); } static List<String> jfind_preprocess(List<String> tok) { for (String type : litlist("quoted", "id", "int")) replaceSublist(tok, ll("<", "", type, "", ">"), ll("<" + type + ">")); replaceSublist(tok, ll("\\", "", "*"), ll("\\*")); return tok; } static boolean checkTokCondition(Object condition, List<String> tok, int i) { if (condition instanceof TokCondition) return ((TokCondition) condition).get(tok, i); return checkCondition(condition, tok, i); } static <A> void replaceCollection(Collection<A> dest, Collection<A> 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); } static List<String> tok_combineRoundCurlySquareBrackets_keep(List<String> tok) { List<String> 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 IMeta initMetaOfJComponent(JComponent c) { if (c == null) return null; IMeta meta = (IMeta) (c.getClientProperty(IMeta.class)); if (meta == null) c.putClientProperty(IMeta.class, meta = new Meta()); return meta; } // custom mainClass only works with hotwire_here static Class<?> hotwire(String src) { return hotwire(src, __1 -> mainClassNameForClassLoader(__1)); } static Class<?> hotwire(String src, IF1<ClassLoader, String> calculateMainClass) { assertFalse(_inCore()); Class j = getJavaX(); if (isAndroid()) { synchronized(j) { // hopefully this goes well... List<File> libraries = new ArrayList<File>(); 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 <A> A callMain(A c, String... args) { callOpt(c, "main", new Object[] {args}); return c; } static void callMain() { callMain(mc()); } static char firstChar(String s) { return s.charAt(0); } static <A> A[] makeArray(Class<A> type, int n) { return (A[]) Array.newInstance(type, n); } 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 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); } // 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<Character, Boolean> f, String s) { return takeCharsWhile(s, f); } // i must point at the (possibly imaginary) opening bracket // index returned is index of closing bracket + 1 static int findEndOfCurlyBracketPart(List<String> 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(); } static String htmlencode2(String s) { return htmlencode_noQuotes(s); } static String format3(String pat, Object... args) { if (args.length == 0) return pat; List<String> 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 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<String> 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<String, Integer> 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 void removeFromMultiPort(long vport) { if (vport == 0) return; for (Object port : getMultiPorts()) call(port, "removePort", vport); } 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<String> history) { return callStaticAnswerMethod(mc(), s, history); } static String callStaticAnswerMethod(Object c, String s, List<String> history) { String answer = (String) callOpt(c, "answer", s, history); if (answer == null) answer = (String) callOpt(c, "answer", s); return emptyToNull(answer); } static List<Object> 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<String> 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<String> 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<Boolean> startDialogServer_quiet = new ThreadLocal(); static Set<String> 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 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 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 String multiLineQuoteWithSpaces(String s) { return multiLineQuote(" " + s + " "); } static <A> A firstWithClassShortNamed(String shortName, Iterable<A> l) { if (l != null) for (A o : l) if (eq(shortClassName(o), shortName)) return o; return null; } static <A> A firstWithClassShortNamed(String shortName, A[] l) { if (l != null) for (A o : l) if (eq(shortClassName(o), shortName)) return o; return null; } 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<JMenuBar>() { 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 JTextArea jTextAreaWithUndo() { return jTextAreaWithUndo(""); } static JTextArea jTextAreaWithUndo(final String text) { return jenableUndoRedo(swingNu(JTextArea.class, text)); } static String getSelectedItem(JList l) { return (String) l.getSelectedValue(); } static String getSelectedItem(JComboBox cb) { return strOrNull(cb.getSelectedItem()); } static List<GraphicsDevice> screenDevices() { return asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()); } 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 Rectangle toRectangle(Rect r) { return r == null ? null : r.getRectangle(); } static Dimension getScreenSize() { return Toolkit.getDefaultToolkit().getScreenSize(); } static String programTitle() { return getProgramName(); } static <A> A setFrameTitle(A c, final String title) { final Frame f = getAWTFrame(c); if (f != null) { swing(() -> { f.setTitle(title); }); } return c; } static <A extends Component> 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<Rectangle>() { public Rectangle get() { try { defaultNewFrameBounds_r.translate(60, 20); var bounds = preferredScreenBounds(); if (!bounds.contains(defaultNewFrameBounds_r)) //defaultNewFrameBounds_r.setLocation(bounds.x+30+random(30), bounds.y+20+random(20)); defaultNewFrameBounds_r.setLocation(centerX(bounds)+random_incl(-30, 30), centerY(bounds)+random_incl(-20, 20)); return new Rectangle(defaultNewFrameBounds_r); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "defaultNewFrameBounds_r.translate(60, 20);\r\n var bounds = preferredScreenB..."; }}); } 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 boolean isAllUpperCase(String s) { return hasLettersAllUpperCase(s); } static boolean isUpperCaseLetter(char c) { return Character.isUpperCase(c); } static List<String> codeTokensOnly(List<String> tok) { int n = l(tok); List<String> l = emptyList(n/2); for (int i = 1; i < n; i += 2) l.add(tok.get(i)); return l; } static <A> A printStructure(String prefix, A o) { if (endsWithLetter(prefix)) prefix += ": "; print(prefix + structureForUser(o)); return o; } static <A> A printStructure(A o) { print(structureForUser(o)); return o; } static ITokCondition tokCondition(IIntPred condition) { return condition == null ? null : (tok, nIdx) -> condition.get(nIdx); } // 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<String> 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 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 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 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 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<Object>() { public Object get() { try { return callStaticAnswerMethod(getJavaX(), _s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return callStaticAnswerMethod(getJavaX(), _s);"; }}); } catch (Throwable e) { e = getInnerException(e); printStackTrace(e); return str(e); } } static <A> A popFirst(List<A> l) { if (empty(l)) return null; A a = first(l); l.remove(0); return a; } static <A> A popFirst(Collection<A> l) { if (empty(l)) return null; A a = first(l); l.remove(a); return a; } static <A, B> Pair<A, B> popFirst(Map<A, B> map) { if (map == null) return null; var it = map.entrySet().iterator(); if (!it.hasNext()) return null; var p = mapEntryToPair(it.next()); it.remove(); return p; } static <A> List<A> popFirst(int n, List<A> l) { List<A> part = cloneSubList(l, 0, n); removeSubList(l, 0, n); return part; } static <A> AppendableChain<A> popFirst(AppendableChain<A> a) { return a == null ? null : a.popFirst(); } static List<Object> getMultiPorts() { return (List) callOpt(getJavaX(), "getMultiPorts"); } static String emptyToNull(String s) { return eq(s, "") ? null : s; } static <A, B> Map<A, B> emptyToNull(Map<A, B> map) { return empty(map) ? null : map; } // 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 <A> Set<A> synchroTreeSet() { return Collections.synchronizedSet(new TreeSet<A>()); } static <A> Set<A> synchroTreeSet(TreeSet<A> set) { return Collections.synchronizedSet(set); } static boolean forbiddenPort(int port) { return port == 5037; // adb } 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); } } static String multiLineQuote(String s) { for (int i = 0; ; i++) { String closer = "]" + rep('=', i) + "]"; if (!contains(s, closer)) return "[" + rep('=', i) + "[" + s + closer; } } 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(() -> { call(f, "setJMenuBar", mb); revalidate((Component) f); }); } } static void setMenuBar(RootPaneContainer f, JMenuBar mb) { setMenuBar(mb, f); } 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 Frame getAWTFrame(final Object _o) { return swing(new F0<Frame>() { 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<String, ImageIcon> imageIcon_cache; static Lock imageIcon_lock = lock(); static ThreadLocal<Boolean> 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 img == null ? null : new ImageIcon(img); } static ImageIcon imageIcon(RGBImage img) { return imageIcon(img.getBufferedImage()); } static void standardTitlePopupMenu(final JFrame frame) { // standard right-click behavior on titles if (!isSubstanceLAF()) return; titlePopupMenu(frame, new VF1<JPopupMenu>() { public void get(JPopupMenu menu) { try { boolean alwaysOnTop = frame.isAlwaysOnTop(); menu.add(jmenuItem("Restart Program", new Runnable() { public void run() { try { restart(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "restart();"; }})); menu.add(jmenuItem("Duplicate Program", new Runnable() { public void run() { try { duplicateThisProgram(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "duplicateThisProgram();"; }})); menu.add(jmenuItem("Show Console", new Runnable() { public void run() { try { showConsole(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "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", r swingBiggerFonts); //addMenuItem(menu, "Smaller fonts", r swingSmallerFonts); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "bool alwaysOnTop = frame.isAlwaysOnTop();\r\n ifndef standardTitlePopupMenu_..."; }}); } static Integer centerX(Rect r) { return rectCenterX(r); } static int random_incl(int min, int max) { return random_incl(min, max, defaultRandomizer()); } static int random_incl(int min, int max, Random random) { return random(min, max+1, random); } static int random_incl(int max) { return random(0, max+1); } static Integer centerY(Rect r) { return rectCenterY(r); } static JFrame consoleFrame() { return (JFrame) getOpt(get(getJavaX(), "console"), "frame"); } static void autoVMExit() { call(getJavaX(), "autoVMExit"); } static boolean hasLettersAllUpperCase(String s) { return hasLetters(s) && !containsLowerCase(s); } static boolean endsWithLetter(String s) { return nempty(s) && isLetter(last(s)); } static void setOptIfNotNull(Object o, String field, Object value) { if (value != null) setOpt(o, field, value); } 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<Throwable> 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 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 void consoleClearInput() { consoleSetInput(""); } static String getOneLineFileInfoField(File f, String field) { File infoFile = associatedInfosFile(f); List<String> lines = lines(loadTextFile(infoFile)); return firstStartingWithIC_drop(lines, field + ": "); } static boolean loadBufferedImageFixingGIFs_debug = false; static ThreadLocal<Var<byte[]>> 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 duplicateThisProgram() { nohupJavax(trim(programID() + " " + smartJoin((String[]) get(getJavaX(), "fullArgs")))); } static void showConsole() { callOpt(get(javax(), "console"), "showConsole"); } // 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<Boolean> r) { return jCheckBoxMenuItem(text, checked, (Object) r); } static void toggleAlwaysOnTop(Window frame) { if (frame == null) return; { swing(() -> { frame.setAlwaysOnTop(!frame.isAlwaysOnTop()); }); } } static Integer rectCenterX(Rect r) { return r == null ? null : r.x+r.w/2; } static Integer rectCenterY(Rect r) { return r == null ? null : r.y+r.h/2; } 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 Class hotwireOnce(String programID) { return hotwireCached(programID, false); } static void consoleSetInput(final String text) { if (headless()) return; setTextAndSelectAll(consoleInputField(), text); focusConsole(); } static File associatedInfosFile(File f) { return replaceExtension(f, ".infos"); } static String firstStartingWithIC_drop(Collection<String> 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<String> l) { return firstStartingWithIC_drop(l, prefix); } 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 <A> void setVar(IVar<A> v, A value) { if (v != null) v.set(value); } static <A> IVF1<A> setVar(IVar<A> 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; } // 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<String> args) { return smartJoin(toStringArray(args)); } static void addActionListener(JTextField tf, final Runnable action) { onEnter(tf, action); } static void addActionListener(final JComboBox cb, final Runnable action) { if (cb != null) { swing(() -> { cb.addActionListener(actionListener(action)); }); } } static void addActionListener(final AbstractButton b, final Runnable action) { if (b != null) { swing(() -> { b.addActionListener(actionListener(action)); }); } } static boolean isChecked(JCheckBox checkBox) { return checkBox != null && (boolean) swing(new F0<Boolean>() { public Boolean get() { try { return checkBox.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return checkBox.isSelected();"; }}); } static boolean isChecked(JCheckBoxMenuItem mi) { return mi != null && (boolean) swing(new F0<Boolean>() { public Boolean get() { try { return mi.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return mi.isSelected();"; }}); } static boolean isChecked(JRadioButton rb) { return rb != null && (boolean) swing(() -> rb.isSelected()); } static boolean isLowerCase(char c) { return Character.isLowerCase(c); } static TreeMap<String, Class> 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 JTextField setTextAndSelectAll(final JTextField tf, final String text) { if (tf != null) { swing(() -> { tf.setText(text); 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 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 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 JComponent getInternalFrameTitlePaneComponent(JInternalFrame f) { return (JComponent) childWithClassNameEndingWith(f, "InternalFrameTitlePane"); } 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 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 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 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 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 List<Class> dependentClasses() { return cleanUpAndGetWeakReferencesList(hotwire_classes); } static String addPrefixIfNotEmpty2(String prefix, String s) { return empty(s) ? "" : addPrefix(prefix, s); } static List<WeakReference<Class>> hotwire_classes = synchroList(); static Class<?> hotwireDependent(String src) { Class c = hotwire(src); makeDependent(c); return c; } static <A> List<A> cleanUpAndGetWeakReferencesList(List<WeakReference<A>> l) { if (l == null) return null; synchronized(l) { List<A> 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 abstract class VF1<A> implements IVF1<A> { public abstract void get(A a); } // Meta - a "minimal" approach to adding meta-level to Java objects static class Meta implements IMeta { // Meta - a "minimal" approach to adding meta-level to Java objects // (implementing the interface IMeta) // We allocate one extra field for each Java object to make it // reasoning-compatible (reasoning-compatible = extensible with // fields of any name at runtime). // // We couldn't go for 0 extra fields (meta values must be linked // directly from the object) 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).] ////////////////////// // The "meta" field // ////////////////////// // 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; } // MOST functions are implemented in IMeta (default implementations) // Scaffolding convenience functions final boolean scaffolding(){ return scaffoldingEnabled(); } boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); } boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); } } static class Var<A> implements IVar<A>, ISetter<A> { 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 void clear() { set(null); } public String toString() { return str(this.get()); } } static class DefunctClassLoader {} static class Scorer<A> { double score, total; List<A> 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<S> 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 interface ITokCondition { boolean get(List<String> tok, int i); // i = N Index } static class HTML implements IF0<String> , IFieldsToList{ String html; HTML() {} HTML(String html) { this.html = 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; } public String toString() { return html; } } static abstract class TokCondition implements ITokCondition { public abstract boolean get(List<String> 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<A> extends Var<A> { SecretValue() {} SecretValue(A a) { super(a); } public String toString() { return "Secret value"; } } static class ConceptsRefChecker { Concepts cc; List<Err> 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<Err> run() { errors.clear(); if (cc == null) return errors; Collection<Concept> 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<Concept.Ref> 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<Concept.Ref> 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) { printStackTrace(e); print("Error processing concept " + c); } return errors; } List<Err> errors() { return errors; } String fixAll() { List<String> 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) { printStackTrace(__e); }} return lines(out); } String runAndFixAll() { run(); return fixAll(); } } static class PopupMenuMaker { final public PopupMenuMaker setAllowScrolling(boolean allowScrolling){ return allowScrolling(allowScrolling); } public PopupMenuMaker allowScrolling(boolean allowScrolling) { this.allowScrolling = allowScrolling; return this; } final public boolean getAllowScrolling(){ return allowScrolling(); } public boolean allowScrolling() { return allowScrolling; } boolean allowScrolling = true; // ignored when using existingMenu final public PopupMenuMaker setEvent(MouseEvent event){ return event(event); } public PopupMenuMaker event(MouseEvent event) { this.event = event; return this; } final public MouseEvent getEvent(){ return event(); } public MouseEvent event() { return event; } MouseEvent event; final public PopupMenuMaker setPtInComponent(PtInComponent ptInComponent){ return ptInComponent(ptInComponent); } public PopupMenuMaker ptInComponent(PtInComponent ptInComponent) { this.ptInComponent = ptInComponent; return this; } final public PtInComponent getPtInComponent(){ return ptInComponent(); } public PtInComponent ptInComponent() { return ptInComponent; } PtInComponent ptInComponent; // if different from event final public PopupMenuMaker setFillMenu(IVF1<JPopupMenu> fillMenu){ return fillMenu(fillMenu); } public PopupMenuMaker fillMenu(IVF1<JPopupMenu> fillMenu) { this.fillMenu = fillMenu; return this; } final public IVF1<JPopupMenu> getFillMenu(){ return fillMenu(); } public IVF1<JPopupMenu> fillMenu() { return fillMenu; } IVF1<JPopupMenu> fillMenu; final public PopupMenuMaker setExistingMenu(JPopupMenu existingMenu){ return existingMenu(existingMenu); } public PopupMenuMaker existingMenu(JPopupMenu existingMenu) { this.existingMenu = existingMenu; return this; } final public JPopupMenu getExistingMenu(){ return existingMenu(); } public JPopupMenu existingMenu() { return existingMenu; } JPopupMenu existingMenu; final public PopupMenuMaker setAddSeparator(boolean addSeparator){ return addSeparator(addSeparator); } public PopupMenuMaker addSeparator(boolean addSeparator) { this.addSeparator = addSeparator; return this; } final public boolean getAddSeparator(){ return addSeparator(); } public boolean addSeparator() { return addSeparator; } boolean addSeparator = true; // add separator if existing meun JPopupMenu menu; PopupMenuMaker() {} PopupMenuMaker(MouseEvent event, IVF1<JPopupMenu> fillMenu) { this.fillMenu = fillMenu; this.event = event;} public void run() { swing(() -> { if (existingMenu != null) { var menu = existingMenu; int emptyCount = menu.getComponentCount(); if (addSeparator) menu.addSeparator(); int emptyCount2 = menu.getComponentCount(); { if (fillMenu != null) fillMenu.get(menu); } if (menu.getComponentCount() == emptyCount2) truncateContainer(menu, emptyCount); //printVars("Extended popup menu", +emptyCount, +emptyCount2, n := menu.getComponentCount()); packWindow(menu); //revalidate(menu); } else { JPopupMenu menu = new JPopupMenu(); int emptyCount = menu.getComponentCount(); { if (fillMenu != null) fillMenu.get(menu); } if (menu.getComponentCount() == emptyCount) return; if (allowScrolling) { menu = new JPopupMenu(); JMenuScroller scroller = JMenuScroller.setScrollerFor(menu); scroller.fillMenu = toVF1(fillMenu); } if (ptInComponent == null) ptInComponent = ptInComponentFromEvent(event); if (hasParentOfType(JPopupMenu.class, ptInComponent.component)) { // component is in a menu itself. TODO: better positioning // Also this doesn't actually seem to work... menu.setInvoker(ptInComponent.component); menu.setVisible(true); } else menu.show(ptInComponent.component, ptInComponent.p.x, ptInComponent.p.y); } }); } } // 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<String> unshownFields; // not shown in table or form Set<String> uneditableFields; // not shown in form Set<String> 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<String, String> 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<String, String> 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<String, String> 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<String> nav; String nav() { return nav != null ? nav.get() : nav_base(); } final String nav_fallback(IF0<String> _f) { return _f != null ? _f.get() : nav_base(); } String nav_base() { List<String> l = new ArrayList(); if (actuallyAllowCreate()) l.add(ahref(newLink(), "New " + itemName())); if (showSearchField) l.add(hInlineSearchForm("search", searchQuery, "")); return joinWithVBar(l); } String handleCommands(Map<String, String> params) { List<String> 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<String> 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<Map<String, String>, List<String>, String> refreshAfterCommand; String refreshAfterCommand(Map<String, String> params, List<String> msgs) { return refreshAfterCommand != null ? refreshAfterCommand.get(params, msgs) : refreshAfterCommand_base(params, msgs); } final String refreshAfterCommand_fallback(IF2<Map<String, String>, List<String>, String> _f, Map<String, String> params, List<String> msgs) { return _f != null ? _f.get(params, msgs) : refreshAfterCommand_base(params, msgs); } String refreshAfterCommand_base(Map<String, String> params, List<String> 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<String, Object, String> renderValue; String renderValue(String field, Object value) { return renderValue != null ? renderValue.get(field, value) : renderValue_base(field, value); } final String renderValue_fallback(IF2<String, Object, String> _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<Object, String> 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<Object, String> _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<Map<String, Object>> 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(__122 -> valueToSortable(__122), l, sortByField); } if (descending) l = reversed(l); } // l2 is the rendered map. use keyEncoding to get from l keys to l2 keys Map<String, String> keyEncoding = new HashMap(); List<Map<String, Object>> l2 = lazyMap(l, _map -> { Object id = itemID(_map); return data.new Item(id) { public Map<String, Object> calcFullMap() { Map<String, Object> map = _map; map = mapMinusKeys(map, joinSets(unshownFields, unlistedFields)); Map<String, Object> 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<String> out = new ArrayList(); if (paginate) { paginator.processParams(params); paginator.baseLink = addParamsToURL(baseLink, filterKeys(__80 -> keepParamInPagination(__80), params)); paginator.max = l(l2); out.add(divUnlessEmpty(paginator.renderNav())); List<Map<String, Object>> 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(__81 -> isHighlighted(__81), l3))) { l3 = llNonNulls(firstThat(__82 -> isHighlighted(__82), l2)); //printVars_str(+objectIDToHighlight, total := l(l2), found := l(l3)); } l2 = l3; } Map<String, String> replaceHeaders = new HashMap(); if (sortable && !singleton) for (Map.Entry<? extends String, ? extends String> __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<String, Object[]> paramsByColName = null; if (cellColumnToolTips) { paramsByColName = new HashMap(); for (Map<String, Object> 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<String, Object> item) { return item == null ? null : dropAllTags(strOrNull(item.get(encodeField(idField())))); } // item is after encodeField boolean isHighlighted(Map<String, Object> item) { Object id = mapToID(item); //print("isHighlighted ID: " + toStringWithClass(id) + " / " + toStringWithClass(objectIDToHighlight)); return eq(id, objectIDToHighlight); } transient IF0<String> renderBulkCmds; String renderBulkCmds() { return renderBulkCmds != null ? renderBulkCmds.get() : renderBulkCmds_base(); } final String renderBulkCmds_fallback(IF0<String> _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<String, Object> addCmdsToTableRow(Map<String, Object> map, Map<String, Object> 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<String, Object> map) { AutoCloseable __5 = tempSetTL(htmlencode_forParams_useV2, true); try { //print("renderForm: filteredFields=" + data.filteredFields()); map = mapMinusKeys(map, joinSets(unshownFields, uneditableFields, data.filteredFields())); Map<String, Object> mapWithoutID = mapWithoutKey(map, data.idField()); List<List<String>> 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<String> 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<HTMLAceEditor> customizeACEEditor; void customizeACEEditor(HTMLAceEditor ace) { if (customizeACEEditor != null) customizeACEEditor.get(ace); else customizeACEEditor_base(ace); } final void customizeACEEditor_fallback(IVF1<HTMLAceEditor> _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<String, String> params) { Map<String, String> filteredMap = subMapStartingWith_dropPrefix(params, fieldPrefix); Map<String, Object> map = joinMaps(data.emptyObject(), (Map) filteredMap); data.rawFormValues = params; // pre-populate list fields for (Map.Entry<? extends String, ? extends String> __0 : _entrySet( filteredMap)) { String key = __0.getKey(); String value = __0.getValue(); List<String> l = splitAt(key, "_"); if (l(l) == 2) { String field = first(l); int idx = parseInt(second(l)); List<String> list = (List<String>) (map.get(field)); if (!(list instanceof ArrayList)) map.put(field, list = new ArrayList()); listPut(list, idx, value); } } return renderNewForm(map); } String renderNewForm(Map<String, Object> 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<Object[]> formParameters; Object[] formParameters() { return formParameters != null ? formParameters.get() : formParameters_base(); } final Object[] formParameters_fallback(IF0<Object[]> _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<String, Object> 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<String, String> 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 "<!-- cmds -->"; } String checkBoxKey() { return "<!-- checkbox -->"; } String itemName() { return data.itemName(); } transient IF2<Map<String, Object>, Map<String, Object>, Map<String, Object>> postProcessTableRow; Map<String, Object> postProcessTableRow(Map<String, Object> data, Map<String, Object> rendered) { return postProcessTableRow != null ? postProcessTableRow.get(data, rendered) : postProcessTableRow_base(data, rendered); } final Map<String, Object> postProcessTableRow_fallback(IF2<Map<String, Object>, Map<String, Object>, Map<String, Object>> _f, Map<String, Object> data, Map<String, Object> rendered) { return _f != null ? _f.get(data, rendered) : postProcessTableRow_base(data, rendered); } Map<String, Object> postProcessTableRow_base(Map<String, Object> data, Map<String, Object> rendered) { return rendered; } Object itemID(Map<String, Object> item) { Object id = mapGet(item, data.idField()); // getVarOpt decodes HTML record if (cleanItemIDs) id = htmlDecode_dropTags(strOrNull(getVarOpt(id))); return id; } long itemIDAsLong(Map<String, Object> item) { return parseLong(itemID(item)); } // return list of HTMLs for commands in pop down button transient IF1<Map<String, Object>, List<String>> additionalCmds; List<String> additionalCmds(Map<String, Object> item) { return additionalCmds != null ? additionalCmds.get(item) : additionalCmds_base(item); } final List<String> additionalCmds_fallback(IF1<Map<String, Object>, List<String>> _f, Map<String, Object> item) { return _f != null ? _f.get(item) : additionalCmds_base(item); } List<String> additionalCmds_base(Map<String, Object> item) { return null; } transient IF1<Map<String, Object>, String> renderCmds; String renderCmds(Map<String, Object> item) { return renderCmds != null ? renderCmds.get(item) : renderCmds_base(item); } final String renderCmds_fallback(IF1<Map<String, Object>, String> _f, Map<String, Object> item) { return _f != null ? _f.get(item) : renderCmds_base(item); } String renderCmds_base(Map<String, Object> item) { Object id = itemID(item); List<String> 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<Map<String, Object>, List<List<String>>> massageFormMatrix; void massageFormMatrix(Map<String, Object> map, List<List<String>> matrix) { if (massageFormMatrix != null) massageFormMatrix.get(map, matrix); else massageFormMatrix_base(map, matrix); } final void massageFormMatrix_fallback(IVF2<Map<String, Object>, List<List<String>>> _f, Map<String, Object> map, List<List<String>> matrix) { if (_f != null) _f.get(map, matrix); else massageFormMatrix_base(map, matrix); } void massageFormMatrix_base(Map<String, Object> map, List<List<String>> matrix) { } String renderComboBox(String name, String value, List<String> 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(__83 -> hoption(__83), 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<String, String> 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 '<div><span>'+item.text+'</span>'\r\n + '<div style=\"float: right\">'\r\n + '<a title=\"Go to object\" class=\"clickable\" href=\"/' + id + '\" target=\"_blank\">↗</a>'\r\n + '</div></div>';\r\n }\r\n }\r\n "); } transient IF1<String, String> moreSelectizeOptions; String moreSelectizeOptions(String name) { return moreSelectizeOptions != null ? moreSelectizeOptions.get(name) : moreSelectizeOptions_base(name); } final String moreSelectizeOptions_fallback(IF1<String, String> _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<String, String> 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<String, String>, Map<String, String>> preprocessUpdateParams; Map<String, String> preprocessUpdateParams(Map<String, String> params) { return preprocessUpdateParams != null ? preprocessUpdateParams.get(params) : preprocessUpdateParams_base(params); } final Map<String, String> preprocessUpdateParams_fallback(IF1<Map<String, String>, Map<String, String>> _f, Map<String, String> params) { return _f != null ? _f.get(params) : preprocessUpdateParams_base(params); } Map<String, String> preprocessUpdateParams_base(Map<String, String> params) { return params; } void disableAllMutationRights() { mutationRights = allowCreateOrDelete = allowCreate = allowEdit = false; } transient IF1<String, Boolean> keepParamInPagination; boolean keepParamInPagination(String name) { return keepParamInPagination != null ? keepParamInPagination.get(name) : keepParamInPagination_base(name); } final boolean keepParamInPagination_fallback(IF1<String, Boolean> _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<String> selectizeLayoutFix; String selectizeLayoutFix() { return selectizeLayoutFix != null ? selectizeLayoutFix.get() : selectizeLayoutFix_base(); } final String selectizeLayoutFix_fallback(IF0<String> _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<String, String> 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, "<deleted>")); params.remove("rename_" + field); // Try to catch f_myField, metaInfo_myField, f_myField_1... String re = "^([^_]+_)" + regexpQuote(field) + "(_[^_]+)?$"; for (String key : keysList(params)) { List<String> 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); } } } } } 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; } } // 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<A> extends MinimalChain<A> implements Iterable<A> { MinimalChain<A> 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<A> next) { this.next = next; this.element = element; if (next == null) return; MinimalChain<A> 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<A> popFirst() { if (next == null) return null; element = next.element; if (last == next) last = this; next = next.next; --size; return this; } ArrayList<A> toList() { ArrayList<A> l = emptyList(size); MinimalChain<A> c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } //public Iterator<A> iterator() { ret toList().iterator(); } class ACIt extends IterableIterator < A > { MinimalChain<A> c = AppendableChain.this; public boolean hasNext() { return c != null; } public A next() { var a = c.element; c = c.next; return a; } } public IterableIterator<A> iterator() { return new ACIt(); } } final static class ConceptFieldIndexDesc<A extends Concept, Val> extends ConceptFieldIndexBase<A, Val> { ConceptFieldIndexDesc(Class<A> cc, String field) { super(cc, field); } ConceptFieldIndexDesc(Concepts concepts, Class<A> 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<A> objectsWithValueGreaterThan(Object value) { SortedMap<Object, Set<A>> subMap = ((SortedMap<Object, Set<A>>) valueToObject.data).headMap(value); return concatLists(values(subMap)); } } 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<AutoCloseable> enter; // optional ownership marker, e.g. for DynModules int cancelTimeOut = 10000; boolean trigger = false; Thread thread; WeakReference<Thread> threadBeingCancelled; List<Runnable> 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(); } synchronized boolean hasThread() { return thread != null; } } static abstract class DynModule extends DynamicObject implements Enterable, ChangeTriggerable { // 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<String, Collection<String>> mechLists; Map<String, Boolean> _persistenceInfo; transient Object _host; // pointer to this module's stem transient Map<Object, IVF1> timers = newWeakHashMap(); // all kinds of resources actually; value is closer helper transient Set<AutoCloseable> _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<String> componentFieldsToKeep; // TODO: use DynamicObject for this transient Map<String, Object> transientGeneralMap = synchroHashMap(); transient Q q; // module command queue transient List onFieldChange; // also for non-persistent fields. L<voidfunc(S field)> 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 extends AutoCloseable> A ownResource(A a) { if (a != null) _resources.add(a); return a; } <A> A ownTimer(A timer) { if (timer instanceof AutoCloseable) ownResource((AutoCloseable) timer); else ownTimer(timer, __10 -> cancelTimerOrInterruptThread(__10)); return timer; } void ownTimer(Object timer, IVF1 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) { printStackTrace(__e); } } for (Map.Entry<? extends Object, ? extends IVF1> __0 : _entrySet( getAndClearMap(timers))) { Object timer = __0.getKey(); IVF1 closerHelper = __0.getValue(); if (verboseTimers) print("Stopping timer: " + closerHelper + " / " + timer); pcallF(closerHelper, timer); } } boolean isDeleted() { return deleted; } boolean deleted() { return deleted; } void cleanMeUp_dynModule() { deleted = true; { cleanUp(q); q = null; } stopAllTimers(); } void persistMe() { synchronized(changeCountSync) { ++changeCount; } pcallFAll(onChange); callOpt(_host, "_change"); updateMe(); } void fireChangeWithoutPersisting() { pcallFAll(onChange); } void fireTransientChange() { pcallFAll(onTransientChange); } final void _change() { persistMe(); } final public 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 __6 = 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) { printStackTrace(__e); } return setField_noCheck(name, value, noPersist); } finally { _close(__6); }} // always set even if equal boolean setField_noCheck(String name, Object value) { return setField_noCheck(name, value, false); } boolean setField_noCheck(String name, Object value, boolean noPersist) { AutoCloseable __7 = enter(); try { //set(this, name, value); setOpt(this, name, value); // XXX allow virtual fields pcallFAll(onFieldChange, name); // XXX - new check for transient field if (!noPersist && dm_isPersistentField(me(), name)) _change(); return true; } finally { _close(__7); }} <A> 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<String>() { 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(dm_os(), "revisualizeModule", _host); } boolean useEnterShortcut() { return false; } public 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<String, Boolean>() { 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<String, Boolean> _printToModuleInterceptor() { return new F1<String, Boolean>() { 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); if (printAlsoToSystemOut) 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 __2 = lock; lock(__2); try { if (onChange == null) onChange = synchroList(); addIfNotThere(onChange, r); } finally { unlock(__2); } } public void removeChangeListener(Runnable r) { remove(onChange, r); } void onFieldChange(VF1<String> r) { Lock __3 = lock; lock(__3); try { if (onFieldChange == null) onFieldChange = synchroList(); addIfNotThere(onFieldChange, r); } finally { unlock(__3); } } void removeFieldChangeListener(VF1<String> r) { remove(onFieldChange, r); } void onChangeAndNow(Runnable r) { onChange(r); callF(r); } // forward to main class void onChangeAndNow(JComponent c, Object r) { onUpdateAndNow(c, r); } /*static*/ <A extends JComponent> A onChange(A tc, Object r) { onUpdate(tc, r); return tc; } void update() {} void unvisualize() { zeroAllFieldsOfTypeExcept(this, Component.class, componentFieldsToKeep); } // return L<AbstractAction> (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<String> 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 __8 = enter(); try { update(); } finally { _close(__8); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp enter(); update();"; }}); return rstUpdate; } Q q() { AutoCloseable __9 = enter(); try { if (q == null) { Lock __4 = lock; lock(__4); try { if (q == null) q = dm_startQ(); } finally { unlock(__4); } } return q; } finally { _close(__9); }} <A> 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; } Object getHost() { return _host; } <A> boolean addAndChange(Collection<A> cl, A a) { if (cl == null || !cl.add(a)) return false; change(); return true; } <A> void clearAndChange(Collection<A> cl) { if (cl == null) return; cl.clear(); change(); } } // end of DynModule static void _registerTimer(java.util.Timer timer) { _registerTimer_original(timer); var mod = dm_currentModule(); { if (mod != null) mod.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 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<Entry> 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() { try { cancel(); } catch (Exception __e) { throw rethrow(__e); } } } static class Fail extends RuntimeException implements IFieldsToList{ Object[] objects; Fail() {} Fail(Object... objects) { this.objects = objects;}public Object[] _fieldsToList() { return new Object[] {objects}; } Fail(Throwable cause, Object... objects) { super(cause); this.objects = objects; } public String toString() { return joinNemptiesWithColon("Fail", commaCombine(getCause(), objects)); } } static interface IWebRequest { public String uri(); public Map<String, String> params(); public Map<String, String> headers(); public Map<String, String> 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(); } default boolean uriIs(String uri) { return eq(uri(), uri); } } 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 contains(Rectangle r) { return rectContains(this, r); } boolean empty() { return w <= 0 || h <= 0; } int getWidth() { return w; } int getHeight() { return h; } } static class Pt implements Comparable<Pt>, IDoublePt { 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; } double length() { return sqrt(x*x+y*y); } Pt minus(Pt p) { return ptMinus(this, p); } public double x_double() { return x; } public double y_double() { return 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<Program> scan() { try { return scan(1, 65535); } catch (Exception __e) { throw rethrow(__e); } } static List<Program> scan(int fromPort, int toPort) { return scan(fromPort, toPort, new int[0]); } static List<Program> scan(int fromPort, int toPort, int[] preferredPorts) { try { Set<Integer> preferredPortsSet = new HashSet<Integer>(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<Future<Program>> futures = new ArrayList(); List<Integer> 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<Program> programs = new ArrayList(); long time = now(); int i = 0; for (final Future<Program> 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<Program> checkPort(final ExecutorService es, final String ip, final int port, final int timeout) { return es.submit(new Callable<Program>() { @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<Program> quickScan() { return scan(quickScanFrom, quickScanTo); } static List<Program> quickBotScan() { return quickBotScan(new int[0]); } static List<Program> quickBotScan(int[] preferredPorts) { if (maxNumberOfVMs == 0) maxNumberOfVMs = isAndroid() ? maxNumberOfVMs_android : maxNumberOfVMs_nonAndroid; return scan(4999, 5000+maxNumberOfVMs-1, preferredPorts); } } static class ConceptFieldIndex<A extends Concept, Val> extends ConceptFieldIndexBase<A, Val> { ConceptFieldIndex(Class<A> cc, String field) { super(cc, field); } ConceptFieldIndex(Concepts concepts, Class<A> cc, String field) { super(concepts, cc, field); } void init() { valueToObject = new MultiSetMap(); } void register() { concepts.addFieldIndex(cc, field, this); } } // could almost add IVar<A> here - but void set() and bool set() are incompatible in some places static interface IRef<A> extends IF0<A> { // called by the referencee to atomically replace itself // Passing oldValue to avoid race conditions public default void replaceValue(A oldValue, A newValue) {} } 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 abstract class F0<A> { abstract A get(); } static abstract class F1<A, B> { abstract B get(A a); } // you still need to implement hasNext() and next() static abstract class IterableIterator<A> implements Iterator<A>, Iterable<A> { public Iterator<A> iterator() { return this; } public void remove() { unsupportedOperation(); } } static abstract class F2<A, B, C> { abstract C get(A a, B b); } 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<RC> list() { return adopt((List<RC>) rpc(db, "xlist")); } List<RC> list(String className) { return adopt((List<RC>) rpc(db, "xlist", className)); } List<RC> xlist() { return list(); } List<RC> xlist(String className) { return list(className); } // adopt is an internal method List<RC> adopt(List<RC> 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<RC> 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); } } static class RGB { // usually in range [0, 1] 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 float brightnessToFloat(int brightness) { return brightness/255f; } } static interface MakesBufferedImage extends WidthAndHeight { BufferedImage getBufferedImage(); public default void drawAt(Graphics2D g, int x, int y) { g.drawImage(getBufferedImage(), x, y, null); } } /** 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(); } } // 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<A> extends RandomAccessAbstractList<A> implements IContentsIndexedList<A>, IContentsIndexedList2<A> { Map<A, TreeSet<Elem<A>>> index = new HashMap(); // tokens by contents, sorted by index final ArrayList<Elem<A>> list = new ArrayList(); final static class Elem<A> extends HasIndex { A s; // actual token public String toString() { return "Elem " + quote(s) + "@" + idx; } } ContentsIndexedList() {} ContentsIndexedList(Map<A, TreeSet<Elem<A>>> index) { this.index = index;} // use different type of index (e.g. ciMap) ContentsIndexedList(Collection<A> 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<A> 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<A> 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<A> t = new Elem(); t.s = s; t.idx = i; list.add(i, t); reorder(i+1); addToIdx(t); } public boolean addAll(int i, Collection<? extends A> l) { int n = l.size(); if (n == 0) return false; ++modCount; List<Elem<A>> l2 = emptyList(n); int j = i; for (A s : l) { Elem<A> t = new Elem(); t.s = s; t.idx = j++; l2.add(t); } list.addAll(i, l2); reorder(i+n); for (Elem<A> t : l2) addToIdx(t); return true; } public A remove(int i) { ++modCount; Elem<A> 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<A> t) { TreeSet<Elem<A>> idx = index.get(t.s); idx.remove(t); if (idx.isEmpty()) index.remove(t.s); } void addToIdx(Elem<A> t) { TreeSet<Elem<A>> 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<Elem<A>> l = index.get(s); return l == null ? -1 : first(l).idx; } @Override public int lastIndexOf(Object s) { TreeSet<Elem<A>> 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(A o) { TreeSet<Elem<A>> idx = index.get(o); if (idx == null) return emptyIntArray(); int[] a = new int[idx.size()]; int i = 0; for (Elem<A> t : idx) a[i++] = t.idx; return a; } public TreeSet<HasIndex> indicesOf_treeSetOfHasIndex(A o) { return (TreeSet) index.get(o); } } public static interface IF0<A> { A get(); } static interface Hasher<A> { int hashCode(A a); boolean equals(A a, A b); } static class DefaultValueConverterForField implements ValueConverterForField { public OrError<Object> 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 if (type == GlobalID.class && value instanceof String) return new OrError(new GlobalID((String) value)); return new OrError(value); } } static abstract class CloseableIterableIterator<A> extends IterableIterator<A> implements AutoCloseable { public void close() throws Exception {} } static class ExpiringMap2<A, B> extends AbstractMap<A, B> { Map<A, Pair<Long, B>> byKey = new HashMap(); // key -> pair(expiry sys time, value) PriorityBlockingQueue<Pair<Long, A>> 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(); 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<Long, A> p; long time = sysTime(); while ((p = queue.peek()) != null && time >= p.a) { if (useCountdown) countdown.stop(); p = queue.poll(); Pair<Long, B> v = byKey.get(p.b); if (v != null /*&& v.a == p.a*/) { byKey.remove(p.b); changes = true; change(); } } if (useCountdown) countdown.setTargetTime(p != null ? p.a : 0, 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<Long, B> 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<Long, B> 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<Long, B> 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<Map.Entry<A,B>> entrySet() { clean(); // TODO: mutex return synchronizedSet(mapValues(__123 -> pairB(__123), byKey).entrySet()); } synchronized public Set<A> keySet() { clean(); return synchronizedSet(byKey.keySet()); } synchronized public int size() { clean(); return byKey.size(); } void setStandardExpiryTime(long ms) { standardExpiryTime = ms; } synchronized ExpiringMap2<A, B> setMap(Map innerMap) { byKey = innerMap; return this; } } static interface IFieldsToList { Object[] _fieldsToList(); } static interface IF2<A, B, C> { C get(A a, B b); } static class ConceptFieldIndexCI<A extends Concept> extends ConceptFieldIndexBase<A, Object> { ConceptFieldIndexCI(Class<A> cc, String field) { super(cc, field); } ConceptFieldIndexCI(Concepts concepts, Class<A> cc, String field) { super(concepts, cc, field); } void init() { valueToObject = generalizedCIMultiSetMap(); } void register() { concepts.addCIFieldIndex(cc, field, this); } } static interface Producer<A> { public A next(); // null when end } static interface IF1<A, B> { B get(A a); } static interface IVF1<A> { void get(A a); } static interface IVF2<A, B> { void get(A a, B b); } static interface IVar<A> extends IF0<A> { void set(A a); A get(); // reified type of value (if available) default Class<A> getType() { return null; } default boolean has() { return get() != null; } default void clear() { set(null); } } static interface IIntPred { boolean get(int a); } 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(); var cb = makeEnabledCheckBox(); buttons = jrightalignedline(cb); 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 addControl(Swingable component) { addComponentsFirst(buttons, wrap(component)); } void setMainControl(JComponent control) { scpSet(scpStretcherControl, control); } } static class Cache<A> { 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<A> 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); } } // 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<String, List<Method>> cache = new HashMap(); _MethodCache(Class c) { this.c = c; _init(); } void _init() { Class _c = c; java.lang.Module myModule = getClass().getModule(); boolean anyHiddenClasses = false; while (_c != null) { boolean exported = classIsExportedTo(_c, myModule); if (!exported) anyHiddenClasses = true; else for (Method m : _c.getDeclaredMethods()) if ((anyHiddenClasses || !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. // If any classes in the hierarchy were inaccessible, we add // all interface methods (see test_callForbiddenMethodByReflection for a test) for (Class intf : allInterfacesImplementedBy(c)) for (Method m : intf.getDeclaredMethods()) if ((anyHiddenClasses || m.isDefault()) && !reflection_isForbiddenMethod(m)) multiMapPut(cache, m.getName(), makeAccessible(m)); } // Returns only matching methods Method findMethod(String method, Object[] args) { try { List<Method> 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<Method> 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); } } //Cl<Method> allMethods() { ret allValues(cache); } } // 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 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); } } // 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); double remaining = timeout-age; print("Lock file age: " + lockFile + ": " + iround(age) + " s" + (remaining <= 0 ? " - old, deleting" : "- please start again in " + nSeconds(iceil(remaining)))); if (remaining <= 0) { 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) { printStackTrace(__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) { printStackTrace(__e); }} synchronized void _simulateCrash() { { cleanUp(touchTimer); touchTimer = null; } } void deleteOnExit() { if (haveLock) lockFile.deleteOnExit(); } } // 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() {} }; } // actually query another object default public Object getMeta(Object obj, Object key){ return metaGet(obj, key); } default public Object metaGet(Object obj, Object key) { // call global function return metaMapGet(obj, key); } default public Object metaGet(String key, Object obj) { // call global function return metaMapGet(obj, key); } default public Object getMeta(Object key){ return metaGet(key); } default public Object metaGet(Object key) { if (key == null) return null; Object meta = _getMeta(); if (meta instanceof Map) return ((Map) meta).get(key); return null; } default public void metaSet(IMeta obj, Object key, Object value){ metaPut(obj, key, value); } default public void metaPut(IMeta obj, Object key, Object value) { // call global function metaMapPut(obj, key, value); } default public void metaSet(Object key, Object value){ metaPut(key, value); } default public void metaPut(Object key, Object value) { if (key == null) return; Map map = convertObjectMetaToMap(this); syncMapPutOrRemove(map, key, value); } } static class HCRUD_Concepts<A extends Concept> extends HCRUD_Data { Concepts cc = db_mainConcepts(); Class<A> cClass; List<IVF1<A>> onCreateOrUpdate = new ArrayList(); List<IVF1<A>> onCreate = new ArrayList(); IVF2<A, Map<String, Object>> afterUpdate; // parameter 2: old values Map<String, Object> filters; // fields to filter by/add to new objects Map<String, String> ciFilters; // case-insensitive filters IF1<Collection<A>, Collection<A>> customFilter; ValueConverterForField valueConverter; boolean referencesBlockDeletion = false; boolean trimAllSingleLineValues = false; Set<String> 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<String, Boolean> 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<A> cClass) { this.cClass = cClass;} HCRUD_Concepts(Concepts cc, Class<A> cClass) { this.cClass = cClass; this.cc = cc;} // XXX - breaking change from just shortName() transient IF0<String> itemName; String itemName() { return itemName != null ? itemName.get() : itemName_base(); } final String itemName_fallback(IF0<String> _f) { return _f != null ? _f.get() : itemName_base(); } String itemName_base() { return humanizeShortName(cClass); } transient IF0<String> itemNamePlural; String itemNamePlural() { return itemNamePlural != null ? itemNamePlural.get() : itemNamePlural_base(); } final String itemNamePlural_fallback(IF0<String> _f) { return _f != null ? _f.get() : itemNamePlural_base(); } String itemNamePlural_base() { return super.itemNamePlural(); } //LS fields() { ret conceptFields(cClass); } List<A> itemsForListing() { return defaultSort(asList(listConcepts())); } @Override List<Map<String, Object>> list() { //ret lazyMap itemToMapForList(itemsForListing()); return lazyMap(itemsForListing(), a -> new Item(str(a.id)) { public Map<String, Object> 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<Map<String, Object>> list(IntRange range) { return lambdaMap(__84 -> itemToMapForList(__84), subListOrFull(itemsForListing(), range)); } Collection<A> listConcepts() { Collection<A> l = listConcepts_firstStep(); return postProcess(customFilter, l); } transient IF0<Collection<A>> listConcepts_firstStep; Collection<A> listConcepts_firstStep() { return listConcepts_firstStep != null ? listConcepts_firstStep.get() : listConcepts_firstStep_base(); } final Collection<A> listConcepts_firstStep_fallback(IF0<Collection<A>> _f) { return _f != null ? _f.get() : listConcepts_firstStep_base(); } Collection<A> 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<A> l = conceptsWhere(cc, cClass, mapToParams(filters)); return filterConceptsIC(l, mapToParams(ciFilters)); } } transient IF0<Pair<String, Boolean>> defaultSortField; Pair<String, Boolean> defaultSortField() { return defaultSortField != null ? defaultSortField.get() : defaultSortField_base(); } final Pair<String, Boolean> defaultSortField_fallback(IF0<Pair<String, Boolean>> _f) { return _f != null ? _f.get() : defaultSortField_base(); } Pair<String, Boolean> defaultSortField_base() { return pair("id", true); } transient IF1<List<A>, List<A>> defaultSort; List<A> defaultSort(List<A> l) { return defaultSort != null ? defaultSort.get(l) : defaultSort_base(l); } final List<A> defaultSort_fallback(IF1<List<A>, List<A>> _f, List<A> l) { return _f != null ? _f.get(l) : defaultSort_base(l); } List<A> defaultSort_base(List<A> l) { return sortedByConceptIDDesc(l); } transient IF0<A> emptyConcept; A emptyConcept() { return emptyConcept != null ? emptyConcept.get() : emptyConcept_base(); } final A emptyConcept_fallback(IF0<A> _f) { return _f != null ? _f.get() : emptyConcept_base(); } A emptyConcept_base() { return unlisted(cClass); } transient IF0<Map<String, Object>> emptyObject; Map<String, Object> emptyObject() { return emptyObject != null ? emptyObject.get() : emptyObject_base(); } final Map<String, Object> emptyObject_fallback(IF0<Map<String, Object>> _f) { return _f != null ? _f.get() : emptyObject_base(); } Map<String, Object> emptyObject_base() { A c = emptyConcept(); // not actually necessary here, we do it on create /*cset(c, mapToParams(filters)); cset(c, mapToParams(ciFilters));*/ Map<String, Object> map = itemToMap(c); //printVars_str emptyObject(+cClass, +filters, +ciFilters, +map); return mapMinusKeys(fieldsToHideInCreationForm, map); } Map<String, Object> itemToMap(A c) { if (c == null) return null; return putKeysFirst(getFieldOrder(c), conceptToMap_gen_withNullValues(c)); } Map<String, Object> itemToMapForList(A c) { if (c == null) return null; Map<String, Object> map = itemToMap(c); massageItemMapForList(c, map); return map; } transient IVF2<A, Map<String, Object>> massageItemMapForList; void massageItemMapForList(A c, Map<String, Object> map) { if (massageItemMapForList != null) massageItemMapForList.get(c, map); else massageItemMapForList_base(c, map); } final void massageItemMapForList_fallback(IVF2<A, Map<String, Object>> _f, A c, Map<String, Object> map) { if (_f != null) _f.get(c, map); else massageItemMapForList_base(c, map); } void massageItemMapForList_base(A c, Map<String, Object> map) { } transient IVF2<A, Map<String, Object>> massageItemMapForUpdate; void massageItemMapForUpdate(A c, Map<String, Object> map) { if (massageItemMapForUpdate != null) massageItemMapForUpdate.get(c, map); else massageItemMapForUpdate_base(c, map); } final void massageItemMapForUpdate_fallback(IVF2<A, Map<String, Object>> _f, A c, Map<String, Object> map) { if (_f != null) _f.get(c, map); else massageItemMapForUpdate_base(c, map); } void massageItemMapForUpdate_base(A c, Map<String, Object> map) { // Concepts.RefL magic Collection<Field> refLFields = nonStaticNonTransientFieldObjectsOfType(Concept.RefL.class, c); printVars_str("refLFields", refLFields, "c", c); for (Field f : refLFields) { TreeMap<Integer, Object> values = new TreeMap(); Matches m = new Matches(); for (Map.Entry<? extends String, ? extends Object> __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<String, Object> 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<Integer, String> values = new TreeMap(); Matches m = new Matches(); for (Map.Entry<? extends String, ? extends Object> __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<String, Object> map, String key) { Object value = map.get(key); if (value instanceof String) { Concept concept = getConceptFromString((String) value); map.put(key, concept); } } void replaceStringValueWithBool(Map<String, Object> map, String key) { Object value = map.get(key); if (value instanceof String) { map.put(key, englishStringToBool((String) value)); } } transient IF1<Object, Map<String, Object>> getObject; Map<String, Object> getObject(Object id) { return getObject != null ? getObject.get(id) : getObject_base(id); } final Map<String, Object> getObject_fallback(IF1<Object, Map<String, Object>> _f, Object id) { return _f != null ? _f.get(id) : getObject_base(id); } Map<String, Object> getObject_base(Object id) { return itemToMap(conceptForID(id)); } Map<String, Object> getObjectForEdit(Object id) { currentConcept = conceptForID(id); return getObject(id); } Object createObject(Map<String, String> fullMap, String fieldPrefix) { rawFormValues = fullMap; try { Map<String, String> 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<String, String> map, boolean creating) { Lock __3 = lockDB && !creating ? dbLock(cc) : null; lock(__3); try { Map<String, Object> map2 = (Map) cloneMap(map); massageItemMapForUpdate(c, map2); if (verbose) { print("setValues " + map); print("backRefs: " + c.backRefs); } Map<String, Object> 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<String, Object> map) { for (Map.Entry<? extends String, ? extends Object> __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<String, String> fullMap, String fieldPrefix) { rawFormValues = fullMap; try { Map<String, String> 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<A> actuallyDeleteConcept; void actuallyDeleteConcept(A c) { if (actuallyDeleteConcept != null) actuallyDeleteConcept.get(c); else actuallyDeleteConcept_base(c); } final void actuallyDeleteConcept_fallback(IVF1<A> _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<A> addFilters(Map<String, Object> map) { for (Map.Entry<? extends String, ? extends Object> __7 : _entrySet( unnullForIteration(map))) { String field = __7.getKey(); Object value = __7.getValue(); addFilter(field, value); } return this; } HCRUD_Concepts<A> 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<Object, Boolean> isEditableValue; boolean isEditableValue(Object value) { return isEditableValue != null ? isEditableValue.get(value) : isEditableValue_base(value); } final boolean isEditableValue_fallback(IF1<Object, Boolean> _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 (isSubtypeOf(type, Concept.Ref.class)) { Class<? extends Concept> 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<? extends Concept> 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<? extends Concept> 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<? extends Concept> 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<? extends Concept> 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<String> 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? <A extends Concept> Collection<A> listConceptClass(Class<A> c) { return cc.list(c); } List<String> comboBoxItemsForConceptClass(Class<? extends Concept> c) { return comboBoxItems(listConceptClass(c)); } List<String> comboBoxItems(Collection<? extends Concept> l) { return comboBoxItems_static(l); } static List<String> comboBoxItems_static(Collection<? extends Concept> l) { return itemPlus("", lmap(__85 -> comboBoxItem_static(__85), 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<Object, Boolean> objectCanBeDeleted; boolean objectCanBeDeleted(Object id) { return objectCanBeDeleted != null ? objectCanBeDeleted.get(id) : objectCanBeDeleted_base(id); } final boolean objectCanBeDeleted_fallback(IF1<Object, Boolean> _f, Object id) { return _f != null ? _f.get(id) : objectCanBeDeleted_base(id); } boolean objectCanBeDeleted_base(Object id) { return !referencesBlockDeletion || !hasBackRefs(conceptForID(id)); } Set<String> filteredFields() { return joinSets(keys(filters), keys(ciFilters)); } Class<? extends Concept> fieldTypeArg(String field) { return getTypeArgumentAsClass(genericFieldType(or(currentConcept, cClass), field)); } transient IF2<String, String, Class<? extends Concept>> conceptClassForComboBoxSearch; Class<? extends Concept> conceptClassForComboBoxSearch(String info, String query) { return conceptClassForComboBoxSearch != null ? conceptClassForComboBoxSearch.get(info, query) : conceptClassForComboBoxSearch_base(info, query); } final Class<? extends Concept> conceptClassForComboBoxSearch_fallback(IF2<String, String, Class<? extends Concept>> _f, String info, String query) { return _f != null ? _f.get(info, query) : conceptClassForComboBoxSearch_base(info, query); } Class<? extends Concept> 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<? extends Concept> 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<String, String, List<String>> comboBoxSearchBaseItems; List<String> comboBoxSearchBaseItems(String info, String query) { return comboBoxSearchBaseItems != null ? comboBoxSearchBaseItems.get(info, query) : comboBoxSearchBaseItems_base(info, query); } final List<String> comboBoxSearchBaseItems_fallback(IF2<String, String, List<String>> _f, String info, String query) { return _f != null ? _f.get(info, query) : comboBoxSearchBaseItems_base(info, query); } List<String> comboBoxSearchBaseItems_base(String info, String query) { var c = conceptClassForComboBoxSearch(info, query); if (c == null) return emptyList(); return comboBoxItemsForConceptClass(c); } List<String> comboBoxSearch(String info, String query) { List<String> items = comboBoxSearchBaseItems(info, query); return takeFirst(10, scoredSearch(query, items)); } // to find the concept e.g. within massageFormMatrix A conceptForMap(Map<String, Object> map) { if (map == null) return null; long id = toLong(map.get(idField())); return id == 0 ? null : (A) _getConcept(cc, id); } A getConcept(Map<String, Object> map) { return conceptForMap(map); } Map<String, String> extractFieldValues(Map<String, String> 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<Object, String> titleForObjectID; String titleForObjectID(Object id) { return titleForObjectID != null ? titleForObjectID.get(id) : titleForObjectID_base(id); } final String titleForObjectID_fallback(IF1<Object, String> _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<String>, Htmlable { 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); } public String html() { return hjs(code); } } // 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 { try { vmBus_send("threadStarted", this); if (target != null) target.run(); } finally { vmBus_send("threadEnded", this); } } catch (Exception __e) { throw rethrow(__e); } } Runnable getTarget() { return target; } } static abstract class LiveValue<A> implements IGetterWithNotify<A> { abstract Class<A> getType(); public abstract A get(); } // 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 Timestamp implements Comparable<Timestamp> , 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(); } long unixDate() { return date; } 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); } Timestamp plus(Seconds seconds) { return plus(seconds == null ? null : seconds.getDouble()); } final Timestamp plusSeconds(double seconds){ return plus(seconds); } Timestamp plus(double seconds) { return new Timestamp(date+toMS(seconds)); } // returns milliseconds long minus(Timestamp ts) { return unixDate()-ts.unixDate(); } long sysTime() { return clockTimeToSystemTime(date); } } /* e.g. overlay <- ScreenOverlay bounds <- rightScreenBounds overlay bounds bounds overlay show Can separate commands with ";" also. For how to define functions in a script see #1033988. Note: LeftArrowScriptAutoCompleter uses a lot of this class's internals */ static class GazelleV_LeftArrowScriptParser extends SimpleLeftToRightParser { final public GazelleV_LeftArrowScriptParser setG22utils(G22Utils g22utils){ return g22utils(g22utils); } public GazelleV_LeftArrowScriptParser g22utils(G22Utils g22utils) { this.g22utils = g22utils; return this; } final public G22Utils getG22utils(){ return g22utils(); } public G22Utils g22utils() { return g22utils; } G22Utils g22utils = new G22Utils(); List functionContainers = new ArrayList(); LinkedHashMap<String, LASValueDescriptor> knownVars = new LinkedHashMap(); BuildingScript currentReturnableScript; boolean inParens = false; int idCounter; // object can be a class static class MethodOnObject implements IFieldsToList{ static final String _fieldOrder = "object method"; Object object; String method; MethodOnObject() {} MethodOnObject(Object object, String method) { this.method = method; this.object = object;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + object + ", " + method + ")"; } public boolean equals(Object o) { if (!(o instanceof MethodOnObject)) return false; MethodOnObject __3 = (MethodOnObject) o; return eq(object, __3.object) && eq(method, __3.method); } public int hashCode() { int h = 791808543; h = boostHashCombine(h, _hashCode(object)); h = boostHashCombine(h, _hashCode(method)); return h; } public Object[] _fieldsToList() { return new Object[] {object, method}; } } class BuildingScript { int id = ++idCounter; boolean returnable = false; BuildingScript returnableParent; GazelleV_LeftArrowScript.Script script = new GazelleV_LeftArrowScript.Script(); List<GazelleV_LeftArrowScript.Evaluable> steps = new ArrayList(); Map<String, GazelleV_LeftArrowScript.FunctionDef> functionDefs = new HashMap(); BuildingScript(boolean returnable) { this.returnable = returnable;} void add(GazelleV_LeftArrowScript.Evaluable step) { if (step != null) steps.add(step); } GazelleV_LeftArrowScript.Evaluable get() { // if the last command is a return from THIS script, // convert into a simple expression var lastStep = last(steps); if (lastStep instanceof GazelleV_LeftArrowScript.ReturnFromScript) if (((GazelleV_LeftArrowScript.ReturnFromScript) lastStep).script == script) replaceLast(steps, ((GazelleV_LeftArrowScript.ReturnFromScript) lastStep).value); // if the script is only step, is not returnable // and defines no functions, replace it with its only step if (!returnable && l(steps) == 1 && empty(functionDefs)) return first(steps); // make and return actual script script.functionDefs = functionDefs; script.steps = toTypedArray(GazelleV_LeftArrowScript.Evaluable.class, steps); return script; } String toStringLong() { return pnlToLines(steps); } public String toString() { return formatRecordVars("BuildingScript", "id", id, "returnable", returnable , "returnableParent", returnableParent, "script", script); } } // end of BuildingScript GazelleV_LeftArrowScript.Script parse(String text) { setText(text); return (GazelleV_LeftArrowScript.Script) parseScript(true); } // if returnable is true, it's always a Script GazelleV_LeftArrowScript.Evaluable parseScript(boolean returnable) { BuildingScript script = new BuildingScript(returnable); var lastReturnableScript = currentReturnableScript; if (returnable) { script.returnableParent = currentReturnableScript; currentReturnableScript = script; } try { parseScript_2(script); var builtScript = script.get(); currentReturnableScript = lastReturnableScript; return builtScript; } catch (Throwable e) { if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("Parsed so far:\n" + script); throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn()))); } } void parseScript_2(BuildingScript script) { while (mainLoop()) { if (is(";")) { next(); continue; } if (is("}")) break; if (is("def")) { parseFunctionDefinition(); continue; } if (is("param")) { consume(); String var = assertIdentifier(tpp()); knownVars.put(var, new LASValueDescriptor()); continue; } if (is("return")) { consume(); var expr = parseAssignmentOrExpr(); { script.add(new GazelleV_LeftArrowScript.ReturnFromScript(currentReturnableScript.script, expr)); continue; } } script.add(parseAssignmentOrExpr()); } } GazelleV_LeftArrowScript.Evaluable parseAssignmentOrExpr() { String t = token(); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("next tokens: " + quote(token(1)) + " " + quote(token(2))); if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) { if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("Found assignment"); next(3); knownVars.put(t, new LASValueDescriptor()); return new GazelleV_LeftArrowScript.Assignment(t, parseExpr()); } else return parseExpr(); } GazelleV_LeftArrowScript.Evaluable parseOptionalInnerExpression() { if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars("parseOptionalInnerExpression", "token", token()); if (atCmdEnd() || is("{")) return null; return parseInnerExpr(); } GazelleV_LeftArrowScript.Evaluable _const(Object o) { return new GazelleV_LeftArrowScript.Const(o); } GazelleV_LeftArrowScript.Evaluable parseInnerExpr() { return parseExpr(true); } GazelleV_LeftArrowScript.Evaluable parseExpr() { return parseExpr(false); } GazelleV_LeftArrowScript.Evaluable parseExpr(boolean inner) { if (atEnd()) return null; String t = token(); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars("parseExpr", "token" , t); if (is(";")) return null; // empty command // int or double literal if (is("-") && empty(nextSpace()) && startsWithDigit(token(1)) || startsWithDigit(t)) { t = consumeMultiTokenLiteral(); if (isInteger(t)) return _const(parseInt(t)); if (endsWith(t, "f")) return _const(parseFloat(t)); return _const(parseDouble(t)); } if (isQuoted(t)) { consume(); return _const(unquote(t)); } if (isIdentifier(t)) { if (is("while")) return parseWhileLoop(); if (is("for")) return parseForEach(); if (is("if")) return parseIfStatement(); consume(); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner); return parseExprStartingWithIdentifier(t, inner); } // nested expression if (eq(t, "(")) { boolean inParensOld = inParens; inParens = true; consume(); //print("Consumed opening parens (level " + parenLevels + ")"); var e = parseExpr(); //print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")"); consume(")"); inParens = inParensOld; return inner ? e : parseCall(e); } throw fail("Identifier, literal or opening parentheses expected (got: " + quote(t)); } // t is last consumed token (the identifier the expression starts with) GazelleV_LeftArrowScript.Evaluable parseExprStartingWithIdentifier(String t, boolean inner) { if (eq(t, "true")) return _const(true); if (eq(t, "false")) return _const(false); if (eq(t, "null")) return _const(null); if (eq(t, "new")) { String className = assertIdentifier(tpp()); Object o = findExternalObject(className); if (o instanceof Class) return new GazelleV_LeftArrowScript.NewObject(((Class) o), parseArguments()); throw fail("Class not found: " + className); } var type = knownVars.get(t); if (type != null) { var e = new GazelleV_LeftArrowScript.GetVar(t).returnType(type); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression")); return inner ? e : parseCall(e); } if (!inner) { var fdef = lookupFunction(t); if (fdef != null) return new GazelleV_LeftArrowScript.CallFunction(fdef, parseArguments()); } if (eq(t, "_context")) return new GazelleV_LeftArrowScript.GetVarContext(); Object o = findExternalObject(t); if (o == null) throw new UnknownObject(t); else if (inner) return _const(o); else if (o instanceof Class) { Class c = (Class) o; /*if (atCmdEnd()) ret new Toolbox.NewObject(o);*/ /* old object creation syntax (e.g. Pair new a b) if (is("new")) { next(); ret new Toolbox.NewObject(c, parseArguments()); } else*/ if (isIdentifier()) { String name = tpp(); // look for a static method call if (hasMethodNamed(c, name)) return new GazelleV_LeftArrowScript.CallMethod(_const(c), name, parseArguments()); // if the class is an interface, it's a lambda method reference if (isInterface(c)) return parseLambdaMethodRef(c, name); // look for field second var field = getField(((Class) o), name); if (field != null) { assertCmdEnd(); return new GazelleV_LeftArrowScript.GetStaticField(field); } throw fail(name + " not found in " + ((Class) o) + " (looked for method or field)"); } else throw fail("Method name expected: " + token()); } else if (o instanceof MethodOnObject) { if (inner) throw fail("Can't call methods in arguments"); return new GazelleV_LeftArrowScript.CallMethod(_const(((MethodOnObject) o).object), ((MethodOnObject) o).method, parseArguments()); } else return parseCall(_const(o)); } // (e.g. "IF1 l" meaning "l1 l") // c must be a single method interface GazelleV_LeftArrowScript.Evaluable parseLambdaMethodRef(Class c, String name) { var fdef = lookupFunction(name); if (fdef != null) { GazelleV_LeftArrowScript.Evaluable[] curriedArguments = parseArguments(); return new GazelleV_LeftArrowScript.CurriedScriptFunctionLambda(c, fdef, curriedArguments); } Object function = findExternalObject(name); if (function == null) throw new UnknownObject(name); if (!(function instanceof MethodOnObject)) throw fail(function + " is not a callable method"); Object target = ((MethodOnObject) function).object; String targetMethod = ((MethodOnObject) function).method; GazelleV_LeftArrowScript.Evaluable[] curriedArguments = parseArguments(); return new GazelleV_LeftArrowScript.CurriedMethodLambda(c, target, targetMethod, curriedArguments); } GazelleV_LeftArrowScript.FunctionDef lookupFunction(String name) { var script = currentReturnableScript; while (script != null) { var f = script.functionDefs.get(name); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars("lookupFunction", "script", script, "name", name, "f", f); if (f != null) return f; script = script.returnableParent; } return null; } GazelleV_LeftArrowScript.Evaluable[] parseArguments() { return toArrayOrNull(GazelleV_LeftArrowScript.Evaluable.class, parseArgumentsAsList()); } List<GazelleV_LeftArrowScript.Evaluable> parseArgumentsAsList() { //ret collectWhileNotNull(-> parseOptionalInnerExpression()); List<GazelleV_LeftArrowScript.Evaluable> l = new ArrayList(); try { while (true) { GazelleV_LeftArrowScript.Evaluable a = parseOptionalInnerExpression(); if (a == null) break; l.add(a); } return l; } catch (Throwable _e) { if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("Arguments parsed so far: " + l); throw rethrow(_e); } } String consumeMultiTokenLiteral() { return consumeUntilSpaceOr(() -> atCmdEnd()); } boolean atCmdEnd() { return !inParens && atEndOrLineBreak() || is(";") || is("}") || is(")"); } void assertCmdEnd() { if (!atCmdEnd()) throw fail("Expected end of command"); } GazelleV_LeftArrowScript.Evaluable parseCall(GazelleV_LeftArrowScript.Evaluable target) { if (atCmdEnd() || !isIdentifier()) return target; var start = ptr(); String methodName = tpp(); var args = parseArguments(); if (nempty(args)) return new GazelleV_LeftArrowScript.CallMethod(target, methodName, args); else return src(start, new GazelleV_LeftArrowScript.CallMethodOrGetField(target, methodName)); } <A> A src(ListAndIndex<String> start, A a) { if (a instanceof IHasTokenRangeWithSrc) ((IHasTokenRangeWithSrc) a).setTokenRangeWithSrc(new TokenRangeWithSrc(start, ptr())); return a; } // can return MethodOnObject transient IF1<String, Object> findExternalObject; Object findExternalObject(String name) { return findExternalObject != null ? findExternalObject.get(name) : findExternalObject_base(name); } final Object findExternalObject_fallback(IF1<String, Object> _f, String name) { return _f != null ? _f.get(name) : findExternalObject_base(name); } Object findExternalObject_base(String name) { try { //try object findClassThroughDefaultClassFinder(name); //try object findClassInStandardImports(name); String fullName = globalClassNames().get(name); if (fullName != null) return Class.forName(fullName); for (var container : unnullForIteration(functionContainers)) if (hasMethodNamed(container, name)) return new MethodOnObject(container, name); return null; } catch (Exception __e) { throw rethrow(__e); } } GazelleV_LeftArrowScriptParser allowTheWorld() { return allowTheWorld(mc()); } // first containers within argument list have priority // last calls to allowTheWorld have priority GazelleV_LeftArrowScriptParser allowTheWorld(Object... functionContainers) { for (Object o : unnullForIteration(reversed(functionContainers))) if (!contains(this.functionContainers, o)) { this.functionContainers.add(0, o); globalClassNames_cache = null; // recalculate } return this; } void printFunctionDefs(GazelleV_LeftArrowScript.Script script) { if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print(values(script.functionDefs)); } void parseFunctionDefinition() { consume("def"); String functionName = assertIdentifier(tpp()); List<String> args = new ArrayList(); while (isIdentifier()) args.add(tpp()); AutoCloseable __1 = tempMapPutAll(knownVars, mapWithSingleValue(args, new LASValueDescriptor())); try { var functionBody = parseCurlyBlock(true); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("Defined function " + functionName + " in " + currentReturnableScript); currentReturnableScript.functionDefs.put(functionName, new GazelleV_LeftArrowScript.FunctionDef(functionName, args, functionBody)); } finally { _close(__1); }} GazelleV_LeftArrowScript.Evaluable parseCurlyBlock(boolean returnable) { //print(+knownVars); consume("{"); boolean inParensOld = inParens; inParens = false; var script = parseScript(returnable); consume("}"); inParens = inParensOld; return script; } GazelleV_LeftArrowScript.Evaluable parseWhileLoop() { consume("while"); var condition = parseExpr(); var body = parseCurlyBlock(false); return new GazelleV_LeftArrowScript.While(condition, body); } GazelleV_LeftArrowScript.Evaluable parseForEach() { return new ParseForEach().get(); } class ParseForEach { GazelleV_LeftArrowScript.Evaluable collection, body; IF0<GazelleV_LeftArrowScript.Evaluable> finish; IF0<AutoCloseable> enterBody; GazelleV_LeftArrowScript.Evaluable get() { consume("for"); int iIn = relativeIndexOf("in"); if (iIn < 0) throw fail("for without in"); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("iIn", iIn); if (iIn == 1) { // just a variable String var = consumeIdentifier(); enterBody = () -> tempMapPut(knownVars, var, new LASValueDescriptor()); finish = () -> new GazelleV_LeftArrowScript.ForEach(collection, var, body); } else if (iIn == 3) { // "pair a b" or "Pair a b" consumeOneOf("pair", "Pair"); String varA = consumeIdentifier(); String varB = consumeIdentifier(); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars("varA", varA, "varB", varB); enterBody = () -> combineAutoCloseables( tempMapPut(knownVars, varA, new LASValueDescriptor()), tempMapPut(knownVars, varB, new LASValueDescriptor())); finish = () -> new GazelleV_LeftArrowScript.ForPairs(collection, body, varA, varB); } else throw fail("Unknown pattern for 'for' loop"); consume("in"); collection = parseExpr(); if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print("collection", collection); AutoCloseable __2 = enterBody.get(); try { body = parseCurlyBlock(false); return finish.get(); } finally { _close(__2); }} } GazelleV_LeftArrowScript.Evaluable parseIfStatement() { consume("if"); var condition = parseExpr(); var body = parseCurlyBlock(false); GazelleV_LeftArrowScript.Evaluable elseBranch = null; if (is("else")) { consume(); if (is("if")) elseBranch = parseIfStatement(); else elseBranch = parseCurlyBlock(false); } return new GazelleV_LeftArrowScript.IfThen(condition, body, elseBranch); } // declare an external variable with optional type info void addVar(String var) { addVar(var, new LASValueDescriptor()); } void addVar(String var, LASValueDescriptor type) { knownVars.put(var, type); } void addVar(String var, Class type, boolean canBeNull) { addVar(var, new LASValueDescriptor.NonExact(type, canBeNull)); } // short name to full name // TODO: sort by priority if classes appear twice Map<String, String> globalClassNames_cache; Map<String, String> globalClassNames() { if (globalClassNames_cache == null) globalClassNames_cache = globalClassNames_load(); return globalClassNames_cache;} Map<String, String> globalClassNames_load() { var packages = mapToTreeSet(importedPackages(), pkg -> pkg + "."); // add inner classes of function containers var classContainers = classContainerPrefixes(); Map<String, String> out = new HashMap(); for (var className : g22utils.classNameResolver().allFullyQualifiedClassNames()) { if (isAnonymousClassName(className)) continue; if (!contains(className, '$')) { String pkg = longestPrefixInTreeSet(className, packages); if (pkg != null) { String shortName = dropPrefix(pkg, className); if (!shortName.contains(".")) out.put(shortName, className); } } String container = longestPrefixInTreeSet(className, classContainers); if (container != null) out.put(dropPrefix(container, className), className); } return out; } transient IF0<Collection<String>> importedPackages; Collection<String> importedPackages() { return importedPackages != null ? importedPackages.get() : importedPackages_base(); } final Collection<String> importedPackages_fallback(IF0<Collection<String>> _f) { return _f != null ? _f.get() : importedPackages_base(); } Collection<String> importedPackages_base() { return itemPlus("java.lang", standardImports_fullyImportedPackages()); } TreeSet<String> classContainerPrefixes() { return mapToTreeSet(functionContainers, fc -> className(fc) + "$"); } static class UnknownObject extends RuntimeException implements IFieldsToList{ String name; UnknownObject() {} UnknownObject(String name) { this.name = name;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + name + ")"; }public Object[] _fieldsToList() { return new Object[] {name}; } public String getMessage() { return "Unknown object: " + name; } } } // end of GazelleV_LeftArrowScriptParser static class MultiMap<A,B> { Map<A, List<B>> data = new HashMap<A, List<B>>(); int fullSize; MultiMap() {} MultiMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); } MultiMap(MultiMap<A, B> map) { putAll(map); } MultiMap(Map<A, List<B>> data) { this.data = data;} void put(A key, B value) { synchronized(data) { List<B> 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<B> values) { putAll(key, values); } void addAllIfNotThere(A key, Collection<B> 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<A> keys, B value) { synchronized(data) { for (A key : unnullForIteration(keys)) put(key, value); }} void putAll(A key, Collection<B> values) { synchronized(data) { if (nempty(values)) getActual(key).addAll(values); }} void putAll(Iterable<Pair<A, B>> pairs) { synchronized(data) { for (Pair<A, B> p : unnullForIteration(pairs)) put(p.a, p.b); }} void removeAll(A key, Collection<B> values) { synchronized(data) { for (B value : values) remove(key, value); }} List<B> get(A key) { synchronized(data) { List<B> list = data.get(key); return list == null ? Collections.<B> emptyList() : list; }} List<B> getOpt(A key) { synchronized(data) { return data.get(key); }} List<B> getAndClear(A key) { synchronized(data) { List<B> l = cloneList(data.get(key)); remove(key); return l; }} // returns actual mutable live list // creates the list if not there List<B> getActual(A key) { synchronized(data) { List<B> list = data.get(key); if (list == null) data.put(key, list = _makeEmptyList()); return list; }} void clean(A key) { synchronized(data) { List<B> list = data.get(key); if (list != null && list.isEmpty()) { fullSize -= l(list); data.remove(key); } }} Set<A> keySet() { synchronized(data) { return data.keySet(); }} Set<A> keys() { synchronized(data) { return data.keySet(); }} void remove(A key) { synchronized(data) { fullSize -= l(this.getOpt(key)); data.remove(key); }} final void remove(Pair<A, B> p){ removePair(p); } void removePair(Pair<A, B> p) { if (p != null) remove(p.a, p.b); } void remove(A key, B value) { synchronized(data) { List<B> 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<B> list = get(key); return list.isEmpty() ? null : list.get(0); }} void addAll(MultiMap<A, B> map) { putAll(map); } void putAll(MultiMap<A, B> map) { synchronized(data) { for (A key : map.keySet()) putAll(key, map.get(key)); }} void putAll(Map<A, B> map) { synchronized(data) { if (map != null) for (Map.Entry<A, B> 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<A> reverseGet(B b) { synchronized(data) { List<A> l = new ArrayList(); for (A key : data.keySet()) if (data.get(key).contains(b)) l.add(key); return l; }} Map<A, List<B>> asMap() { synchronized(data) { return cloneMap(data); }} boolean isEmpty() { synchronized(data) { return data.isEmpty(); }} // override in subclasses List<B> _makeEmptyList() { return new ArrayList(); } // returns live lists Collection<List<B>> allLists() { synchronized(data) { return new ArrayList(data.values()); } } Collection<List<B>> values() { return allLists(); } List<B> allValues() { return concatLists(data.values()); } Object mutex() { return data; } public String toString() { return "mm" + str(data); } } static class OrError<A> extends Var<A> { 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 <B> OrError<B> 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); } } 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 + "]"; } } static class Lowest<A> { 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<A, Double> pair() { return best == null ? null : new Pair(best, bestScore()); } public String toString() { return "Score " + formatDouble_significant2(score, 4) + ": " + best; } } static class JavaScript extends Var<String> { JavaScript() {} JavaScript(String src) { super(src); } String javaScriptSource() { return get(); } public String toString() { return hjs(get()); } } static class _PrintIndent extends F1<String, Boolean> { 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 interface IResourceLoader { String loadSnippet(String snippetID); String getTranspiled(String snippetID); // with libs int getSnippetType(String snippetID); String getSnippetTitle(String snippetID); File loadLibrary(String snippetID); //ifndef NoJavaXJar default File pathToJavaXJar() { return pathToJavaxJar_noResourceLoader(); } //endifndef // 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<K,V> extends AbstractMap<K,V> implements Map<K,V> { 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<? super K> 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<K> { 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<? super K> q) { super(k, q); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k, ReferenceQueue<? super K> 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<WeakKey,V> hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue<? super K> queue = new ReferenceQueue<K>(); /* 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 <code>WeakHashMap</code> with the given * initial capacity and the given load factor. * * @param initialCapacity the initial capacity of the * <code>WeakHashMap</code> * * @param loadFactor the load factor of the <code>WeakHashMap</code> * * @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<WeakKey,V>(initialCapacity, loadFactor); } /** * Constructs a new, empty <code>WeakHashMap</code> with the given * initial capacity and the default load factor, which is * <code>0.75</code>. * * @param initialCapacity the initial capacity of the * <code>WeakHashMap</code> * * @throws IllegalArgumentException If the initial capacity is less than * zero */ public WeakHasherMap(int initialCapacity) { hash = new HashMap<WeakKey,V>(initialCapacity); } /** * Constructs a new, empty <code>WeakHashMap</code> with the default * capacity and the default load factor, which is <code>0.75</code>. */ public WeakHasherMap() { hash = new HashMap<WeakKey,V>(); } /** * Constructs a new, empty <code>WeakHashMap</code> with the default * capacity and the default load factor, which is <code>0.75</code>. * The <code>WeakHashMap</code> 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<WeakKey,V>(); hasher = h; } /* -- Simple queries -- */ /** * Returns the number of key-value mappings in this map. * <strong>Note:</strong> <em>In contrast to most implementations of the * <code>Map</code> interface, the time required by this operation is * linear in the size of the map.</em> */ /*@Pure*/ @Override public int size() { return entrySet().size(); } /** * Returns <code>true</code> if this map contains no key-value mappings. */ /*@Pure*/ @Override public boolean isEmpty() { return entrySet().isEmpty(); } /** * Returns <code>true</code> 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 <code>key</code>. * If this map does not contain a value for this key, then return * <code>null</code>. * * @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 <code>key</code> maps to the given * <code>value</code>. If the map previously contained a mapping for * <code>key</code> then that mapping is replaced and the previous value is * returned. * * @param key the key that is to be mapped to the given * <code>value</code> * @param value the value to which the given <code>key</code> is to be * mapped * * @return the previous value to which this key was mapped, or * <code>null</code> 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 <code>key</code> 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 <code>null</code> 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<K,V> implements Map.Entry<K,V> { private Map.Entry<WeakKey,V> 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<WeakKey,V> 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<K,V> e /* Object o*/) { // if (! (o instanceof Map.Entry)) return false; // Map.Entry<K,V> e = (Map.Entry<K,V>)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<Map.Entry<K,V>> { Set<Map.Entry<WeakKey,V>> hashEntrySet = hash.entrySet(); @Override public Iterator<Map.Entry<K,V>> iterator() { return new Iterator<Map.Entry<K,V>>() { Iterator<Map.Entry<WeakKey,V>> hashIterator = hashEntrySet.iterator(); Map.Entry<K,V> next = null; @Override public boolean hasNext() { while (hashIterator.hasNext()) { Map.Entry<WeakKey,V> 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<K,V>(ent, k); return true; } return false; } @Override public Map.Entry<K,V> next() { if ((next == null) && !hasNext()) throw new NoSuchElementException(); Map.Entry<K,V> 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<Map.Entry<K,V>> 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<K,V> e = (Map.Entry<K,V>)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<Map.Entry<WeakKey,V>> i = hashEntrySet.iterator(); i.hasNext(); ) { Map.Entry<WeakKey,V> 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<Map.Entry<K,V>> entrySet = null; /** * Returns a <code>Set</code> view of the mappings in this map. */ /*@SideEffectFree*/ @Override public Set<Map.Entry<K,V>> 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 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)); } } 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<MMOPattern> l; And() {} And(List<MMOPattern> 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<MMOPattern> l; Or() {} Or(List<MMOPattern> 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}; } } } // uses HashMap by default static class MultiSet<A> implements IMultiSet<A> { Map<A, Integer> 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<A> c) { addAll(c); } MultiSet(MultiSet<A> 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<A> c) { if (c != null) for (A a : c) add(a); } synchronized void addAll(MultiSet<A> 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<A> topTen() { return getTopTen(); } synchronized List<A> getTopTen() { return getTopTen(10); } synchronized List<A> getTopTen(int maxSize) { List<A> list = getSortedListDescending(); return list.size() > maxSize ? list.subList(0, maxSize) : list; } synchronized List<A> highestFirst() { return getSortedListDescending(); } synchronized List<A> lowestFirst() { return reversedList(getSortedListDescending()); } synchronized List<A> getSortedListDescending() { List<A> list = new ArrayList<A>(map.keySet()); Collections.sort(list, new Comparator<A>() { 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<A> asSet() { return map.keySet(); } synchronized NavigableSet<A> navigableSet() { return navigableKeys((NavigableMap) map); } synchronized Set<A> keySet() { return map.keySet(); } synchronized A getMostPopularEntry() { int max = 0; A a = null; for (Map.Entry<A,Integer> 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<A> mergeWith(MultiSet<A> set) { MultiSet<A> result = new MultiSet<A>(); 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 Map<A, Integer> toMap(){ return asMap(); } synchronized Map<A, Integer> asMap() { return cloneMap(map); } } // A is the type of object being listed by the search static class ScoredSearcher<A> { int maxResults = 1000; List<String> preparedTerms; Map<A, Double> 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<String> fields) { add(object, scoreFields(fields)); } // process fields with weights void putWithWeights(A object, Collection<Pair<String, Double>> fields) { scoredSearch_scoreWeighted2(fields, preparedTerms); } int scoreFields(Collection<String> 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<String> fields) { put(object, fields); } void put(A object, double score) { add(object, score); } void add(A object, double score) { putUnlessZero(scores, object, score); } List<A> get() { return takeFirst(maxResults, keysSortedByValuesDesc(scores)); } List<Scored<A>> withScores() { return map(get(), a -> scoredWithMapValue(scores, a)); } A best() { return keyWithHighestValue(scores); } } static class T3<A, B, C> { 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<A, B, C> 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) + ")"; } } static class Pair<A, B> implements Comparable<Pair<A, B>> { 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<A, B> p) { if (p == null) return 1; int i = ((Comparable<A>) a).compareTo(p.a); if (i != 0) return i; return ((Comparable<B>) b).compareTo(p.b); } } static interface IResourceHolder { <A extends AutoCloseable> A add(A a); Collection<AutoCloseable> takeAll(); } static class HTMLFramer1 { String title; boolean titleIsHTML = false; List<NavItem> navItems = new ArrayList(); List<Object> contents = new ArrayList(); // items will be evaluated to strings upon rendering LinkedHashSet<String> headElements = new LinkedHashSet(); // automatically avoid duplication boolean navBeforeTitle = false; List<Runnable> 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<String> render; String render() { return render != null ? render.get() : render_base(); } final String render_fallback(IF0<String> _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<String> renderTitle; String renderTitle() { return renderTitle != null ? renderTitle.get() : renderTitle_base(); } final String renderTitle_fallback(IF0<String> _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<String> renderNav; String renderNav() { return renderNav != null ? renderNav.get() : renderNav_base(); } final String renderNav_fallback(IF0<String> _f) { return _f != null ? _f.get() : renderNav_base(); } String renderNav_base() { if (empty(navItems)) return ""; return div_vbar(map(__86 -> renderNavItem(__86), 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<String> htmls) { forEach(__87 -> addNavItem(__87), 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<Object> 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<String> l) { forEach(__88 -> add(__88), 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<String> headElements() { return headElements; } } static class HCRUD_Data { Map<String, Renderer> renderers = new HashMap(); Map<String, String> fieldHelp = new HashMap(); Object currentValue; // current field value held temporarily boolean humanizeFieldNames = true; Map<String, String> rawFormValues; // temporary map with raw form values abstract static class Renderer { String metaInfo; transient IF1<Object, Object> preprocessValue; Object preprocessValue(Object value) { return preprocessValue != null ? preprocessValue.get(value) : preprocessValue_base(value); } final Object preprocessValue_fallback(IF1<Object, Object> _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<Object, String> valueToEntry; String valueToEntry(Object value) { return valueToEntry != null ? valueToEntry.get(value) : valueToEntry_base(value); } final String valueToEntry_fallback(IF1<Object, String> _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<String> entries; ComboBox() {} ComboBox(List<String> 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<String> entries) { this(entries); this.editable = editable; } // star constructor syntax not working here (transpiler bug) ComboBox(List<String> entries, IF1<Object, String> 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<Map<String, Object>> list() { return null; } List<Map<String, Object>> list(IntRange range) { return subListOrFull(list(), range); } String idField() { return "id"; } Map<String, Object> emptyObject() { return null; } Map<String, Object> getObject(Object id) { return null; } Map<String, Object> getObjectForEdit(Object id) { return getObject(id); } transient IF1<Object, Map<String, Object>> getObjectForDuplication; Map<String, Object> getObjectForDuplication(Object id) { return getObjectForDuplication != null ? getObjectForDuplication.get(id) : getObjectForDuplication_base(id); } final Map<String, Object> getObjectForDuplication_fallback(IF1<Object, Map<String, Object>> _f, Object id) { return _f != null ? _f.get(id) : getObjectForDuplication_base(id); } Map<String, Object> getObjectForDuplication_base(Object id) { return getObject(id); } // return ID Object createObject(Map<String, String> fullMap, String fieldPrefix) { throw unimplemented(); } // return text msg String deleteObject(Object id) { throw unimplemented(); } boolean objectCanBeDeleted(Object id) { return true; } transient IF1<Object, Boolean> objectCanBeEdited; boolean objectCanBeEdited(Object id) { return objectCanBeEdited != null ? objectCanBeEdited.get(id) : objectCanBeEdited_base(id); } final boolean objectCanBeEdited_fallback(IF1<Object, Boolean> _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<String, String> 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<String> filteredFields() { return null; } List<String> 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<String, Boolean> defaultSortField() { return null; } abstract class Item extends AbstractMap<String, Object> { Object id; Map<String, Object> fullMap; Item(Object id) { this.id = id;} abstract Map<String, Object> calcFullMap(); Map<String, Object> fullMap() { if (fullMap == null) fullMap = calcFullMap(); return fullMap; } public int size() { return l(fullMap()); } public Set<Map.Entry<String, Object>> 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 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 interface IConceptCounter { Class<? extends Concept> conceptClass(); int countConcepts(); Collection<Concept> 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 + "]"; } } interface ChangeTriggerable { public void change(); } 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>")) // HTML labels make super-huge, confusing tool tips setToolTipText(nullIfEmpty(text)); } } // moved outside of class for GC reasons (see above) static VF1<JPopupMenu> BetterLabel_menuItems(final WeakReference<BetterLabel> me) { return new VF1<JPopupMenu>() { 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<A, B> extends LinkedHashMap<A, B> { 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; } } // see https://howtodoinjava.com/java/regex/java-regex-validate-international-phone-numbers/ static class InternationalPhoneValidator { String rawInput; String filtered; List<CountryDialCode> 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() { try { filtered = dropPrefix("+", dropSpecialChars(rawInput)); if (!isAllDigits(filtered)) { error = "Bad characters"; return; } splitLocal(); } catch (Exception __e) { throw rethrow(__e); } } 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; } } /* * 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<String> { 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; } RuntimeException asRuntimeException() { return new Fail(this); } } // Describes a value during script optimization/type inference. // In the best case, the exact value is known. // Next best case is we're knowing its precise type (value of object.getClass()). // Third best case is we're knowing it's castable to a certain type. // A completely unknown value is described simply by "new LASValueDescriptor". static class LASValueDescriptor { LASValueDescriptor() {} boolean knownValue() { return false; } Object value() { return null; } Class javaClass() { return null; } boolean javaClassIsExact() { return false; } boolean canBeNull() { return true; } boolean canFail() { return false; } boolean willFail() { return false; } static class Exact extends LASValueDescriptor implements IFieldsToList{ Class c; boolean canBeNull = false; Exact() {} Exact(Class c, boolean canBeNull) { this.canBeNull = canBeNull; this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + canBeNull + ")"; } public boolean equals(Object o) { if (!(o instanceof Exact)) return false; Exact __1 = (Exact) o; return eq(c, __1.c) && eq(canBeNull, __1.canBeNull); } public int hashCode() { int h = 67394271; h = boostHashCombine(h, _hashCode(c)); h = boostHashCombine(h, _hashCode(canBeNull)); return h; } public Object[] _fieldsToList() { return new Object[] {c, canBeNull}; } Class javaClass() { return c; } boolean javaClassIsExact() { return true; } boolean canBeNull() { return canBeNull; } } static class NonExact extends LASValueDescriptor implements IFieldsToList{ Class c; boolean canBeNull = false; NonExact() {} NonExact(Class c, boolean canBeNull) { this.canBeNull = canBeNull; this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + canBeNull + ")"; } public boolean equals(Object o) { if (!(o instanceof NonExact)) return false; NonExact __2 = (NonExact) o; return eq(c, __2.c) && eq(canBeNull, __2.canBeNull); } public int hashCode() { int h = 1445514322; h = boostHashCombine(h, _hashCode(c)); h = boostHashCombine(h, _hashCode(canBeNull)); return h; } public Object[] _fieldsToList() { return new Object[] {c, canBeNull}; } Class javaClass() { return c; } boolean javaClassIsExact() { return false; } boolean canBeNull() { return canBeNull; } } static class KnownValue extends LASValueDescriptor implements IFieldsToList{ Object value; KnownValue() {} KnownValue(Object value) { this.value = value;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + value + ")"; } public boolean equals(Object o) { if (!(o instanceof KnownValue)) return false; KnownValue __3 = (KnownValue) o; return eq(value, __3.value); } public int hashCode() { int h = -1456305138; h = boostHashCombine(h, _hashCode(value)); return h; } public Object[] _fieldsToList() { return new Object[] {value}; } boolean knownValue() { return true; } Object value() { return value; } Class javaClass() { return value == null ? null : value.getClass(); } boolean javaClassIsExact() { return value != null; } boolean canBeNull() { return value == null; } } // Indicates an exception can be thrown instead of returning a value. // Probably we don't need this. static class WillFail extends LASValueDescriptor { boolean canFail() { return true; } boolean willFail() { return true; } } static LASValueDescriptor fromClass(Class c) { return new NonExact(c, true); } } interface Enterable { AutoCloseable enter(); } static class TokenRangeWithSrc extends TokenRange { List<String> tok; TokenRangeWithSrc() {} TokenRangeWithSrc(List<String> tok, int start) { this.start = start; end = start; } TokenRangeWithSrc(List<String> tok, int start, int end) { this.end = end; this.start = start;} TokenRangeWithSrc(ListAndIndex<String> startPtr, ListAndIndex<String> endPtr) { assertSame(tok = startPtr.list(), endPtr.list()); start = startPtr.idx(); end = endPtr.idx(); } ListAndIndex<String> startPtr() { return new ListAndIndex(tok, start); } ListAndIndex<String> endPtr() { return new ListAndIndex(tok, end); } LineAndColumn startLineAndCol() { return tokenToLineAndColumn(startPtr()); } LineAndColumn endLineAndCol() { return tokenToLineAndColumn(endPtr()); } public String toString() { var start = startLineAndCol(); if (eq(start, end)) return str(start); return start + " to " + endLineAndCol(); } } static interface IMultiSet<A> { // returns new count int add(A key); /*void addAll(Iterable<A> c) { if (c != null) for (A a : c) add(a); } void addAll(MultiSet<A> 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<A> topTen(); synchronized List<A> getTopTen() { ret getTopTen(10); } synchronized List<A> getTopTen(int maxSize) { List<A> list = getSortedListDescending(); return list.size() > maxSize ? list.subList(0, maxSize) : list; } synchronized L<A> highestFirst() { ret getSortedListDescending(); } synchronized L<A> lowestFirst() { ret reversedList(getSortedListDescending()); } synchronized L<A> getSortedListDescending() { List<A> list = new ArrayList<A>(map.keySet()); Collections.sort(list, new Comparator<A>() { 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<A> asSet() { return map.keySet(); } synchronized NavigableSet<A> navigableSet() { return navigableKeys((NavigableMap) map); } synchronized Set<A> keySet() { return map.keySet(); } synchronized A getMostPopularEntry() { int max = 0; A a = null; for (Map.Entry<A,Integer> 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<A> mergeWith(MultiSet<A> set) { MultiSet<A> result = new MultiSet<A>(); 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<A, Int> asMap() { ret cloneMap(map); }*/ } // uses hash sets as inner sets unless subclassed // uses a hash map as the outer map by default static class MultiSetMap<A, B> { Map<A, Set<B>> data = new HashMap<A, Set<B>>(); int size; // number of values MultiSetMap() {} MultiSetMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); } MultiSetMap(MultiSetMap<A, B> map) { putAll(map); } MultiSetMap(Map<A, Set<B>> data) { this.data = data;} boolean put(A key, B value) { synchronized(data) { Set<B> 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<B> values) { synchronized(data) { putAll(key, values); }} void addAllIfNotThere(A key, Collection<B> 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<B> values) { synchronized(data) { for (B value : values) put(key, value); }} void removeAll(A key, Collection<B> values) { synchronized(data) { for (B value : values) remove(key, value); }} Set<B> get(A key) { synchronized(data) { Set<B> set = data.get(key); return set == null ? Collections.<B> emptySet() : set; }} List<B> getAndClear(A key) { synchronized(data) { List<B> l = cloneList(data.get(key)); remove(key); return l; }} // return null if empty Set<B> getOpt(A key) { synchronized(data) { return data.get(key); }} // returns actual mutable live set // creates the set if not there Set<B> getActual(A key) { synchronized(data) { Set<B> 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<B> list = data.get(key); if (list != null && list.isEmpty()) data.remove(key); }} Set<A> keySet() { synchronized(data) { return data.keySet(); }} Set<A> 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<B> 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<A, B> map) { putAll(map); } void putAll(MultiSetMap<A, B> map) { synchronized(data) { for (A key : map.keySet()) putAll(key, map.get(key)); }} void putAll(Map<A, B> map) { synchronized(data) { if (map != null) for (Map.Entry<A, B> 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<A> reverseGet(B b) { synchronized(data) { Set<A> 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<A, Set<B>> asMap() { synchronized(data) { return cloneMap(data); }} boolean isEmpty() { synchronized(data) { return data.isEmpty(); }} // override in subclasses Set<B> _makeEmptySet() { return new HashSet(); } Collection<Set<B>> allLists() { synchronized(data) { return new HashSet(data.values()); } } List<B> allValues() { return concatLists(values(data)); } List<Pair<A, B>> allEntries() { synchronized(data) { List<Pair<A, B>> l = emptyList(size); for (Map.Entry<? extends A, ? extends Set<B>> __0 : _entrySet( data)) { A a = __0.getKey(); Set<B> 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<A, B> firstEntry() { synchronized(data) { if (empty(data)) return null; Map.Entry<A, Set<B>> 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); }} } interface IGetterWithNotify<A> extends IF0<A>, IHasChangeListeners { } static interface ValueConverterForField { // convert value "value" for storage in "field" of "object" // can return null in case of unknown conversion OrError<Object> convertValue(Object object, Field field, Object value); } // Some of the functions are dependent on the concepts field, // others are global. static class G22Utils implements AutoCloseable { final public G22Utils setBackgroundProcessesUI(BackgroundProcessesUI backgroundProcessesUI){ return backgroundProcessesUI(backgroundProcessesUI); } public G22Utils backgroundProcessesUI(BackgroundProcessesUI backgroundProcessesUI) { this.backgroundProcessesUI = backgroundProcessesUI; return this; } final public BackgroundProcessesUI getBackgroundProcessesUI(){ return backgroundProcessesUI(); } public BackgroundProcessesUI backgroundProcessesUI() { return backgroundProcessesUI; } BackgroundProcessesUI backgroundProcessesUI; final public G22Utils setModule(Enterable module){ return module(module); } public G22Utils module(Enterable module) { this.module = module; return this; } final public Enterable getModule(){ return module(); } public Enterable module() { return module; } Enterable module; final public G22Utils setMasterStuff(G22MasterStuff masterStuff){ return masterStuff(masterStuff); } public G22Utils masterStuff(G22MasterStuff masterStuff) { this.masterStuff = masterStuff; return this; } final public G22MasterStuff getMasterStuff(){ return masterStuff(); } public G22MasterStuff masterStuff() { return masterStuff; } G22MasterStuff masterStuff; final public G22Utils setConcepts(Concepts concepts){ return concepts(concepts); } public G22Utils concepts(Concepts concepts) { this.concepts = concepts; return this; } final public Concepts getConcepts(){ return concepts(); } public Concepts concepts() { return concepts; } Concepts concepts; final public G22Utils setProjectActions(G22ProjectActions projectActions){ return projectActions(projectActions); } public G22Utils projectActions(G22ProjectActions projectActions) { this.projectActions = projectActions; return this; } final public G22ProjectActions getProjectActions(){ return projectActions(); } public G22ProjectActions projectActions() { return projectActions; } G22ProjectActions projectActions; final public G22AutoStarter getAutoStarter(){ return autoStarter(); } public G22AutoStarter autoStarter() { return autoStarter; } G22AutoStarter autoStarter = new G22AutoStarter(this); final public CombinedStringifier getStringifier(){ return stringifier(); } public CombinedStringifier stringifier() { return stringifier; } CombinedStringifier stringifier = new CombinedStringifier( o -> o instanceof BufferedImage ? "Image (" + ((BufferedImage) o).getWidth() + "*" + ((BufferedImage) o).getHeight() + " px)" : null ); ImageSurface stdImageSurface() { var is = pixelatedImageSurface().setAutoZoomToDisplay(true).repaintInThread(false); is.defaultImageDir = () -> dbDir(); is.specialPurposed = true; new ImageSurface_PositionToolTip(is); return is; } ImageSurface stdImageSurface(MakesBufferedImage img) { return stdImageSurface(toBufferedImage(img)); } ImageSurface stdImageSurface(BufferedImage img) { var is = stdImageSurface(); is.setImage(img); return is; } String stringify(Object o) { return stringifier.toString(o); } transient Set<IVF1<GazelleV_LeftArrowScriptParser>> onSettingUpParser; public G22Utils onSettingUpParser(IVF1<GazelleV_LeftArrowScriptParser> f) { onSettingUpParser = createOrAddToSyncLinkedHashSet(onSettingUpParser, f); return this; } public G22Utils removeSettingUpParserListener(IVF1<GazelleV_LeftArrowScriptParser> f) { main.remove(onSettingUpParser, f); return this; } public void settingUpParser(GazelleV_LeftArrowScriptParser parser) { pcallFAll(onSettingUpParser, parser); } transient Set<IVF1<JLeftArrowScriptIDE>> onSettingUpScriptIDE; public G22Utils onSettingUpScriptIDE(IVF1<JLeftArrowScriptIDE> f) { onSettingUpScriptIDE = createOrAddToSyncLinkedHashSet(onSettingUpScriptIDE, f); return this; } public G22Utils removeSettingUpScriptIDEListener(IVF1<JLeftArrowScriptIDE> f) { main.remove(onSettingUpScriptIDE, f); return this; } public void settingUpScriptIDE(JLeftArrowScriptIDE ide) { pcallFAll(onSettingUpScriptIDE, ide); } GazelleV_LeftArrowScriptParser leftArrowParser() { GazelleV_LeftArrowScriptParser parser = new GazelleV_LeftArrowScriptParser(); parser.g22utils(this); settingUpParser(parser); return parser; } void basicParserTest() { var parser = leftArrowParser(); print("classContainerPrefixes" , parser.classContainerPrefixes()); assertEquals(pair(1, 2), parser.parse("new Pair 1 2").get()); } JLeftArrowScriptIDE leftArrowIDE() { JLeftArrowScriptIDE ide = new JLeftArrowScriptIDE(); ide.g22utils(this); settingUpScriptIDE(ide); return ide; } File byteCodePath() { return assertNotNull(getBytecodePathForClass(this)); } ClassNameResolver classNameResolver_cache; ClassNameResolver classNameResolver() { if (classNameResolver_cache == null) classNameResolver_cache = classNameResolver_load(); return classNameResolver_cache;} ClassNameResolver classNameResolver_load() { return new ClassNameResolver().byteCodePath(byteCodePath()).init(); } File databasesMotherDir() { return javaxDataDir("Gazelle-22"); } AutoCloseable enter() { return module == null ? null : module.enter(); } String defaultDBName() { return "Default"; } File lastOpenedDBsFile() { return newFile(databasesMotherDir(), "Last Opened"); } File autoUpdateFile() { return newFile(databasesMotherDir(), "Auto-Update"); } boolean autoUpdateEnabled() { return fileExists(autoUpdateFile()); } void setAutoUpdate(boolean b) { createOrRemoveFile(autoUpdateFile(), b); } List<String> dbsToOpen() { List<String> dbNames = new ArrayList(); for (String name : tlft(loadTextFile(lastOpenedDBsFile()))) if (fileExists(newFile(databasesMotherDir(), name))) dbNames.add(name); if (empty(dbNames)) dbNames.add(defaultDBName()); return dbNames; } void setOpenDBs(Collection<File> dbDirs) { List<String> dbNames = new ArrayList(); for (File dbDir : dbDirs) if (sameFile(databasesMotherDir(), dirOfFile(dbDir))) dbNames.add(fileName(dbDir)); saveTextFile(lastOpenedDBsFile(), lines(dbNames)); } Map<String, Object> scriptToMap(G22LeftArrowScript c) { return scriptToMap(c, false); } Map<String, Object> scriptToMap(G22LeftArrowScript c, boolean allowRunOnProjectOpen) { return litorderedmap( "Description" , str(c), "Status" , renderScriptStatus(c), "LoC" , renderScriptLoC(c), "Run on project open" , allowRunOnProjectOpen && c.runOnProjectOpen ? "Yes" + appendBracketed(c.runOrder) : null); } String renderScriptStatus(G22LeftArrowScript c) { return or2_rev("Empty", joinNemptiesWithSpacedPlus( c.isClearForAutoRun() ? "Clear for auto-run" : null, c.isSavedDistinctFromAutoRunVersion() ? "Saved (not cleared)" : null, c.isEditing() ? "Editing" : null )); } String renderScriptLoC(G22LeftArrowScript c) { return n2(intMax(mapLL(__89 -> linesOfCode_javaTok(__89), c.editingText, c.text, c.codeForAutoRun()))); } // e.g. for an image file List<G22Label> labelsForFile(File file) { if (file == null) return null; File labelsFile = appendToFileName(file, ".labels"); List<String> labels = tlft(loadTextFile(labelsFile)); return map(__90 -> getLabel(__90), labels); } File labelsFile(File file) { if (file == null) return null; return appendToFileName(file, ".labels"); } void setLabelsForFile(File file, List<G22Label> labels) { List<String> list = map(labels, label -> label.name); File f = labelsFile(file); saveTextFile(f, lines(list)); print("Saved " + nLabels(list) + " (" + joinWithComma(list) + ") to " + f); } G22Label getLabel(String name) { if (empty(name)) return null; if (containsNewLine(name)) throw fail("No newlines in label names allowed: " + name); return uniqCI(concepts, G22Label.class, "name", name); } File dbDir() { return conceptsDir(concepts); } File fileInDbDir(String name) { return newFile(dbDir(), name); } class GazelleDB implements IFieldsToList{ String name; File dir; GazelleDB() {} GazelleDB(String name, File dir) { this.dir = dir; this.name = name;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + name + ", " + dir + ")"; } public boolean equals(Object o) { if (!(o instanceof GazelleDB)) return false; GazelleDB __0 = (GazelleDB) o; return eq(name, __0.name) && eq(dir, __0.dir); } public int hashCode() { int h = 1669530526; h = boostHashCombine(h, _hashCode(name)); h = boostHashCombine(h, _hashCode(dir)); return h; } public Object[] _fieldsToList() { return new Object[] {name, dir}; } Boolean loaded_cache; boolean loaded() { if (loaded_cache == null) loaded_cache = loaded_load(); return loaded_cache;} Boolean loaded_load() { return containsFile(masterStuff.openConceptDirs(), dir); } File conceptsFile() { return conceptsFileIn(dir); } } List<GazelleDB> gazelleDBs() { List<GazelleDB> dbs = new ArrayList(); for (File dir : listDirsContainingFileNamed(databasesMotherDir(), "concepts.structure.gz")) dbs.add(new GazelleDB(fileName(dir), dir)); return dbs; } RSyntaxTextAreaWithSearch newSyntaxTextArea() { return newSyntaxTextArea(null); } RSyntaxTextAreaWithSearch newSyntaxTextArea(IF1<JComponent, JComponent> wrapStatusLabel) { RSyntaxTextAreaWithSearch ta = new RSyntaxTextAreaWithSearch(wrapStatusLabel); ta.textArea().setHighlightCurrentLine(false); ta.menuLessOperation(); return ta; } File projectStoryTextFile() { return newFile(dbDir(), "story.txt"); } String projectName() { return fileName(dbDir()); } public void close() { try { autoStarter.close(); } catch (Exception __e) { throw rethrow(__e); } } } static interface IContentsIndexedList<A> extends List<A> { int[] indicesOf(A o); } static class Q implements AutoCloseable { String name = "Unnamed Queue"; List<Runnable> q = synchroLinkedList(); ReliableSingleThread rst = new ReliableSingleThread(new Runnable() { public void run() { try { _run() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_run()"; }}); final public boolean getRetired(){ return retired(); } public boolean retired() { return retired; } volatile boolean retired = false; final public Runnable getCurrentJob(){ return currentJob(); } public Runnable currentJob() { return currentJob; } volatile Runnable currentJob; AtomicLong jobsDone = new AtomicLong(); Q() {} Q(String name) { this.name = name;} void add(Runnable r) { assertNotRetired(); q.add(r); _trigger(); } void addInFront(Runnable r) { assertNotRetired(); 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) { currentJob = r; inc(jobsDone); try { r.run(); } catch (Throwable __e) { printStackTrace(__e); } currentJob = null; } 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<Runnable> snapshot() { return cloneList(q); } // override this void onIdle() {} boolean busy() { return currentJob != null; } void assertNotRetired() { assertFalse("Queue is retired", retired()); } boolean hasThread() { return rst.hasThread(); } long nJobsDone() { return jobsDone.get(); } public String toString() { return (retired ? "Retired " : "") + "Q " + systemHashCodeHex(this) + " (" + (isEmpty() ? "empty" : nEntries(size()) + ", current job: " + currentJob) + ")"; } } abstract static class ConceptFieldIndexBase<A extends Concept, Val> implements IConceptIndex, IFieldIndex<A, Val>, IConceptCounter, AutoCloseable { Concepts concepts; Class<A> cc; String field; Map<A, Val> objectToValue = syncHashMap(); MultiSetMap<Val, A> 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<A> cc, String field) { this(db_mainConcepts(), cc, field); } ConceptFieldIndexBase(Concepts concepts, Class<A> 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<A> 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<A> getAll(Val value) { return valueToObject.get(value); } public synchronized List<Val> allValues() { return cloneKeys_noSync(valueToObject.data); } public IterableIterator<A> objectIterator() { return navigableMultiSetMapValuesIterator_concurrent(valueToObject, this); } public synchronized MultiSet<Val> allValues_multiSet() { return multiSetMapToMultiSet(valueToObject); } public Class<? extends Concept> conceptClass() { return cc; } public int countConcepts() { return l(objectToValue); } public Collection<Concept> allConcepts() { return (Collection) keys(objectToValue); } Object mutex() { return this; } public void close() { try { concepts.removeConceptIndex(this); } catch (Exception __e) { throw rethrow(__e); } } } interface IHasChangeListeners { IHasChangeListeners onChange(Runnable r); IHasChangeListeners removeChangeListener(Runnable r); default IHasChangeListeners onChangeAndNow(Runnable l) { onChange(l); callF(l); return 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 RestartableCountdown implements AutoCloseable { java.util.Timer timer; long targetTime; // in sys time long /*firings,*/ totalSleepTime; // stats synchronized void setTargetTime(long targetTime, Runnable action) { if (targetTime <= 0) stop(); else if (targetTime != this.targetTime) { start(targetTime-sysNow(), action); this.targetTime = targetTime; } } // stops the countdown and restarts it synchronized void start(long delayMS, Object action) { stop(); if (delayMS <= 0) { startThread(new Runnable() { public void run() { try { callF(action); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(action);"; }}); } else { totalSleepTime += delayMS; timer = doLater_daemon(delayMS, action); targetTime = sysNow()+delayMS; } } void start(double delaySeconds, Object action) { start(toMS(delaySeconds), action); } synchronized void stop() { cancelTimer(timer); timer = null; targetTime = 0; } public void close() { stop(); } } static class SimpleLeftToRightParser extends Meta { String text; List<String> tok; final public ListAndIndex<String> getPtr(){ return ptr(); } public ListAndIndex<String> ptr() { return ptr; } ListAndIndex<String> ptr; ListAndIndex<String> mainLoopPtr; String currentToken; boolean caseInsensitive = false; List warnings = new ArrayList(); SimpleLeftToRightParser() {} SimpleLeftToRightParser(String text) { this.text = text;} SimpleLeftToRightParser(List<String> tok) { this.tok = tok;} final String token(){ return t(); } String t() { return currentToken; } String token(int i) { return get(tok, ptr.idx()+i*2); } final String consume(){ return next(); } final String tpp(){ return next(); } String next() { var t = t(); next(1); return t; } String lastSpace() { return get(tok, ptr.idx()-1); } String nextSpace() { return get(tok, ptr.idx()+1); } final boolean eqTok(String a, String b){ return tokEq(a, b); } boolean tokEq(String a, String b) { return eqOrEqic(caseInsensitive, a, b); } boolean tokEqOneOf(String a, String... l) { return any(l, b -> tokEq(a, b)); } boolean is(String t) { return tokEq(currentToken, t); } boolean was(String t) { return tokEq(token(-1), t); } boolean isOneOf(String... tokens) { return tokEqOneOf(currentToken, tokens); } boolean isInteger() { return isInteger(t()); } boolean isInteger(String s) { return main.isInteger(s); } boolean isIdentifier() { return isIdentifier(t()); } boolean isIdentifier(String s) { return main.isIdentifier(s); } String consumeIdentifier() { return assertIdentifier(consume()); } void consume(String token) { if (!is(token)) throw fail("Expected " + quote(token) + ", got " + quote(token())); consume(); } String consumeOneOf(String... tokens) { if (!isOneOf(tokens)) throw fail("Expected one of " + asList(tokens)); return consume(); } void ptr(ListAndIndex<String> ptr) { this.ptr = ptr; fetch(); } int idx() { return ptr.idx(); } int lTok() { return l(tok); } int nRemainingTokens() { return (lTok()-idx())/2; } final boolean endOfText(){ return atEnd(); } boolean atEnd() { return ptr.atEnd(); } void fetch() { currentToken = ptr.get(); } boolean lineBreak() { return containsLineBreak(get(tok, ptr.idx()-1)); } boolean atEndOrLineBreak() { return atEnd() || lineBreak(); } void init() { if (tok == null) tok = javaTok(text); if (ptr == null) ptr(new ListAndIndex(tok, 1)); } boolean mainLoop() { init(); if (atEnd()) return false; if (eq(mainLoopPtr, ptr)) throw fail("main loop didn't advance (current token: " + quote(token()) + ")"); mainLoopPtr = ptr; return true; } void unknownToken() { warn("Unknown token: " + t()); } void warn(String msg) { warnings.add(print(msg)); } void next(int n) { ptr(min(lTok(), ptr.idx()+n*2)); } // if i points to an N token, it is incremented void ptr(int i) { ptr(new ListAndIndex(tok, min(i | 1, l(tok)))); } LineAndColumn lineAndColumn() { return tokenToLineAndColumn(ptr); } String consumeUntilSpaceOr(IF0<Boolean> pred) { int i = idx(); do next(); while (!atEnd() && empty(lastSpace()) && !pred.get()); return joinSubList(tok, i, idx()-1); } void setText(String text) { this.text = text; tok = null; ptr = null; } int relativeIndexOf(String token) { int n = nRemainingTokens(); for (int i = 0; i < n; i++) if (eqTok(token(i), token)) return i; return -1; } } static class HTMLPaginator { String startParam = "start"; String baseLink; int start, step = 50; int max; // number of items in list void processParams(Map<String, String> 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 interface Htmlable { default String headStuff() { return ""; } String html(); } 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(() -> { // 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); } }); } 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); } } static class PtInComponent<A extends Component> implements IFieldsToList{ A component; Pt p; PtInComponent() {} PtInComponent(A component, Pt p) { this.p = p; this.component = component;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + component + ", " + p + ")"; } public boolean equals(Object o) { if (!(o instanceof PtInComponent)) return false; PtInComponent __1 = (PtInComponent) o; return eq(component, __1.component) && eq(p, __1.p); } public int hashCode() { int h = -1774729068; h = boostHashCombine(h, _hashCode(component)); h = boostHashCombine(h, _hashCode(p)); return h; } public Object[] _fieldsToList() { return new Object[] {component, p}; } PtInComponent(Pt p, A component) { this.component = component; this.p = p;} } /** * 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. * <P> * <B>Implementation note:</B> The default number of items to display * at a time is 15, and the default scrolling interval is 125 milliseconds. * <P> * * @version 1.5.0 04/05/12 * @author Darryl * https://tips4java.wordpress.com/2009/02/01/menu-scroller/ */ static class JMenuScroller { VF1<JPopupMenu> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 <code>JMenuScroller</code> 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 * <code>null</code> 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 <code>-1</code> 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 <code>dispose</code> 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 MinimalChain<A> implements Iterable<A> { A element; MinimalChain<A> next; MinimalChain() {} MinimalChain(A element) { this.element = element;} MinimalChain(A element, MinimalChain<A> next) { this.next = next; this.element = element;} public String toString() { return str(toList()); } ArrayList<A> toList() { ArrayList<A> l = new ArrayList(); MinimalChain<A> c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } void setElement(A a) { element = a; } void setNext(MinimalChain<A> next) { this.next = next; } // TODO: optimize public Iterator<A> iterator() { return toList().iterator(); } A get() { return element; } } static interface ISetter<A> { void set(A a); } static interface IAutoCloseableF0<A> extends IF0<A>, AutoCloseable {} 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<String> msgs, Object... __) { String anchor = (String) (optPar("anchor", __)); Map<String, String> params = (Map<String, String>) (optPar("params", __)); return hrefresh(addAnchorToURL(appendQueryToURL(baseLink, mapPlus(params, "msg" , htmlEncode_nlToBr(lines_rtrim(msgs)))), anchor)); } String renderMsgs(Map<String, String> params) { return pUnlessEmpty(params.get("msg")); } } abstract static class RandomAccessAbstractList<A> extends AbstractList<A> implements RandomAccess { } static class Not<A> 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)); } } static interface IContentsIndexedList2<A> extends List<A> { TreeSet<HasIndex> indicesOf_treeSetOfHasIndex(A o); } interface IHasTokenRangeWithSrc { void setTokenRangeWithSrc(TokenRangeWithSrc src); TokenRangeWithSrc tokenRangeWithSrc(); } // See GazelleV_LeftArrowScriptParser // TODO: decide whether we allow calling methods/getting fields on // a null reference (just returning null), or whether we throw a // NullPointerException. Currently we just return null. Probably // that's pretty cool. Null propagation as a default, just like in // JavaX. Just more automatic! static class GazelleV_LeftArrowScript { // Base = any script element with a reference to its source code abstract static class Base implements IHasTokenRangeWithSrc { TokenRangeWithSrc src; public void setTokenRangeWithSrc(TokenRangeWithSrc src) { this.src = src; } public TokenRangeWithSrc tokenRangeWithSrc() { return src; } RuntimeException rethrowWithSrc(Throwable e) { if (src != null) throw rethrowAndAppendToMessage(e, squareBracketed(str(src))); else throw rethrow(e); } } // Evaluable = a script element that can be evaluated interface Evaluable extends IF0 { public default Object get() { return get(new VarContext()); } public Object get(VarContext ctx); public default LASValueDescriptor returnType() { return null; } public default Evaluable optimize() { return this; } } // Base + Evaluable + explicitly stored return type abstract static class BaseEvaluable extends Base implements Evaluable { final public BaseEvaluable setReturnType(LASValueDescriptor returnType){ return returnType(returnType); } public BaseEvaluable returnType(LASValueDescriptor returnType) { this.returnType = returnType; return this; } final public LASValueDescriptor getReturnType(){ return returnType(); } public LASValueDescriptor returnType() { return returnType; } LASValueDescriptor returnType; } /*interface Cmd { // returns true if a return was issued public bool run(VarContext ctx default new); }*/ static AtomicLong scriptIDCounter = new AtomicLong(); static long scriptID() { return incAtomicLong(scriptIDCounter); } static class Script extends Base implements Evaluable { long id = scriptID(); // just for printing Map<String, FunctionDef> functionDefs; Evaluable[] steps; public Object get(VarContext ctx) { Object result = null; for (var step : steps) { ping(); result = step.get(ctx); // exiting from anything? var exiting = ctx.exitFromScript; if (exiting != null) { // we're the exit point if (exiting == this) ctx.exitFromScript = null; // exit further break; } } return result; } String toStringLong() { return pnlToLines(steps); } public String toString() { return "Script " + n2(id); } FunctionDef getFunction(String name) { return mapGet(functionDefs, name); } } // end of Script static class FunctionDef extends Base implements IFieldsToList{ String name; List<String> args; Evaluable body; FunctionDef() {} FunctionDef(String name, List<String> args, Evaluable body) { this.body = body; this.args = args; this.name = name;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + name + ", " + args + ", " + body + ")"; }public Object[] _fieldsToList() { return new Object[] {name, args, body}; } public Object call(VarContext ctx, Object... args) { var ctx2 = new VarContext(ctx); int n = min(l(args), l(this.args)); for (int i = 0; i < n; i++) ctx2.put(this.args.get(i), args[i]); return body.get(ctx2); } } static class Assignment extends Base implements Evaluable , IFieldsToList{ String var; Evaluable expression; Assignment() {} Assignment(String var, Evaluable expression) { this.expression = expression; this.var = var;}public Object[] _fieldsToList() { return new Object[] {var, expression}; } public Object get(VarContext ctx) { Object o = expression.get(ctx); ctx.set(var, o); return o; } public String toString() { return var + " <- " + expression; } } static class NewObject extends Base implements Evaluable { NewObject() {} Class c; Evaluable[] args; NewObject(Class c) { this.c = c;} NewObject(Class c, Evaluable[] args) { this.args = args; this.c = c;} public Object get(VarContext ctx) { return preciseNuObject(c, mapToArrayOrNull(args, arg -> arg.get(ctx))); } public String toString() { return "new " + formatFunctionCall(className(c), args); } } static class CallFunction extends Base implements Evaluable , IFieldsToList{ FunctionDef f; Evaluable[] args; CallFunction() {} CallFunction(FunctionDef f, Evaluable[] args) { this.args = args; this.f = f;}public Object[] _fieldsToList() { return new Object[] {f, args}; } public Object get(VarContext ctx) { //ping(); return f.call(ctx, mapToArrayOrNull(args, a -> a.get(ctx))); } public String toString() { return formatFunctionCall(f.name, args); } } static class GetVar extends BaseEvaluable implements IFieldsToList{ String var; GetVar() {} GetVar(String var) { this.var = var;}public Object[] _fieldsToList() { return new Object[] {var}; } public Object get(VarContext ctx) { return ctx.get(var); } public String toString() { return var; } } static class Const extends Base implements Evaluable , IFieldsToList{ Object value; Const() {} Const(Object value) { this.value = value;}public Object[] _fieldsToList() { return new Object[] {value}; } public Object get(VarContext ctx) { return value; } public String toString() { return strOrClassName(value); } public LASValueDescriptor returnType() { return new LASValueDescriptor.KnownValue(value); } } static class GetStaticField extends Base implements Evaluable , IFieldsToList{ Field field; GetStaticField() {} GetStaticField(Field field) { this.field = field;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + field + ")"; }public Object[] _fieldsToList() { return new Object[] {field}; } public Object get(VarContext ctx) { try { return field.get(null); } catch (Exception __e) { throw rethrow(__e); } } } static class CallMethodOrGetField extends Base implements Evaluable , IFieldsToList{ Evaluable target; String name; CallMethodOrGetField() {} CallMethodOrGetField(Evaluable target, String name) { this.name = name; this.target = target;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + target + ", " + name + ")"; }public Object[] _fieldsToList() { return new Object[] {target, name}; } public Object get(VarContext ctx) { try { Object object = target.get(ctx); if (object == null) return null; // throw new NullPointerException(); // could optimize more for sure if (canCallWithVarargs(object, name)) return call(object, name); // TODO: better error message when neither field nor method found return _get(object, name); } catch (Throwable e) { throw rethrowWithSrc(e); } } } static class GetVarContext extends Base implements Evaluable { public Object get(VarContext ctx) { return ctx; } } static class ThrowMethodNotFoundException extends Base implements Evaluable , IFieldsToList{ CallMethod instruction; ThrowMethodNotFoundException() {} ThrowMethodNotFoundException(CallMethod instruction) { this.instruction = instruction;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + instruction + ")"; }public Object[] _fieldsToList() { return new Object[] {instruction}; } public Object get(VarContext ctx) { throw fail("Method not found: " + instruction); } } static class ThrowNullPointerException extends Base implements Evaluable , IFieldsToList{ CallMethod instruction; ThrowNullPointerException() {} ThrowNullPointerException(CallMethod instruction) { this.instruction = instruction;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + instruction + ")"; }public Object[] _fieldsToList() { return new Object[] {instruction}; } public Object get(VarContext ctx) { throw fail("Null pointer exception: " + instruction); } } static class CallMethod extends Base implements Evaluable , IFieldsToList{ Evaluable target; String methodName; Evaluable[] args; CallMethod() {} CallMethod(Evaluable target, String methodName, Evaluable[] args) { this.args = args; this.methodName = methodName; this.target = target;}public Object[] _fieldsToList() { return new Object[] {target, methodName, args}; } public Object get(VarContext ctx) { return /*call*/newPreciseCall(target.get(ctx), methodName, mapToArrayOrNull(args, arg -> arg.get(ctx))); } public String toString() { return target + "." + formatFunctionCall(methodName, args); } public Evaluable optimize() { var targetType = target.returnType(); if (targetType.knownValue()) { Object o = targetType.value(); if (o == null) return new ThrowNullPointerException(this); Class[] argTypes = new Class[l(args)]; for (int i = 0; i < l(args); i++) { var type = args[i].returnType(); if (!type.javaClassIsExact()) return this; argTypes[i] = type.javaClass(); } // TODO: varargs var method = findMethod_precise_onTypes(o, methodName, argTypes); if (method == null) return new ThrowMethodNotFoundException(this); return new DirectMethodCallOnKnownTarget(o instanceof Class ? null : o, method, args); } return this; } } abstract static class CurriedLambdaBase extends Base implements Evaluable , IFieldsToList{ Class intrface; Evaluable[] curriedArgs; CurriedLambdaBase() {} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + intrface + ", " + curriedArgs + ")"; }public Object[] _fieldsToList() { return new Object[] {intrface, curriedArgs}; } Class declaringClass; Method implementedMethod; // !customConstructor CurriedLambdaBase(Class intrface, Evaluable[] curriedArgs) { this.curriedArgs = curriedArgs; this.intrface = intrface; implementedMethod = findSingleInterfaceMethod(intrface); if (implementedMethod == null) throw fail(intrface + " is not a single method interface"); declaringClass = implementedMethod.getDeclaringClass(); } public Object get(VarContext ctx) { Object[] curriedArguments = mapToArrayOrNull(curriedArgs, arg -> arg.get(ctx)); return proxyFromInvocationHandler(intrface, (proxy, method, args) -> { if (method.getDeclaringClass() != declaringClass) throw fail("No handler for method " + method + " (only have " + implementedMethod + ")"); return forwardCall(ctx, concatMethodArgs(curriedArguments, args)); }); } abstract Object forwardCall(VarContext ctx, Object[] args); } static class CurriedMethodLambda extends CurriedLambdaBase { Object target; String targetMethod; CurriedMethodLambda(Class intrface, Object target, String targetMethod, Evaluable[] curriedArgs) { super(intrface, curriedArgs); this.targetMethod = targetMethod; this.target = target; } Object forwardCall(VarContext ctx, Object[] args) { return call(target, targetMethod, args); } } static class CurriedScriptFunctionLambda extends CurriedLambdaBase { FunctionDef f; CurriedScriptFunctionLambda(Class intrface, FunctionDef f, Evaluable[] curriedArgs) { super(intrface, curriedArgs); this.f = f; } Object forwardCall(VarContext ctx, Object[] args) { return f.call(ctx, args); } } static class DirectMethodCallOnKnownTarget extends Base implements Evaluable , IFieldsToList{ Object target; Method method; Evaluable[] args; DirectMethodCallOnKnownTarget() {} DirectMethodCallOnKnownTarget(Object target, Method method, Evaluable[] args) { this.args = args; this.method = method; this.target = target;}public Object[] _fieldsToList() { return new Object[] {target, method, args}; } public Object get(VarContext ctx) { return invokeMethod(method, target, mapToArrayOrNull(args, arg -> arg.get(ctx))); } public String toString() { return (target == null ? "" : target + ".") + formatFunctionCall(str(method), args); } public LASValueDescriptor returnType() { return LASValueDescriptor.fromClass(method.getReturnType()); } } static class While extends Base implements Evaluable , IFieldsToList{ Evaluable condition; Evaluable body; While() {} While(Evaluable condition, Evaluable body) { this.body = body; this.condition = condition;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + condition + ", " + body + ")"; }public Object[] _fieldsToList() { return new Object[] {condition, body}; } public Object get(VarContext ctx) { while (ping() && (Boolean) condition.get(ctx)) { body.get(ctx); } // while loops don't return anything return null; } } abstract static class ForEachBase extends Base implements Evaluable , IFieldsToList{ Evaluable collection; Evaluable body; ForEachBase() {} ForEachBase(Evaluable collection, Evaluable body) { this.body = body; this.collection = collection;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + collection + ", " + body + ")"; }public Object[] _fieldsToList() { return new Object[] {collection, body}; } public Object get(VarContext ctx) { var coll = collection.get(ctx); Iterator iterator; List out = new ArrayList(); if (coll instanceof Object[]) for (var element : ((Object[]) coll)) { ping(); processElement(ctx, out, element); } else if (coll instanceof Iterable) { for (var element : ((Iterable) coll)) { ping(); processElement(ctx, out, element); } } else if (coll == null) {} // ok else throw fail("Not iterable: " + className(coll)); loopDone(ctx); return out; } abstract void processElement(VarContext ctx, List out, Object o); abstract void loopDone(VarContext ctx); } static class ForEach extends ForEachBase { ForEach() {} String var; ForEach(Evaluable collection, String var, Evaluable body) { this.body = body; this.var = var; this.collection = collection;} void processElement(VarContext ctx, List out, Object o) { ctx.set(var, o); out.add(body.get(ctx)); } void loopDone(VarContext ctx) { ctx.unset(var); } } static class ForPairs extends ForEachBase { ForPairs() {} String varA, varB; ForPairs(Evaluable collection, Evaluable body, String varA, String varB) { this.varB = varB; this.varA = varA; this.body = body; this.collection = collection;} void processElement(VarContext ctx, List out, Object o) { Pair p = (Pair) o; ctx.set(varA, p.a); ctx.set(varB, p.b); out.add(body.get(ctx)); } void loopDone(VarContext ctx) { ctx.unset(varA); ctx.unset(varB); } } static class IfThen extends Base implements Evaluable , IFieldsToList{ Evaluable condition; Evaluable body; Evaluable elseBranch; IfThen() {} IfThen(Evaluable condition, Evaluable body, Evaluable elseBranch) { this.elseBranch = elseBranch; this.body = body; this.condition = condition;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + condition + ", " + body + ", " + elseBranch + ")"; }public Object[] _fieldsToList() { return new Object[] {condition, body, elseBranch}; } IfThen(Evaluable condition, Evaluable body) { this.condition = condition; this.body = body; } public Object get(VarContext ctx) { if ((Boolean) condition.get(ctx)) return body.get(ctx); else if (elseBranch != null) return elseBranch.get(ctx); else return null; } } static class ReturnFromScript extends Base implements Evaluable , IFieldsToList{ Script script; Evaluable value; ReturnFromScript() {} ReturnFromScript(Script script, Evaluable value) { this.value = value; this.script = script;}public Object[] _fieldsToList() { return new Object[] {script, value}; } public Object get(VarContext ctx) { Object result = value.get(ctx); ctx.exitFromScript(script); return result; } public String toString() { return formatFunctionCall("ReturnFromScript", script, value); } } } static class Seconds implements Comparable<Seconds> , IFieldsToList{ double seconds; Seconds() {} Seconds(double seconds) { this.seconds = seconds;} public boolean equals(Object o) { if (!(o instanceof Seconds)) return false; Seconds __1 = (Seconds) o; return seconds == __1.seconds; } public int hashCode() { int h = -660217249; h = boostHashCombine(h, _hashCode(seconds)); return h; } public Object[] _fieldsToList() { return new Object[] {seconds}; } final double get(){ return seconds(); } final double getDouble(){ return seconds(); } double seconds() { return seconds; } public String toString() { return formatDouble(seconds, 3) + " s"; } public int compareTo(Seconds s) { return cmp(seconds, s.seconds); } Seconds div(double x) { return new Seconds(get()/x); } Seconds minus(Seconds x) { return new Seconds(get()-x.get()); } } interface IDoublePt { public double x_double(); public double y_double(); } static class Scored<A> extends Var<A> { 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 WidthAndHeight { default int w(){ return getWidth(); } int getWidth(); default int h(){ return getHeight(); } int getHeight(); public default Rect bounds() { return rect(0, 0, getWidth(), getHeight()); } } static class HasIndex implements Comparable<HasIndex> { int idx; HasIndex() {} HasIndex(int idx) { this.idx = idx;} public int compareTo(HasIndex h) { return idx-h.idx; } final int get() { return idx; } } static class BackgroundProcessesUI { Set<Entry> processes = setWithNotify(syncLinkedHashSet(), () -> updateCount()); SimpleLiveValue<Integer> lvCount = new SimpleLiveValue<Integer>(Integer.class, 0); class Entry implements AutoCloseable , IFieldsToList{ String name; Entry() {} Entry(String name) { this.name = name;} public boolean equals(Object o) { if (!(o instanceof Entry)) return false; Entry __0 = (Entry) o; return eq(name, __0.name); } public int hashCode() { int h = 67115090; h = boostHashCombine(h, _hashCode(name)); return h; } public Object[] _fieldsToList() { return new Object[] {name}; } final public Entry setMenuItem(JMenuItem menuItem){ return menuItem(menuItem); } public Entry menuItem(JMenuItem menuItem) { this.menuItem = menuItem; return this; } final public JMenuItem getMenuItem(){ return menuItem(); } public JMenuItem menuItem() { return menuItem; } JMenuItem menuItem; public String toString() { return name; } public void close() { try { processes.remove(this); } catch (Exception __e) { throw rethrow(__e); } } } JLabel shortLabel() { var lbl = bindToolTipToTransformedLiveValue( n -> makeToolTip(), lvCount, simpleTransformedLiveValueLabel(n -> n2(n), lvCount)); onMouseDown_anyButton(lbl, e -> { var l = cloneList(processes); // make sure title is displayed fully JPopupMenu menu = new JPopupMenu();/* { public Dimension getMinimumSize() { TitledBorder border = optCast TitledBorder(getBorder()); _print(+border); ret maxDimension(super.getMinimumSize(), border?.getMinimumSize(this)); } };*/ int n = componentCount(menu); for (Entry a : l) addMenuItem(menu, processToMenuItem(a)); if (componentCount(menu) != n) { var border = jRaisedSectionBorder("Background Processes"); setBorder(menu, border); showPopupMenu(menu, e); var size = menu.getSize(); var borderSize = dimensionPlus(10 /* hack */, 0, border.getMinimumSize(menu)); printVars("size", size, "borderSize", borderSize); menu.setPopupSize(maxDimension(size, borderSize)); } }); return lbl; } void add(Entry process) { processes.add(process); } void remove(Entry process) { processes.remove(process); } void addOrRemove(boolean add, Entry process) { if (add) add(process); else remove(process); } Entry tempAdd(String name) { return tempAdd(new Entry(or2(name, "Unnamed process"))); } Entry tempAdd(Entry process) { if (process == null) return null; processes.add(process); return process; } void updateCount() { lvCount.set(l(processes)); } String makeToolTip() { var l = cloneList(processes); return empty(l) ? "No background processes" : n2(l, "background process", "background processes") + ": " + joinWithComma(processes); } transient IF1<Entry, JMenuItem> processToMenuItem; JMenuItem processToMenuItem(Entry process) { return processToMenuItem != null ? processToMenuItem.get(process) : processToMenuItem_base(process); } final JMenuItem processToMenuItem_fallback(IF1<Entry, JMenuItem> _f, Entry process) { return _f != null ? _f.get(process) : processToMenuItem_base(process); } JMenuItem processToMenuItem_base(Entry process) { return process.getMenuItem(); } } // takes ~70 ms to set up, do it only once if possible static class ClassNameResolver { final public ClassNameResolver setByteCodePath(File byteCodePath){ return byteCodePath(byteCodePath); } public ClassNameResolver byteCodePath(File byteCodePath) { this.byteCodePath = byteCodePath; return this; } final public File getByteCodePath(){ return byteCodePath(); } public File byteCodePath() { return byteCodePath; } File byteCodePath = byteCodePathForClass(getClass()); List<String> importedPackages = itemPlusList("java.lang", endingWith_dropSuffix(standardImports(), ".*")); Set<String> allFullyQualifiedClassNames_cache; Set<String> allFullyQualifiedClassNames() { if (allFullyQualifiedClassNames_cache == null) allFullyQualifiedClassNames_cache = allFullyQualifiedClassNames_load(); return allFullyQualifiedClassNames_cache;} Set<String> allFullyQualifiedClassNames_load() { Set<String> set = new HashSet(); assertNotNull(byteCodePath); set.addAll(classNamesInJarOrDir(byteCodePath)); printVars("ClassNameResolver", "byteCodePath", byteCodePath, "classesFound" , l(set)); set.addAll(classNamesInLoadedJigsawModules()); return set; } ClassNameResolver init() { allFullyQualifiedClassNames(); return this; } String findClass(String name) { for (String pkg : importedPackages) { String fullName = pkg + "." + name; if (allFullyQualifiedClassNames().contains(fullName)) return fullName; } return null; } void printMe() { printVars("ClassNameResolver", "byteCodePath", byteCodePath); print("importedPackages", importedPackages); } } static class VarContext { VarContext parent; Map<String, Object> vars; VarContext() {} VarContext(VarContext parent) { this.parent = parent;} Object get(String name) { if (containsKey(vars, name)) return mapGet(vars, name); if (parent != null) return parent.get(name); return null; } final void put(String name, Object value){ set(name, value); } void set(String name, Object value) { vars = putOrCreate(vars, name, value); } void unset(String name) { remove(vars, name); } void printMe() { pnl(vars); print("parent", parent); } // mechanism for returning from a script final public VarContext setExitFromScript(Object exitFromScript){ return exitFromScript(exitFromScript); } public VarContext exitFromScript(Object exitFromScript) { this.exitFromScript = exitFromScript; return this; } final public Object getExitFromScript(){ return exitFromScript(); } public Object exitFromScript() { return exitFromScript; } Object exitFromScript; } static interface Visitable { void visitUsing(IVF1 f); } static interface Transformable { Object transformUsing(IF1 f); } interface G22ProjectActions { void openObjectInProject(long id); void openPathInProject(String path); void editProjectStory(); void editScripts(); } // Both lines and columns are numbered from 1 static class LineAndColumn { int line, col; LineAndColumn(int line, int col) { this.col = col; this.line = line;} // get text of a line. may return null if no text available transient IF1<Integer, String> getLineText; String getLineText(int line) { return getLineText != null ? getLineText.get(line) : getLineText_base(line); } final String getLineText_fallback(IF1<Integer, String> _f, int line) { return _f != null ? _f.get(line) : getLineText_base(line); } String getLineText_base(int line) { return null; } public String toString() { return "Line " + n2(line) + ", col " + n2(col); } } static class FailedRule extends RuleWithParams { List<Exp> satisfiedConditions; Exp remainingCondition; FailedRule() {} FailedRule(IfThen rule, VarMatches matches, Exp remainingCondition) { this.remainingCondition = remainingCondition; this.matches = matches; this.rule = rule;} FailedRule(IfThen rule, VarMatches matches, List<Exp> satisfiedConditions, Exp remainingCondition) { this.remainingCondition = remainingCondition; this.satisfiedConditions = satisfiedConditions; this.matches = matches; this.rule = rule;} } static class RuleWithParams implements IFieldsToList{ static final String _fieldOrder = "rule matches"; IfThen rule; VarMatches matches; RuleWithParams() {} RuleWithParams(IfThen rule, VarMatches matches) { this.matches = matches; this.rule = rule;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + rule + ", " + matches + ")"; } public boolean equals(Object o) { if (!(o instanceof RuleWithParams)) return false; RuleWithParams __7 = (RuleWithParams) o; return eq(rule, __7.rule) && eq(matches, __7.matches); } public int hashCode() { int h = 1254104616; h = boostHashCombine(h, _hashCode(rule)); h = boostHashCombine(h, _hashCode(matches)); return h; } public Object[] _fieldsToList() { return new Object[] {rule, matches}; } String ruleID() { return rule.globalID; } } static class IfThen implements IFieldsToList{ static final String _fieldOrder = "in out globalID options originalText"; Exp in; Exp out; IfThen() {} IfThen(Exp in, Exp out) { this.out = out; this.in = in;} public boolean equals(Object o) { if (!(o instanceof IfThen)) return false; IfThen __8 = (IfThen) o; return eq(in, __8.in) && eq(out, __8.out); } public int hashCode() { int h = -2108234502; h = boostHashCombine(h, _hashCode(in)); h = boostHashCombine(h, _hashCode(out)); return h; } public Object[] _fieldsToList() { return new Object[] {in, out}; } String globalID; Set<String> options; String originalText; String text() { Collection<String> opt = options; if (nempty(globalID)) opt = concatLists(ll("id: " + globalID), opt); return (nempty(opt) ? "[" + joinWithComma(opt) + "] " : "") + (in == null ? "" : in.text() + "\n => ") + nlLogic_text(out); } public String toString() { return text(); } } abstract static class Exp { abstract String text(); public String toString() { return text(); } } static class Func extends Exp implements IFieldsToList{ String name; Exp arg; Func() {} Func(String name, Exp arg) { this.arg = arg; this.name = name;} public boolean equals(Object o) { if (!(o instanceof Func)) return false; Func __0 = (Func) o; return eq(name, __0.name) && eq(arg, __0.arg); } public int hashCode() { int h = 2201316; h = boostHashCombine(h, _hashCode(name)); h = boostHashCombine(h, _hashCode(arg)); return h; } public Object[] _fieldsToList() { return new Object[] {name, arg}; } List<String> options; Func(String name, List<String> options, Exp arg) { this.arg = arg; this.options = options; this.name = name;} String text() { return name + (empty(options) ? "" : "[" + joinWithComma(options) + "]") + "(" + arg.text() + ")"; } String argText() { return nlLogic_text(arg); } } static class And extends Exp implements IFieldsToList{ Exp a; Exp b; And() {} And(Exp a, Exp b) { this.b = b; this.a = a;} public boolean equals(Object o) { if (!(o instanceof And)) return false; And __1 = (And) o; return eq(a, __1.a) && eq(b, __1.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}; } String text() { return a.text() + "\n && " + b.text(); } } static class ExpNot extends Exp implements IFieldsToList{ Exp a; ExpNot() {} ExpNot(Exp a) { this.a = a;} public boolean equals(Object o) { if (!(o instanceof ExpNot)) return false; ExpNot __2 = (ExpNot) o; return eq(a, __2.a); } public int hashCode() { int h = 2089649046; h = boostHashCombine(h, _hashCode(a)); return h; } public Object[] _fieldsToList() { return new Object[] {a}; } String text() { return "!" + a.text(); } } abstract static class Literal extends Exp {} static class Sentence extends Literal implements IFieldsToList{ List<String> tok; Sentence() {} Sentence(List<String> tok) { this.tok = tok;} public boolean equals(Object o) { if (!(o instanceof Sentence)) return false; Sentence __3 = (Sentence) o; return eq(tok, __3.tok); } public int hashCode() { int h = 1327381123; h = boostHashCombine(h, _hashCode(tok)); return h; } public Object[] _fieldsToList() { return new Object[] {tok}; } String text() { return join(tok); } } static class Sentence2 extends Literal implements IFieldsToList{ String text; Sentence2() {} Sentence2(String text) { this.text = text;} public boolean equals(Object o) { if (!(o instanceof Sentence2)) return false; Sentence2 __4 = (Sentence2) o; return eq(text, __4.text); } public int hashCode() { int h = -1800858097; h = boostHashCombine(h, _hashCode(text)); return h; } public Object[] _fieldsToList() { return new Object[] {text}; } String text() { return text; } } static class Eq extends Exp implements IFieldsToList{ Exp left; Exp right; Eq() {} Eq(Exp left, Exp right) { this.right = right; this.left = left;} public boolean equals(Object o) { if (!(o instanceof Eq)) return false; Eq __5 = (Eq) o; return eq(left, __5.left) && eq(right, __5.right); } public int hashCode() { int h = 2252; h = boostHashCombine(h, _hashCode(left)); h = boostHashCombine(h, _hashCode(right)); return h; } public Object[] _fieldsToList() { return new Object[] {left, right}; } String text() { return left.text() + " = " + right.text(); } } static class ImageSurface extends Surface { BufferedImage image; double zoomX = 1, zoomY = 1, zoomFactor = 1.5; private Rectangle selection; List<AutoCloseable> tools = new ArrayList(); // use overlays now Object overlay; // voidfunc(Graphics2D) List<G2Drawable> overlays = syncL(); Runnable onSelectionChange; final public ImageSurface setVerbose(boolean verbose){ return verbose(verbose); } public ImageSurface verbose(boolean verbose) { this.verbose = verbose; return this; } final public boolean getVerbose(){ return verbose(); } public boolean verbose() { return verbose; } boolean verbose = false; boolean noMinimumSize = true; String titleForUpload; Object onZoom; boolean specialPurposed = false; // true = don't show image changing commands in popup menu final public ImageSurface setZoomable(boolean zoomable){ return zoomable(zoomable); } public ImageSurface zoomable(boolean zoomable) { this.zoomable = zoomable; return this; } final public boolean getZoomable(){ return zoomable(); } public boolean zoomable() { return zoomable; } boolean zoomable = true; boolean noAlpha = false; // set to true to speed up drawing if you don't use alpha Object interpolationMode = RenderingHints.VALUE_INTERPOLATION_BILINEAR; Object onNewImage; BufferedImage imageToDraw; // if you want to draw a different image File file; // where image was loaded from boolean autoZoomToDisplay = false; // only works 100% when not in scrollpane final public ImageSurface setRepaintInThread(boolean repaintInThread){ return repaintInThread(repaintInThread); } public ImageSurface repaintInThread(boolean repaintInThread) { this.repaintInThread = repaintInThread; return this; } final public boolean getRepaintInThread(){ return repaintInThread(); } public boolean repaintInThread() { return repaintInThread; } boolean repaintInThread = false; // after setImage, repaint in same thread BoolVar showingVar; Pt mousePosition; transient Set<Runnable> onMousePositionChanged; public ImageSurface onMousePositionChanged(Runnable r) { onMousePositionChanged = createOrAddToSyncLinkedHashSet(onMousePositionChanged, r); return this; } public ImageSurface removeMousePositionChangedListener(Runnable r) { main.remove(onMousePositionChanged, r); return this; } public void mousePositionChanged() { pcallFAll(onMousePositionChanged); } public ImageSurface() { this(dummyImage()); } static BufferedImage dummyImage() { return whiteImage(1); } ImageSurface(MakesBufferedImage image) { this(image != null ? image.getBufferedImage() : dummyImage()); } ImageSurface(BufferedImage image) { setImage(image); clearSurface = false; onResize(this, () -> performAutoZoom()); bindToComponent(this, () -> performAutoZoom(), null); componentPopupMenu2(this, ImageSurface_popupMenuMaker()); new ImageSurfaceSelector(this); jHandleFileDrop(this, new VF1<File>() { public void get(File f) { try { setImage(loadBufferedImage(f)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setImage(loadBufferedImage(f))"; }}); imageSurfaceOnHover(this, p -> { mousePosition = p; mousePositionChanged(); }); } public ImageSurface(RGBImage image, double zoom) { this(image); setZoom(zoom); } // point is already in image coordinates protected void fillPopupMenu(JPopupMenu menu, final Point point) { if (zoomable) { JMenuItem miZoomReset = new JMenuItem("Zoom 100%"); miZoomReset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { setZoom(1.0); centerPoint(point); } }); menu.add(miZoomReset); JMenuItem miZoomIn = new JMenuItem("Zoom in"); miZoomIn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { zoomIn(zoomFactor); centerPoint(point); } }); menu.add(miZoomIn); JMenuItem miZoomOut = new JMenuItem("Zoom out"); miZoomOut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { zoomOut(zoomFactor); centerPoint(point); } }); menu.add(miZoomOut); /*JMenuItem miZoomToWindow = new JMenuItem("Zoom to window"); miZoomToWindow.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { zoomToDisplaySize(); } }); menu.add(miZoomToWindow);*/ menu.add(jCheckBoxMenuItem_dyn(/*"Auto-zoom to window"*/"Zoom to window", () -> autoZoomToDisplay, b -> { setAutoZoomToDisplay(b); })); addMenuItem(menu, "Show full screen", new Runnable() { public void run() { try { showFullScreen() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "showFullScreen()"; }}); addMenuItem(menu, "Point: " + point.x + "," + point.y + " (image: " + w() + "*" + h() + ")", null); menu.addSeparator(); } if (!specialPurposed) addMenuItem(menu, "Load image...", new Runnable() { public void run() { try { selectFile("Load image", new VF1<File>() { public void get(File f) { try { setImage(loadImage2(f)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setImage(loadImage2(f))"; }}) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "selectFile(\"Load image\",\r\n new VF1<File>() { public void get(File f) c..."; }}); addMenuItem(menu, "Save image...", new Runnable() { public void run() { try { saveImage() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "saveImage()"; }}); addMenuItem(menu, "Copy image to clipboard", new Runnable() { public void run() { try { copyImageToClipboard(getImage()) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyImageToClipboard(getImage())"; }}); if (!specialPurposed) { addMenuItem(menu, "Paste image from clipboard", new Runnable() { public void run() { try { loadFromClipboard() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "loadFromClipboard()"; }}); addMenuItem(menu, "Load image snippet...", new Runnable() { public void run() { try { selectImageSnippet(new VF1<String>() { public void get(String imageID) { try { setImage(loadImage2(imageID)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setImage(loadImage2(imageID))"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "selectImageSnippet(new VF1<String>() { public void get(String imageID) ctex {..."; }}); } if (selection != null) addMenuItem(menu, "Crop", new Runnable() { public void run() { try { crop() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "crop()"; }}); if (!specialPurposed) addMenuItem(menu, "No image", new Runnable() { public void run() { try { noImage() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "noImage()"; }}); } void noImage() { setImage((BufferedImage) null); } void crop() { if (selection == null) return; BufferedImage img = cloneClipBufferedImage(getImage(), selection); selection = null; setImage(img); } void loadFromClipboard() { BufferedImage img = getImageFromClipboard(); if (img != null) setImage(img); } transient IF0<File> defaultImageDir; File defaultImageDir() { return defaultImageDir != null ? defaultImageDir.get() : defaultImageDir_base(); } final File defaultImageDir_fallback(IF0<File> _f) { return _f != null ? _f.get() : defaultImageDir_base(); } File defaultImageDir_base() { return getProgramDir(); } void saveImage() { var image = getImage(); JFileChooser fileChooser = new JFileChooser(defaultImageDir()); if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { try { main.saveImage(image, file = fileChooser.getSelectedFile()); } catch (Throwable e) { popup(e); } } } void drawImageItself(int w, int h, Graphics2D g) { int iw = getZoomedWidth(), ih = getZoomedHeight(); if (interpolationMode == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR || zoomX >= 1 || zoomY >= 1) { // faster g.drawImage(image, 0, 0, iw, ih, null); } else g.drawImage(resizeImage(image, iw, ih), 0, 0, null); // smoother } public void render(int w, int h, Graphics2D g) { if (verbose) _print("render"); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolationMode); g.setColor(Color.white); BufferedImage image = or(imageToDraw, this.image); if (!hasImage()) g.fillRect(0, 0, w, h); else { boolean alpha = !noAlpha && hasTransparency(image); if (alpha) g.fillRect(0, 0, w, h); drawImageItself(w, h, g); int iw = getZoomedWidth(), ih = getZoomedHeight(); if (!alpha) { g.fillRect(iw, 0, w-iw, h); g.fillRect(0, ih, iw, h-ih); } } if (overlay != null) { if (verbose) _print("render overlay"); pcallF(overlay, g); } for (var overlay : cloneList(overlays)) { try { overlay.drawOn(g); } catch (Throwable __e) { printStackTrace(__e); }} if (selection != null) { if (verbose) _print("render selection"); // drawRect is inclusive, selection is exclusive, so... whatever, tests show it's cool. drawSelectionRect(g, selection, Color.green, Color.white); } } public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white) { drawSelectionRect(g, selection, green, white, zoomX, zoomY); } public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white, double zoomX, double zoomY) { g.setColor(green); int top = (int) (selection.y * zoomY); int bottom = (int) ((selection.y+selection.height) * zoomY); int left = (int) (selection.x * zoomX); int right = (int) ((selection.x+selection.width) * zoomX); g.drawRect(left-1, top-1, right-left+1, bottom-top+1); g.setColor(white); g.drawRect(left - 2, top - 2, right - left + 3, bottom - top + 3); } public ImageSurface setZoom(double zoom) { setZoom(zoom, zoom); return this; } public void setZoom(double zoomX, double zoomY) { autoZoomToDisplay = false; setZoom_dontChangeAutoZoom(zoomX, zoomY); } public void setZoom_dontChangeAutoZoom(double zoomX) { setZoom_dontChangeAutoZoom(zoomX, zoomX); } public void setZoom_dontChangeAutoZoom(double zoomX, double zoomY) { if (this.zoomX == zoomX && this.zoomY == zoomY) return; if (verbose) _print("Setting zoom"); this.zoomX = zoomX; this.zoomY = zoomY; revalidateMe(); repaint(); centerPoint(new Point(getImage().getWidth()/2, getImage().getHeight()/2)); pcallF(onZoom); } public Dimension getMinimumSize() { if (metaGet("scaffolding") != null) scaffoldCalled(this, "getMinimumSize"); if (noMinimumSize) return new Dimension(1, 1); int w = getZoomedWidth(); int h = getZoomedHeight(); Dimension min = super.getMinimumSize(); return printIfScaffoldingEnabled(this, new Dimension(Math.max(w, min.width), Math.max(h, min.height))); } int getZoomedHeight() { return (int) (h() * zoomY); } int getZoomedWidth() { return (int) (w() * zoomX); } boolean isShowing_quick() { if (showingVar == null) { swing(() -> { if (showingVar == null) showingVar = componentShowingVar(ImageSurface.this); }); } return showingVar.get(); } public void setImageIfShowing_thisThread(MakesBufferedImage image) { setImageIfShowing_thisThread(toBufferedImage(image)); } public void setImageIfShowing_thisThread(BufferedImage image) { if (isShowing_quick()) setImage_thisThread(image); } public void setImage(MakesBufferedImage image) { swing(() -> { setImage_thisThread(image); }); } public void setImage(BufferedImage img) { swing(() -> { setImage_thisThread(img); }); } public void setImage_thisThread(MakesBufferedImage img) { setImage_thisThread(toBufferedImage(img)); } public void setImage_thisThread(BufferedImage img) { BufferedImage newImage = img != null ? img : dummyImage(); BufferedImage oldImage = image; image = newImage; if (verbose) print("Old image size:" + imageSize(oldImage) + ", new image size: " + imageSize(newImage)); boolean sameSize = !imagesHaveSameSize(oldImage, newImage); if (!sameSize) { if (verbose) _print("New image size"); revalidateMe(); } quickRepaint(); pcallF(onNewImage); if (!sameSize && autoZoomToDisplay) zoomToDisplaySize(); } void setImageAndZoomToDisplay(BufferedImage img) { setImage(img); zoomToDisplaySize(); } public BufferedImage getImage() { return image; } public double getZoomX() { return zoomX; } public double getZoomY() { return zoomY; } public Dimension getPreferredSize() { if (metaGet("scaffolding") != null) scaffoldCalled(this, "getPreferredSize"); return printIfScaffoldingEnabled(this, new Dimension(getZoomedWidth(), getZoomedHeight())); } /** returns a scrollpane with the scroll-mode prevent-garbage-drawing fix applied */ public JScrollPane makeScrollPane() { JScrollPane scrollPane = new JScrollPane(this); scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE); return scrollPane; } public void zoomToWindow() { zoomToDisplaySize(); } public void zoomToDisplaySize() { swing(() -> { if (!hasImage()) return; Dimension display = getDisplaySize(); if (display.width == 0 || display.height == 0) return; int w = w(), h = h(); double xRatio = (display.width-5)/(double) w; double yRatio = (display.height-5)/(double) h; if (scaffoldingEnabled(this)) printVars("zoomToDisplaySize", "display", display, "w", w, "h", h, "xRatio", xRatio, "yRatio", yRatio); setZoom_dontChangeAutoZoom(min(xRatio, yRatio)); revalidateMe(); }); } /** tricky magic to get parent scroll pane */ private Dimension getDisplaySize() { if (metaGet("scaffolding") != null) scaffoldCalled(this, "getDisplaySize"); Container c = getParent(); while (c != null) { if (c instanceof JScrollPane) return c.getSize(); c = c.getParent(); } return getSize(); } public void setSelection(Rect r) { setSelection(toRectangle(r)); } public void setSelection(Rectangle r) { if (neq(selection, r)) { selection = r; pcallF(onSelectionChange); quickRepaint(); } } public Rectangle getSelection() { return selection; } public RGBImage getRGBImage() { return new RGBImage(getImage()); } // p is in image coordinates void centerPoint(Point p) { JScrollPane sp = enclosingScrollPane(this); if (sp == null) return; p = new Point((int) (p.x*getZoomX()), (int) (p.y*getZoomY())); final JViewport viewport = sp.getViewport(); Dimension viewSize = viewport.getExtentSize(); //_print("centerPoint " + p); int x = max(0, p.x-viewSize.width/2); int y = max(0, p.y-viewSize.height/2); //_print("centerPoint " + p + " => " + x + "/" + y); p = new Point(x,y); //_print("centerPoint " + p); final Point _p = p; awtLater(new Runnable() { public void run() { try { viewport.setViewPosition(_p); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "viewport.setViewPosition(_p);"; }}); } Pt pointFromEvent(MouseEvent e) { return pointFromComponentCoordinates(new Pt(e.getX(), e.getY())); } Pt pointFromComponentCoordinates(Pt p) { return new Pt((int) (p.x/zoomX), (int) (p.y/zoomY)); } Pt pointToComponentCoordinates(double x, double y) { return new Pt((int) (x*zoomX), (int) (y*zoomY)); } void uploadTheImage() { //call(hotwire(/*#1007313*/#1016427), "go", getImage(), titleForUpload); } void showFullScreen() { showFullScreenImageSurface(getImage()); } void zoomIn(double f) { setZoom(getZoomX()*f, getZoomY()*f); } void zoomOut(double f) { setZoom(getZoomX()/f, getZoomY()/f); } ImageSurface setFile(File f) { file = f; return this; } void setOverlay(IVF1<Graphics2D> overlay) { this.overlay = overlay; } boolean hasImage() { return image != null; } public int w() { return image.getWidth(); } public int h() { return image.getHeight(); } void setPixelated(boolean b) { assertTrue(b); imageSurface_pixelated(this); } ImageSurface setAutoZoomToDisplay(boolean b) { if (autoZoomToDisplay = b) zoomToDisplaySize(); return this; } void quickRepaint() { if (repaintInThread) paintImmediately(0, 0, getWidth(), getHeight()); else repaint(); } void setTool(ImageSurfaceMouseHandler tool) { swing(() -> { removeAllTools(); addTool(tool); }); } boolean hasTool(AutoCloseable tool) { return swing(() -> tools.contains(tool)); } void addTool(ImageSurfaceMouseHandler tool) { swing(() -> { if (!tools.contains(tool)) tool.register(this); }); } void removeTool(AutoCloseable tool) { swing(() -> { if (tools.contains(tool)) { close(tool); tools.remove(tool); } }); } void removeAllTools() { closeAllAndClear(tools); } void performAutoZoom() { if (autoZoomToDisplay) zoomToDisplaySize(); } void revalidateMe() { revalidateIncludingFullCenterContainer(this); } void addOverlay(G2Drawable overlay) { overlays.add(overlay); repaint(); } void clearOverlays() { if (nempty(overlays)) { overlays.clear(); repaint(); } } void loadImage(File f) { setImage(loadImage2(f)); } JComponent visualize() { return jscroll_center_borderless(this); } <A> A print(A a) { return print("", a); } <A> A print(String msg, A a) { return _print(msg, a); } } // end of ImageSurface // static function allows garbage collection static VF2<ImageSurface, JPopupMenu> ImageSurface_popupMenuMaker() { return new VF2<ImageSurface, JPopupMenu>() { public void get(ImageSurface is, JPopupMenu menu) { try { Point p = is.pointFromEvent(componentPopupMenu_mouseEvent.get()).getPoint(); is.fillPopupMenu(menu, p); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Point p = is.pointFromEvent(componentPopupMenu_mouseEvent.get()).getPoint();\r..."; }}; } static class CombinedStringifier<A> implements IStringifier<A> { CopyOnWriteArrayList<IPartialStringifier<A>> stringifiers = new CopyOnWriteArrayList(); IStringifier<A> defaultStringifier = new Stringifier_ToString(); CombinedStringifier(IPartialStringifier<A>... stringifiers) { addAll(this.stringifiers, stringifiers); } public String toString(A o) { for (var stringifier : stringifiers) { String s = stringifier.toStringOpt(o); if (s != null) return s; } return defaultStringifier.toString(o); } public String toString() { return formatFunctionCall("CombinedStringifier", listPlus((List) stringifiers, defaultStringifier) ); } } static class G22AutoStarter extends MetaWithChangeListeners implements AutoCloseable , IFieldsToList{ G22Utils g22utils; G22AutoStarter() {} G22AutoStarter(G22Utils g22utils) { this.g22utils = g22utils;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + g22utils + ")"; }public Object[] _fieldsToList() { return new Object[] {g22utils}; } public transient FieldVar<Boolean> varEnabled_cache; public FieldVar<Boolean> varEnabled() { if (varEnabled_cache == null) varEnabled_cache = varEnabled_load(); return varEnabled_cache;} public FieldVar<Boolean> varEnabled_load() { return new FieldVar<Boolean>(this, "enabled", () -> enabled(), enabled -> enabled(enabled)); } final public G22AutoStarter setEnabled(boolean enabled){ return enabled(enabled); } public G22AutoStarter enabled(boolean enabled) { if (!eq(this.enabled, enabled)) { this.enabled = enabled; change(); } return this; } final public boolean getEnabled(){ return enabled(); } public boolean enabled() { return enabled; } volatile boolean enabled = true; public transient FieldVar<Integer> varInitialDelay_cache; public FieldVar<Integer> varInitialDelay() { if (varInitialDelay_cache == null) varInitialDelay_cache = varInitialDelay_load(); return varInitialDelay_cache;} public FieldVar<Integer> varInitialDelay_load() { return new FieldVar<Integer>(this, "initialDelay", () -> initialDelay(), initialDelay -> initialDelay(initialDelay)); } final public G22AutoStarter setInitialDelay(int initialDelay){ return initialDelay(initialDelay); } public G22AutoStarter initialDelay(int initialDelay) { if (!eq(this.initialDelay, initialDelay)) { this.initialDelay = initialDelay; change(); } return this; } final public int getInitialDelay(){ return initialDelay(); } public int initialDelay() { return initialDelay; } volatile int initialDelay = 3; public transient FieldVar<Integer> varNScriptsRun_cache; public FieldVar<Integer> varNScriptsRun() { if (varNScriptsRun_cache == null) varNScriptsRun_cache = varNScriptsRun_load(); return varNScriptsRun_cache;} public FieldVar<Integer> varNScriptsRun_load() { return new FieldVar<Integer>(this, "nScriptsRun", () -> nScriptsRun(), nScriptsRun -> nScriptsRun(nScriptsRun)); } final public G22AutoStarter setNScriptsRun(int nScriptsRun){ return nScriptsRun(nScriptsRun); } public G22AutoStarter nScriptsRun(int nScriptsRun) { if (!eq(this.nScriptsRun, nScriptsRun)) { this.nScriptsRun = nScriptsRun; change(); } return this; } final public int getNScriptsRun(){ return nScriptsRun(); } public int nScriptsRun() { return nScriptsRun; } volatile int nScriptsRun; public transient FieldVar<AutoStartScript> varCurrentScript_cache; public FieldVar<AutoStartScript> varCurrentScript() { if (varCurrentScript_cache == null) varCurrentScript_cache = varCurrentScript_load(); return varCurrentScript_cache;} public FieldVar<AutoStartScript> varCurrentScript_load() { return new FieldVar<AutoStartScript>(this, "currentScript", () -> currentScript(), currentScript -> currentScript(currentScript)); } final public G22AutoStarter setCurrentScript(AutoStartScript currentScript){ return currentScript(currentScript); } public G22AutoStarter currentScript(AutoStartScript currentScript) { if (!eq(this.currentScript, currentScript)) { this.currentScript = currentScript; change(); } return this; } final public AutoStartScript getCurrentScript(){ return currentScript(); } public AutoStartScript currentScript() { return currentScript; } volatile AutoStartScript currentScript; Flag started = new Flag(); Flag waited = new Flag(); Flag ctrlPressed = new Flag(); List<AutoStartScript> scripts = syncL(); AutoCloseable ctrlListener; ReliableSingleThread rst = rst(() -> _run()); class AutoStartScript extends RunResultWithTimestamps implements IFieldsToList{ G22LeftArrowScript script; AutoStartScript() {} AutoStartScript(G22LeftArrowScript script) { this.script = script;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + script + ")"; }public Object[] _fieldsToList() { return new Object[] {script}; } public void run() { try { LASCompileResult cr = script.compileForAutoRun(); if (cr == null) cr = script.compileSaved(); var _cr_2 = cr; run(() -> { G22AutoStarter.this.change(); if (_cr_2 == null) throw fail("Script is not saved: " + script); var parsed = _cr_2.parsedScriptMandatory(); return parsed.get(); }); } catch (Exception __e) { throw rethrow(__e); } } } void init() { if (ctrlListener != null) return; scripts.clear(); var scripts = conceptsWhere(g22utils.concepts, G22LeftArrowScript.class, "runOnProjectOpen" , true); var sorted = sortByCalculatedFieldAlphaNumIC(scripts, s -> s.runOrder); for (var script : sorted) this.scripts.add(new AutoStartScript(script)); change(); ctrlListener = tempAddGlobalCtrlKeyListener(b -> { if (b) { ctrlPressed.raise(); cancel(); } }); } void start() { print("G22AutoStarter: Start"); if (enabled) rst.get(); } void _run() { if (!enabled) return; AutoCloseable __1 = g22utils.backgroundProcessesUI.tempAdd("Auto-Start Scripts"); try { if (started.raise()) change(); if (!waited.get()) { print("G22AutoStarter: Sleeping"); while (initialDelay > 0) { sleepSeconds(1); initialDelay(initialDelay-1); } waited.raise(); change(); print("G22AutoStarter: Slept"); } while (enabled && !done()) { var script = scripts.get(nScriptsRun); currentScript(script); change(); script.run(); nScriptsRun(nScriptsRun+1); currentScript(null); change(); } change(); close(); } finally { _close(__1); }} boolean done() { return nScriptsRun >= l(scripts); } void cancel() { setEnabled(false); } void resume() { if (done()) { infoBox("Nothing to resume - auto-start is done"); return; } if (enabled) { infoBox("Already running"); return; } infoBox("Resuming auto-start"); setEnabled(true); rst.get(); } String status() { return !enabled ? ctrlPressed.get() ? "Cancelled due to ctrl key" : "Cancelled" : done() ? "Done" : waited.get() ? "Started" : started.get() ? "Pre-start wait (" + formatDouble1(initialDelay) + "s)" : "Not started"; } String stats() { return status() + ". " + scriptsRunStats(); } String scriptsRunStats() { return "Scripts run: " + nScriptsRun() + "/" + l(scripts); } String currentScriptStats() { var s = currentScript; if (s == null) return ""; return "Running: " + s.script; } public void close() { try { { cleanUp(ctrlListener); ctrlListener = null; } } catch (Exception __e) { throw rethrow(__e); } } void waitUntilDone() { waitForVarPredicate(varNScriptsRun(), () -> done()); } } static class G22LeftArrowScript extends ConceptWithChangeListeners { static final String _fieldOrder = "description text clearedForAutoRun editingText runOnProjectOpen runOrder runCount lastResultByMode compileResultForAutoRun compileResultForSaved"; public transient FieldVar<String> varDescription_cache; public FieldVar<String> varDescription() { if (varDescription_cache == null) varDescription_cache = varDescription_load(); return varDescription_cache;} public FieldVar<String> varDescription_load() { return new FieldVar<String>(this, "description", () -> description(), description -> description(description)); } final public G22LeftArrowScript setDescription(String description){ return description(description); } public G22LeftArrowScript description(String description) { if (!eq(this.description, description)) { this.description = description; change(); } return this; } final public String getDescription(){ return description(); } public String description() { return description; } String description; // the script (last "saved" version) public transient FieldVar<String> varText_cache; public FieldVar<String> varText() { if (varText_cache == null) varText_cache = varText_load(); return varText_cache;} public FieldVar<String> varText_load() { return new FieldVar<String>(this, "text", () -> text(), text -> text(text)); } final public G22LeftArrowScript setText(String text){ return text(text); } public G22LeftArrowScript text(String text) { if (!eq(this.text, text)) { this.text = text; change(); } return this; } final public String getText(){ return text(); } public String text() { return text; } String text; // the latest code that has been cleared to auto-run (if any) public transient FieldVar<ClearForAutoRun<String>> varClearedForAutoRun_cache; public FieldVar<ClearForAutoRun<String>> varClearedForAutoRun() { if (varClearedForAutoRun_cache == null) varClearedForAutoRun_cache = varClearedForAutoRun_load(); return varClearedForAutoRun_cache;} public FieldVar<ClearForAutoRun<String>> varClearedForAutoRun_load() { return new FieldVar<ClearForAutoRun<String>>(this, "clearedForAutoRun", () -> clearedForAutoRun(), clearedForAutoRun -> clearedForAutoRun(clearedForAutoRun)); } final public G22LeftArrowScript setClearedForAutoRun(ClearForAutoRun<String> clearedForAutoRun){ return clearedForAutoRun(clearedForAutoRun); } public G22LeftArrowScript clearedForAutoRun(ClearForAutoRun<String> clearedForAutoRun) { if (!eq(this.clearedForAutoRun, clearedForAutoRun)) { this.clearedForAutoRun = clearedForAutoRun; change(); } return this; } final public ClearForAutoRun<String> getClearedForAutoRun(){ return clearedForAutoRun(); } public ClearForAutoRun<String> clearedForAutoRun() { return clearedForAutoRun; } ClearForAutoRun<String> clearedForAutoRun; // text currently being modified in an editor (or null) // Note that "" doesn't count as null. public transient FieldVar<String> varEditingText_cache; public FieldVar<String> varEditingText() { if (varEditingText_cache == null) varEditingText_cache = varEditingText_load(); return varEditingText_cache;} public FieldVar<String> varEditingText_load() { return new FieldVar<String>(this, "editingText", () -> editingText(), editingText -> editingText(editingText)); } final public G22LeftArrowScript setEditingText(String editingText){ return editingText(editingText); } public G22LeftArrowScript editingText(String editingText) { if (!eq(this.editingText, editingText)) { this.editingText = editingText; change(); } return this; } final public String getEditingText(){ return editingText(); } public String editingText() { return editingText; } String editingText; // auto-run when project is opened boolean runOnProjectOpen = false; // only when runOnProjectOpen is seT: // when to run in sequence (sorted with alphanumIC) String runOrder; // run stats long runCount; // info about last result (or error) of script run by mode Map<G22ScriptMode, WithTimestamp<PersistableOKOrError<G22TypeDesc>>> lastResultByMode; @Override void _doneLoading2() { //print("_doneLoading " + this); cMigrateField(this, "code", "text"); } String myType() { return dropPrefix("G22", shortClassName(this)); } public String toString() { return or2(description, myType() + " " + id); } void initEditingText() { editingText(unnull(or(editingText, text))); } void receiveEditingText(String text) { printVars_shorten("receiveEditingText", "text", text); editingText(text); } String stableText() { return text; } final void save(){ completeEdit(); } void completeEdit() { String t = editingText; printVars_shorten("completeEdit", "t", t); if (t != null) { setTextWithHistory(t); printVars_shorten("completeEdit", "editingText", editingText); } } void setTextWithHistory(String text) { if (eq(this.text, text)) return; text(text); saveTextToHistory(); } String textForEditing() { initEditingText(); return editingText; } File historyFile() { return fileInConceptsDir("History/" + shortDynName(this) + id + ".history"); } void saveTextToHistory() { saveFieldToHistory("text", text); } void saveFieldToHistory(String field, String value) { File historyFile = historyFile(); if (historyFile == null) return; String contents = value == null ? " empty" : " (" + nLines(value) + ", " + nChars(value) + "):\n" + indentx(value) + "\n" + "\n"; appendToTextFile(historyFile, "\n===\n" + "Concept ID: " + id + "\n" + "Date: " + dateWithMSUTC() + "\n" + firstToUpper(field) + contents + "===" + "\n"); } boolean isSaved() { return text != null; } boolean isSavedDistinctFromAutoRunVersion() { return isSaved() && !eq(text, codeForAutoRun()); } boolean isEditing() { return editedText() != null; } boolean isClearForAutoRun() { return clearedForAutoRun != null; } String editedText() { return eq(editingText, text) ? null : editingText; } String codeForAutoRun() { return getVar(clearedForAutoRun()); } // return code clear for auto run if available // otherwise returns saved code if available // in all other cases returns null String safestCode() { return or(codeForAutoRun(), stableText()); } void clearForAutoRun() { if (!isSaved()) return; String text = text(); saveFieldToHistory("Auto-runnable code", text); clearedForAutoRun(new ClearForAutoRun(text)); } void forgetAutoRunCode() { if (!isClearForAutoRun()) return; saveFieldToHistory("Auto-runnable code", null); clearedForAutoRun(null); compileResultForAutoRun = null; } GazelleV_LeftArrowScriptParser makeParser() { return g22utils(_concepts).leftArrowParser(); } LASCompileResult newCompileResult() { return new LASCompileResult(); } transient LASCompileResult compileResultForAutoRun; LASCompileResult compileForAutoRun() { String code = codeForAutoRun(); if (code == null) return null; var cr = compileResultForAutoRun; if (cr != null && eq(cr.script, code)) return cr; cr = newCompileResult(); cr.script(code); var g22utils = assertNotNull("g22utils", g22utils(_concepts())); var parser = makeParser(); cr.parser(parser); cr.compile(); return compileResultForAutoRun = cr; } transient LASCompileResult compileResultForSaved; LASCompileResult compileSaved() { String code = stableText(); if (code == null) return null; var cr = compileResultForSaved; if (cr != null && eq(cr.script, code)) return cr; cr = newCompileResult(); cr.script(code); var g22utils = assertNotNull("g22utils", g22utils(_concepts())); var parser = makeParser(); cr.parser(parser); cr.compile(); return compileResultForSaved = cr; } } static class JLeftArrowScriptIDE extends MetaWithChangeListeners implements Swingable { final public JLeftArrowScriptIDE setLvScript(SimpleLiveValue<String> lvScript){ return lvScript(lvScript); } public JLeftArrowScriptIDE lvScript(SimpleLiveValue<String> lvScript) { this.lvScript = lvScript; return this; } final public SimpleLiveValue<String> getLvScript(){ return lvScript(); } public SimpleLiveValue<String> lvScript() { return lvScript; } SimpleLiveValue<String> lvScript = stringLiveValue(); final public JLeftArrowScriptIDE setSectionTitle(String sectionTitle){ return sectionTitle(sectionTitle); } public JLeftArrowScriptIDE sectionTitle(String sectionTitle) { this.sectionTitle = sectionTitle; return this; } final public String getSectionTitle(){ return sectionTitle(); } public String sectionTitle() { return sectionTitle; } String sectionTitle = "Left arrow script"; final public JLeftArrowScriptIDE setWithResultPanel(boolean withResultPanel){ return withResultPanel(withResultPanel); } public JLeftArrowScriptIDE withResultPanel(boolean withResultPanel) { this.withResultPanel = withResultPanel; return this; } final public boolean getWithResultPanel(){ return withResultPanel(); } public boolean withResultPanel() { return withResultPanel; } boolean withResultPanel = true; static String helpText = "Gazelle 22 \"Left Arrow Script\"\r\n------------------------------\r\n\r\n\"Left arrow script\" is one of Gazelle 22's two scripting languages. It is the powerful one of the two. It can do anything Java can do except for defining classes or lambdas.\r\n\r\n-Usually, you write one command per line\r\n-Each command contains at most one action and at most one assignment\r\n-The language is case-sensitive\r\n-You can add comments like in Java with /* */ or //\r\n-You can write multiple commands in one line by separating them with a semicolon\r\n-Arguments to functions are separated by spaces\r\n-You can use integer and string literals like in Java\r\n-You can use true, false and null\r\n\r\nIt's called \"left arrow script\" because the left arrow (variable assignment) is its only actual operator (not counting ; and {}).\r\n\r\n\r\nOperations\r\n----------\r\n\r\nYou can call any global function defined in Gazelle. Example:\r\n\r\n infoBox \"hello\" // show a popup with the text \"hello\"\r\n \r\nYou can assign the result of a function call to a variable:\r\n\r\n time <- tsNow // get current time as a Timestamp object\r\n infoBox time // show as popup\r\n \r\nNote that this script requires 2 lines because only one operation is allowed per command.\r\n \r\nVariables can be overwritten and don't have to be declared.\r\n\r\n\r\nCreating objects\r\n----------------\r\n\r\nYou can create an instance of any Java class and call methods on the object:\r\n\r\n list <- new ArrayList\r\n list add \"hello\"\r\n list add \"world\"\r\n infoBox list // shows [hello, world]\r\n \r\n(Currently you can also say \"list <- ArrayList\", but this is ambiguous and may be removed in a future version.)\r\n \r\nYou can pass parameters to the constructor:\r\n\r\n pair <- new Pair \"hello\" \"world\"\r\n infoBox pair // shows <hello, world>\r\n \r\n\r\nJava operators (+, * etc)\r\n-------------------------\r\n \r\nJava operators can't be used directly but we have functions that do the same thing (if we don't, we'll add them):\r\n\r\n x <- plus 1 2\r\n infoBox x\r\n\r\n\r\nFunction definitions\r\n--------------------\r\n\r\nYou can define functions in your script, with arguments. The function can return a value (which is the result of the last command in the function).\r\n\r\n def doubleMe x { mul x 2 }\r\n\r\n x <- doubleMe 5\r\n infoBox x // shows 10\r\n"; public transient FieldVar<LASCompileResult> varCompileResult_cache; public FieldVar<LASCompileResult> varCompileResult() { if (varCompileResult_cache == null) varCompileResult_cache = varCompileResult_load(); return varCompileResult_cache;} public FieldVar<LASCompileResult> varCompileResult_load() { return new FieldVar<LASCompileResult>(this, "compileResult", () -> compileResult(), compileResult -> compileResult(compileResult)); } final public JLeftArrowScriptIDE setCompileResult(LASCompileResult compileResult){ return compileResult(compileResult); } public JLeftArrowScriptIDE compileResult(LASCompileResult compileResult) { if (!eq(this.compileResult, compileResult)) { this.compileResult = compileResult; change(); } return this; } final public LASCompileResult getCompileResult(){ return compileResult(); } public LASCompileResult compileResult() { return compileResult; } transient LASCompileResult compileResult; transient RSyntaxTextAreaWithSearch taScript; transient Q compileQ = new Q(); final public JPanel getButtons(){ return buttons(); } public JPanel buttons() { return buttons; } transient JPanel buttons = jline(); transient JButton btnRun; transient CollapsibleLeftPanel collapsibleResultPanel; transient SingleComponentPanel scpResult = singleComponentPanel(); transient JPopDownButton popDownButton; transient LeftArrowCompletionProvider completionProvider; final public JLeftArrowScriptIDE setG22utils(G22Utils g22utils){ return g22utils(g22utils); } public JLeftArrowScriptIDE g22utils(G22Utils g22utils) { this.g22utils = g22utils; return this; } final public G22Utils getG22utils(){ return g22utils(); } public G22Utils g22utils() { return g22utils; } transient G22Utils g22utils; final public JLeftArrowScriptIDE setScriptTimeout(double scriptTimeout){ return scriptTimeout(scriptTimeout); } public JLeftArrowScriptIDE scriptTimeout(double scriptTimeout) { this.scriptTimeout = scriptTimeout; return this; } final public double getScriptTimeout(){ return scriptTimeout(); } public double scriptTimeout() { return scriptTimeout; } double scriptTimeout = 10.0; final public JLeftArrowScriptIDE setShowTitle(boolean showTitle){ return showTitle(showTitle); } public JLeftArrowScriptIDE showTitle(boolean showTitle) { this.showTitle = showTitle; return this; } final public boolean getShowTitle(){ return showTitle(); } public boolean showTitle() { return showTitle; } transient boolean showTitle = true; transient IF0<GazelleV_LeftArrowScriptParser> makeParser; GazelleV_LeftArrowScriptParser makeParser() { return makeParser != null ? makeParser.get() : makeParser_base(); } final GazelleV_LeftArrowScriptParser makeParser_fallback(IF0<GazelleV_LeftArrowScriptParser> _f) { return _f != null ? _f.get() : makeParser_base(); } GazelleV_LeftArrowScriptParser makeParser_base() { return g22utils.leftArrowParser(); } class LeftArrowCompletionProvider extends DefaultCompletionProvider { @Override public List<Completion> getCompletionsImpl(JTextComponent comp) { try { String text = getText(comp); GazelleV_LeftArrowScriptParser parser = makeParser2(); LeftArrowScriptAutoCompleter completer = new LeftArrowScriptAutoCompleter(g22utils, parser); enableScaffolding(completer); completer.seek(text, getCaretPosition(comp)); return map(completer.searcher().withScores(), completion -> { BasicCompletion c = new BasicCompletion(this, completion.get()); c.setRelevance((int) completion.score()); return c; }); } catch (Throwable e) { printStackTrace(e); return ll(); } } } JComponent wrapStatusLabel(JComponent lbl) { popDownButton = swing(() -> new JPopDownButton("Help")); popDownButton.onFillingMenu(menu -> { addMenuItems(menu, "Show Scripting Help", runnableThread(new Runnable() { public void run() { try { showTextWordWrapped("Gazelle 'Left arrow script' Help", helpText) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "showTextWordWrapped(\"Gazelle 'Left arrow script' Help\", helpText)"; }}), "Show Global Class Names", runnableThread(new Runnable() { public void run() { try { showGlobalClassNames(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "showGlobalClassNames();"; }})); }); return centerAndEastWithMargin( jBorderlessHigherScrollPane(lbl), jfullcenter(buttons)); } public transient JComponent visualize_cache; public JComponent visualize() { if (visualize_cache == null) visualize_cache = visualize_load(); return visualize_cache;} public JComponent visualize_load() { return markVisualizer(this, visualize_impl()); } JComponent visualize_impl() { return swing(() -> { taScript = g22utils.newSyntaxTextArea(__1 -> wrapStatusLabel(__1)); bindTextComponentToLiveValue_bothWays(textArea(), lvScript); onCtrlEnter(textArea(), runnableThread(new Runnable() { public void run() { try { runScript(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "runScript();"; }})); awtCalcEvery(textArea(), 1.0, () -> compileQ.add(new Runnable() { public void run() { try { compile(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "compile();"; }})); installCompletionProvider(completionProvider = new LeftArrowCompletionProvider(), textArea()); JComponent vis = taScript.visualize(); if (showTitle) vis = jCenteredSection(sectionTitle, vis); vis = wrapSection(vis); addAll(buttons, btnRun = jbutton("Run" , runnableThread(new Runnable() { public void run() { try { runScript(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "runScript();"; }})), popDownButton); if (withResultPanel) { collapsibleResultPanel = new CollapsibleLeftPanel(false, "Result", scpResult, vis); collapsibleResultPanel.sidePanelMargins = c -> withTopAndLeftMargin(c); return collapsibleResultPanel.visualize(); } else return vis; }); } transient IF1<JComponent, JComponent> wrapSection; JComponent wrapSection(JComponent c) { return wrapSection != null ? wrapSection.get(c) : wrapSection_base(c); } final JComponent wrapSection_fallback(IF1<JComponent, JComponent> _f, JComponent c) { return _f != null ? _f.get(c) : wrapSection_base(c); } JComponent wrapSection_base(JComponent c) { return c; } RSyntaxTextArea textArea() { if (taScript == null) visualize(); return taScript.textArea(); } void setText(String text) { main.setText(textArea(), text); } boolean visible() { return isShowing(textArea()); } void compile() { var script = lvScript.get(); var result = compileResult; if (result == null || !eq(result.script, script)) { try { result = newCompileResult(); result.script = script; result.parser = makeParser(); result.parsedScript = result.parser.parse(result.script); print(result.parsedScript); } catch (Throwable e) { print(e); result.compileError = e; } compileResult(result); showStatus(str(compileResult)); updateRunButtonState(); } } void updateRunButtonState() { setEnabled(btnRun, runButtonShouldBeEnabled()); } transient IF0<Boolean> runButtonShouldBeEnabled; boolean runButtonShouldBeEnabled() { return runButtonShouldBeEnabled != null ? runButtonShouldBeEnabled.get() : runButtonShouldBeEnabled_base(); } final boolean runButtonShouldBeEnabled_fallback(IF0<Boolean> _f) { return _f != null ? _f.get() : runButtonShouldBeEnabled_base(); } boolean runButtonShouldBeEnabled_base() { return compileResult != null && compileResult.runnable(); } LASCompileResult freshCompileResult() { runInQAndWait(compileQ, new Runnable() { public void run() { try { compile(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "compile();"; }}); return compileResult; } GazelleV_LeftArrowScript.Script parsedScript() { return freshCompileResult().parsedScript; } transient Runnable runScript; void runScript() { if (runScript != null) runScript.run(); else runScript_base(); } final void runScript_fallback(Runnable _f) { if (_f != null) _f.run(); else runScript_base(); } void runScript_base() { var result = freshCompileResult(); if (result.parsedScript != null) { var value = okOrError(() -> callCompiledObjectWithTimeout(result.parsedScript)); showScriptResult(value); } } void showScriptResult(OKOrError result) { if (result.isOK()) { setStatus(shorten(g22utils.stringify(result.get()))); { if (scpResult != null) scpResult.set(new G22JavaObjectVisualizer(g22utils, result.get())); } } else { setStatus(exceptionToStringShorter_dontDropOuterExceptions(result.error())); { if (scpResult != null) scpResult.set(jErrorView(result.getError())); } } collapsibleResultPanel.expand(); } /* compile result used to include result of execution class CompileResult > LASCompileResult { OKOrError result; }*/ final void showStatus(String status){ setStatus(status); } void setStatus(String status) { { if (taScript != null) taScript.setStatus(" " + unnull(status)); } } void showRuntimeError(Throwable e) { showStatus(exceptionToStringShorter(e)); } transient IF0<VarContext> makeVarContextForExecution; VarContext makeVarContextForExecution() { return makeVarContextForExecution != null ? makeVarContextForExecution.get() : makeVarContextForExecution_base(); } final VarContext makeVarContextForExecution_fallback(IF0<VarContext> _f) { return _f != null ? _f.get() : makeVarContextForExecution_base(); } VarContext makeVarContextForExecution_base() { return new VarContext(); } Object callCompiledObjectWithTimeout(GazelleV_LeftArrowScript.Script object) { return callCompiledObjectWithTimeout(scriptTimeout, object); } Object callCompiledObjectWithTimeout(double timeoutSeconds, GazelleV_LeftArrowScript.Script object) { return callCompiledObjectWithTimeout(scriptTimeout, object, makeVarContextForExecution()); } Object callCompiledObjectWithTimeout(GazelleV_LeftArrowScript.Script object, VarContext ctx) { return callCompiledObjectWithTimeout(scriptTimeout, object, ctx); } Object callCompiledObjectWithTimeout(double timeoutSeconds, GazelleV_LeftArrowScript.Script object, VarContext ctx) { return evalWithTimeoutOrTypedException(timeoutSeconds, () -> object.get(ctx)); } GazelleV_LeftArrowScriptParser makeParser2() { var parser = makeParser(); print("Function containers: " + parser.functionContainers); return parser; } void showGlobalClassNames() { showText("Global Class Names", pnlToString(toCIMap(makeParser().globalClassNames()))); } // only after visualize void setEditable(boolean b) { main.setEditable(textArea(), b); } transient IF0<LASCompileResult> newCompileResult; LASCompileResult newCompileResult() { return newCompileResult != null ? newCompileResult.get() : newCompileResult_base(); } final LASCompileResult newCompileResult_fallback(IF0<LASCompileResult> _f) { return _f != null ? _f.get() : newCompileResult_base(); } LASCompileResult newCompileResult_base() { return new LASCompileResult(); } } static class RSyntaxTextAreaWithSearch implements SearchListener, Swingable { JPanel panel; final public RSyntaxTextArea getTextArea(){ return textArea(); } public RSyntaxTextArea textArea() { return textArea; } RSyntaxTextArea textArea; CollapsibleSectionPanel csp; FindDialog findDialog; ReplaceDialog replaceDialog; FindToolBar findToolBar; ReplaceToolBar replaceToolBar; StatusBar statusBar; RSyntaxTextAreaWithSearch() { init(); } RSyntaxTextAreaWithSearch(IF1<JComponent, JComponent> wrapStatusLabel) { this.wrapStatusLabel = wrapStatusLabel; init(); } RSyntaxTextAreaWithSearch(String text) { init(); setText(text); } RSyntaxTextAreaWithSearch(RSyntaxTextArea textArea) { this.textArea = textArea; init(); } public JComponent visualize() { return panel; } void init() { swing(() -> { rsyntaxTextArea_fixNumPad(); initSearchDialogs(); panel = new JPanel(new BorderLayout()); csp = new CollapsibleSectionPanel(); panel.add(csp); if (textArea == null) textArea = javaxSyntaxTextArea(); RTextScrollPane sp = new RTextScrollPane(textArea); csp.add(sp); ErrorStrip errorStrip = new ErrorStrip(textArea); panel.add(errorStrip, BorderLayout.LINE_END); statusBar = new StatusBar(); panel.add(statusBar, BorderLayout.SOUTH); }); } transient IF1<JComponent, JComponent> wrapStatusLabel; JComponent wrapStatusLabel(JComponent label) { return wrapStatusLabel != null ? wrapStatusLabel.get(label) : wrapStatusLabel_base(label); } final JComponent wrapStatusLabel_fallback(IF1<JComponent, JComponent> _f, JComponent label) { return _f != null ? _f.get(label) : wrapStatusLabel_base(label); } JComponent wrapStatusLabel_base(JComponent label) { return label; } void addItem(Action a, ButtonGroup bg, JMenu menu) { JRadioButtonMenuItem item = new JRadioButtonMenuItem(a); bg.add(item); menu.add(item); } // register keystrokes if we don't have a menu bar void menuLessOperation() { swing(() -> { //print("menuLessOperation"); var mb = createMenuBar(); for (var menu : getMenus(mb)) for (var menuItem : getMenuItems(menu)) { var ks = menuItem.getAccelerator(); //printVars(+ks, +menuItem); if (ks != null) { var action = menuItem.getAction(); //print("Registering keystroke: " + ks + " => " + action); textArea.getInputMap().put(ks, action); } } }); } JMenuBar createMenuBar() { JMenuBar mb = new JMenuBar(); JMenu menu = new JMenu("Search"); menu.add(new JMenuItem(new ShowFindDialogAction())); menu.add(new JMenuItem(new ShowReplaceDialogAction())); menu.add(new JMenuItem(new GoToLineAction())); menu.addSeparator(); int ctrl = getToolkit().getMenuShortcutKeyMask(); int shift = InputEvent.SHIFT_MASK; KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F, ctrl|shift); Action a = csp.addBottomComponent(ks, findToolBar); a.putValue(Action.NAME, "Show Find Search Bar"); menu.add(new JMenuItem(a)); ks = KeyStroke.getKeyStroke(KeyEvent.VK_H, ctrl|shift); a = csp.addBottomComponent(ks, replaceToolBar); a.putValue(Action.NAME, "Show Replace Search Bar"); menu.add(new JMenuItem(a)); mb.add(menu); return mb; } public String getSelectedText() { return textArea.getSelectedText(); } /** * Creates our Find and Replace dialogs. */ public void initSearchDialogs() { findDialog = new FindDialog((java.awt.Dialog) null, this); replaceDialog = new ReplaceDialog((java.awt.Dialog) null, this); // This ties the properties of the two dialogs together (match case, // regex, etc.). SearchContext context = findDialog.getSearchContext(); replaceDialog.setSearchContext(context); // Create tool bars and tie their search contexts together also. findToolBar = new FindToolBar(this); findToolBar.setSearchContext(context); replaceToolBar = new ReplaceToolBar(this); replaceToolBar.setSearchContext(context); } /** * Listens for events from our search dialogs and actually does the dirty * work. */ @Override public void searchEvent(SearchEvent e) { SearchEvent.Type type = e.getType(); SearchContext context = e.getSearchContext(); SearchResult result = null; switch (type) { default: // Prevent FindBugs warning later case MARK_ALL: result = SearchEngine.markAll(textArea, context); break; case FIND: result = SearchEngine.find(textArea, context); if (!result.wasFound()) { //Try to wrap the result... Caret c = textArea.getCaret(); int pos = c.getDot(); if (context.getSearchForward()) { c.setDot(0); } else { c.setDot(textArea.getDocument().getLength()); } result = SearchEngine.find(textArea, context); if (!result.wasFound()) { c.setDot(pos); UIManager.getLookAndFeel().provideErrorFeedback(textArea); } } break; case REPLACE: result = SearchEngine.replace(textArea, context); if (!result.wasFound()) { UIManager.getLookAndFeel().provideErrorFeedback(textArea); } break; case REPLACE_ALL: result = SearchEngine.replaceAll(textArea, context); JOptionPane.showMessageDialog(null, result.getCount() + " occurrences replaced."); break; } String text = null; if (result.wasFound()) { text = "Text found; occurrences marked: " + result.getMarkedCount(); } else if (type==SearchEvent.Type.MARK_ALL) { if (result.getMarkedCount()>0) { text = "Occurrences marked: " + result.getMarkedCount(); } else { text = ""; } } else { text = "Text not found"; } setStatus(text); } class GoToLineAction extends AbstractAction { GoToLineAction() { super("Go To Line..."); int c = getToolkit().getMenuShortcutKeyMask(); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_L, c)); } public void actionPerformed(ActionEvent e) { findDialog.setVisible(false); replaceDialog.setVisible(false); GoToDialog dialog = new GoToDialog((java.awt.Dialog) null); dialog.setMaxLineNumberAllowed(textArea.getLineCount()); showDialogOnSameScreen(dialog, panel); int line = dialog.getLineNumber(); if (line>0) { try { textArea.setCaretPosition(textArea.getLineStartOffset(line-1)); } catch (BadLocationException ble) { // Never happens UIManager.getLookAndFeel().provideErrorFeedback(textArea); ble.printStackTrace(); } } } } class ShowFindDialogAction extends AbstractAction { ShowFindDialogAction() { super("Find..."); int c = getToolkit().getMenuShortcutKeyMask(); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F, c)); } public void actionPerformed(ActionEvent e) { replaceDialog.setVisible(false); showDialogOnSameScreen(findDialog, panel); } } class ShowReplaceDialogAction extends AbstractAction { ShowReplaceDialogAction() { super("Replace..."); int c = getToolkit().getMenuShortcutKeyMask(); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_H, c)); } public void actionPerformed(ActionEvent e) { findDialog.setVisible(false); showDialogOnSameScreen(replaceDialog, panel); } } class StatusBar extends JPanel { JLabel label; StatusBar() { label = new JLabel(" "); setLayout(new BorderLayout()); add(wrapStatusLabel(label), BorderLayout.CENTER); add(new JLabel(new SizeGripIcon()), BorderLayout.EAST); } public void setText(String label) { main.setText(this.label, label); } } void setStatus(String text) { statusBar.setText(text); } void setText(String text) { main.setText(textArea, text); } String getText() { return main.getText(textArea); } void setEditorFont(final Font font) { swing(() -> { SyntaxScheme ss = textArea.getSyntaxScheme(); ss = (SyntaxScheme) ss.clone(); for (int i = 0; i < ss.getStyleCount(); i++) if (ss.getStyle(i) != null) ss.getStyle(i).font = font; textArea.setSyntaxScheme(ss); textArea.setFont(font); }); } Font getEditorFont() { return swing(new F0<Font>() { public Font get() { try { return textArea.getFont(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return textArea.getFont();"; }}); } int getEditorFontSize() { return getEditorFont().getSize(); } void setEditorFontSize(int size) { setEditorFont(deriveFont(getEditorFont(), size)); } RSyntaxDocument getDocument() { return (RSyntaxDocument) textArea.getDocument(); } Toolkit getToolkit() { return panel.getToolkit(); } } static class G22Label extends ConceptWithGlobalID { String name; //new RefL examples; public String toString() { return /*"Label " +*/ name; } final boolean textIs(String text){ return nameIs(text); } boolean nameIs(String text) { return eqic(name, text); } } static class ImageSurface_PositionToolTip extends ImageSurfaceMouseHandler { ImageSurface_PositionToolTip(ImageSurface is) { if (containsInstance(is.tools, ImageSurface_PositionToolTip.class)) return; register(is); } final public void mouseDragged(MouseEvent e){ mouseMoved(e); } public void mouseMoved(MouseEvent e) { setToolTip(is, getPt(e)); } } static class ListAndIndex<A> implements IFieldsToList{ static final String _fieldOrder = "list idx"; List<A> list; int idx; ListAndIndex() {} ListAndIndex(List<A> list, int idx) { this.idx = idx; this.list = list;} public boolean equals(Object o) { if (!(o instanceof ListAndIndex)) return false; ListAndIndex __1 = (ListAndIndex) o; return eq(list, __1.list) && idx == __1.idx; } public int hashCode() { int h = 276903961; h = boostHashCombine(h, _hashCode(list)); h = boostHashCombine(h, _hashCode(idx)); return h; } public Object[] _fieldsToList() { return new Object[] {list, idx}; } boolean atEnd() { return idx >= l(list); } A get() { return _get(list, idx); } int size() { return l(list); } public String toString() { return subList(list, 0, idx) + ", then " + subList(list, idx); } ListAndIndex<A> plus(int ofs) { return new ListAndIndex(list, idx+ofs); } List<A> list() { return list; } final int idx(){ return index(); } int index() { return idx; } } static class JFastLogView_noWrap extends JComponent implements Scrollable { List<String> 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<String> lines) { List<String> 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 class TokenRange extends IntRange { TokenRange() {} TokenRange(int start, int end) { this.end = end; this.start = start;} } static interface G22MasterStuff { void openDatabase(File dir); void closeDatabase(File dir); Collection<File> openConceptDirs(); G22MasterStuff onLoadedDBsChange(Runnable r); G22MasterStuff removeLoadedDBsChangeListener(Runnable r); IF1<String, Class> makeClassFinder(); } static abstract class VF2<A, B> { abstract void get(A a, B b); } static class ConceptWithGlobalID extends Concept implements IHasGlobalID { GlobalID globalID = aGlobalIDObjUnlessLoading(); public GlobalID globalID() { return globalID; } public String globalIDStr() { return strOrNull(globalID); } String prependGlobalID(String s) { return globalID == null ? s : "[" + globalID + "] " + s; } } static class OKOrError<A> implements IF0<A> { A value; final public Throwable getError(){ return error(); } public Throwable error() { return error; } Throwable error; OKOrError() {} OKOrError(A value) { this.value = value;} OKOrError(boolean dummy, Throwable error) { this.error = error; assertNotNull(error); } final boolean isOK(){ return ok(); } boolean ok() { return error == null; } public String toString() { return ok() ? /*"OK: " +*/ str(value) : "Error: " + str(error); } public A get() { return !ok() ? null : value; } public A getMandatory() { if (!ok()) throw rethrow(error); return value; } static <A> OKOrError<A> ok(A a) { return new OKOrError(a); } static OKOrError error(Throwable error) { return new OKOrError(false, error); } } static class LASCompileResult { final public LASCompileResult setScript(String script){ return script(script); } public LASCompileResult script(String script) { this.script = script; return this; } final public String getScript(){ return script(); } public String script() { return script; } String script; final public LASCompileResult setParser(GazelleV_LeftArrowScriptParser parser){ return parser(parser); } public LASCompileResult parser(GazelleV_LeftArrowScriptParser parser) { this.parser = parser; return this; } final public GazelleV_LeftArrowScriptParser getParser(){ return parser(); } public GazelleV_LeftArrowScriptParser parser() { return parser; } GazelleV_LeftArrowScriptParser parser; final public LASCompileResult setCompileError(Throwable compileError){ return compileError(compileError); } public LASCompileResult compileError(Throwable compileError) { this.compileError = compileError; return this; } final public Throwable getCompileError(){ return compileError(); } public Throwable compileError() { return compileError; } Throwable compileError; /*settable broken*/ GazelleV_LeftArrowScript.Script parsedScript; public String toString() { return compileError != null ? exceptionToStringShorter_dontDropOuterExceptions(compileError) : "Compiled OK"; } boolean runnable() { return parsedScript != null; } void compile() { try { parsedScript = parser.parse(script); print(parsedScript); } catch (Throwable e) { print(e); compileError(e); } } GazelleV_LeftArrowScript.Script parsedScript() { return parsedScript; } GazelleV_LeftArrowScript.Script parsedScriptMandatory() { if (compileError != null) throw fail(compileError); return parsedScript; } } static class WithTimestamp<A> extends Var<A> { long timestamp; final Timestamp timestamp(){ return getTimestamp(); } final Timestamp timeStamp(){ return getTimestamp(); } Timestamp getTimestamp() { return timestamp == 0 ? null : new Timestamp(timestamp); } WithTimestamp() {} WithTimestamp(long timestamp, A value) { super(value); this.timestamp = timestamp; } WithTimestamp(A value) { this(now(), value); } Timestamp timestampObj() { return timestamp == 0 ? null : new Timestamp(timestamp); } public String toString() { return toStringWithTimestamp(super.toString()); } String toStringQuoted() { return toStringWithTimestamp(quote(super.toString())); } String toStringWithTimestamp(String s) { return s + " at " + localDateWithSeconds(timestamp); } boolean olderThanMinutes(double minutes) { return elapsedMinutes_timestamp(timestamp) >= minutes; } boolean olderThanSeconds(double seconds) { return elapsedSeconds_timestamp(timestamp) >= seconds; } } static class CollapsibleLeftPanel extends MetaWithChangeListeners implements Swingable { public transient FieldVar<String> varSidePanelName_cache; public FieldVar<String> varSidePanelName() { if (varSidePanelName_cache == null) varSidePanelName_cache = varSidePanelName_load(); return varSidePanelName_cache;} public FieldVar<String> varSidePanelName_load() { return new FieldVar<String>(this, "sidePanelName", () -> sidePanelName(), sidePanelName -> sidePanelName(sidePanelName)); } final public CollapsibleLeftPanel setSidePanelName(String sidePanelName){ return sidePanelName(sidePanelName); } public CollapsibleLeftPanel sidePanelName(String sidePanelName) { if (!eq(this.sidePanelName, sidePanelName)) { this.sidePanelName = sidePanelName; change(); } return this; } final public String getSidePanelName(){ return sidePanelName(); } public String sidePanelName() { return sidePanelName; } volatile String sidePanelName; JComponent sideComponent, mainComponent; JSplitPane splitPane; public transient FieldVar<Boolean> varExpanded_cache; public FieldVar<Boolean> varExpanded() { if (varExpanded_cache == null) varExpanded_cache = varExpanded_load(); return varExpanded_cache;} public FieldVar<Boolean> varExpanded_load() { return new FieldVar<Boolean>(this, "expanded", () -> expanded(), expanded -> expanded(expanded)); } final public CollapsibleLeftPanel setExpanded(boolean expanded){ return expanded(expanded); } public CollapsibleLeftPanel expanded(boolean expanded) { if (!eq(this.expanded, expanded)) { this.expanded = expanded; change(); } return this; } final public boolean getExpanded(){ return expanded(); } public boolean expanded() { return expanded; } volatile boolean expanded = false; SingleComponentPanel scp = singleComponentPanel(); CollapsibleLeftPanel(boolean expanded, String sidePanelName, JComponent sideComponent, JComponent mainComponent) { this.mainComponent = mainComponent; this.sideComponent = sideComponent; this.sidePanelName = sidePanelName; this.expanded = expanded; } void expand() { if (scaffolding()) print("CollapsibleLeftPanel.expand"); setExpanded(true); } void collapse() { setExpanded(false); } void updateScp() { swing(() -> { if (metaGet("scaffolding") != null) scaffoldCalled(this, "updateScp"); if (expanded) { splitPane().setRightComponent(mainComponent); scp.set(splitPane()); } else scp.set(westAndCenterWithMargin( sidePanelMargins(vstack(expandButton())), mainComponent)); }); } JComponent expandButton() { return jClickableImage_instantToolTip("#1103075", "Expand " + sidePanelName, new Runnable() { public void run() { try { expand(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "expand();"; }}); } JComponent collapseButton() { return jClickableImage_instantToolTip("#1103076", "Collapse " + sidePanelName, new Runnable() { public void run() { try { collapse(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "collapse();"; }}); } JComponent wrappedSideComponent() { return sidePanelMargins(northAndCenterWithMargin( westAndCenterWithMargin(collapseButton(), liveValueLabel(varSidePanelName())), sideComponent )); } JSplitPane splitPane() { if (scaffolding()) print("splitPane " + splitPane); if (splitPane == null) { splitPane = jhsplit(0.25, wrappedSideComponent(), null); varExpanded().onChangeAndNow(new Runnable() { public void run() { try { updateScp(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "updateScp();"; }}); } return splitPane; } transient IF1<JComponent, JComponent> sidePanelMargins; JComponent sidePanelMargins(JComponent c) { return sidePanelMargins != null ? sidePanelMargins.get(c) : sidePanelMargins_base(c); } final JComponent sidePanelMargins_fallback(IF1<JComponent, JComponent> _f, JComponent c) { return _f != null ? _f.get(c) : sidePanelMargins_base(c); } JComponent sidePanelMargins_base(JComponent c) { return c; } public JComponent visualize() { splitPane(); return scp; } } static class JPopDownButton extends JButton { String menuPosition = "left"; // or "center" or "right" JPopDownButton() { this(""); } JPopDownButton(String text) { super(joinNemptiesWithSpace(text, unicode_downPointingTriangle())); main.addActionListener(this, new Runnable() { public void run() { try { openMenu(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "openMenu();"; }}); } JPopDownButton(IVF1<JPopupMenu> fillMenu) { this(); this.fillMenu = fillMenu; } transient Set<IVF1<JPopupMenu>> onFillingMenu; public JPopDownButton onFillingMenu(IVF1<JPopupMenu> f) { onFillingMenu = createOrAddToSyncLinkedHashSet(onFillingMenu, f); return this; } public JPopDownButton removeFillingMenuListener(IVF1<JPopupMenu> f) { main.remove(onFillingMenu, f); return this; } public void fillingMenu(JPopupMenu menu) { pcallFAll(onFillingMenu, menu); } transient IVF1<JPopupMenu> fillMenu; void fillMenu(JPopupMenu menu) { if (fillMenu != null) fillMenu.get(menu); else fillMenu_base(menu); } final void fillMenu_fallback(IVF1<JPopupMenu> _f, JPopupMenu menu) { if (_f != null) _f.get(menu); else fillMenu_base(menu); } void fillMenu_base(JPopupMenu menu) { fillingMenu(menu); } void openMenu() { JPopupMenu menu = new JPopupMenu(); int emptyCount = menu.getComponentCount(); fillMenu(menu); if (menu.getComponentCount() == emptyCount) return; int x = 0; if (eq(menuPosition, "center")) x = (getWidth()-getPreferredWidth(menu))/2; else if (eq(menuPosition, "right")) x = getWidth()-getPreferredWidth(menu); menu.show(this, x, getHeight()); } } interface IStringifier<A> { public String toString(A o); } abstract static class Surface extends JPanel implements IMeta { public boolean clearSurface = true; private boolean clearOnce = false; // Meta - a "minimal" approach to adding meta-level to Java objects // (implementing the interface IMeta) // We allocate one extra field for each Java object to make it // reasoning-compatible (reasoning-compatible = extensible with // fields of any name at runtime). // // We couldn't go for 0 extra fields (meta values must be linked // directly from the object) 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).] ////////////////////// // The "meta" field // ////////////////////// // 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; } // MOST functions are implemented in IMeta (default implementations) // Scaffolding convenience functions final boolean scaffolding(){ return scaffoldingEnabled(); } boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); } boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); } Surface() { setDoubleBuffered(false); } Graphics2D createGraphics2D(int width, int height, Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setBackground(getBackground()); if (clearSurface || clearOnce) { g2.clearRect(0, 0, width, height); clearOnce = false; } return g2; } public abstract void render(int w, int h, Graphics2D g); public void paintImmediately(int x,int y,int w, int h) { RepaintManager repaintManager = null; boolean save = true; if (!isDoubleBuffered()) { repaintManager = RepaintManager.currentManager(this); save = repaintManager.isDoubleBufferingEnabled(); repaintManager.setDoubleBufferingEnabled(false); } super.paintImmediately(x, y, w, h); if (repaintManager != null) repaintManager.setDoubleBufferingEnabled(save); } public void paint(Graphics g) { Dimension d = getSize(); Graphics2D g2 = createGraphics2D(d.width, d.height, g); render(d.width, d.height, g2); g2.dispose(); } } static class ConceptWithChangeListeners extends Concept implements IHasChangeListeners, ChangeTriggerable { transient Set<Runnable> onChange; public ConceptWithChangeListeners onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public ConceptWithChangeListeners removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void fireChange() { pcallFAll(onChange); } void _onChange() { super._onChange(); fireChange(); } public void change() { super.change(); } } static class SimpleLiveValue<A> extends LiveValue<A> implements IVarWithNotify<A> { transient Set<Runnable> onChange; public SimpleLiveValue<A> onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public SimpleLiveValue<A> removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void change() { pcallFAll(onChange); } Class<A> type; volatile A value; SimpleLiveValue(Class<A> type) { this.type = type;} SimpleLiveValue(Class<A> type, A value) { this.value = value; this.type = type;} public Class<A> getType() { return type; } public A get() { return value; } void fireChanged() { change(); } public void set(A a) { if (neq(value, a)) { value = a; fireChanged(); } } } static class RunResultWithTimestamps<A> { final public RunResultWithTimestamps<A> setStarted(Timestamp started){ return started(started); } public RunResultWithTimestamps<A> started(Timestamp started) { this.started = started; return this; } final public Timestamp getStarted(){ return started(); } public Timestamp started() { return started; } Timestamp started; final public RunResultWithTimestamps<A> setEnded(Timestamp ended){ return ended(ended); } public RunResultWithTimestamps<A> ended(Timestamp ended) { this.ended = ended; return this; } final public Timestamp getEnded(){ return ended(); } public Timestamp ended() { return ended; } Timestamp ended; final public RunResultWithTimestamps<A> setResult(OKOrError<A> result){ return result(result); } public RunResultWithTimestamps<A> result(OKOrError<A> result) { this.result = result; return this; } final public OKOrError<A> getResult(){ return result(); } public OKOrError<A> result() { return result; } OKOrError<A> result; final public RunResultWithTimestamps<A> setPrintOutput(CharSequence printOutput){ return printOutput(printOutput); } public RunResultWithTimestamps<A> printOutput(CharSequence printOutput) { this.printOutput = printOutput; return this; } final public CharSequence getPrintOutput(){ return printOutput(); } public CharSequence printOutput() { return printOutput; } CharSequence printOutput; void run(IF0<A> f) { printOutput(hijackPrint_tee(new Runnable() { public void run() { try { started(tsNow()); result(okOrError(f)); ended(tsNow()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "started(tsNow());\r\n result(okOrError(f));\r\n ended(tsNow());"; }})); } } static class FieldVar<A> extends VarWithNotify<A> { IHasChangeListeners containingObject; String fieldName; IF0<A> getter; IVF1<A> setter; FieldVar(IHasChangeListeners containingObject, String fieldName, IF0<A> getter, IVF1<A> setter) { this.setter = setter; this.getter = getter; this.fieldName = fieldName; this.containingObject = containingObject; containingObject.onChangeAndNow(() -> _updateFromObject()); } void _updateFromObject() { set(getter.get()); } public void fireChange() { setter.get(get()); super.fireChange(); } public FieldVar<A> onChange(IVF1<A> r) { if (r != null) onChange(() -> r.get(get())); return this; } } interface G2Drawable { void drawOn(Graphics2D g); default void drawOn(BufferedImage img) { drawOn(img.createGraphics()); } } static class VarMatches extends WrappedMap<String, String> { VarMatches() { super(new LinkedHashMap()); } VarMatches(Map<String, String> map) { this(); main.putAll(this, map); } } static class SingleComponentPanel extends JPanel { SingleComponentPanel() { super(new BorderLayout()); } SingleComponentPanel(Component component) { this(); if (component != null) setComponent(component); } void set(Swingable component) { set(wrap(component)); } final void set(Component component){ setComponent(component); } void setComponent(Component component) { { swing(() -> { if (getComponent() == component) return; removeAll(); if (component != null) add(BorderLayout.CENTER, wrap(component)); _revalidate(SingleComponentPanel.this); }); } } // Sometimes we need this? void setComponentAndRevalidateParent(Component component) { setComponent(component); _revalidate(_getParent(this)); } final void clear(){ noComponent(); } void noComponent() { setComponent(null); } Component getComponent() { return swing( () -> getComponentCount() == 0 ? null : getComponent(0)); } boolean isEmpty() { return getComponent() == null; } } static class ImageSurfaceSelector extends ImageSurfaceMouseHandler { Point startingPoint; boolean enabled = true; static boolean verbose = false; ImageSurfaceSelector() {} ImageSurfaceSelector(ImageSurface is) { if (containsInstance(is.tools, ImageSurfaceSelector.class)) return; this.register(is); } public void mousePressed(MouseEvent evt) { if (verbose) print("mousePressed"); if (evt.getButton() != MouseEvent.BUTTON1) return; if (enabled) startingPoint = getPoint(evt); } public void mouseDragged(MouseEvent e) { if (verbose) print("mouseDragged"); if (startingPoint != null) { Point endPoint = getPoint(e); Rectangle r = new Rectangle(startingPoint, new Dimension(endPoint.x-startingPoint.x+1, endPoint.y-startingPoint.y+1)); normalize(r); r.width = min(r.width, is.getImage().getWidth()-r.x); r.height = min(r.height, is.getImage().getHeight()-r.y); is.setSelection(r); } if (verbose) print("mouseDragged done"); } public static void normalize(Rectangle r) { if (r.width < 0) { r.x += r.width; r.width = -r.width; } if (r.height < 0) { r.y += r.height; r.height = -r.height; } } public void mouseReleased(MouseEvent e) { if (verbose) print("mouseReleased"); mouseDragged(e); if (getPoint(e).equals(startingPoint)) is.setSelection((Rectangle) null); startingPoint = null; } } abstract static class ImageSurfaceMouseHandler extends MouseAdapter implements AutoCloseable { ImageSurface is; void register(ImageSurface is) { this.is = is; is.tools.add(this); is.addMouseListener(this); is.addMouseMotionListener(this); } public void close() { try { if (is == null) return; is.tools.remove(this); is.removeMouseListener(this); is.removeMouseMotionListener(this); is = null; } catch (Exception __e) { throw rethrow(__e); } } Pt getPt(MouseEvent e) { return toPt(getPoint(e)); } Point getPoint(MouseEvent e) { return new Point((int) (e.getX()/is.getZoomX()), (int) (e.getY()/is.getZoomY())); } ImageSurface getImageSurface() { return is; } } enum G22ScriptMode { edit, saved, autoRunnable } static interface Swingable { JComponent visualize(); } static class LeftArrowScriptAutoCompleter extends Meta implements IFieldsToList{ GazelleV_LeftArrowScriptParser parser; LeftArrowScriptAutoCompleter() {} LeftArrowScriptAutoCompleter(GazelleV_LeftArrowScriptParser parser) { this.parser = parser;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + parser + ")"; }public Object[] _fieldsToList() { return new Object[] {parser}; } List<String> tok; CharInToken cursor; String typedCharacters; ScoredStringSearcher searcher; final public LeftArrowScriptAutoCompleter setG22utils(G22Utils g22utils){ return g22utils(g22utils); } public LeftArrowScriptAutoCompleter g22utils(G22Utils g22utils) { this.g22utils = g22utils; return this; } final public G22Utils getG22utils(){ return g22utils(); } public G22Utils g22utils() { return g22utils; } G22Utils g22utils; LeftArrowScriptAutoCompleter(G22Utils g22utils, GazelleV_LeftArrowScriptParser parser) { this.parser = parser; this.g22utils = g22utils;} void seek(String text, int iChar) { parser.setText(text); parser.init(); tok = parser.tok; cursor = charIndexToCharInToken(tok, iChar); // pointing to space? move left to code token if (even(cursor.iTok) && cursor.iTok > 0 && cursor.iChar == 0) { cursor.iTok--; cursor.iChar = l(token()); } // move one token left if necessary if (cursor.iTok > 1 && cursor.iChar == 0 && empty(space())) { cursor.iTok -= 2; cursor.iChar = l(token()); } typedCharacters = takeFirst(cursor.token(), cursor.iChar); } String typedCharacters() { return typedCharacters; } String space() { return get(tok, cursor.iTok-1); } String token() { return get(tok, cursor.iTok); } String prevToken() { return get(tok, cursor.iTok-2); } boolean beginningOfCmd() { return eqOneOf(prevToken(), ";", "{", "}") || containsNewLine(space()); } Collection<String> getCompletions() { //ret searcher()!; return searcher().get_transformListWithinScore(__1 -> stringsSortedByLength(__1)); } ScoredStringSearcher searcher() { searcher = new ScoredStringSearcher(typedCharacters); searcher.uniquify(true); if (empty(typedCharacters)) searcher.returnAll = true; String prev = prevToken(); if (scaffoldingEnabled(this)) printVars("LeftArrowScriptAutoCompleter", "beginningOfCmd", beginningOfCmd(), "prev", prev, "cursor", cursor, "typedCharacters", typedCharacters, "globalClassNames" , l(parser.globalClassNames())); if (beginningOfCmd() || !isIdentifier(prev) || eq(prev, "new")) { addToSearcher(globalMethodNames()); } addToSearcher(keys(parser.globalClassNames())); addToSearcher("true", "false", "null"); return searcher; } transient IF0<Collection<String>> globalMethodNames; Collection<String> globalMethodNames() { return globalMethodNames != null ? globalMethodNames.get() : globalMethodNames_base(); } final Collection<String> globalMethodNames_fallback(IF0<Collection<String>> _f) { return _f != null ? _f.get() : globalMethodNames_base(); } Collection<String> globalMethodNames_base() { return concatMap(__91 -> methodNames(__91), parser.functionContainers); } void addToSearcher(String... l) { addToSearcher(asList(l)); } void addToSearcher(Iterable<String> l) { for (var s : unnullForIteration(l)) if (!eq(s, typedCharacters)) searcher.add(s); } } static class PersistableOKOrError<A> implements IF0<A> { A value; final public PersistableThrowable getError(){ return error(); } public PersistableThrowable error() { return error; } PersistableThrowable error; PersistableOKOrError() {} PersistableOKOrError(A value) { this.value = value;} PersistableOKOrError(boolean dummy, PersistableThrowable error) { this.error = error; assertNotNull(error); } final boolean isOK(){ return ok(); } boolean ok() { return error == null; } public String toString() { return ok() ? /*"OK: " +*/ str(value) : "Error: " + str(error); } public A get() { return !ok() ? null : value; } public A getMandatory() { if (!ok()) throw error.asRuntimeException(); return value; } static <A> PersistableOKOrError<A> ok(A a) { return new PersistableOKOrError(a); } static PersistableOKOrError error(PersistableThrowable error) { return new PersistableOKOrError(false, error); } } static class BoolVar { boolean a = false; // you can access this directly if you use one thread BoolVar() {} BoolVar(boolean a) { this.a = a;} public synchronized void set() { set(true); } public synchronized void set(boolean v) { if (v != a) { a = v; notifyAll(); } } public synchronized boolean get() { return a; } //public synchronized bool has() { true; } final public void unset(){ clear(); } public void clear() { set(false); } boolean waitUntilTrue () { return waitForValue(true); } boolean waitUntilFalse() { return waitForValue(false); } synchronized boolean waitForValue(boolean wantedValue) { try { while (a != wantedValue) wait(); return a; } catch (Exception __e) { throw rethrow(__e); } } } static class G22JavaObjectVisualizer implements Swingable , IFieldsToList{ G22Utils g22utils; G22JavaObjectVisualizer() {} G22JavaObjectVisualizer(G22Utils g22utils) { this.g22utils = g22utils;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + g22utils + ")"; }public Object[] _fieldsToList() { return new Object[] {g22utils}; } final public G22JavaObjectVisualizer setWithTypeAndTime(boolean withTypeAndTime){ return withTypeAndTime(withTypeAndTime); } public G22JavaObjectVisualizer withTypeAndTime(boolean withTypeAndTime) { this.withTypeAndTime = withTypeAndTime; return this; } final public boolean getWithTypeAndTime(){ return withTypeAndTime(); } public boolean withTypeAndTime() { return withTypeAndTime; } boolean withTypeAndTime = true; final public G22JavaObjectVisualizer setSingleLineLayout(boolean singleLineLayout){ return singleLineLayout(singleLineLayout); } public G22JavaObjectVisualizer singleLineLayout(boolean singleLineLayout) { this.singleLineLayout = singleLineLayout; return this; } final public boolean getSingleLineLayout(){ return singleLineLayout(); } public boolean singleLineLayout() { return singleLineLayout; } boolean singleLineLayout = false; // TODO final public G22JavaObjectVisualizer setMaxElements(int maxElements){ return maxElements(maxElements); } public G22JavaObjectVisualizer maxElements(int maxElements) { this.maxElements = maxElements; return this; } final public int getMaxElements(){ return maxElements(); } public int maxElements() { return maxElements; } int maxElements = 1000; // max elements of collection to show final public G22JavaObjectVisualizer setObject(Object object){ return object(object); } public G22JavaObjectVisualizer object(Object object) { this.object = object; return this; } final public Object getObject(){ return object(); } public Object object() { return object; } transient Object object; final public G22JavaObjectVisualizer setNanos(long nanos){ return nanos(nanos); } public G22JavaObjectVisualizer nanos(long nanos) { this.nanos = nanos; return this; } final public long getNanos(){ return nanos(); } public long nanos() { return nanos; } transient long nanos = -1; G22JavaObjectVisualizer(G22Utils g22utils, Object object) { this.object = object; this.g22utils = g22utils;} public transient JComponent visualize_cache; public JComponent visualize() { if (visualize_cache == null) visualize_cache = visualize_load(); return visualize_cache;} public JComponent visualize_load() { return markVisualizer(this, visualize_impl()); } JComponent visualize_impl() { List<String> infos = new ArrayList(); JComponent c = visualizeObject(object); if (withTypeAndTime) { infos.add(shortClassName(object)); if (object instanceof Collection) infos.add("size " + n2((Collection) object)); } if (withTypeAndTime) { String type = joinNemptiesWithComma(infos); String timeDesc = nanos < 0 ? "" : formatElapsedTimeWithAppropriateUnit(nanosToSeconds(nanos)); c = northAndCenterWithMargins( westAndEastWithMargin( withLabel("Type:", jlabel(type)), empty(timeDesc) ? jpanel() : withLabel("Execution time:", jlabel(timeDesc)) ), c); } return c; } JComponent visualizeObject(Object object) { return mainVisualizer().visualize(object); } ObjectVisualizer mainVisualizer() { return new ObjectVisualizer().recurse(true).bigFont(true); } class ObjectVisualizer { final public ObjectVisualizer setRecurse(boolean recurse){ return recurse(recurse); } public ObjectVisualizer recurse(boolean recurse) { this.recurse = recurse; return this; } final public boolean getRecurse(){ return recurse(); } public boolean recurse() { return recurse; } boolean recurse = false; final public ObjectVisualizer setBigFont(boolean bigFont){ return bigFont(bigFont); } public ObjectVisualizer bigFont(boolean bigFont) { this.bigFont = bigFont; return this; } final public boolean getBigFont(){ return bigFont(); } public boolean bigFont() { return bigFont; } boolean bigFont = false; ObjectVisualizer subVisualizer() { return new ObjectVisualizer(); } JComponent makeBig(JComponent c) { if (bigFont) return fontSizePlus(10, c); return c; } JComponent visualize(Object object) { try { return visualize2(object); } catch (Throwable e) { return jErrorView(new RuntimeException("Visualization error", e)); } } JComponent visualize2(Object object) { // exception if (object instanceof Throwable) return jErrorView((Throwable) object); // null or empty string if (object == null || eq(object, "")) return jpanel(); // number if (object instanceof Integer) object = toLong((Integer) object); if (object instanceof Long) return makeBig(jcenteredLabel(n2((Long) object))); if (object instanceof Number) return makeBig(jcenteredLabel(str((Number) object))); // image if (object instanceof MakesBufferedImage) object = toBufferedImage((MakesBufferedImage) object); if (object instanceof BufferedImage) return jscroll_centered_borderless(g22utils.stdImageSurface((BufferedImage) object)); // collection if (recurse) { if (object instanceof Collection) { var sub = subVisualizer(); return jscroll_centered_borderless(vstackWithSpacing( map(cloneTakeFirst(maxElements, ((Collection) object)), element -> sub.visualize(element)))); } } String string = str(object); if (containsNewLine(string)) return jscroll_borderless(wordWrapTypeWriterTextArea(string)); return jcenteredlabel(shorten(string)); } } } static class ClearForAutoRun<A> extends Var<A> { ClearForAutoRun() {} ClearForAutoRun(A code) { super(code); } } static class Stringifier_ToString implements IStringifier { public String toString(Object o) { return str(o); } } interface IPartialStringifier<A> { // return null if not handled public String toStringOpt(A o); } static class G22TypeDesc { String exactClassName; Set<String> implementedClassNames; // also, superclasses } static List<String> _stickyLibs_1400546 = ll("#1400546", "#1400547", "#1400548"); // RSyntaxTextArea // AutoComplete // RSTAUI static class MetaWithChangeListeners extends Meta implements IHasChangeListeners, ChangeTriggerable { transient Set<Runnable> onChange; public MetaWithChangeListeners onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public MetaWithChangeListeners removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void change() { pcallFAll(onChange); } } static class RGBImage implements MakesBufferedImage, IRGBImage { transient BufferedImage bufferedImage; int width, height; int[] pixels; RGBImage() {} RGBImage(BufferedImage image) { bufferedImage = image; width = image.getWidth(); height = image.getHeight(); pixels = new int[width*height]; var gp = grabbableIntPixels_fastOrSlow(image); if (gp.scanlineStride == width && gp.offset == 0) pixels = gp.data; else { pixels = new int[width*height]; int iIn = 0, iOut = 0; for (int y = 0; y < height; y++) { arrayCopy(gp.data, iIn, pixels, iOut, width); iIn += gp.scanlineStride; iOut += width; } } cleanPixels(); // set upper byte to 0 } RGBImage(Dimension size, Color color) { this(size.width, size.height, color); } RGBImage(Dimension size, RGB color) { this(size.width, size.height, color); } private void cleanPixels() { var pixels = this.pixels; for (int i = 0; i < pixels.length; i++) pixels[i] &= 0xFFFFFF; } RGBImage(int width, int height, int[] pixels) { this.width = width; this.height = height; this.pixels = pixels; } RGBImage(int w, int h, RGB[] pixels) { this.width = w; this.height = h; this.pixels = asInts(pixels); } public static int[] asInts(RGB[] pixels) { int[] ints = new int[pixels.length]; for (int i = 0; i < pixels.length; i++) ints[i] = pixels[i] == null ? 0 : pixels[i].getColor().getRGB(); return ints; } public RGBImage(int w, int h) { this(w, h, Color.black); } RGBImage(int w, int h, RGB rgb) { this.width = w; this.height = h; this.pixels = new int[w*h]; int col = rgb.asInt(); if (col != 0) for (int i = 0; i < pixels.length; i++) pixels[i] = col; } RGBImage(RGBImage image) { this(image.width, image.height, copyPixels(image.pixels)); } RGBImage(int width, int height, Color color) { this(width, height, new RGB(color)); } RGBImage(MakesBufferedImage img) { this(toBufferedImage(img)); } private static int[] copyPixels(int[] pixels) { int[] copy = new int[pixels.length]; System.arraycopy(pixels, 0, copy, 0, pixels.length); return copy; } public int getIntPixel(int x, int y) { if (inRange(x, y)) return pixels[y * width + x]; else return 0xFFFFFF; } public static RGB asRGB(int packed) { int r = (packed >> 16) & 0xFF; int g = (packed >> 8) & 0xFF; int b = packed & 0xFF; return new RGB(r / 255f, g / 255f, b / 255f); } public RGB getRGB(int x, int y) { if (inRange(x, y)) return asRGB(pixels[y * width + x]); else return new RGB(0xFFFFFF); } /** alias of getRGB - I kept typing getPixel instead of getRGB all the time, so I finally created it */ RGB getPixel(int x, int y) { return getRGB(x, y); } RGB getPixel(Pt p) { return getPixel(p.x, p.y); } public int getWidth() { return width; } public int getHeight() { return height; } public int w() { return width; } public int h() { return height; } /** Attention: cached, i.e. does not change when image itself changes */ /** @NotNull */ public BufferedImage getBufferedImage() { if (bufferedImage == null) { bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //bufferedImage.setData(Raster.createRaster(new SampleModel())); for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) bufferedImage.setRGB(x, y, pixels[y*width+x]); } return bufferedImage; } RGBImage clip(Rect r) { return r == null ? null : clip(r.getRectangle()); } RGBImage clip(Rectangle r) { r = fixClipRect(r); if (r.x == 0 && r.y == 0 && r.width == width && r.height == height) return this; int[] newPixels; try { newPixels = new int[r.width*r.height]; } catch (RuntimeException e) { System.out.println(r); throw e; } for (int y = 0; y < r.height; y++) { System.arraycopy(pixels, (y+r.y)*width+r.x, newPixels, y*r.width, r.width); } return new RGBImage(r.width, r.height, newPixels); } private Rectangle fixClipRect(Rectangle r) { r = r.intersection(new Rectangle(0, 0, width, height)); if (r.isEmpty()) r = new Rectangle(r.x, r.y, 0, 0); return r; } public int getInt(int x, int y) { return pixels[y * width + x]; } public void save(File file) { saveImage(file, getBufferedImage()); } public static RGBImage dummyImage() { return new RGBImage(1, 1, new int[] {0xFFFFFF}); } public int[] getPixels() { return pixels; } void setPixel(int x, int y, int r, int g, int b) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = (limitToUByte(r) << 16) | (limitToUByte(g) << 8) | limitToUByte(b); } public void setPixel(int x, int y, RGB rgb) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = rgb.asInt(); } final public void set(int x, int y, Color color){ setPixel(x, y, color); } public void setPixel(int x, int y, Color color) { setPixel(x, y, new RGB(color)); } void setInt(int x, int y, int rgb) { setPixel(x, y, rgb); } public void setPixel(int x, int y, int rgb) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = rgb; } void setPixel(Pt p, RGB rgb) { setPixel(p.x, p.y, rgb); } void setPixel(Pt p, Color color) { setPixel(p.x, p.y, color); } public RGBImage copy() { return new RGBImage(this); } public boolean inRange(int x, int y) { return x >= 0 && y >= 0 && x < width && y < height; } public Dimension getSize() { return new Dimension(width, height); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RGBImage rgbImage = (RGBImage) o; if (height != rgbImage.height) return false; if (width != rgbImage.width) return false; if (!Arrays.equals(pixels, rgbImage.pixels)) return false; return true; } @Override public int hashCode() { int result = width; result = 31 * result + height; result = 31 * result + Arrays.hashCode(pixels); return result; } public String getHex(int x, int y) { return getPixel(x, y).getHexString(); } public RGBImage clip(int x, int y, int width, int height) { return clip(new Rectangle(x, y, width, height)); } public RGBImage clipLine(int y) { return clip(0, y, width, 1); } public int numPixels() { return width*height; } RGBImage uncacheBufferedImage() { bufferedImage = null; return this; } } interface IVarWithNotify<A> extends IVar<A>, IF0WithChangeListeners<A> { default IVarWithNotify<A> onChange(IVF1<A> r) { if (r == null) return this; onChange(() -> r.get(get())); return this; } default IVarWithNotify<A> onChangeAndNow(IVF1<A> r) { if (r == null) return this; onChangeAndNow(() -> r.get(get())); return this; } } static class WrappedMap<A, B> extends AbstractMap<A, B> { Map<A, B> map; WrappedMap() {} WrappedMap(Map<A, B> map) { this.map = map;} public B get(Object key) { return map.get(key); } public B put(A key, B value) { return map.put(key, value); } public boolean containsKey(Object key) { return map.containsKey(key); } public Set<Map.Entry<A,B>> entrySet() { return map.entrySet(); } } static interface IRGBImage extends MakesBufferedImage { int getIntPixel(int x, int y); } static interface IHasGlobalID { public GlobalID globalID(); } static class ScoredStringSearcher extends ScoredSearcher_stable<String> { Set<String> seen; ScoredStringSearcher() {} ScoredStringSearcher(String query, Object... __) { super(query); } // true = duplicate entries are filtered out ScoredStringSearcher uniquify(boolean b) { seen = b ? new HashSet() : null; return this; } final void add(String s){ put(s); } void put(String s) { if (seen == null || seen.add(s)) put(s, s); } void addAll(Iterable<String> l) { for (var s : unnullForIteration(l)) add(s); } void addAll(String... l) { addAll(asList(l)); } } static class VarWithNotify<A> extends Var<A> implements IVarWithNotify<A> { transient Set<Runnable> onChange; public VarWithNotify<A> onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public VarWithNotify<A> removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void fireChange() { pcallFAll(onChange); } VarWithNotify() {} VarWithNotify(A a) { super(a); } // clever way (I hope) to do eq() outside of synchronization block public void set(A a) { A v = get(); if (eq(v, a)) { return; } synchronized(this) { this.v = a; notifyAll(); } fireChange(); } } static class CharInToken implements IFieldsToList{ static final String _fieldOrder = "tok iTok iChar"; List<String> tok; int iTok; int iChar; CharInToken() {} CharInToken(List<String> tok, int iTok, int iChar) { this.iChar = iChar; this.iTok = iTok; this.tok = tok;} public boolean equals(Object o) { if (!(o instanceof CharInToken)) return false; CharInToken __1 = (CharInToken) o; return eq(tok, __1.tok) && iTok == __1.iTok && iChar == __1.iChar; } public int hashCode() { int h = 2081721214; h = boostHashCombine(h, _hashCode(tok)); h = boostHashCombine(h, _hashCode(iTok)); h = boostHashCombine(h, _hashCode(iChar)); return h; } public Object[] _fieldsToList() { return new Object[] {tok, iTok, iChar}; } String token() { return get(tok, iTok); } public String toString() { return renderRecordVars("CharInToken", "iTok", iTok, "iChar", iChar, "token", token()); } } // A is the type of object being listed by the search static class ScoredSearcher_stable<A> { int maxResults = 1000; boolean returnAll = false; // special mode to return all items List<String> preparedTerms; MultiMap<Double, A> byScore = descTreeMultiMap(); ScoredSearcher_stable() {} ScoredSearcher_stable(String query, Object... __) { maxResults = optPar("maxResults", __, maxResults); setQuery(query); } void setQuery(String query) { preparedTerms = scoredSearch_prepare(query); } void put(A object, String s) { putScored(object, score(s)); } void putScored(A object, double score) { if (score != 0) byScore.add(score, object); } void put(A object, Collection<String> fields) { add(object, scoreFields(fields)); } // process fields with weights void putWithWeights(A object, Collection<Pair<String, Double>> fields) { scoredSearch_scoreWeighted2(fields, preparedTerms); } int scoreFields(Collection<String> fields) { return scoredSearch_score(fields, preparedTerms); } int score(String text) { return returnAll ? 1 : scoredSearch_score(text, preparedTerms); } void add(A object) { put(object, str(object)); } void add(A object, Collection<String> fields) { put(object, fields); } void put(A object, double score) { add(object, score); } void add(A object, double score) { putScored(object, score); } List<A> get() { return pairsB(takeFirst(maxResults, multiMapPairIterator(byScore))); } List<A> get_transformListWithinScore(IF1<List<A>, List<A>> f) { return pairsB(takeFirst(maxResults, multiMapPairIterator_transformValueList(byScore, f))); } List<Scored<A>> withScores() { return map(takeFirst(maxResults, multiMapPairIterator(byScore)), p -> new Scored(p.b, p.a)); } A best() { return firstValue(byScore); } } interface IF0WithChangeListeners<A> extends IF0<A>, IHasChangeListeners {} static class GlobalID implements Comparable<GlobalID> { // We need 76 bits for 26^16 IDs long a; // lower 64 bits int b; // 76-64=12 higher bits; could be short. change to short when unstructure() is smarter GlobalID() {} GlobalID(String id) { assertGlobalID(id); BigInteger value = bigint(0); for (int i = 0; i < l(id); i++) value = plus(mul(value, 26), charDiff(id.charAt(i), 'a')); a = value.longValue(); value = value.shiftRight(64); b = value.shortValue(); } public String toString() { BigInteger value = bigint(b); value = value.shiftLeft(32); value = plus(value, (a >> 32) & 0xFFFFFFFFL); value = value.shiftLeft(32); value = plus(value, a & 0xFFFFFFFFL); return bigintToGlobalID(value); } public boolean equals(Object o) { if (!(o instanceof GlobalID)) return false; return ((GlobalID) o).a == a && ((GlobalID) o).b == b; } public int hashCode() { return (int) a; } // matches ID's lexical ordering public int compareTo(GlobalID id) { int diff = b-id.b; return diff != 0 ? diff : Long.compareUnsigned(a, id.a); } } static List<String> myInnerClasses_list=litlist( null, "IVarWithNotify", "Meta", "NotifyingCollection", "SingleThread", "RandomAvatar", "DefunctClassLoader", "G22ProjectActions", "ImageSurface", "ConceptsRefChecker", "IntVar", "IFieldIndex", "talkToThisVM_IO", "BotRandomAction", "SimpleLiveValue", "ConceptFieldIndexDesc", "VarMatches", "SingleComponentPanel", "Best", "DynModule", "OnNewDialog", "Stringable", "IWebRequest", "Rect", "BackgroundProcessesUI", "Pt", "AbstractLayoutManager", "LeftAlignedLine", "IterableIterator", "betterCIComparator_C", "RemoteDB", "MakesBufferedImage", "JLeftArrowScriptIDE", "Concepts", "RC", "Hasher", "CloseableIterableIterator", "ExpiringMap2", "getOpt_Map", "IFieldsToList", "IInputHandler", "structure_Data", "VarWithNotify", "jsonDecode_Y", "IVar", "DynPrintLogAndEnabled", "Enterable", "TokenRangeWithSrc", "Matches", "ConceptWithGlobalID", "WithTimestamp", "HCRUD_Concepts", "ParameterizedTypeImpl", "Dyn_FieldWatcher", "Timestamp", "GazelleV_LeftArrowScriptParser", "DoubleRect", "Surface", "Q", "ConceptFieldIndexBase", "OrError", "LongRange", "EvtCallSubroutine", "RestartableCountdown", "MultiSet", "BotStep", "T3", "IResourceHolder", "Htmlable", "HCRUD_Data", "mapI_if1_It", "VarContext", "IConceptCounter", "IntRange", "ActiveSequence", "UnsynchronizedCompactHashSet", "BetterLabel", "componentPopupMenu_Adapter", "Stringifier_ToString", "G22Label", "G22TypeDesc", "InternationalPhoneValidator", "UndoState", "FailedRule", "IHasTokenRangeWithSrc", "ScheduledAction", "RGBImage", "SmartTimerTask", "JSection", "ValueConverterForField", "Visitable", "CollapsibleLeftPanel", "IF0WithChangeListeners", "FullChange", "G22Utils", "ConceptDelete", "CombinedStringifier", "IStringifier", "BotSwitchLanguage", "Literal", "ReliableSingleThread", "ListAndIndex", "unstructure_Handler", "DeliveredDomain", "PtInComponent", "ProgramScan", "LeftArrowScriptAutoCompleter", "ConceptFieldIndex", "Sentence2", "PersistableOKOrError", "BotMessage", "BotClearStack", "BotIdleMode", "F0", "F1", "F2", "RGB", "RandomAccessAbstractList", "IfThen", "G22AutoStarter", "BoolVar", "unstructure_Receiver", "IF0", "Not", "IHasGlobalID", "AbstractAvatar", "BaseXRef", "DefaultValueConverterForField", "IF2", "ConceptFieldIndexCI", "IF1", "IVF1", "IVF2", "Domain", "Cache", "_MethodCache", "IConcept", "Form", "structure_ClassInfo", "Func", "ScoredStringSearcher", "BotOutgoingQuestion", "LiveValue", "BotSaveFeedback", "Eq", "RSyntaxTextAreaWithSearch", "BotNewDialog", "Language", "DialogHandler", "JavaScript", "_PrintIndent", "proxy_InvocationHandler", "MMOPattern", "G2Drawable", "ExecutedStep", "Pair", "ImageSurfaceMouseHandler", "Swingable", "HTMLFramer1", "BotPause", "RuleWithParams", "LineAndColumn", "Avatar", "ChangeTriggerable", "GrabbableIntPixels", "TransferableImage", "UploadedFile", "ConversationFeedback", "IConceptIndex", "MetaWithChangeListeners", "ConceptCreate", "Seconds", "TokenRange", "LASValueDescriptor", "MultiSetMap", "BotSendTyping", "LASCompileResult", "Var", "ClassNameResolver", "Best_comparable", "Scorer", "ITokCondition", "SecretValue", "PopupMenuMaker", "CountingOutputStream", "HTMLAceEditor", "AppendableChain", "RunResultWithTimestamps", "ConceptChange", "CompactAbstractMap", "ExpNot", "CompactLinkedHashSet", "FixedRateTimer", "Settings", "Fail", "ISetter", "OnUserMessage", "Out", "ScannedBot", "XRef", "GazelleV_LeftArrowScript", "IIntPred", "G22MasterStuff", "CountryDialCode", "CompactHashMap", "JS", "Conversation", "JPopDownButton", "WidthAndHeightImpl", "G22LeftArrowScript", "Complex", "MultiMap", "IHasChangeListeners", "CriticalAction", "Lead", "IResourceLoader", "WeakHasherMap", "ImageSurface_PositionToolTip", "Worker", "ScheduledActionOnConversation", "Either", "BotSaveLead", "ScoredSearcher", "NotifyingSet", "Msg", "G22ScriptMode", "JMenuScroller", "DialogIO", "IAutoCloseableF0", "CharInToken", "G22JavaObjectVisualizer", "MRUCache", "IPartialStringifier", "AbstractBotImage", "AlphanumComparator", "PersistableThrowable", "talkToSubBot_IO", "IDoublePt", "Scored", "NumPadFixingInputMap", "WidthAndHeight", "HasIndex", "Responder", "VF1", "VF2", "IMultiSet", "ConceptsChange", "mapI_It", "HTML", "TokCondition", "BotGoBack", "RightAlignedLine", "GlobalID", "HCRUD", "findCodeTokens_Matcher", "ConceptWithChangeListeners", "EvtJumpTo", "FieldVar", "UploadedImage", "JFastLogView_noWrap", "SimpleLeftToRightParser", "ImageSurfaceSelector", "Sentence", "UserKeyword", "TimeoutException", "Concept", "IRGBImage", "IRef", "HAbstractRenderable", "Sequence", "TreeMultiMap", "Flag", "generalizedCIComparator_C", "ContentsIndexedList", "FileTransferable", "BotEndConversation", "IContentsIndexedList2", "Producer", "DynNewBot2", "EvtMatchedUserKeyword", "And", "Value", "talkTo_IO", "Evt", "OKOrError", "FileBasedLock", "Symbol", "IGetterWithNotify", "IMeta", "BetterThread", "IContentsIndexedList", "LetterLayout", "AuthedDialogID", "InputHandled", "Lowest", "InstantNeverHideToolTip", "Action_NextStep", "HTMLPaginator", "UploadedSound", "ChangeTrigger", "WrappedMap", "ScoredSearcher_stable", "DynPrintLog", "MinimalChain", "Transformable", "CannedAnswer", "BotDoNothing", "jLiveValueSection_class", "ClearForAutoRun", "Android3", "Exp", "BotImage"); static List<String> myInnerClasses() { return myInnerClasses_list; } 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 <A> A _print(String s, A a) { return print(s, a); } static <A> 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 <A> boolean allUnique(Collection<A> l) { return l(uniquify(l)) == l(l); } static <A> List<A> cloneAndClear(Collection<A> l) { return cloneAndClearList(l); } static void addSeparator(JMenu menu) { menu.addSeparator(); } static void addSeparator(JPopupMenu menu) { menu.addSeparator(); } static void truncateContainer(Container c, int n) { swing(() -> { if (c != null) while (c.getComponentCount() > n) c.remove(c.getComponentCount()-1); }); } static <A extends Component> A packWindow(final A c) { { swing(() -> { Window w = getWindow(c); if (w != null) w.pack(); }); } return c; } static <A> VF1<A> toVF1(IVF1<A> f) { return ivf1ToVF1(f); } static PtInComponent ptInComponentFromEvent(MouseEvent evt) { return new PtInComponent(evt.getComponent(), pt(evt.getX(), evt.getY())); } static boolean hasParentOfType(Class<?> type, Component c) { return parentOfType(c, type) != null; } static String divUnlessEmpty(String s, Object... params) { return empty(s) ? "" : div(s, params); } 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 boolean eqGet(List l, int i, Object o) { return eq(get(l, i), o); } static <A, B> boolean eqGet(Map<A, B> map, A key, Object o) { return eq(mapGet(map, key), o); } static List<String> keysDeprefixNemptyValue(Map<String, String> map, String prefix) { List<String> 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 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 <B, C, A extends Map<B, C>> List<A> sortByTransformedMapKey_alphaNum(IF1<C, String> transform, Iterable<A> c, B key) { List<A> 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 <A, B> List<B> lazyMap(List<A> l, IF1<A, B> f) { return lazilyMap(f, l); } static <A, B> List<B> lazyMap(IF1<A, B> f, List<A> l) { return lazilyMap(f, l); } static <A, B> Map<A, B> mapMinusKeys(Map<A, B> map, A... keys) { return mapMinusKeys(map, asList(keys)); } static <A, B> Map<A, B> mapMinusKeys(Map<A, B> map, Collection<A> keys) { if (empty(keys) || empty(map)) return map; keys = asSet(keys); Map<A, B> m2 = similarEmptyMap(map); for (Map.Entry<? extends A, ? extends B> __0 : _entrySet( map)) { A key = __0.getKey(); B val = __0.getValue(); if (!keys.contains(key)) m2.put(key, val); } return m2; } static <A, B> Map<A, B> mapMinusKeys(Collection<A> keys, Map<A, B> map) { return mapMinusKeys(map, keys); } static <A, B, C> Map<B, C> mapToMap(IF1<A, Pair<B, C>> f, Iterable<A> l) { Map<B, C> map = new HashMap(); for (A o : unnullForIteration(l)) { Pair<B, C> p = callF(f, o); map.put(p.a, p.b); } return map; } static <A, B, C> Map<B, C> mapToMap(Iterable<A> l, IF1<A, Pair<B, C>> f) { return mapToMap(f, l); } static <A, B, C, D> Map<C, D> mapToMap(Map<A, B> m, IF2<A, B, Pair<C, D>> f) { return mapMapToMap(f, m); } static <A, B, C, D> Map<C, D> mapToMap(IF2<A, B, Pair<C, D>> f, Map<A, B> m) { return mapMapToMap(f, m); } static <A, B> B mapPut_returnValue(Map<A, B> map, A key, B value) { mapPut(map, key, value); return value; } static <A, B> A firstKey(Map<A, B> map) { return first(keys(map)); } static <A, B> A firstKey(MultiSetMap<A, B> map) { return map == null ? null : firstKey(map.data); } static <A, B> A firstKey(MultiMap<A, B> 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 <A> List<A> subListOrFull(List<A> l, IntRange r) { return r == null ? l : subList(l, r.start, r.end); } static <A> List<A> llNonNulls(A... a) { List<A> l = new ArrayList(); for (A x : a) if (x != null) l.add(x); return l; } static String unicode_downOrUpPointingTriangle(boolean down) { return down ? unicode_downPointingTriangle() : unicode_upPointingTriangle(); } 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)); } 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 <A, B> Map<A, B> mapWithoutKey(Map<A, B> map, A key) { if (map == null || !map.containsKey(key)) return map; Map m = cloneMap(map); m.remove(key); return m; } static <A, B> Map<A, B> mapWithoutKey(A key, Map<A, B> map) { return mapWithoutKey(map, key); } static String htableRaw_valignTop(List<? extends 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) + "</textarea>"; } 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 <T> Map<String, T> subMapStartingWith_dropPrefix(Map<String, T> map, String prefix) { if (map == null) return null; Map<String, T> map2 = new HashMap(); for (Map.Entry<? extends String, ? extends T> __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 <A, B> Map<A, B> joinMaps(Map<A, B> map1, Map<A, B> map2) { if (empty(map2)) return map1; if (empty(map1)) return map2; Map map3 = cloneMap(map1); putAll(map3, map2); return map3; } static <A> void listPut(List<A> l, int i, A a, A emptyElement) { listSet(l, i, a, emptyElement); } static <A> void listPut(List<A> l, int i, A a) { listSet(l, i, a); } // TODO: work the other way around when set is small static <A, B> Map<A, B> onlyKeys(Map<A, B> map, Collection<A> keys) { Set<A> keySet = asSet(keys); return filterKeys(map, a -> contains(keySet, a)); } static <A, B> Map<A, B> onlyKeys(Map<A, B> map, A... keys) { return onlyKeys(map, litset(keys)); } static List<String> tok_identifiersOnly(String s) { return tok_identifiersInOrder(s); } static String p_vbar(String... items) { return p_vbar(asList(items)); } static String p_vbar(Collection<String> items, Object... __) { return pUnlessEmpty(joinNemptiesWithVBar(items), __); } static String hbuttonOnClick_noSubmit(String text, String onClick, Object... params) { return hfulltag("button", text, paramsPlus(params, "onclick" , onClick, "type" , "button")); } 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 hselect_list(String name, Collection<String> entries, Object... params) { return hselect_list(entries, paramsPlus_skipFirst(params, "name", name)); } static String hselect_list(Collection<String> 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("<option></option>\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 hoption(String text) { return tag("option", htmlEncode2(text)); } static List<String> 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 List<String> regexpGroups(String pat, String s) { return regexpFirstGroups(pat, s); } static <A, B> MultiSetMap<A, B> treeMultiSetMap() { return new MultiSetMap(true); } static <A, B> MultiSetMap<A, B> treeMultiSetMap(Comparator<A> comparator) { return new MultiSetMap(new TreeMap<A, Set<B>>(comparator)); } static Comparator reverseOrder() { return new Comparator() { public int compare(Object a, Object b) { return cmp(b, a); } }; } static Class _run(String progID, String... args) { Class main = hotwire(progID); callMain(main, args); return main; } static <A> List<A> syncGetAndClear(Collection<A> l) { return syncCloneAndClearList(l); } static void cancelAndInterruptThread(Thread t) { if (t == null) return; cancelThread(t); t.interrupt(); } 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<Thread>() { 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<Map>() { 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<Map>() { 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> A getCreatorOpt(String field) { return (A) getOpt(creator(), field); } static <A extends Component> 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 void cancelTimerOrInterruptThread(Object timer) { if (timer instanceof Thread && !(timer instanceof AutoCloseable)) // AutoCloseable takes precedence interruptThread((Thread) timer); else cancelTimer(timer); } static <A> List<A> getAndClearList(Collection<A> l) { if (l == null) return emptyList(); synchronized(collectionMutex(l)) { List<A> out = cloneList(l); l.clear(); return out; } } static <A, B> Map<A, B> getAndClearMap(Map<A, B> map) { if (map == null) return emptyMap(); synchronized(collectionMutex(map)) { Map<A, B> out = cloneMap(map); map.clear(); return out; } } static boolean dm_isPersistentField(String field) { return dm_isPersistentField(dm_current_mandatory(), field); } static boolean dm_isPersistentField(DynModule m, String field) { return m.persistOnChangedField && !isTransientField(m, field) && !isFalse(mapGet(m._persistenceInfo, field)); } 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<String> r) { final DynModule m = dm_current_mandatory(); dm_onTopInput(new VF1<String>() { 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 <A> AutoCloseable tempSetBetterThreadLocalIfNecessary_weakRef(x30_pkg.x30_util.BetterThreadLocal<WeakReference> 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<AutoCloseable> l) { return foldl(new F2<AutoCloseable, AutoCloseable, AutoCloseable>() { 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 <A> boolean addIfNotThere(Collection<A> c, A a) { return setAdd(c, a); } static <A> void remove(List<A> l, int i) { if (l != null && i >= 0 && i < l(l)) l.remove(i); } static <A> void remove(Collection<A> l, A a) { if (l != null) l.remove(a); } static <A, B> B remove(Map<A, B> map, Object a) { return map == null ? null : map.remove(a); } static void remove(BitSet bs, int i) { bs.clear(i); } static <A extends JSpinner> A onChange(A spinner, Object r) { return onChange(spinner, toRunnable(r)); } static <A extends JSpinner> A onChange(A spinner, Runnable r) { if (r != null) { swing(() -> { spinner.addChangeListener(changeListener(r)); }); } return spinner; } static <A extends AbstractButton> A onChange(A b, Object r) { { swing(() -> { b.addItemListener(itemListener(r)); }); } return b; } static void onChange(JTextComponent tc, Object r) { onUpdate(tc, r); } static void onChange(JTextComponent tc, Runnable r) { onUpdate(tc, r); } static <A extends JSlider> A onChange(A slider, final Object r) { { swing(() -> { slider.addChangeListener(changeListener(r)); }); } return slider; } static <A> JComboBox<A> onChange(JComboBox<A> cb, Runnable r) { addActionListener(cb, r); return cb; } static <A> JComboBox<A> onChange(JComboBox<A> cb, IVF1<A> f) { if (f != null) addActionListener(cb, () -> f.get(getSelectedItem_typed(cb))); return cb; } 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<String>() { public void get(String s) { try { callF(r) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(r)"; }}); return cb; } static <A extends JTabbedPane> A onChange(A tabs, Runnable r) { { swing(() -> { tabs.addChangeListener(changeListener(r)); }); } return tabs; } static <A extends JColorChooser> A onChange(Runnable r, A cc) { if (cc != null && r != null) { swing(() -> { cc.getSelectionModel().addChangeListener(changeListener(r)); }); } return cc; } static void onChange(IHasChangeListeners a, ChangeTriggerable b) { if (a != null && b != null) a.onChange(new ChangeTrigger(b)); } // action = runnable or method name static void onUpdateAndNow(JComponent c, final Object r) { onUpdate(c, r); callF(r); } static void onUpdateAndNow(List<? extends JComponent> l, Object r) { for (JComponent c : l) onUpdate(c, r); callF(r); } static void onUpdate(JComponent c, Runnable r) { onUpdate(c, (Object) r); } // legacy signature static void onUpdate(JComponent c, 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<? extends JComponent> 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<String> exceptions) { Set<String> set = asSet(exceptions); for (String field : allFields(o)) if (!set.contains(field) && instanceOf(getOpt(o, field), type)) set(o, field, null); } static List<AbstractAction> paramsToAbstractActions(Object... params) { List<AbstractAction> 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<AbstractAction> 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 <A> LinkedHashSet<A> asLinkedHashSet(A[] array) { LinkedHashSet<A> set = new LinkedHashSet(); for (A o : array) if (o != null) set.add(o); return set; } static <A> LinkedHashSet<A> asLinkedHashSet(Collection<A> l) { if (l instanceof LinkedHashSet) return (LinkedHashSet) l; LinkedHashSet<A> set = new LinkedHashSet(); for (A o : l) if (o != null) set.add(o); return set; } static Map<Class, Set<String>> allFields_cache = weakHashMap(); static Set<String> allFields(Object o) { if (o == null) return emptySet(); Class _c = _getClass(o); Set<String> fields = allFields_cache.get(_c); if (fields == null) allFields_cache.put(_c, fields = asTreeSet(keys(getOpt_getFieldMap(o)))); return fields; } static Class fieldType(Object o, String field) { Field f = getField(o, field); return f == null ? null : f.getType(); } 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<JPopupMenu>() { public void get(JPopupMenu menu) { try { menu.add(jCheckBoxMenuItem(humanized, (boolean) _get(m, field), new VF1<Boolean>() { 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 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<java.util.Timer> _registerTimer_list = newWeakHashSet(); static void _registerTimer_original(java.util.Timer timer) { _registerTimer_list.add(timer); } static void cleanMeUp__registerTimer() { cancelTimers(getAndClearList(_registerTimer_list)); } static String joinNemptiesWithColon(String... strings) { return joinNempties(": ", strings); } static String joinNemptiesWithColon(Collection<String> strings) { return joinNempties(": ", strings); } static String commaCombine(Object... l) { return joinNemptiesWithComma(flattenCollectionsAndArrays(ll(l))); } static String getClientIPFromHeaders(Map<String, String> 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 RuntimeException unimplemented(Object obj) { throw fail("TODO: implement method in " + className(obj)); } static URI uri(String uri) { try { return new URI(uri); } catch (Exception __e) { throw rethrow(__e); } } static double sqrt(double x) { return Math.sqrt(x); } static <A> List<A> minus(Collection<A> a, Object... b) { Set set = asSet(b); List l = new ArrayList(); for (Object s : unnull(a)) if (!set.contains(s)) l.add(s); return l; } static BigInteger minus(BigInteger a, BigInteger b) { return a.subtract(b); } static Complex minus(Complex c) { return c == null ? null : complex(-c.re(), -c.im()); } static int minus(int a, int b) { return a-b; } static double minus(double a, double b) { return a-b; } static Pt ptMinus(Pt a, Pt b) { if (b == null) return a; return new Pt(a.x-b.x, a.y-b.y); } 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 UnsupportedOperationException unsupportedOperation() { throw new UnsupportedOperationException(); } 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 void close(AutoCloseable c) { _close(c); } static Color getColor(BufferedImage img, int x, int y) { return colorFromRGBA(img.getRGB(x, y)); } static Color getColor(BufferedImage img, Pt p) { return colorFromRGBA(img.getRGB(p.x, p.y)); } 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 Class primitiveToBoxedTypeOpt(Class type) { return or(primitiveToBoxedType(type), type); } static Object trimIfString(Object o) { return o instanceof String ? trim((String) o) : 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 <A, B, C> Map<A, C> mapValues(Map<A, B> map, IF1<B, C> f) { return mapValues(f, map); } static <A, B, C> Map<A, C> mapValues(IF1<B, C> f, Map<A, B> map) { Map m = similarEmptyMap(map); for (Map.Entry<? extends A, ? extends B> __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 <A, B, C> MultiMap<A, C> mapValues(IF1<B, C> func, MultiMap<A, B> mm) { return mapMultiMapValues(func, mm); } static <A, B, C> MultiMap<A, C> mapValues(MultiMap<A, B> mm, IF1<B, C> func) { return mapValues(func, mm); } static <A, B> Set<A> keySet(Map<A, B> map) { return map == null ? new HashSet() : map.keySet(); } static Set keySet(Object map) { return keys((Map) map); } static <A> Set<A> keySet(MultiSet<A> ms) { return ms.keySet(); } static <A, B> Set<A> keySet(MultiMap<A, B> mm) { return mm.keySet(); } static <A> MultiSetMap<Object, A> generalizedCIMultiSetMap() { MultiSetMap<Object, A> mm = new MultiSetMap(); mm.data = generalizedCIMap(); return mm; } static boolean hasLock(Lock lock) { return ((ReentrantLock) lock).isHeldByCurrentThread(); } static boolean classIsExportedTo(Class c, java.lang.Module destModule) { if (c == null || destModule == null) return false; java.lang.Module srcModule = c.getModule(); String packageName = c.getPackageName(); return srcModule.isExported(packageName, destModule); } static Set<Class> allInterfacesImplementedBy(Class c) { if (c == null) return null; HashSet<Class> set = new HashSet(); allInterfacesImplementedBy_find(c, set); return set; } static void allInterfacesImplementedBy_find(Class c, Set<Class> 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 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<String> quoteAll(String[] l) { return quoteAll(asList(l)); } static List<String> quoteAll(Collection<String> l) { List<String> 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 double fileAgeInSeconds(File f) { return f == null ? -1 : msToSeconds(now()-fileModificationTime(f)); } static String nSeconds(long n) { return n2(n, "second"); } static String nSeconds(Collection l) { return nSeconds(l(l)); } 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); } } 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 Object metaGet(IMeta o, Object key) { return metaMapGet(o, key); } static Object metaGet(Object o, Object key) { return metaMapGet(o, key); } static Object metaGet(String key, IMeta o) { return metaMapGet(o, key); } static Object metaGet(String key, Object o) { return metaMapGet(o, key); } static Object metaMapGet(IMeta o, Object key) { return o == null ? null : o.metaGet(key); // We now let the object itself do it (overridable!) } static Object metaMapGet(Object o, Object key) { return metaMapGet(toIMeta(o), key); } static void metaPut(IMeta o, Object key, Object value) { metaMapPut(o, key, value); } static void metaPut(Object o, Object key, Object value) { metaMapPut(o, key, value); } static Map convertObjectMetaToMap(IMeta o) { return convertObjectMetaToMap(o, () -> makeObjectMetaMap()); } static Map convertObjectMetaToMap(IMeta o, IF0<Map> 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 <A, B> void syncMapPutOrRemove(Map<A, B> map, A key, B value) { syncMapPut2(map, key, value); } static String humanizeShortName(Object o) { return humanizeLabel(shortName(o)); } static <A> A postProcess(Object f, A a) { return callPostProcessor(f, a); } static <A> A postProcess(IF1<A, A> f, A a) { return callPostProcessor(f, a); } static <A extends Concept> Collection<A> conceptsWhereCI(Class<A> c, Object... params) { return findConceptsWhereCI(c, params); } static Collection<Concept> conceptsWhereCI(String c, Object... params) { return findConceptsWhereCI(c, params); } static <A extends Concept> Collection<A> conceptsWhereCI(Concepts concepts, Class<A> c, Object... params) { return findConceptsWhereCI(concepts, c, params); } static List<Concept> conceptsWhereCI(Concepts concepts, String c, Object... params) { return findConceptsWhereCI(concepts, c, params); } static <A extends Concept> List<A> filterConceptsIC(Collection<A> list, Object... params) { List<A> l = new ArrayList(); if (list != null) for (A x : list) if (checkConceptFieldsIC(x, params)) l.add(x); return l; } static <A extends Concept> List<A> sortedByConceptIDDesc(Collection<A> c) { return sortedByCalculatedFieldDesc(__124 -> conceptID(__124), c); } 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<Field> nonStaticNonTransientFieldObjectsOfType(Class type, Object o) { return filter(nonStaticNonTransientFieldObjects(o), f -> eq(f.getType(), type)); } static <A, B> B lastValue(NavigableMap<A, B> map) { if (map == null) return null; Map.Entry<A, B> entry = map.lastEntry(); return entry == null ? null : entry.getValue(); } static <A, B> Map.Entry<A, B> lastEntry(NavigableMap<A, B> map) { return map == null ? null : map.lastEntry(); } static <A, B> void removeLastKey(SortedMap<A, B> map) { if (nempty(map)) map.remove(lastKey(map)); } static <A, B> List<B> valuesAsList(Map<A, B> map) { return valuesList(map); } static <A, B> List<A> cloneKeys(Map<A, B> 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"); } static Map<String, Object> cgetAll_cloneLists(Concept c, Collection<String> fields) { return mapToValues(fields, f -> cloneIfList(cget(c, f))); } // 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) { printStackTrace(__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 extends Concept> A _getConcept(Class<A> cc, long id) { return getConcept(cc, id); } static <A extends Concept> A _getConcept(Concepts concepts, Class<A> cc, long id) { return getConcept(concepts, cc, id); } static <A, B> Map<A, B> orderedMapPutOrCreate(Map<A, B> map, A key, B value) { if (key != null && value != null) { if (map == null) map = new LinkedHashMap(); map.put(key, value); } return map; } static <A> A trueFalseNull(Boolean b, A onTrue, A onFalse, A onNull) { return b == null ? onNull : b.booleanValue() ? onTrue : onFalse; } static String firstWhereFirstLongIs(Collection<String> l, long i) { return firstThat(l, s -> parseFirstLong(s) == i); } 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<String> scoredSearch(String query, Iterable<String> data) { Map<String, Integer> scores = new HashMap(); List<String> 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 long parseFirstLong(String s) { return parseLong(jextract("<int>", s)); } static <A, B> Map<A, B> putOrCreate(Map<A, B> map, A key, B value) { if (map == null) map = new HashMap(); map.put(key, value); return map; } static String jsDollarVars(String s, Object... __) { return replaceDollarVars_js(s, __); } static <A> Set<A> addToSet_create(Set<A> set, A a) { return createOrAddToSet(set, a); } static Type getRawType(Type t) { if (t instanceof ParameterizedType) return ((ParameterizedType) t).getRawType(); return t; } static BigInteger plus(BigInteger a, BigInteger b) { return a.add(b); } static BigInteger plus(BigInteger a, long b) { return a.add(bigint(b)); } static long plus(long a, long b) { return a+b; } static int plus(int a, int b) { return a+b; } static float plus(float a, float b) { return a+b; } static double plus(double a, double b) { return a+b; } static long clockTimeToSystemTime(long now) { return now == 0 ? 0 : now + clockToSysTimeDiff(); } static G22Utils g22utils(Concepts cc) { return (G22Utils) cc.miscMapGet(G22Utils.class); } static <A> void replaceLast(List<A> l, A a) { replaceLastElement(l, a); } static <A> A[] toTypedArray(Class<A> type, Iterable<A> c) { return toArray(c, type); } static <A> String pnlToLines(String prefix, Iterable<A> l) { return pnlToString(prefix, l); } static <A> String pnlToLines(Iterable<A> l) { return pnlToString(l); } static <A> String pnlToLines(A[] l) { return pnlToString(l); } static <A, B> String pnlToLines(Map<A, B> map) { return pnlToString(map); } static <A, B> String pnlToLines(MultiMap<A, B> map) { return pnlToString(map); } // Use like this: renderRecordVars("MyRecord", +x, +y) static String formatRecordVars(String recordName, Object... params) { return renderRecordVars(recordName, params); } static RuntimeException rethrowAndAppendToMessage(Throwable t, String msg) { String haveMsg = t.getMessage(); if (empty(msg) || endsWith(haveMsg, " " + msg)) throw rethrow(t); throw new RuntimeException(joinWithSpace(t.getMessage(), msg), t); } static boolean is(String a, String b) { return false; } // 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 boolean isInterface(Class c) { return c != null && c.isInterface(); } 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> A[] toArrayOrNull(Class<A> type, Collection<A> c) { if (empty(c)) return null; return toArray(type, c); } // assumes map never contains null values static <A, B> AutoCloseable tempMapPutAll(Map<A, B> map, Map<A, B> toAdd) { if (map != null && nempty(toAdd)) { List<Pair<A, B>> toRestore = new ArrayList(); for (var __0 : _entrySet( toAdd)) { var key = __0.getKey(); var value = __0.getValue(); if (key != null && value != null) { B old = map.put(key, value); toRestore.add(pair(key, value)); } } return () -> { for (var p : toRestore) mapPutOrRemove(map, p.a, p.b); }; } return null; } static <A, B> Map<A, B> mapWithSingleValue(Collection<A> l, B b) { HashMap<A, B> map = new HashMap(); for (A a : unnull(l)) map.put(a, b); return map; } // assumes map never contains null values static <A, B> AutoCloseable tempMapPut(Map<A, B> map, A key, B value) { if (map != null && key != null && value != null) { B old = map.put(key, value); return () -> mapPutOrRemove(map, key, old); } return null; } static TreeSet mapToTreeSet(Object f, Iterable l) { TreeSet x = new TreeSet(); if (l != null) for (Object o : l) x.add(callF(f, o)); return x; } static <A, B> TreeSet<B> mapToTreeSet(IF1<A, B> f, Iterable<A> l) { TreeSet<B> x = new TreeSet(); if (l != null) for (var o : l) x.add(f.get(o)); return x; } static <A, B> TreeSet<B> mapToTreeSet(Iterable<A> l, IF1<A, B> f) { return mapToTreeSet(f, l); } static String longestPrefixInTreeSet(String s, TreeSet<String> set) { return longestPrefixInNavigableSet(s, set); } static List<String> standardImports_fullyImportedPackages() { return endingWith_dropSuffix(standardImports(), ".*"); } static <A> A getAndClear(IVar<A> v) { A a = v.get(); v.set(null); return a; } static <A, B> int keysSize(MultiMap<A, B> mm) { return lKeys(mm); } static <A> A reverseGet(List<A> 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 ok(Object o) { return format("ok *", o); } static RuntimeException error() { throw new RuntimeException("fail"); } static RuntimeException error(String msg) { throw new RuntimeException(msg); } static int hashOfLong(long l) { return Long.hashCode(l); } 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 Method hashMap_findKey_method; static <A, B> A hashMap_findKey(HashMap<A, B> map, Object key) { try { if (hashMap_findKey_method == null) hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode"); Map.Entry<A, B> entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key); // java.util.Map.Entry<A, B> 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 <A, B> NavigableSet<A> navigableKeys(NavigableMap<A, B> map) { return map == null ? new TreeSet() : map.navigableKeySet(); } static <A> NavigableSet<A> navigableKeys(MultiSet<A> ms) { return ((NavigableMap) ms.map).navigableKeySet(); } static <A, B> NavigableSet<A> navigableKeys(MultiMap<A, B> mm) { return ((NavigableMap) mm.data).navigableKeySet(); } static <A, B> Map<A, List<B>> toMap(MultiMap<A, B> m) { return multiMapToMap(m); } static List<String> scoredSearch_prepare(String query) { return map(__125 -> replacePlusWithSpace(__125), splitAtSpace(query)); } static <A> void putUnlessZero(Map<A, Integer> map, A key, int value) { if (map != null && key != null && value != 0) map.put(key, value); } static <A> void putUnlessZero(Map<A, Double> 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<String> l, List<String> 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<String> 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<Pair<String, Double>> l, List<String> words) { double score = 0; if (l != null) for (Pair<String, Double> __0 : l) { String s = pairA(__0); double weight = pairB(__0); score += scoredSearch_score(s, words)*weight; } return score; } static <A, B> List<A> keysSortedByValuesDesc(final Map<A, B> map) { List<A> l = new ArrayList(map.keySet()); sort(l, mapComparatorDesc(map)); return l; } static <A, B extends Number> Scored<A> scoredWithMapValue(Map<A, B> map, A a) { return new Scored(a, toDouble(mapGet(map, a))); } static <A, B extends Number> A keyWithHighestValue(Map<A, B> map) { return keyWithBiggestValue(map); } 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 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 h1(String s, Object... params) { return hfulltag("h1", s, params); } static String div_vbar(String... items) { return div_vbar(asList(items)); } static String div_vbar(Collection<String> items, Object... params) { return divUnlessEmpty(joinNemptiesWithVBar(items), params); } static <A> A setAll(A o, Map<String, Object> fields) { if (fields == null) return o; for (String field : keys(fields)) set(o, field, fields.get(field)); return o; } static <A> 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 Stringable lambdaToToString(IF0<?> f) { return f == null ? null : new Stringable(f); } static boolean notNullOrEmptyString(Object o) { return o != null && !eq(o, ""); } static <A extends Component> A setTitle(A c, String title) { return setFrameTitle(c, title); } static <A extends Component> 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 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 <A, B> LinkedHashMap<A, B> cloneLinkedHashMap(Map<A, B> map) { return map == null ? new LinkedHashMap() : new LinkedHashMap(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 <A> List<A> replaceAll(List<A> l, final A a, final A b) { return map(l, new F1<A, A>() { 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<String> 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<MultiMap<String, CountryDialCode>> countryDialCodes_rawNumbersTreeMultiMap_cache = new Cache<>(() -> countryDialCodes_rawNumbersTreeMultiMap_load()); static MultiMap<String, CountryDialCode> countryDialCodes_rawNumbersTreeMultiMap() { return countryDialCodes_rawNumbersTreeMultiMap_cache.get(); } static MultiMap<String, CountryDialCode> countryDialCodes_rawNumbersTreeMultiMap_load() { return mapToMultiMap( mapKeys(__126 -> digitsOnly(__126), multiMapToMap(countryDialCodesMultiMap()))); } static void assertSame(Object a, Object b) { assertSame("", a, b); } static void assertSame(String msg, Object a, Object b) { if (a != b) throw fail(joinNemptiesWithColon(msg, a + " != " + b + " (" + identityHash(a) + "/" + identityHash(b) + ")")); } static void assertSame(IF0<String> msg, Object a, Object b) { if (a != b) throw fail(joinNemptiesWithColon(msg.get(), a + " != " + b + " (" + identityHash(a) + "/" + identityHash(b) + ")")); } static LineAndColumn tokenToLineAndColumn(ListAndIndex<String> ptr) { return ptr == null ? null : tokenToLineAndColumn(ptr.list(), ptr.idx()); } static LineAndColumn tokenToLineAndColumn(List<String> tok, int tokenIndex) { int line = 1, col = 1; tokenIndex = min(tokenIndex, l(tok)); for (int i = 0; i < tokenIndex; i++) { String t = tok.get(i); int n = l(t); for (int j = 0; j < n; j++) if (t.charAt(j) == '\n') { ++line; col = 1; } else ++col; } return new LineAndColumn(line, col); } static Set emptySet() { return new HashSet(); } static <A, B> Map.Entry<A, B> firstEntry(Map<A, B> map) { return empty(map) ? null : first(map.entrySet()); } static ImageSurface pixelatedImageSurface() { var is = imageSurface(); imageSurface_pixelated(is); return is; } static ImageSurface pixelatedImageSurface(MakesBufferedImage img) { return pixelatedImageSurface(toBufferedImage(img)); } static ImageSurface pixelatedImageSurface(BufferedImage img) { var is = pixelatedImageSurface(); is.setImage(img); return is; } static File dbDir() { return programDir(dbProgramID()); } static BufferedImage toBufferedImage(Object o) { return toBufferedImageOpt(o); } static <A> Set<A> createOrAddToSyncLinkedHashSet(Set<A> set, A a) { if (set == null) set = syncLinkedHashSet(); set.add(a); return set; } static File getBytecodePathForClass(Object o) { return getBytecodePathForClass(_getClass(o)); } static File getBytecodePathForClass(Class c) { try { return c == null ? null : new File(c.getProtectionDomain().getCodeSource().getLocation().toURI()); } catch (Exception __e) { throw rethrow(__e); } } static void createOrRemoveFile(File f, boolean create) { if (create) { if (!fileExists(f)) touchFile(f); } else if (fileExists(f)) removeFile(f); } static boolean sameFile(File a, File b) { try { return a == null ? b == null : b != null && eq(a.getCanonicalPath(), b.getCanonicalPath()); } catch (Exception __e) { throw rethrow(__e); } } static <A extends Component> A jMinHeight(A c, int h) { return jMinHeight(h, c); } static <A extends Component> A jMinHeight(int h, A c) { Dimension size = c.getMinimumSize(); c.setMinimumSize(new Dimension(size.width, max(h, size.height))); return jPreferHeight(h, c); } static String or2_rev(String b, String a) { return or2(a, b); } static String joinNemptiesWithSpacedPlus(Object... strings) { return joinNempties(" + ", strings); } static String joinNemptiesWithSpacedPlus(Iterable strings) { return joinNempties(" + ", strings); } static int intMax(Collection c, String field) { int max = Integer.MIN_VALUE; for (Object o : c) max = Math.max(max, toInt(getOpt(o, field))); return max; } static int intMax(Iterable<Integer> l) { int max = Integer.MIN_VALUE; for (int i : unnullForIteration(l)) max = Math.max(max, i); return max; } static int intMax(int... l) { int max = Integer.MIN_VALUE; if (l != null) for (int i : l) max = Math.max(max, i); return max; } static int linesOfCode_javaTok(String text) { List<String> tok = javaTok(text); int lines = 0; boolean codeInCurrentLine = false; for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i)) { // code token int lTok = l(t); for (int j = 0; j < lTok; j++) { char c = t.charAt(j); if (c == '\n') codeInCurrentLine = false; else if (!isSpaceEtc(c) && !codeInCurrentLine) { codeInCurrentLine = true; ++lines; } } } else { // space token if (codeInCurrentLine && containsNewLine(t)) codeInCurrentLine = false; } } return lines; } static File appendToFileName(File f, String suffix) { return fileAppendToName(f, suffix); } static File appendToFileName(String suffix, File f) { return appendToFileName(f, suffix); } static String nLabels(long n) { return n2(n, "label"); } static String nLabels(Collection l) { return nLabels(l(l)); } static String nLabels(Map map) { return nLabels(l(map)); } static boolean containsFile(File dir, String name) { return new File(dir, name).exists(); } static boolean containsFile(Iterable<File> l, File f) { return any(l, file -> sameFile(f, file)); } static File conceptsFileIn(File dir) { return newFile(dir, conceptsFileName()); } static List<File> listDirsContainingFileNamed(File dir, String fileName) { return filter(listDirs(dir), d -> isFile(newFile(d, fileName))); } static <A> A syncPopFirst(List<A> l) { if (empty(l)) return null; synchronized(l) { A a = first(l); l.remove(0); return a; } } static int inc(AtomicInteger i) { return incAtomicInt(i); } static int inc(AtomicInteger i, int delta) { return incAtomicInt(i, delta); } static long inc(AtomicLong l) { return incAtomicLong(l); } static long inc(AtomicLong l, long b) { return l.addAndGet(b); } static int inc(IntVar v) { synchronized(v) { int val = v.get() + 1; v.set(val); return val; } } static String systemHashCodeHex(Object o) { return intToHex(identityHashCode(o)); } static void register(Concept c) { registerConcept(c); } static <A, B> List<A> cloneKeys_noSync(Map<A, B> map) { return cloneList_noSync(keys(map)); } static <A, B> IterableIterator<B> navigableMultiSetMapValuesIterator_concurrent(final MultiSetMap<A, B> mm) { return navigableMultiSetMapValuesIterator_concurrent(mm, mm.data); } static <A, B> IterableIterator<B> navigableMultiSetMapValuesIterator_concurrent(final MultiSetMap<A, B> mm, Object mutex) { return iteratorFromFunction(new F0<B>() { Iterator<Set<B>> it = concurrentlyIterateValues((NavigableMap) mm.data, mutex); Iterator<B> it2; public B get() { while (it2 == null || !it2.hasNext()) { if (!it.hasNext()) return null; it2 = iterator(asList(it.next())); } return it2.next(); } }); } static <A, B> MultiSet<A> multiSetMapToMultiSet(MultiSetMap<A, B> mm) { MultiSet<A> ms = new MultiSet(); for (A a : keys(mm)) ms.add(a, l(mm.get(a))); return ms; } // action = runnable or method name static void onChangeAndNow(JComponent c, Object r) { onUpdateAndNow(c, r); } static void onChangeAndNow(List<? extends JComponent> l, Object r) { onUpdateAndNow(l, r); } static <A> JComboBox<A> onChangeAndNow(JComboBox<A> cb, IVF1<A> f) { onChange(cb, f); { if (f != null) f.get(getSelectedItem_typed(cb)); } return cb; } static <A extends JTabbedPane> A onChangeAndNow(A tabs, Runnable r) { if (r != null) { onChange(tabs, r); r.run(); } return tabs; } static String linesLL(Object... x) { return lines(ll(x)); } 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 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) { printStackTrace(__e); }} } static boolean eqOrEqic(boolean caseInsensitive, String a, String b) { return caseInsensitive ? eqic(a, b) : eq(a, b); } static boolean containsLineBreak(String s) { return containsNewLine(s); } static JPanel jSection(Component c) { return jSection("", c); } static JPanel jSection(final String title, final Component c) { return swing(new F0<JPanel>() { 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 void dm_clearModulePrintLog() { clearStringBuffer_gen(dm_current_mandatory()._actualPrintLog()); } static <A extends JComponent> A awtEvery(A component, long delay, Object r) { swingEvery(component, delay, r); return component; } static <A extends JComponent> 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 extends JComponent> A awtEvery(A component, double delaySeconds, Object r) { return awtEvery(component, toMS(delaySeconds), r); } static boolean scrollAllTheWayDown_bruteForce = true; static <A extends JComponent> A scrollAllTheWayDown(A c) { if (c != null) { swing(() -> { int h = c.getHeight()-1; if (scrollAllTheWayDown_bruteForce) h = max(h, 100000); c.scrollRectToVisible(new Rectangle(0, h, 1, 1)); }); } return c; } // independent timer static void awtLater(int delay, final Object r) { swingLater(delay, r); } static void awtLater(int delay, Runnable r) { swingLater(delay, r); } static void awtLater(Object r) { swingLater(r); } static void awtLater(double delaySeconds, Runnable r) { swingLater(toMS(delaySeconds), 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 void clearPopupMenu(final JPopupMenu menu) { if (menu != null) { swing(() -> { menu.removeAll(); }); } } static String h1_title(String s) { return htitle_h1(s); } static Boolean not(Boolean b) { return b == null ? null : !b; } static long incAtomicLong(AtomicLong l) { return l.incrementAndGet(); } static boolean preciseNuObject_debug = false; static <A> A preciseNuObject(Class<A> c, Object... args) { try { Constructor[] methods = c.getDeclaredConstructors(); Constructor best = null; int bestScore = Integer.MAX_VALUE; boolean widening = false; if (preciseNuObject_debug) printVars("preciseNuObject", "c", c, "methods" , l(methods)); for (var m : methods) { int score = methodApplicabilityScore_withPrimitiveWidening(m, args); if (score == 0) return (A) invokeConstructor(m, args); if (preciseNuObject_debug) print("Method score: " + m + " " + score); if (abs(score) < bestScore) { best = m; bestScore = abs(score); widening = score < 0; } } if (best != null) if (widening) return (A) invokeConstructorWithWidening(best, args); else return (A) invokeConstructor(best, args); // try varargs - TODO: make this precise too methodSearch: for (var m : methods) { { if (!(m.isVarArgs())) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return (A) invokeConstructor(m, newArgs); } throw fail("No matching constructor found: " + formatFunctionCall(c, map(__127 -> _getClass(__127), args))); } catch (Exception __e) { throw rethrow(__e); } } static <A> Object[] mapToArrayOrNull(A[] l, IF1<A, Object> f) { if (l == null) return null; int n = l.length; if (n == 0) return null; Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = f.get(l[i]); return array; } static <A> Object[] mapToArrayOrNull(IF1<A, Object> f, A[] l) { return mapToArrayOrNull(l, f); } static <A> Object[] mapToArrayOrNull(IF1<A, Object> f, Collection<A> l) { int n = l(l); if (n == 0) return null; 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 <A> Object[] mapToArrayOrNull(Collection<A> l, IF1<A, Object> f) { return mapToArrayOrNull(l, f); } // binary legacy syntax static String formatFunctionCall(String fname, Object... args) { return formatFunctionCall((Object) fname, args); } static String formatFunctionCall(Object fname, Object... args) { return fname + "(" + joinWithComma(allToString(args)) + ")"; } static String formatFunctionCall(String fname, Iterable args) { return formatFunctionCall((Object) fname, args); } static String formatFunctionCall(Object fname, Iterable args) { return formatFunctionCall(fname, toObjectArray(args)); } static String strOrClassName(Object o) { if (o instanceof Class) return className((Class) o); return str(o); } static boolean canCallWithVarargs(Object o, String method, Object... args) { if (o == null) return false; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); if (cache.findStaticMethod(method, args) != null) return true; // try varargs List<Method> methods = cache.cache.get(method); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs() && isStaticMethod(m))) continue; } if (massageArgsForVarArgsCall(m, args) != null) return true; } } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); if (cache.findMethod(method, args) != null) return true; // try varargs List<Method> methods = cache.cache.get(method); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } if (massageArgsForVarArgsCall(m, args) != null) return true; } } return false; } static boolean newPreciseCall_debug = false; static Object newPreciseCall(Object o, String method, Object... args) { try { if (o == null) return null; boolean staticCall = o instanceof Class; Class c = staticCall ? (Class) o : o.getClass(); Object target = staticCall ? null : o; _MethodCache cache = callOpt_getCache(c); List<Method> methods = cache.cache.get(method); Method best = null; int bestScore = Integer.MAX_VALUE; boolean widening = false; if (newPreciseCall_debug) printVars("newPreciseCall", "method", method, "staticCall", staticCall, "c", c, "methods" , l(methods)); if (methods != null) for (Method m : methods) { if (staticCall && !isStaticMethod(m)) continue; int score = methodApplicabilityScore_withPrimitiveWidening(m, args); if (score == 0) return invokeMethod(m, target, args); if (newPreciseCall_debug) print("Method score: " + m + " " + score); if (abs(score) < bestScore) { best = m; bestScore = abs(score); widening = score < 0; } } if (best != null) if (widening) return invokeMethodWithWidening(best, target, args); else return invokeMethod(best, target, args); return callWithVarargs(o, method, args); // TODO: make this precise too } catch (Exception __e) { throw rethrow(__e); } } static Method findMethod_precise_onTypes(Object o, String method, Class... argTypes) { try { if (o instanceof Class) { _MethodCache cache = callOpt_getCache((Class) o); List<Method> methods = cache.cache.get(method); Lowest<Method> 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) best.put(m, score); } return best.get(); } if (o == null) return null; _MethodCache cache = callOpt_getCache(o.getClass()); List<Method> methods = cache.cache.get(method); Lowest<Method> 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 Method findSingleInterfaceMethod(Class intrface) { return singletonOpt(findNonDefaultInterfaceMethods(intrface)); } static <A> A proxyFromInvocationHandler(Class<A> intrface, InvocationHandler handler) { return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, handler); } static Object[] concatMethodArgs(Object[] args1, Object[] args2) { int n1, n2; if (args1 == null || (n1 = args1.length) == 0) return args2; if (args2 == null || (n2 = args2.length) == 0) return args1; // actual concat Object[] args = new Object[n1+n2]; for (int i = 0; i < n1; i++) args[i] = args1[i]; for (int i = 0; i < n2; i++) args[n1+i] = args2[i]; return args; } static int seconds() { return seconds(Calendar.getInstance()); } static int seconds(Calendar c) { return c.get(Calendar.SECOND); } static int toIntPercent(double ratio) { return roundToInt(ratio*100); } // when used with map function static int toIntPercent(float ratio) { return toIntPercent((double) ratio); } 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 <A> Set<A> setWithNotify(final Runnable onChange) { return setWithNotify(new HashSet(), onChange); } static <A> Set<A> setWithNotify(Set<A> set, final Runnable onChange) { return new NotifyingSet<A>(set) { void change() { onChange.run(); } }; } static <A> Set<A> syncLinkedHashSet() { return synchroLinkedHashSet(); } static <A extends JComponent, B> A bindToolTipToTransformedLiveValue(IF1<B, Object> f, LiveValue<B> lv, A c) { bindLiveValueListenerToComponent(c, lv, new Runnable() { public void run() { try { setToolTipText(c, strOrNull(f.get(lv.get()))) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setToolTipText(c, strOrNull(f.get(lv!)))"; }}); return c; } static <A> JLabel simpleTransformedLiveValueLabel(IF1<A, Object> f, LiveValue<A> lv) { return bindJLabelToTransformedLiveValue(f, swing(() -> new JLabel()), lv); } static MouseAdapter onMouseDown_anyButton(Component c, Runnable r) { return onMouseDown(c, runnableToIVF1(r)); } static MouseAdapter onMouseDown_anyButton(Component c, IVF1<MouseEvent> f) { return c == null || f == null ? null : swing(() -> { MouseAdapter ma = new MouseAdapter() { public void mousePressed(MouseEvent e) { f.get(e); } }; c.addMouseListener(ma); return ma; }); } static int componentCount(Container c) { return numberOfChildren(c); } static TitledBorder jRaisedSectionBorder(String title) { return swing(() -> { Border border = BorderFactory.createBevelBorder(BevelBorder.RAISED); return BorderFactory.createTitledBorder(border, title); }); } static <A extends JComponent> A setBorder(Border border, A c) { if (c != null) { swing(() -> { c.setBorder(border); }); } return c; } static <A extends JComponent> A setBorder(A c, Border border) { return setBorder(border, c); } static void showPopupMenu(JPopupMenu menu, MouseEvent e) { if (menu != null && e != null) menu.show(e.getComponent(), e.getX(), e.getY()); } static Dimension dimensionPlus(int w, int h, Dimension d) { return d == null ? null : new Dimension(d.width+w, d.height+h); } static Dimension maxDimension(Dimension a, Dimension b) { return a == null ? b : b == null ? a : new Dimension(max(a.width, b.width), max(a.height, b.height)); } // finds the byte code path root (dir or jar) // TODO: inner classes will not be found (need to replace . with $) static File byteCodePathForClass(Class c) { if (c == null) return null; ClassLoader cl = getClassLoader(c); // handle JavaXClassLoader Collection<File> files = (Collection<File>) (getOpt(cl, "files")); if (files != null) { String name = c.getName().replace('.', '/') + ".class"; for (File location : files) if (dirOrZipContainsPath(location, name)) return location; throw fail(name + " not found in: " + files); } return null; } static File byteCodePathForClass(Object o) { return byteCodePathForClass(_getClass(o)); } static List<String> endingWith_dropSuffix(Collection<String> l, String suffix) { List<String> out = new ArrayList(); for (String s : unnullForIteration(l)) if (endsWith(s, suffix)) out.add(dropLast(l(suffix), s)); return out; } static List<String> endingWith_dropSuffix(String suffix, Collection<String> l) { return endingWith_dropSuffix(l, suffix); } static List<String> standardImports_cache; static List<String> standardImports() { if (standardImports_cache == null) standardImports_cache = standardImports_load(); return standardImports_cache;} static List<String> standardImports_load() { return ll( "java.util.*", "java.util.zip.*", "java.util.List", "java.util.regex.*", "java.util.concurrent.*", "java.util.concurrent.atomic.*", "java.util.concurrent.locks.*", "java.util.function.*", "javax.swing.*", "javax.swing.event.*", "javax.swing.text.*", "javax.swing.table.*", "java.io.*", "java.net.*", "java.lang.reflect.*", "java.lang.ref.*", "java.lang.management.*", "java.security.*", "java.security.spec.*", "java.awt.*", "java.awt.event.*", "java.awt.image.*", "java.awt.geom.*", "javax.imageio.*", "java.math.*"); } static List<String> classNamesInJarOrDir(File dir) { return classNamesInJarOrDir(dir, ""); } static List<String> classNamesInJarOrDir(File dir, String prefixInJar) { List<String> classes = new ArrayList(); if (dir == null) {} else if (dir.isDirectory()) { for (File f : listFiles(dir)) { // TODO: subdirectories String s = f.getName(); if (s.endsWith(".class")) classes.add(dropSuffix(".class", s)); } } else if (dir.isFile()) { try { JarFile jarFile = new JarFile(dir); try { Enumeration<JarEntry> e = jarFile.entries(); while (e.hasMoreElements()) { JarEntry je = e.nextElement(); if (je.isDirectory() || je.getName().startsWith("META-INF/") || !je.getName().endsWith(".class")) continue; String className = dropSuffix(".class", je.getName()); className = dropPrefixOrNull(prefixInJar, className); if (className == null) continue; if (className.contains("-")) continue; className = className.replace('/', '.'); classes.add(className); } } finally { jarFile.close(); } } catch (Throwable __e) { printStackTrace(__e); }} return classes; } static List<String> classNamesInLoadedJigsawModules() { return concatMap(loadedJigsawModuleNames(), moduleName -> classNamesInJigsawModule(moduleName)); } static String nlLogic_text(Exp e) { return e == null ? null : e.text(); } static String nlLogic_text(IfThen r) { return r == null ? null : r.text(); } static BufferedImage whiteImage(int w, int h) { return newBufferedImage(w, h, Color.white); } static BufferedImage whiteImage(int size) { return whiteImage(size, size); } static JLabel setImage(final BufferedImage img, final JLabel lbl) { if (lbl != null) { swing(() -> { lbl.setIcon(imageIcon(img)); }); } return lbl; } static JLabel setImage(JLabel lbl, BufferedImage img) { return setImage(img, lbl); } static JLabel setImage(final String imageID, final JLabel lbl) { if (lbl != null) { swing(() -> { lbl.setIcon(imageIcon(imageID)); }); } return lbl; } static JLabel setImage(JLabel lbl, String imageID) { return setImage(imageID, lbl); } static <A extends ImageSurface> A setImage(A is, BufferedImage img) { { if (is != null) is.setImage(img); } return is; } static <A extends Component> A onResize(A c, Runnable r) { if (c != null && r != null) { swing(() -> { c.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { pcallF(r); } }); }); } return c; } static <A extends Component> A onResize(Runnable r, A c) { return onResize(c, r); } static <A extends JComponent> void componentPopupMenu2(A component, final VF2<A, JPopupMenu> menuMaker) { final WeakReference < A > ref = new WeakReference<>(component); componentPopupMenu(component, new VF1<JPopupMenu>() { public void get(JPopupMenu menu) { try { callF(menuMaker, ref.get(), menu); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(menuMaker, ref!, menu);"; }}); } // onDrop: voidfunc(File), but may also return false static <A extends JComponent> A jHandleFileDrop(A c, final Object onDrop) { new DropTarget(c, new DropTargetAdapter() { public void drop(DropTargetDropEvent e) { try { Transferable tr = e.getTransferable(); DataFlavor[] flavors = tr.getTransferDataFlavors(); for (DataFlavor flavor : flavors) { if (flavor.isFlavorJavaFileListType()) { e.acceptDrop(e.getDropAction()); File file = first((List<File>) tr.getTransferData(flavor)); if (file != null && !isFalse(callF(onDrop, file))) e.dropComplete(true); return; } } } catch (Throwable __e) { printStackTrace(__e); } e.rejectDrop(); } }); return c; } static <A extends JComponent> A jHandleFileDrop(Object onDrop, A c) { return jHandleFileDrop(c, onDrop); } // onHover is called with null value when mouse moves outside of image surface // onHover receives image coordinates static void imageSurfaceOnHover(ImageSurface is, VF1<Pt> onHover) { if (is == null || onHover == null) return; { swing(() -> { MouseAdapter ma = new MouseAdapter() { public void mouseMoved(MouseEvent e) { pick(e); } public void mouseEntered(MouseEvent e) { pick(e); } public void mouseExited(MouseEvent e) { pick(null); } void pick(MouseEvent e) { callF(onHover, e == null ? (Pt) null : is.pointFromEvent(e)); } }; is.addMouseMotionListener(ma); is.addMouseListener(ma); }); } } static void imageSurfaceOnHover(ImageSurface is, IVF1<Pt> onHover) { imageSurfaceOnHover(is, toVF1(onHover)); } // IMPORTANT: doesn't work with JTattoo (so not currently in Stefan's OS). listeners are only called on first show+hide // r : runnable or voidfunc(bool) static JCheckBoxMenuItem jCheckBoxMenuItem_dyn(String text, IF0<Boolean> checked, Object r) { JCheckBoxMenuItem mi = jCheckBoxMenuItem(text, false, r); if (checked != null) bindToComponent(mi, new Runnable() { public void run() { try { boolean b = isTrue(checked.get()); setChecked(mi, b); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "boolean b = isTrue(checked!);\r\n ifdef jCheckBoxMenuItem_dyn_debug\r\n p..."; }}, new Runnable() { public void run() { try { ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef jCheckBoxMenuItem_dyn_debug\r\n print(\"jCheckBoxMenuItem_dyn: hiding..."; }}); return mi; } static JCheckBoxMenuItem jCheckBoxMenuItem_dyn(String text, IF0<Boolean> checked, IVF1<Boolean> r) { return jCheckBoxMenuItem_dyn(text, checked, (Object) r); } static JFrame showFullScreen(JComponent c) { return showFullScreen(defaultFrameTitle(), c); } static JFrame showFullScreen(final String title, final JComponent c) { return (JFrame) swingAndWait(new F0<Object>() { public Object get() { try { GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice(); if (!gd.isFullScreenSupported()) throw fail("No full-screen mode supported!"); boolean dec = JFrame.isDefaultLookAndFeelDecorated(); if (dec) JFrame.setDefaultLookAndFeelDecorated(false); final JFrame window = new JFrame(title); window.setUndecorated(true); if (dec) JFrame.setDefaultLookAndFeelDecorated(true); registerEscape(window, new Runnable() { public void run() { try { disposeWindow(window) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "disposeWindow(window)"; }}); window.add(wrap(c)); gd.setFullScreenWindow(window); // Only this hides the task bar in Peppermint Linux w/Substance for (int i = 100; i <= 1000; i += 100) awtLater(i, new Runnable() { public void run() { try { window.toFront() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "window.toFront()"; }}); return window; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()\r\n ..."; }}); } static void selectFile(final String msg, VF1<File> action) { selectFile(msg, userDir(), action); } static void selectFile(final String msg, final File defaultFile, VF1<File> action) { inputFilePath(msg, defaultFile, action); } static void saveImage(File f, BufferedImage img) { if (hasJPEGExtension(f)) saveJPG(f, img); else savePNG(f, img); } static void saveImage(BufferedImage img, File f) { saveImage(f, img); } static <A extends Image> A copyImageToClipboard(A img) { TransferableImage trans = new TransferableImage(img); Toolkit.getDefaultToolkit().getSystemClipboard().setContents( trans, null); vmBus_send("newClipboardContents", img); print("Copied image to clipboard (" + img.getWidth(null) + "*" + img.getHeight(null) + " px)"); return img; } static JComponent selectImageSnippet(VF1<String> onSelect) { return selectSnippetID_v1(onSelect); } static JComponent selectImageSnippet(String defaultID, VF1<String> onSelect) { return selectSnippetID_v1(defaultID, onSelect); } static BufferedImage cloneClipBufferedImage(BufferedImage src, Rectangle clip) { return cloneBufferedImage(clipBufferedImage(src, clip)); } static BufferedImage cloneClipBufferedImage(BufferedImage src, Rect r) { return cloneBufferedImage(clipBufferedImage(src, r)); } static BufferedImage cloneClipBufferedImage(BufferedImage src, int x, int y, int w, int h) { return cloneBufferedImage(clipBufferedImage(src, x, y, w, h)); } static BufferedImage getImageFromClipboard() { try { Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); if (t == null) return null; List<File> l = (List<File>) (getTransferData(t, DataFlavor.javaFileListFlavor)); if (nempty(l)) return loadImage2(first(l)); if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) return (BufferedImage) t.getTransferData(DataFlavor.imageFlavor); return imageFromDataURL(getTextFromClipboard()); } catch (Exception __e) { throw rethrow(__e); } } static void popup(final Throwable throwable) { popupError(throwable); } static void popup(final String msg) { print(msg); SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(null, msg); } }); } static BufferedImage resizeImage(BufferedImage img, int newW, int newH) { return resizeImage(img, newW, newH, Image.SCALE_SMOOTH); } static BufferedImage resizeImage(BufferedImage img, int newW, int newH, int scaleType) { if (newW == img.getWidth() && newH == img.getHeight()) return img; Image tmp = img.getScaledInstance(newW, newH, scaleType); BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = dimg.createGraphics(); g2d.drawImage(tmp, 0, 0, null); g2d.dispose(); return dimg; } static BufferedImage resizeImage(BufferedImage img, int newW) { int newH = iround(img.getHeight()*(double) newW/img.getWidth()); return resizeImage(img, newW, newH); } static BufferedImage resizeImage(int newW, BufferedImage img) { return resizeImage(img, newW); } static boolean hasTransparency(BufferedImage img) { return img.getColorModel().hasAlpha(); } static <A extends Component> A repaint(A c) { if (c != null) c.repaint(); return c; } static void scaffoldCalled(Object obj, Object function, Object... args) { printShortenedFunctionCall(200, (obj == null ? "" : shorten(20, str(obj)) + " :: ") + function, args); } static <A> A printIfScaffoldingEnabled(Object o, A a) { return printIfScaffoldingEnabled(o, "", a); } static <A> A printIfScaffoldingEnabled(Object o, String s, A a) { return printIf(scaffoldingEnabled(o), s, a); } static BoolVar componentShowingVar(JComponent component) { return swing(() -> { BoolVar flag = new BoolVar(component.isShowing()); component.addAncestorListener(new AncestorListener() { public void ancestorAdded(AncestorEvent event) { if (flag.get()) print("Warning: bindToComponent logic failure"); flag.set(true); } public void ancestorRemoved(AncestorEvent event) { if (!flag.get()) print("Warning: bindToComponent logic failure"); flag.set(false); } public void ancestorMoved(AncestorEvent event) { } }); return flag; }); } static WidthAndHeight imageSize(BufferedImage img) { return img == null ? null : widthAndHeight(img.getWidth(), img.getHeight()); } static boolean imagesHaveSameSize(BufferedImage a, BufferedImage b) { return a != null && b != null && a.getWidth() == b.getWidth() && a.getHeight() == b.getHeight(); } static boolean scaffoldingEnabled(Object o) { return metaGet(o, "scaffolding") != null; } static Container getParent(final Component c) { return c == null ? null : swing(new F0<Container>() { public Container get() { try { return c.getParent(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getParent();"; }}); } static JScrollPane enclosingScrollPane(Component c) { while (c.getParent() != null && !(c.getParent() instanceof JViewport) && c.getParent().getComponentCount() == 1) c = c.getParent(); // for jscroll_center if (!(c.getParent() instanceof JViewport)) return null; c = c.getParent().getParent(); return c instanceof JScrollPane ? (JScrollPane) c : null; } static ImageSurface showFullScreenImageSurface(BufferedImage img) { ImageSurface is = jImageSurface(img); showFullScreen(jscroll_centered(disposeFrameOnClick(is))); return is; } static void imageSurface_pixelated(ImageSurface imageSurface) { if (imageSurface == null) return; imageSurface.setDoubleBuffered(true); // solve flickering when partially obscured imageSurface.noAlpha = true; imageSurface.interpolationMode = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; repaint(imageSurface); } static void imageSurface_pixelated(ImageSurface imageSurface, boolean pixelated) { if (pixelated) imageSurface_pixelated(imageSurface); else imageSurface_unpixelated(imageSurface); } static void closeAllAndClear(Collection<? extends AutoCloseable> l) { if (l == null) return; for (AutoCloseable c : cloneList(l)) { try { close(c); } catch (Throwable __e) { printStackTrace(__e); }} l.clear(); } static void revalidateIncludingFullCenterContainer(Component c) { if (c == null) return; { swing(() -> { c.revalidate(); var parent = c.getParent(); if (parent != null && parent.getLayout() instanceof GridBagLayout) parent.revalidate(); }); } } static JScrollPane jscroll_center_borderless(Component c) { return jscroll_centered_borderless(c); } static <A> List<A> listPlus(Collection<A> l, A... more) { return concatLists(l, asList(more)); } static ReliableSingleThread rst(Runnable r) { return new ReliableSingleThread(r); } static <A> List<A> sortByCalculatedFieldAlphaNumIC(Iterable<A> c, IF1<A, String> f) { List<A> l = cloneList(c); sort(l, (a, b) -> cmpAlphanumIC(f.get(a), f.get(b))); return l; } // This works when the ctrl key is pressed/released when a Swing window // is in focus or the mouse is inside a Swing window. // If you press ctrl before the program starts and the mouse is never // inside any of the windows, the key press will not be registered. static AutoCloseable tempAddGlobalCtrlKeyListener(IVF1<Boolean> onStatusChanged) { AWTEventListener l = new AWTEventListener() { Boolean status; public void eventDispatched(AWTEvent evt) { if (evt instanceof InputEvent) { Boolean b = ((InputEvent) evt).isControlDown(); if (!eq(b, status)) pcallF_typed(onStatusChanged, status = b); } } }; Toolkit.getDefaultToolkit().addAWTEventListener(l, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); return () -> Toolkit.getDefaultToolkit().removeAWTEventListener(l); } static String formatDouble1(double d) { return formatDouble(d, 1); } static <A> A waitForVarPredicate(Var<A> v, IF0<Boolean> pred) { try { synchronized(v) { while (!pred.get()) v.wait(); return v.get(); } } catch (Exception __e) { throw rethrow(__e); } } static void cMigrateField(Concept c, String oldField, String newField) { if (c == null) return; Object oldValue = cget(c, oldField); Object newValue = cget(c, newField); if (oldValue != null && newValue == null) { try { String name = shortDynName(c); print("Migrating " + name + "." + oldField + " -> " + name + "." + newField + " [id " + c.id + "]"); cset(c, newField, oldValue); cset(c, oldField, null); } catch (Throwable __e) { printStackTrace(__e); }} } // Use like this: printVars_str(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(+x); static void printVars_shorten(Object... params) { print(renderVars_stringifier(o -> str_shorten(o), params)); } static String shortDynName(Object o) { return shortDynamicClassName(o); } 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 String nChars(long n) { return n2(n, "char"); } static String nChars(String s) { return nChars(l(s)); } static void appendToTextFile(File file, String s) { appendToFile(file, s); } static void appendToTextFile(String file, String s) { appendToTextFile(programFile(file), s); } static String dateWithMSUTC() { return dateWithMSUTC(now()); } static String dateWithMSUTC(long time) { var format = simpleDateFormat_UTC("yyyy/MM/dd"); return format.format(time) + " " + formatUTCWithMS_24(time); } static SimpleLiveValue<String> stringLiveValue() { return new SimpleLiveValue(String.class); } static SimpleLiveValue<String> stringLiveValue(String value) { return new SimpleLiveValue(String.class, value); } static void setSectionTitle(JComponent c, String title) { swing(() -> { var _c_1 = c; while (_c_1 != null) { var b = _c_1.getBorder(); if (b != null) { if (b instanceof javax.swing.border.TitledBorder) { ((javax.swing.border.TitledBorder) b).setTitle(title); revalidate(_c_1); return; } } if (_c_1.getParent() instanceof JComponent) _c_1 = (JComponent) _c_1.getParent(); else break; } }); } static JPanel jline(final Component... components) { return swing(new F0<JPanel>() { public JPanel get() { try { return new LeftAlignedLine(components); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return new LeftAlignedLine(components);"; }}); } static <A extends Component> JPanel jline(List<A> components) { return jline(asArray(Component.class, components)); } static SingleComponentPanel singleComponentPanel() { return singleComponentPanel(null); } static SingleComponentPanel singleComponentPanel(final Component c) { return swing(new F0<SingleComponentPanel>() { public SingleComponentPanel get() { try { return new SingleComponentPanel(c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return new SingleComponentPanel(c);"; }}); } static <A> A enableScaffolding(A o) { if (o instanceof IMeta) ((IMeta) o).metaPut("scaffolding", true); return o; } static int getCaretPosition(final JTextComponent c) { return c == null ? 0 : swing(new F0<Integer>() { public Integer get() { try { return c.getCaretPosition(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getCaretPosition();"; }}); } static void addMenuItems(JMenu m, Object... x) { fillJMenu(m, x); } static void addMenuItems(JPopupMenu m, Object... x) { fillJPopupMenu(m, x); } static JTextArea showTextWordWrapped(final String title, final String text) { return showWrappedText(title, text); } static JTextArea showTextWordWrapped(Object text) { return showWrappedText(text); } static JPanel centerAndEastWithMargin(Component c, Component e) { return centerAndEastWithMarginInbetween(c, e); } static JScrollPane jBorderlessHigherScrollPane(JComponent c) { return borderlessScrollPane(jHigherScrollPane(c)); } static JPanel jfullcenter(Component c) { return jFullCenter(c); } static <A extends Component> A markVisualizer(Object visualizer, A a) { return setMetaSrc(a, visualizer); } static <A extends JTextComponent> A bindTextComponentToLiveValue_bothWays(A tc, SimpleLiveValue<String> lv) { bindLiveValueListenerToComponent(tc, lv, new Runnable() { public void run() { try { setTextKeepCaret(tc, lv.get()) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setTextKeepCaret(tc, lv!)"; }}); onChange(tc, new Runnable() { public void run() { try { lv.set(getText(tc)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "lv.set(getText(tc))"; }}); return tc; } static <A extends JComponent> A onCtrlEnter(A c, Runnable action) { return onCtrlEnter(c, (Object) action); } static <A extends JComponent> A onCtrlEnter(A c, Object action) { addKeyListener(c, ctrlEnterKeyListener(action)); return c; } static SingleThread awtCalcEvery(JFrame f, int delay, int firstDelay, final Object runnable) { return awtCalcRegularly(f, delay, firstDelay, runnable); } static SingleThread awtCalcEvery(JComponent c, int delay, int firstDelay, final Object runnable) { return awtCalcRegularly(c, delay, firstDelay, runnable); } static SingleThread awtCalcEvery(JFrame f, int delay, final Object runnable) { return awtCalcRegularly(f, delay, delay, runnable); } static SingleThread awtCalcEvery(JComponent c, int delay, Runnable runnable) { return awtCalcRegularly(c, delay, delay, runnable); } static SingleThread awtCalcEvery(JComponent c, int delay, final Object runnable) { return awtCalcRegularly(c, delay, delay, runnable); } static SingleThread awtCalcEvery(JComponent c, double delay, Runnable runnable) { return awtCalcEvery(c, delay, (Object) runnable); } static SingleThread awtCalcEvery(JComponent c, double delay, Object runnable) { return awtCalcEvery(c, toMS_int(delay), runnable); } static SingleThread awtCalcEvery(JComponent c, double firstDelay, double delay, final Object runnable) { return awtCalcEvery(c, toMS_int(delay), toMS_int(firstDelay), runnable); } static AutoCompletion installCompletionProvider(DefaultCompletionProvider completionProvider, RSyntaxTextArea textArea) { return swing(() -> { if (completionProvider == null || textArea == null) return null; completionProvider.setAutoActivationRules(true, " "); var autoComplete = new AutoCompletion(completionProvider); autoComplete.setAutoCompleteSingleChoices(false); autoComplete.setAutoActivationEnabled(true); autoComplete.setAutoActivationDelay(2000); autoComplete.install(textArea); return autoComplete; }); } static JPanel jCenteredSection(Component c) { return jCenteredSection("", c); } static JPanel jCenteredSection(String title, Swingable c) { return jCenteredSection(title, wrap(c)); } static JPanel jCenteredSection(String title, Component c) { return swing(new F0<JPanel>() { public JPanel get() { try { JPanel p = jSection(title, c); ((TitledBorder) p.getBorder()).setTitleJustification(TitledBorder.CENTER); return p; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel p = jSection(title, c);\r\n ((TitledBorder) p.getBorder()).setTitleJu..."; }}); } static JPanel jCenteredSection(String title) { return jCenteredSection(title, jpanel()); } static JPanel withTopAndLeftMargin(Component c) { return withTopAndLeftMargin(defaultMargin(), c); } static JPanel withTopAndLeftMargin(int w, Component c) { return swing(() -> { JPanel p = marginPanel(); p.setBorder(BorderFactory.createEmptyBorder(w, w, 0, 0)); p.add(c); return p; }); } static boolean isShowing(Component c) { return isComponentShowing(c); } static void runInQAndWait(Q q, Runnable r) { if (r == null) return; if (isInQ(q)) { callF(r); return; } final Flag done = new Flag(); final Var<Throwable> error = new Var(); q.add(new Runnable() { public void run() { try { try { callF(r); } catch (Throwable e) { error.set(e); } finally { done.raise(); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "try {\r\n callF(r);\r\n } catch (Throwable e) {\r\n error.set(e);\r\n ..."; }}); done.waitUntilUp(); if (error.has()) throw rethrow(error.get()); } static <A> OKOrError<A> okOrError(IF0<A> f) { try { return OKOrError_ok(f.get()); } catch (Throwable e) { printStackTrace(e); return OKOrError_error(e); } } static <A> OKOrError<A> okOrError(Runnable r) { return okOrError(runnableToIF0(r)); } static String exceptionToStringShorter_dontDropOuterExceptions(Throwable e) { String msg = hideCredentials(unnull(e.getMessage())); String className = baseClassName(e); if (!eq(className, "RuntimeException") && (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0)) return className + prependIfNempty(": ", msg); else return msg; } static JComponent jErrorView(Throwable e) { //ret jscroll(jMultiLineLabel(renderStackTrace(e))); return jSmallErrorView(e); } static String exceptionToStringShorter(Throwable e) { e = getInnerException(e); return exceptionToStringShorter_dontDropOuterExceptions(e); } static <A> A evalWithTimeoutOrTypedException(int timeoutMS, IF0<A> f) { return evalWithTimeoutOrTypedException(toSeconds(timeoutMS), f); } static <A> A evalWithTimeoutOrTypedException(double timeoutSeconds, IF0<A> f) { Either<A, Thread> e = evalWithTimeout(timeoutSeconds, f); if (e.isA()) return (A) e.a(); throw new TimeoutException( timeoutSeconds, f, e.b()); } // Note: very SLOW with big text (JTextArea) // Use showText_fast_noWrap instead static JTextArea showText(final String title, Object text) { return showText(null, title, text); } static JTextArea showText(JTextArea ta, final String title, Object text) { final String _text = str(text); if (ta != null) return activateFrameAndReturnComponent(setFrameTitle(title, setText(ta, _text))); return swing(new F0<JTextArea>() { public JTextArea get() { try { JTextArea textArea = newTypeWriterTextArea(_text); makeFrame(title, new JScrollPane(textArea)); return textArea; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JTextArea textArea = newTypeWriterTextArea(_text);\r\n makeFrame(title, new ..."; }}); } static JTextArea showText(Object text) { return showText(str(text)); } static JTextArea showText(String text) { return showText(autoFrameTitle(), text); } static JTextArea showText() { return showText(""); } static <B> TreeMap<String, B> toCIMap(Map<String, B> map) { return asCIMap(map); } static <A extends JTextComponent> A setEditable(A a, boolean b) { if (a != null) { swing(() -> { a.setEditable(b); }); } return a; } static Map<Integer, Integer> rsyntaxTextArea_fixNumPad_map = litmap( KeyEvent.VK_UP, KeyEvent.VK_KP_UP, KeyEvent.VK_DOWN, KeyEvent.VK_KP_DOWN, KeyEvent.VK_LEFT, KeyEvent.VK_KP_LEFT, KeyEvent.VK_RIGHT, KeyEvent.VK_KP_RIGHT ); static boolean rsyntaxTextArea_fixNumPad_done = false; static void rsyntaxTextArea_fixNumPad() { if (rsyntaxTextArea_fixNumPad_done) return; RSyntaxTextAreaDefaultInputMap inputMap = new RSyntaxTextAreaDefaultInputMap(); for (KeyStroke key : inputMap.keys()) { Integer code = rsyntaxTextArea_fixNumPad_map.get(key.getKeyCode()); if (code != null) inputMap.put(KeyStroke.getKeyStroke(code, key.getModifiers()), inputMap.get(key)); } UIManager.put("RSyntaxTextAreaUI.inputMap", inputMap); //set rsyntaxtextarea_done; } static RSyntaxTextArea javaxSyntaxTextArea() { return swing(new F0<RSyntaxTextArea>() { public RSyntaxTextArea get() { try { RSyntaxTextArea textArea = new RSyntaxTextArea(1, 10); initSyntaxTextArea(textArea); textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); return textArea; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "RSyntaxTextArea textArea = new RSyntaxTextArea(1, 10);\r\n initSyntaxTextAre..."; }}); } static List<JMenu> getMenus(JMenuBar mb) { if (mb == null) return null; return countIteratorAsList(mb.getMenuCount(), i -> mb.getMenu(i)); } static List<JMenuItem> getMenuItems(JMenu menu) { if (menu == null) return null; return nonNulls(countIterator(menu.getItemCount(), i -> menu.getItem(i))); } static void showDialogOnSameScreen(JDialog dialog, Component c) { swing(() -> { if (dialog == null) return; Window w = getWindow(c); if (w != null) centerWindowWithin(dialog, screenBounds_safe(screenNrOfWindow(w))); dialog.setVisible(true); }); } static Font deriveFont(Font font, float size) { return font == null ? null : font.deriveFont(size); } static boolean containsInstance(Iterable i, Class c) { if (i != null) for (Object o : i) if (isInstanceX(c, o)) return true; return false; } static <A extends JComponent> A setToolTip(A c, Object toolTip) { return setToolTipText(c, toolTip); } static <A extends JComponent> A setToolTip(Object toolTip, A c) { return setToolTipText(c, toolTip); } static void setToolTip(TrayIcon trayIcon, String toolTip) { setTrayIconToolTip(trayIcon, toolTip); } static Color getBackground(final Component c) { return c == null ? null : swing(new F0<Color>() { public Color get() { try { return c.getBackground(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getBackground();"; }}); } static FontMetrics componentFontMetrics(JComponent c) { Font font = getFont(c); return font == null ? null : swing(new F0<FontMetrics>() { public FontMetrics get() { try { return c.getFontMetrics(font); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return 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 <A> List<A> asSyncList(Iterable<A> l) { return asSynchroList(l); } static <A extends Component> A _revalidate(A c) { return revalidate(c); } static void _revalidate(JFrame f) { revalidate(f); } static void _revalidate(JInternalFrame f) { revalidate(f); } static boolean endsWithNewLine(String s) { return endsWith(s, "\n"); } static String lines_rtrimIf(boolean rtrim, List<String> lines) { return rtrim ? lines_rtrim(lines) : lines(lines); } static double elapsedMinutes_timestamp(long time) { return toMinutes(now()-time); } static JPanel westAndCenterWithMargin(Component w, Component c) { return westAndCenter(withRightMargin(w), c); } static JPanel westAndCenterWithMargin(int margin, Component w, Component c) { return westAndCenter(withRightMargin(margin, w), c); } static JPanel vstack(Object... parts) { return vstack2(parts); } static JPanel vstack(List parts) { return vstack(toObjectArray(parts)); } static JLabel jClickableImage_instantToolTip(String imageID, String toolTip, Runnable action) { return onLeftClick(instaToolTip(toolTip, jImageLabel(imageID)), action); } static String collapse(String s) { return collapseWord(s); } static JPanel northAndCenterWithMargin(int margin, Component n, Component c) { return northAndCenter(withBottomMargin(margin, n), c); } static JPanel northAndCenterWithMargin(Component n, Component c) { return northAndCenter(withBottomMargin(n), c); } static JLabel liveValueLabel(LiveValue lv) { return jLiveValueLabel(lv); } static JLabel liveValueLabel(IVarWithNotify<?> var) { return jVarLabel(var); } static JSplitPane jhsplit(Component l, Component r) { return jhsplit(l, r, 0.5); } static JSplitPane jhsplit(Component l, Component r, double splitPoint, int delay) { // delay now ignored return jhsplit(l, r, splitPoint); } static JSplitPane jhsplit(Component l, Component r, double splitPoint) { return setSplitPaneOnFirstShowing( swingNu(JSplitPane.class, JSplitPane.HORIZONTAL_SPLIT, wrap(l), wrap(r)), splitPoint); } static JSplitPane jhsplit(double splitPoint, Component l, Component r) { return jhsplit(l, r, splitPoint); } static <A extends JComponent> A setDoubleBuffered(A c, boolean b) { { swing(() -> { { if (c != null) c.setDoubleBuffered(b); } }); } return c; } static String hijackPrint_tee(Runnable r) { final StringBuilder buf = new StringBuilder(); Object old = interceptPrintInThisThread(new F1<String, Boolean>() { Boolean get(String s) { buf.append(s); return true; } }); try { callF(r); return str(buf); } finally { interceptPrintInThisThread(old); } } static Timestamp tsNow() { return new Timestamp(); } static void setComponent(SingleComponentPanel scp, Component c) { setSCPComponent(scp, c); } static void setComponent(SingleComponentPanel scp, IF0<Component> c) { if (scp != null) setComponent(scp, callF(c)); } static <A> void removeAll(Collection<A> a, Collection<A> b) { if (a != null && b != null) a.removeAll(b); } static <A, B> void removeAll(Map<A, B> a, Collection<A> b) { if (a != null && b != null) for (A x : b) a.remove(x); } static <A, B extends A> boolean removeAll(Collection<A> c, B... b) { return c != null && b != null && c.removeAll(Arrays.asList(b)); } static <A, B> void removeAll(Map<A, B> a, A... b) { if (a != null && b != null) for (A x : b) a.remove(x); } static Container _getParent(Component c) { return getParent(c); } static Pt toPt(Point p) { return p == null ? null : new Pt(p.x, p.y); } static Pt toPt(Dimension d) { return d == null ? null : new Pt(d.width, d.width); } static CharInToken charIndexToCharInToken(List<String> tok, int charIndex) { int i = 0, idx = 0, lTok; while (i < l(tok) && idx+(lTok = l(tok.get(i))) <= charIndex) { idx += lTok; i++; } return new CharInToken(tok, i, charIndex-idx); } static List<String> stringsSortedByLength(Iterable<String> l) { return sortedByCalculatedField(l, new F1<String, Integer>() { public Integer get(String s) { try { return l(s); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "l(s)"; }}); } static List<String> methodNames(Object o) { return allMethodNames(o); } static long nanos() { return nanoTime(); } static String joinNemptiesWithComma(Object... strings) { return joinNempties(", ", strings); } static String joinNemptiesWithComma(Iterable strings) { return joinNempties(", ", strings); } static String formatElapsedTimeWithAppropriateUnit(double seconds) { if (seconds >= 1) return formatDouble(seconds, 3) + " s"; double ms = seconds*1000; if (ms >= 1) return formatDouble(ms, 3) + " ms"; double us = ms*1000; if (us >= 1) return formatDouble(us, 3) + " µs"; double ns = us*1000; return formatDouble(ns, 3) + " ns"; } static double nanosToSeconds(double nanos) { return nanoSecondsToSeconds(nanos); } static JPanel northAndCenterWithMargins(Component n, Component c) { return applyDefaultMargin(northAndCenter(withBottomMargin(n), c)); } static JPanel northAndCenterWithMargins(int margin, Component n, Component c) { return applyMargin(margin, northAndCenter(withBottomMargin(margin, n), c)); } static JPanel westAndEastWithMargin(Component w, Component e) { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.WEST, wrap(w)); panel.add(BorderLayout.CENTER, horizontalStrut(10)); panel.add(BorderLayout.EAST, wrap(e)); return panel; } static JComponent withLabel(String label, JComponent component) { return westAndCenter(jlabel(label + " "), component); } static JComponent withLabel(JComponent label, JComponent component) { return westAndCenterWithMargin(label, component); } static <A extends JComponent> A fontSizePlus(final int delta, final A c) { if (c != null) { swing(() -> { Font font = c.getFont(); c.setFont(font.deriveFont(font.getSize2D()+delta)); }); } return c; } static JLabel jcenteredLabel(String text) { return setHorizontalAlignment(JLabel.CENTER, jLabel(text)); } static JLabel jcenteredLabel() { return jcenteredLabel(" "); } static JScrollPane jscroll_centered_borderless(Component c) { return borderlessScrollPane(jscroll_centered(c)); } static <A> List<A> cloneTakeFirst(Collection<A> l, int n) { n = min(n, l(l)); List<A> out = emptyList(n); if (n != 0) for (A a : l) { out.add(a); if (l(out) >= n) break; } return out; } static <A> List<A> cloneTakeFirst(int n, Collection<A> l) { return cloneTakeFirst(l, n); } static <A> List<A> cloneTakeFirst(List<A> l, int n) { return l(l) <= n ? l : cloneSubList(l, 0, n); } static <A> List<A> cloneTakeFirst(int n, List<A> l) { return cloneTakeFirst(l, n); } static JScrollPane jscroll_borderless(Component c) { return borderlessScrollPane(jscroll(c)); } static JTextArea wordWrapTypeWriterTextArea() { return wrappedTextArea(newTypeWriterTextArea()); } static JTextArea wordWrapTypeWriterTextArea(String text) { return wrappedTextArea(newTypeWriterTextArea(text)); } static JLabel jcenteredlabel(String text) { return jcenteredLabel(text); } static JLabel jcenteredlabel() { return jcenteredLabel(); } static GrabbableIntPixels grabbableIntPixels_fastOrSlow(BufferedImage image) { try { try { GrabbableIntPixels gp = grabbableIntPixels(image); if (gp != null) return gp; } catch (Throwable __e) { printStackTrace(__e); } // Use pixelGrabber if quick method fails int w = image.getWidth(), h = image.getHeight(); int[] data = new int[w*h]; PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, w, h, data, 0, w); if (!pixelGrabber.grabPixels()) throw fail("Could not grab pixels"); return new GrabbableIntPixels(data, w, h, 0, w); } catch (Exception __e) { throw rethrow(__e); } } static void arrayCopy(Object[] a, Object[] b) { arraycopy(a, b); } static void arrayCopy(Object src, int srcPos, Object dest, int destPos, int n) { arraycopy(src, srcPos, dest, destPos, n); } static boolean inRange(int x, int n) { return x >= 0 && x < n; } static boolean inRange(int x, List l) { return inRange(x, l(l)); } static boolean inRange(int x, int a, int b) { return x >= a && x < b; } static int getPixel(BufferedImage img, int x, int y) { return img.getRGB(x, y); } static int getPixel(BufferedImage img, Pt p) { return img.getRGB(p.x, p.y); } static int limitToUByte(int i) { return max(0, min(255, i)); } static void setPixel(BufferedImage img, Pt p, Color color) { { if (img != null) img.setRGB(p.x, p.y, colorToIntOpaque(color)); } } static String copy(String text) { return copyToClipboard(text); } static <A extends Image> A copy(A image) { return copyToClipboard(image); } static File copy(File f) { return copyToClipboard(f); } static <A> List<A> uniquify(Collection<A> l) { return uniquifyList(l); } // Use like this: renderRecordVars("MyRecord", +x, +y) static String renderRecordVars(String recordName, Object... params) { List<String> l = new ArrayList(); int i = 0; for (; i+1 < l(params); i += 2) l.add(params[i] + "=" + params[i+1]); return formatFunctionCall(recordName, l); } static <A, B> MultiMap<A, B> descTreeMultiMap() { return new MultiMap(descTreeMap()); } static float score(Scored s) { return s == null ? 0 : s.score(); } static <A, B> List<B> pairsB(Collection<Pair<A, B>> l) { return secondOfPairs(l); } static <A, B> IterableIterator<Pair<A, B>> multiMapPairIterator(MultiMap<A, B> mm) { return nestedIterator(iterator(keys(mm.data)), key -> (IterableIterator<Pair<A,B>>) mapI(iterator(mm.data.get(key)), val -> pair(key, val))); } static <A, B> IterableIterator<Pair<A, B>> multiMapPairIterator_transformValueList(MultiMap<A, B> mm, IF1<List<B>, List<B>> f) { return nestedIterator(iterator(keys(mm.data)), key -> (IterableIterator<Pair<A,B>>) mapI(iterator(f.get(mm.data.get(key))), val -> pair(key, val))); } static String assertGlobalID(String s) { return assertPossibleGlobalID(s); } static BigInteger mul(BigInteger a, BigInteger b) { return a.multiply(b); } static BigInteger mul(BigInteger a, long b) { return a.multiply(bigint(b)); } static int mul(int a, int b) { return a*b; } static String bigintToGlobalID(BigInteger value) { char[] buf = new char[16]; for (int i = 16-1; i >= 0; i--) { buf[i] = charPlus('a', mod(value, 26).intValue()); value = div(value, 26); } return str(buf); } static Object _onAWTEnter_f; static AutoCloseable _onAWTEnter() { return (AutoCloseable) callF(_onAWTEnter_f); } static <A> List<A> cloneAndClearList(Collection<A> l) { List<A> l2 = cloneList(l); l.clear(); return l2; } static <A> VF1<A> ivf1ToVF1(IVF1<A> f) { return f == null ? null : new VF1<A>() { public void get(A a) { try { f.get(a) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.get(a)"; }}; } static Pt pt(int x, int y) { return new Pt(x, y); } static Pt pt(int x) { return new Pt(x, x); } static <A> A parentOfType(Component _c, Class<A> theClass) { return swing(() -> { Component c = _c; while (c != null) { if (isInstance(theClass, c)) return (A) c; c = c.getParent(); } return null; }); } static String hform(Object contents, Object... params) { return htag("form", contents, params); } static int cmpAlphaNum(String a, String b) { return alphaNumComparator().compare(a, b); } static <A, B> List<B> lazilyMap(IF1<A, B> f, List<A> l) { return lazyMap((Object) f, l); } static List lazilyMap(final Object f, final List l) { return new RandomAccessAbstractList() { final int size = l(l); HashMap<Integer, Object> 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 <A, B, C, D> Map<C, D> mapMapToMap(IF2<A, B, Pair<C, D>> f, Map<A, B> m) { if (m == null) return null; Map<C, D> map = similarEmptyMap(m); for (Map.Entry<A, B> e : m.entrySet()) { Pair<C, D> p = f.get(e.getKey(), e.getValue()); map.put(p.a, p.b); } return map; } static <A, B, C, D> Map<C, D> mapMapToMap(Map<A, B> m, IF2<A, B, Pair<C, D>> f) { return mapMapToMap(f, m); } static String unicode_upPointingTriangle() { return charToString(0x25B2); } static List<String> dropTagsAndHTMLComments(List<String> tok) { return dropHTMLComments(dropTags(tok)); } static String dropTagsAndHTMLComments(String html) { return join(dropTagsAndHTMLComments(htmlTok(html))); } static List<String> tok_identifiersInOrder(String s) { return filter(__128 -> isIdentifier(__128), javaTokC(s)); } static List<String> tok_identifiersInOrder(List<String> tok) { return filter(__129 -> isIdentifier(__129), codeTokens(tok)); } static List<String> regexpFirstGroups(String pat, String s) { if (s == null) return null; Matcher m = regexp(pat, s); return m.find() ? regexpGetGroups(m) : null; } static <A> List<A> syncCloneAndClearList(Collection<A> l) { if (l == null) return emptyList(); synchronized(collectionMutex(l)) { List<A> l2 = cloneList(l); l.clear(); return l2; } } static void cancelThread(Thread t) { if (t == null) return; ping(); synchronized(ping_actions) { ping_actions.put(t, "cancelled"); ping_anyActions = 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<Throwable> handler) { _handleException_addHandler((Object) handler); } static <A> A assertNull(A a) { assertTrue(a == null); return a; } static <A> A assertNull(String msg, A a) { assertTrue(msg, a == null); return a; } static <A extends Component> A internalFrameTitle(A c, final String title) { final JInternalFrame f = getInternalFrame(c); if (f != null) { swing(() -> { f.setTitle(unnull(title)); toolTip(getInternalFrameTitlePaneComponent(f), title); }); } return c; } static <A extends Component> 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<String>() { public String get() { try { return f.getTitle(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return f.getTitle();"; }}); } static <A extends Component> A frameTitle(A c, String title) { return setFrameTitle(c, title); } static <A extends Component> A frameTitle(String title, A c) { return setFrameTitle(c, title); } static String frameTitle(Component c) { return getFrameTitle(c); } static Set<AutoCloseable> _registerAutoCloseable_set = synchroHashSet(); static void _registerAutoCloseable(AutoCloseable c) { addIfNotNull(_registerAutoCloseable_set, c); } static void cleanMeUp__registerAutoCloseable() { closeAutoCloseables(getAndClearList(_registerAutoCloseable_set)); } static boolean isTransientField(Object object, String name) { Field f = getField(object, name); return f != null && (f.getModifiers() & Modifier.TRANSIENT) != 0; } static <A> A shallowCloneToClass(Class<A> c, Object o) { if (o == null) return null; A a = nuInstance(c); copyFields(o, a); return a; } static void dm_onTopInput(final VF1<String> r) { final DynModule m = dm_current_mandatory(); dm_ownResource(vmBus_onMessage("topInput", new VF1<String>() { 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<String>() { 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, B> A foldl(F2<A, B, A> f, A seed, Iterable<B> l) { A a = seed; if (l != null) for(B b : l) a = callF(f, a, b); return a; } static <A, B> A foldl(F2<A, B, A> f, A seed, B[] l) { A a = seed; if (l != null) for(B b : l) a = callF(f, a, b); return a; } static <A, B> 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 <A> boolean setAdd(Collection<A> 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 <A> A getSelectedItem_typed(JList<A> l) { return swing(() -> l.getSelectedValue()); } static <A> A getSelectedItem_typed(JComboBox<A> cb) { return swing(() -> (A) cb.getSelectedItem()); } static JTextField textFieldFromComboBox(JComboBox cb) { return (JTextField) cb.getEditor().getEditorComponent(); } static JComboBox onSelectedItem(final JComboBox cb, final VF1<String> 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 JComboBox onSelectedItem(final JComboBox cb, IVF1<String> 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 <A> TreeSet<A> asTreeSet(Collection<A> set) { return set == null ? null : set instanceof TreeSet ? (TreeSet) set : new TreeSet(set); } // menuMaker = voidfunc(JPopupMenu) static void internalFrameTitlePopupMenu(final JInternalFrame f, final Object... params) { { swing(() -> { Object menuMaker; if (l(params) == 1 && !(params[0] instanceof JMenu)) menuMaker = params[0]; else menuMaker = new VF1<JPopupMenu>() { 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); }); } } 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 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 JLabel jimage(Image img) { return jImageLabel(img); } static JLabel jimage(String imageID) { return jImageLabel(imageID); } static void cancelTimers(Collection timers) { for (Object timer : timers) cancelTimer(timer); } static List flattenCollectionsAndArrays(Iterable a) { List l = new ArrayList(); for (Object x : a) if (x instanceof Collection) l.addAll(flattenCollectionsAndArrays((Collection) x)); else if (x instanceof Object[]) l.addAll(flattenCollectionsAndArrays(asList((Object[]) x))); else l.add(x); return l; } static Complex complex(double re, double im) { return new Complex(re, im); } static Complex complex(double re) { return new Complex(re, 0.0); } static Complex complex(double[] reIm) { if (empty(reIm)) return null; if (l(reIm) != 2) throw fail("Need 2 doubles to make complex number"); return complex(reIm[0], reIm[1]); } static String getBotAddress(String bot) { List<ScannedBot> 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 Color colorFromRGBA(int rgba) { return new Color(rgba, true); } 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 MultiMap mapMultiMapValues(Object func, MultiMap mm) { MultiMap m = similarEmptyMultiMap(mm); for (Object key : keys(mm)) for (Object value : mm.get(key)) m.put(key, callF(func, value)); return m; } static MultiMap mapMultiMapValues(MultiMap mm, Object func) { return mapMultiMapValues(func, mm); } static <A, B, C> MultiMap<A, C> mapMultiMapValues(IF1<B, C> func, MultiMap<A, B> mm) { return mapMultiMapValues((Object) func, mm); } static <A> TreeMap<Object, A> generalizedCIMap() { return new TreeMap(generalizedCIComparator()); } static double msToSeconds(long ms) { return toSeconds(ms); } static double msToSeconds(double 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 IMeta toIMeta(Object o) { if (o == null) return null; if (o instanceof IMeta) return ((IMeta) o); if (o instanceof JComponent) return initMetaOfJComponent((JComponent) o); if (o instanceof BufferedImage) return optCast(IMeta.class, ((BufferedImage) o).getProperty("meta")); return null; } static Map makeObjectMetaMap() { //ret synchroLinkedHashMap(); return new CompactHashMap(); } static IAutoCloseableF0 tempMetaMutex(IMeta o) { return o == null ? null : o._tempMetaMutex(); } static <A, B> void syncMapPut2(Map<A, B> 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 <A> A callPostProcessor(Object f, A a) { return f == null ? a : (A) callF(f, a); } static <A> A callPostProcessor(IF1<A, A> f, A a) { return f == null ? a : f.get(a); } static <A extends Concept> Collection<A> findConceptsWhereCI(Class<A> c, Object... params) { return findConceptsWhereCI(db_mainConcepts(), c, params); } static List<Concept> findConceptsWhereCI(String c, Object... params) { return findConceptsWhereCI(db_mainConcepts(), c, params); } static <A extends Concept> Collection<A> findConceptsWhereCI(Concepts concepts, Class<A> c, Object... params) { Collection<A> l = findConceptsWhereCI_noParent(concepts, c, params); if (concepts.parent == null) return l; return concatCollections_conservative(l, findConceptsWhereCI(concepts.parent, c, params)); } static <A extends Concept> Collection<A> findConceptsWhereCI_noParent(Concepts concepts, Class<A> c, Object... params) { params = expandParams(c, params); // indexed if (concepts.ciFieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex<A, Object> index = concepts.getCIFieldIndex(c, (String) params[i]); if (index != null) { Collection<A> rawList = index.getAll(params[i+1]); params = dropEntryFromParams(params, i); if (params == null) return rawList; List<A> 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<Concept> findConceptsWhereCI(Concepts concepts, String c, Object... params) { return filterConceptsIC(concepts.list(c), params); } // f: A -> Comparable static <A> List<A> sortedByCalculatedFieldDesc(Collection<A> c, final Object f) { return sortByCalculatedFieldDesc(c, f); } static <A> List<A> sortedByCalculatedFieldDesc(Object f, Collection<A> c) { return sortByCalculatedFieldDesc(f, c); } static <A, B> List<A> sortedByCalculatedFieldDesc(Iterable<A> c, IF1<A, B> f) { return sortByCalculatedFieldDesc(c, f); } static <A, B> List<A> sortedByCalculatedFieldDesc(IF1<A, B> f, Iterable<A> c) { return sortByCalculatedFieldDesc(f, c); } 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<String> conceptFields_gen(Object c) { return setMinus(mergeTreeSets(allNonStaticNonTransientFields(c), keys((Map) getOpt_raw(c, "fieldValues"))), conceptFields_gen_drop); } static Set<String> conceptFields_gen(Class c) { return setMinus(allNonStaticNonTransientFields(c), conceptFields_gen_drop); } static List<Field> nonStaticNonTransientFieldObjects(Object o) { if (o == null) return null; List<Field> 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, B> List<B> valuesList(Map<A, B> map) { return cloneListSynchronizingOn(values(map), map); } static <A, B> List<B> valuesList(MultiMap<A, B> 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; } static Map mapToValues(Iterable l, Object f) { return mapKeyAndFunction(l, f); } static Map mapToValues(Object f, Iterable l) { return mapKeyAndFunction(f, l); } static <A, B> Map<A, B> mapToValues(Iterable<A> l, IF1<A, B> f) { return mapKeyAndFunction(f, l); } static <A, B> Map<A, B> mapToValues(IF1<A, B> f, Iterable<A> l) { return mapKeyAndFunction(f, l); } static <A, B, C> Map<A, C> mapToValues(Map<A, B> map, IF2<A, B, C> f) { return mapKeyAndFunction(map, f); } static Object cloneIfList(Object o) { return o instanceof List ? cloneList((List) o) : o; } // 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 if (type == GlobalID.class && value instanceof String) return _csetField(c, field, new GlobalID((String) value)); } return _csetField(c, field, value); } catch (Exception __e) { throw rethrow(__e); } } static <A extends Concept> Object[] unrollAndExpandParams(Class<A> 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<Object> 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<String, Field> 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; } // returns from C to C static String jextract(String pat, String s) { return jextract(pat, javaTok(s)); } static String jextract(String pat, List<String> tok) { List<String> 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); } // TODO: handle null values static String replaceDollarVars_js(String s, Object... __) { //ret replaceDollarVars_withCustomToString(s, o -> jsonEncode_extended(o), _); Map<String, Object> vars = paramsToMap_withNulls(__); if (empty(vars)) return s; var vars2 = mapKeys(__130 -> dropDollarPrefix(__130), vars); return replaceDollarVars_dyn(s, var -> { if (!vars2.containsKey(var)) return null; Object value = vars2.get(var); return jsonEncode_extended(value); }); } static <A> Set<A> createOrAddToSet(Set<A> set, A a) { if (set == null) set = new HashSet(); set.add(a); return set; } static <A> Set<A> createOrAddToSet(Set<A> set, A... l) { for (A a : unnullForIteration(l)) set = createOrAddToSet(set, a); return set; } static long clockToSysTimeDiff() { return sysNow()-now(); } static <A> void replaceLastElement(List<A> l, A a) { if (nempty(l)) l.set(l(l)-1, a); } static <A, B> int lKeys(MultiMap<A, B> 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 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 <A, B> Comparator<A> mapComparatorDesc(final Map<A, B> map) { return new Comparator<A>() { public int compare(A a, A b) { return cmp(map.get(b), map.get(a)); } }; } static <A, B extends Number> A keyWithBiggestValue(Map<A, B> 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<String> allToStrOrNull(Iterable c) { return lmap(__131 -> strOrNull(__131), c); } static List<String> allToStrOrNull(Object... c) { return lmap(__132 -> strOrNull(__132), 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 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 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 <A, B, C extends Collection<B>> MultiMap<A, B> mapToMultiMap(Map<A, C> map) { if (map == null) return null; MultiMap<A, B> mm = similarEmptyMultiMap(map); for (Map.Entry<? extends A, ? extends Collection<B>> __0 : _entrySet( map)) { A key = __0.getKey(); Collection<B> values = __0.getValue(); mm.putAll(key, values); } return mm; } static String digitsOnly(String s) { return filterChars(__133 -> isDigit(__133), s); } static int identityHash(Object o) { return identityHashCode(o); } static ImageSurface imageSurface(BufferedImage img) { return swingNu(ImageSurface.class, img); } static ImageSurface imageSurface(MakesBufferedImage img) { return swingNu(ImageSurface.class, img); } static ImageSurface imageSurface() { return swingNu(ImageSurface.class); } static BufferedImage toBufferedImageOpt(Object o) { if (o instanceof BufferedImage) return ((BufferedImage) o); if (o instanceof Image) return copyImage((Image) o); if (o instanceof MakesBufferedImage) return ((MakesBufferedImage) o).getBufferedImage(); if (o instanceof File) if (isImageFile((File) o)) return loadBufferedImageFile((File) o); String c = getClassName(o); // Keep this because it also works on imported objects if (eqOneOf(c, "main$BWImage", "main$RGBImage")) return (BufferedImage) call(o, "getBufferedImage"); if (eq(c, "main$PNGFile")) return (BufferedImage) call(o, "getImage"); return null; } static boolean removeFile(File file) { return deleteFile(file); } static <A extends Component> A jPreferHeight(int h, A c) { Dimension size = c.getPreferredSize(); c.setPreferredSize(new Dimension(size.width, max(h, size.height))); return c; } static boolean isSpaceEtc(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } static File fileAppendToName(File f, String suffix) { return newFile(f.getPath() + suffix); } static List<File> listDirs(File dir) { if (dir == null) return emptyList(); File[] files = dir.listFiles(); List<File> l = new ArrayList(); if (files != null) for (File f : files) if (f.isDirectory()) l.add(f); return l; } static List<File> listDirs(String dir) { return listDirs(newFile(dir)); } static int incAtomicInt(AtomicInteger i) { return i.incrementAndGet(); } static int incAtomicInt(AtomicInteger i, int delta) { return i.addAndGet(delta); } static String intToHex(int i) { return bytesToHex(intToBytes(i)); } static <A> ArrayList<A> cloneList_noSync(Iterable<A> l) { return l instanceof Collection ? cloneList_noSync((Collection) l) : asList(l); } static <A> ArrayList<A> cloneList_noSync(Collection<A> l) { if (l == null) return new ArrayList(); return new ArrayList<A>(l); } // f: func -> A (stream ends when f returns null) static <A> IterableIterator<A> iteratorFromFunction(final Object f) { class IFF extends IterableIterator<A> { 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 <A> IterableIterator<A> iteratorFromFunction(F0<A> f) { return iteratorFromFunction_f0(f); } static <A> IterableIterator<A> iteratorFromFunction(IF0<A> f) { return iteratorFromFunction_if0(f); } // iterate safely (& quickly) in the face of concurrent modifications static <A, B> IterableIterator<B> concurrentlyIterateValues(NavigableMap<A, B> map) { return concurrentlyIterateValues(map, map); } static <A, B> IterableIterator<B> concurrentlyIterateValues(final NavigableMap<A, B> map, Object mutex) { return iteratorFromFunction(new F0<B>() { Iterator<A> 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 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(() -> { sp.getViewport().setBackground(c); }); } return sp; } static <A extends JComponent> 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<Object>() { public Object get() { try { final Var<Timer> 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) { printStackTrace(__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> 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 htitle_h1(String s) { return htitle_noEncode(dropTags(s)) + h1(s); } // returns Int.MAX_VALUE if not applicable // returns -score if conversions are needed // or +score if no conversions are needed // Lower score is better static int methodApplicabilityScore_withPrimitiveWidening(Executable m, Object[] args) { Class<?>[] types = m.getParameterTypes(); if (types.length != l(args)) return Integer.MAX_VALUE; int score = 0; boolean widenings = false; for (int i = 0; i < types.length; i++) { Object a = args[i]; Class c = types[i]; // null can be assigned to any non-primitive type if (a == null) { if (c.isPrimitive()) return Integer.MAX_VALUE; } else { Class t = a.getClass(); int s = typeConversionScoreWithUnboxing(t, c); if (s == Integer.MAX_VALUE) return Integer.MAX_VALUE; if (s < 0) widenings = true; score += abs(s); } } return widenings ? -score : score; } static Object invokeConstructor(Constructor m, Object... args) { try { makeAccessible(m); return m.newInstance(args); } catch (Exception __e) { throw rethrow(__e); } } static Object invokeConstructorWithWidening(Constructor m, Object... args) { try { try { Class<?>[] types = m.getParameterTypes(); int n = types.length; Object[] args2 = new Object[n]; for (int i = 0; i < n; i++) args2[i] = convertPrimitiveIfNecessary(args[i], types[i]); return invokeConstructor(m, args2); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(e.getMessage() + " - was calling: " + m + ", args: " + joinWithSpace(classNames(args))); } } catch (Exception __e) { throw rethrow(__e); } } static Object invokeMethodWithWidening(Method m, Object o, Object... args) { try { try { Class<?>[] types = m.getParameterTypes(); int n = types.length; Object[] args2 = new Object[n]; for (int i = 0; i < n; i++) args2[i] = convertPrimitiveIfNecessary(args[i], types[i]); return m.invoke(o, args2); } 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 Object callWithVarargs(Object o, String method, Object... args) { return call_withVarargs(o, method, args); } // return Int.MAX_VALUE if not applicable // Lower score is better static int methodApplicabilityScore_onTypes(Method m, Class[] argTypes) { return methodApplicabilityScore_onTypes((Executable) m, argTypes); } static int methodApplicabilityScore_onTypes(Executable 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 <A> A singletonOpt(Collection<A> l) { return l(l) == 1 ? first(l) : null; } static List<Method> findNonDefaultInterfaceMethods(Class intrface) { if (!isInterface(intrface)) return null; List<Method> l = new ArrayList(); _MethodCache cache = getMethodCache(intrface); Class c = intrface; do { for (Method m : c.getDeclaredMethods()) if (m.getDeclaringClass() != Object.class && !m.isDefault() && !isStaticMethod(m)) l.add(m); c = c.getSuperclass(); } while (c != null); return l; } static int roundToInt(double d) { return (int) Math.round(d); } static Object swingCall(final Object o, final String method, final Object... args) { return swing(new F0<Object>() { public Object get() { try { return call(o, method, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return call(o, method, args);"; }}); } // BREAKING CHANGE! // Also NOTE: Iterators of these sync-wrapped collections // after generally NOT thread-safe! // TODO: change that? static <A> Set<A> synchroLinkedHashSet() { return Collections.synchronizedSet(new CompactLinkedHashSet()); } static <A extends JLabel, B> A bindJLabelToTransformedLiveValue(IF1<B, Object> f, A label, LiveValue<B> lv) { bindLiveValueListenerToComponent(label, lv, () -> { setText(label, strOrNull(f.get(lv.get()))); }); return label; } static MouseAdapter onMouseDown(Component c, Runnable r) { return onMouseDown(c, runnableToIVF1(r)); } static MouseAdapter onMouseDown(Component c, IVF1<MouseEvent> f) { return c == null || f == null ? null : swing(() -> { MouseAdapter ma = new MouseAdapter() { public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) f.get(e); } }; c.addMouseListener(ma); return ma; }); } static <A> IVF1<A> runnableToIVF1(Runnable r) { return r == null ? null : a -> r.run(); } static int numberOfChildren(final Container c) { return c == null ? 0 : swing(new F0<Integer>() { public Integer get() { try { return c.getComponentCount(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getComponentCount();"; }}); } static ClassLoader getClassLoader(Object o) { return o == null ? null : _getClass(o).getClassLoader(); } // subPath is relative and with slashes static boolean dirOrZipContainsPath(File location, String subPath) { try { if (location.isDirectory()) { return new File(location, subPath).exists(); } else if (location.isFile()) { return zipFileContains_falseOnError(location, subPath); } return false; } catch (Exception __e) { throw rethrow(__e); } } static List<String> loadedJigsawModuleNames() { var moduleLayer = ModuleLayer.boot(); var modules = moduleLayer.modules(); return map(modules, mod -> mod.getName()); } // moduleName = e.g. "java.scripting" static List<String> classNamesInJigsawModule(String moduleName) { return classNamesInJigsawModule(moduleName, javaHome()); } static List<String> classNamesInJigsawModule(String moduleName, File javaHome) { return classNamesInJarOrDir(jigsawModuleFile(moduleName, javaHome), "classes/"); } // undefined color, seems to be all black in practice // This is without alpha? static BufferedImage newBufferedImage(int w, int h) { return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } static BufferedImage newBufferedImage(int w, int h, RGB rgb) { return newBufferedImage(w, h, rgb.getColor()); } static BufferedImage newBufferedImage(int w, int h, Color color) { BufferedImage img = newBufferedImage(w, h); Graphics2D g = img.createGraphics(); g.setColor(or(color, Color.white)); g.fillRect(0, 0, w, h); return img; } static BufferedImage newBufferedImage(Pt p, Color color) { return newBufferedImage(p.x, p.y, color); } // This one is with alpha... static BufferedImage newBufferedImage(int w, int h, int[] pixels) { return intArrayToBufferedImage(pixels, w, h); } static void setChecked(JCheckBox checkBox, boolean b) { if (checkBox != null) { swing(() -> { if (isChecked(checkBox) != b) checkBox.setSelected(b); }); } } static void setChecked(JCheckBoxMenuItem mi, boolean b) { if (mi != null) { swing(() -> { mi.setSelected(b); }); } } static String defaultFrameTitle() { return autoFrameTitle(); } static void defaultFrameTitle(String title) { autoFrameTitle_value = title; } static void registerEscape(JFrame frame, final Runnable r) { registerEscape_rootPane(frame.getRootPane(), r); } static void inputFilePath(final String msg, final Object action) { inputFilePath(msg, userDir(), action); } // action: voidfunc(File) static void inputFilePath(final String msg, final File defaultFile, final Object action) { swingLater(new Runnable() { public void run() { try { final JTextField tfPath = jtextfield(f2s(or(defaultFile, userDir()))); String title = joinStrings(" | ", msg, programName()); JComponent form = showFormTitled(title, unnull(msg), centerAndEast(tfPath, jbutton("Browse...", new Runnable() { public void run() { try { JFileChooser fileChooser = new JFileChooser(getTextTrim(tfPath)); if (fileChooser.showOpenDialog(tfPath) == JFileChooser.APPROVE_OPTION) { tfPath.setText(fileChooser.getSelectedFile().getAbsolutePath()); tfPath.requestFocus(); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JFileChooser fileChooser = new JFileChooser(getTextTrim(tfPath));\r\n if..."; }})), new Runnable() { public void run() { try { callF(action, new File(getTextTrim(tfPath))) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(action, new File(getTextTrim(tfPath)))"; }}); renameSubmitButton(form, "OK"); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final JTextField tfPath = jtextfield(f2s(or(defaultFile, userDir())));\r\n S..."; }}); } static boolean hasJPEGExtension(File f) { return ewicOneOf(fileName(f), ".jpg", ".jpeg"); } static void saveJPG(BufferedImage img, File file) { try { if (!ImageIO.write(img, "jpeg", mkdirsFor(file))) { print("Reconstructing image for saving JPEG"); img = reconstructBufferedImage(img); if (!ImageIO.write(img, "jpeg", file)) throw fail("Couldn't write JPEG: " + file + " (" + img + ")"); } vmBus_send("wroteFile", file); } catch (Exception __e) { throw rethrow(__e); } } static void saveJPG(File file, BufferedImage img) { try { saveJPG(img, file); } catch (Exception __e) { throw rethrow(__e); } } static void savePNG(BufferedImage img, File file) { try { File tempFile = new File(file.getPath() + "_temp"); CriticalAction ca = beginCriticalAction("Save " + f2s(file)); try { ImageIO.write(img, "png", mkdirsFor(tempFile)); file.delete(); tempFile.renameTo(file); } finally { ca.done(); } } catch (Exception __e) { throw rethrow(__e); } } // gotta love convenience & program-smartness static void savePNG(String file, BufferedImage img) { savePNG(toFile(file), img); } static void savePNG(File file, BufferedImage img) { savePNG(img, file); } static void savePNG(File file, RGBImage img) { savePNG(file, img.getBufferedImage()); } static JComponent selectSnippetID_v1(final VF1<String> onSelect) { return selectSnippetID_v1("#", onSelect); } static JComponent selectSnippetID_v1(String defaultID, final VF1<String> onSelect) { final JTextField tfSnippetID = jtextfield(defaultID); if (eq(defaultID, "#")) moveCaretToEnd(tfSnippetID); JComponent panel; renameSubmitButton(panel = showTitledForm("Select Snippet", "Snippet ID:", tfSnippetID, runnableThread(new Runnable() { public void run() { try { callF(onSelect, fsI(getTextTrim(tfSnippetID))); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(onSelect, fsI(getTextTrim(tfSnippetID)));"; }})), "Select snippet"); return panel; } static BufferedImage cloneBufferedImage(BufferedImage image) { return copyImage(image); } static BufferedImage clipBufferedImage(BufferedImage src, Rectangle clip) { return clipBufferedImage(src, new Rect(clip)); } static BufferedImage clipBufferedImage(BufferedImage src, Rect r) { if (src == null || r == null) return null; // fixClipRect r = intersectRects(r, new Rect(0, 0, src.getWidth(), src.getHeight())); if (rectEmpty(r)) return null; // can't make zero-sized BufferedImage return src.getSubimage(r.x, r.y, r.w, r.h); } static BufferedImage clipBufferedImage(BufferedImage src, int x, int y, int w, int h) { return clipBufferedImage(src, new Rect(x, y, w, h)); } static Object getTransferData(Transferable t, DataFlavor flavor) { try { return t != null && t.isDataFlavorSupported(flavor) ? t.getTransferData(flavor) : null; } catch (Exception __e) { throw rethrow(__e); } } static BufferedImage imageFromDataURL(String url) { return decodeImage(bytesFromDataURL(url)); } static String getTextFromClipboard() { try { Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) return (String) transferable.getTransferData(DataFlavor.stringFlavor); return null; } catch (Exception __e) { throw rethrow(__e); } } static void popupError(final Throwable throwable) { throwable.printStackTrace(); // print stack trace to console for the experts SwingUtilities.invokeLater(new Runnable() { public void run() { String text = throwable.toString(); //text = cutPrefix(text, "java.lang.RuntimeException: "); JOptionPane.showMessageDialog(null, text); } }); } static void printShortenedFunctionCall(Object fname, Object... args) { printShortenedFunctionCall(100, fname, args); } static void printShortenedFunctionCall(int len, Object fname, Object... args) { print(shorten(len, formatFunctionCall(fname, args))); } static <A> A printIf(boolean b, A a) { if (b) print(a); return a; } static <A> A printIf(boolean b, String s, A a) { if (b) print(s, a); return a; } static WidthAndHeight widthAndHeight(BufferedImage image) { return image == null ? null : widthAndHeight(image.getWidth(), image.getHeight()); } static WidthAndHeightImpl widthAndHeight(int w, int h) { return new WidthAndHeightImpl(w, h); } static ImageSurface jImageSurface() { return swingNu(ImageSurface.class); } // for BWImage static ImageSurface jImageSurface(MakesBufferedImage img) { return swingNu(ImageSurface.class, img.getBufferedImage()); } static ImageSurface jImageSurface(BufferedImage img) { return swingNu(ImageSurface.class, img); } static JScrollPane jscroll_centered(Component c) { return c instanceof JScrollPane ? ((JScrollPane) c) : jscroll(jFullCenter(c)); } static <A extends JComponent> A disposeFrameOnClick(final A c) { onClick(c, new Runnable() { public void run() { try { disposeFrame(c) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "disposeFrame(c)"; }}); return c; } static void imageSurface_unpixelated(ImageSurface imageSurface) { if (imageSurface == null) return; imageSurface.interpolationMode = RenderingHints.VALUE_INTERPOLATION_BILINEAR; repaint(imageSurface); } static int cmpAlphanumIC(String a, String b) { return cmpAlphaNum(a, b); } static <A> A pcallF_typed(F0<A> f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A, B> B pcallF_typed(F1<A, B> f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A> void pcallF_typed(VF1<A> f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { printStackTrace(__e); } } static <A> void pcallF_typed(IVF1<A> f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { printStackTrace(__e); } } static Object pcallF_typed(Runnable r) { try { { if (r != null) r.run(); } } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A> A pcallF_typed(IF0<A> f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static <A, B> B pcallF_typed(IF1<A, B> f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } // Use like this: renderVars(+x, +y) static String renderVars_stringifier(IStringifier stringifier, Object... params) { List<String> l = new ArrayList(); int i = 0; if (odd(l(params))) { l.add(stringifier.toString(first(params))); ++i; } for (; i+1 < l(params); i += 2) l.add(params[i] + "=" + stringifier.toString(params[i+1])); return trim(joinWithComma(l)); } static String str_shorten(Object o) { return shorten_str(o); } static String str_shorten(Object o, int max) { return shorten_str(o, max); } static String str_shorten(int max, Object o) { return shorten_str(max, o); } static int countLines(String s) { return l(toLines(s)); // yeah could be optimized :-) } static SimpleDateFormat simpleDateFormat_UTC(String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); return sdf; } static String formatUTCWithMS_24() { return formatUTCWithMS_24(now()); } static String formatUTCWithMS_24(long time) { var sdf = new SimpleDateFormat("HH:mm:ss.SSSS"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); return sdf.format(time) + " UTC"; } 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<String> lv = (LiveValue<String>) 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!;\r\n if (isCurlyBracketed(s)) {\r\n setEnabled(mi,..."; }}); 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 JTextArea showWrappedText(final String title, final String text) { return (JTextArea) swingAndWait(new F0<Object>() { public Object get() { try { JTextArea textArea = wrappedTextArea(text); caretToHome(textArea); textArea.setFont(typeWriterFont()); makeFrame(title, new JScrollPane(textArea)); return textArea; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JTextArea textArea = wrappedTextArea(text);\r\n caretToHome(textArea);\r\n ..."; }}); } static JTextArea showWrappedText(Object text) { return showWrappedText(autoFrameTitle(), str(text)); } static JPanel centerAndEastWithMarginInbetween(Component c, final Component e) { return centerAndEast(c, withLeftMargin(e)); } static JScrollPane borderlessScrollPane(JScrollPane sp) { return setBorder(null, withoutViewportBorder(sp)); } static JScrollPane jHigherScrollPane(final JComponent c) { return swing(new F0<JScrollPane>() { public JScrollPane get() { try { return new JScrollPane(c) { public Dimension getPreferredSize() { Component view = getViewport().getView(); if (view == null) return super.getPreferredSize(); int pref_width = view.getPreferredSize().width; setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); Dimension dim = new Dimension(pref_width, super.getPreferredSize().height + getHorizontalScrollBar().getSize().height); setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); return dim; } }; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret new JScrollPane(c) {\r\n public Dimension getPreferredSize() {\r\n Co..."; }}); } static JPanel jFullCenter(final Component c) { return swing(new F0<JPanel>() { public JPanel get() { try { JPanel panel = new JPanel(new GridBagLayout()); panel.add(c); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new GridBagLayout);\r\n panel.add(c);\r\n ret panel;"; }}); } static <A extends IMeta> A setMetaSrc(A a, Object src) { setMetaAndVerify(a, "src", src); return a; } static <A> A setMetaSrc(A a, Object src) { setMetaAndVerify(a, "src", src); return a; } static <A extends JTextComponent> A setTextKeepCaret(final String text, final A c) { if (c != null) { swing(() -> { int caret = c.getCaretPosition(); setText(c, text); setCaretPosition(c, caret); }); } return c; } static <A extends JTextComponent> A setTextKeepCaret(A c, String text) { return setTextKeepCaret(text, c); } static void addKeyListener(final Component c, final KeyListener l) { if (c != null) { swing(() -> { c.addKeyListener(l); }); } } static KeyListener ctrlEnterKeyListener(final Object action) { final Object info = _threadInfo(); return new KeyAdapter() { public void keyReleased(KeyEvent ke) { if (ke.getKeyCode() == KeyEvent.VK_ENTER && ke.getModifiers() == KeyEvent.CTRL_MASK) { _threadInheritInfo(info); pcallF(action); } } }; } static SingleThread awtCalcRegularly(JComponent component, int delay, int firstDelay, Object runnable) { final SingleThread thread = new SingleThread(); final Runnable r2 = addThreadInfoToRunnable(runnable); final Object info = _threadInfo(); installTimer(component, delay, firstDelay, new Runnable() { public void run() { if (!licensed()) return; _threadInheritInfo(info); thread.go(r2); } }); return thread; } static SingleThread awtCalcRegularly(JFrame frame, int delay, int firstDelay, final Object runnable) { return awtCalcRegularly(getRootPane(frame), delay, firstDelay, runnable); } static int defaultMargin() { return withMargin_defaultWidth; } static boolean isComponentShowing(final Component c) { return c != null && swing(new F0<Boolean>() { public Boolean get() { try { return c.isShowing(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.isShowing();"; }}); } static boolean isInQ(Q q) { return q != null && isCurrentThread(q.rst.thread); } static <A> OKOrError<A> OKOrError_ok(A a) { return new OKOrError(a); } static OKOrError OKOrError_error(Throwable e) { return new OKOrError(false, e); } static IF0 runnableToIF0(Runnable r) { return r == null ? null : () -> { r.run(); return null; }; } static JComponent jSmallErrorView(Throwable e) { var btn = jimageButtonScaledToWidth(32, errorIconID(), "Show error details", () -> showErrorFrame(e)); var label = jlabel_noAutoToolTip(exceptionType(e)); return jline(btn, label); } static Set<Thread> evalWithTimeout_inTime = synchroSet(); static Set<Thread> evalWithTimeout_allThreads = newWeakHashSet(); static ThreadLocal<String> evalWithTimeout_threadName = new ThreadLocal(); static <A> Either<A, Thread> evalWithTimeout(double timeoutSeconds, IF0<A> r) { return (Either) evalWithTimeout(timeoutSeconds, (Object) r); } // Either calculation result or Thread with timed-out computation // Tries to interrupt thread after timeout // Errors are rethrown. static Either<Object, Thread> evalWithTimeout(int timeoutMS, final Object r) { final Flag done = new Flag(); final Flag doneWaiting = new Flag(); final Var var = new Var(); final Var<Throwable> error = new Var(); Thread t = newThread(getAndClearThreadLocal(evalWithTimeout_threadName), new Runnable() { public void run() { try { try { try { var.set(callF(r)); } finally { evalWithTimeout_allThreads.remove(currentThread()); } } catch (Throwable e) { error.set(e); if (doneWaiting.isUp()) printStackTrace_inPossiblyCancelledThread(e); } finally { done.raise(); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "try {\r\n try {\r\n var.set(callF(r));\r\n } finally {\r\n ev..."; }}); beforeDelegatingToThread(t); try { startThread(t); evalWithTimeout_inTime.add(t); evalWithTimeout_allThreads.add(t); try { done.waitUntilUp(timeoutMS); doneWaiting.raise(); } finally { evalWithTimeout_inTime.remove(t); } // timeout! cancel/interrupt and return thread object if (!done.isUp()) { print("Cancelling thread (timeout)"); cancelAndInterruptThread(t); return either2(t); } // thread ended with error if (error.get() != null) rethrow(error.get()); // thread ended naturally return either1(var.get()); } finally { afterDelegatingToThread(t); } } static Either<Object, Thread> evalWithTimeout(double timeoutSeconds, final Object r) { return evalWithTimeout(toMS_int(timeoutSeconds), r); } static <A extends Component> A activateFrameAndReturnComponent(A c) { activateFrame(c); return c; } static JTextArea newTypeWriterTextArea() { return newTypeWriterTextArea(""); } static JTextArea newTypeWriterTextArea(String text) { return withTypeWriterFont(jTextArea(text)); } static String autoFrameTitle_value; static String autoFrameTitle() { return autoFrameTitle_value != null ? autoFrameTitle_value : getProgramTitle(); } static void autoFrameTitle(Component c) { setFrameTitle(getFrame(c), autoFrameTitle()); } static boolean initSyntaxTextArea_numPadFix = true; static void initSyntaxTextArea(RSyntaxTextArea textArea) { swing(() -> { if (initSyntaxTextArea_numPadFix) new NumPadFixingInputMap().replaceOn(textArea); textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); textArea.setHighlightCurrentLine(false); textArea.setTabSize(2); textArea.setTabsEmulated(true); textArea.setCodeFoldingEnabled(false); textArea.setMarkOccurrences(true); }); } static List<Integer> countIteratorAsList(int b) { return countIteratorAsList(0, b); } static List<Integer> countIteratorAsList(int a, int b) { return countIteratorToList(a, b); } static <A> List<A> countIteratorAsList(int b, IF1<Integer, A> f) { return countIteratorAsList(0, b, f); } static <A> List<A> countIteratorAsList(int a, int b, IF1<Integer, A> f) { return countIteratorToList(a, b, f); } static List<Integer> countIteratorAsList(int a, int b, int step) { return countIteratorToList(a, b, step); } static <A> List<A> countIteratorAsList(double a, double b, double step, IF1<Double, A> f) { return countIteratorToList(a, b, step, f); } static <A> List<Double> countIteratorAsList(double a, double b, double step) { return countIteratorToList(a, b, step); } static <A> List<A> countIteratorAsList(IF1<Double, A> f, double a, double b, double step) { return countIteratorToList(f, a, b, step); } static <A> List<A> countIteratorAsList(IF1<Integer, A> f, int b) { return countIteratorAsList(f, 0, b); } static <A> List<A> countIteratorAsList(IF1<Integer, A> f, int a, int b) { return countIteratorToList(f, a, b); } static IterableIterator<Integer> countIterator(int b) { return countIterator(0, b); } static IterableIterator<Integer> countIterator(int a, int b) { return countIterator_exclusive(a, b); } static <A> IterableIterator<A> countIterator(int b, IF1<Integer, A> f) { return countIterator(0, b, f); } static <A> IterableIterator<A> countIterator(int a, int b, IF1<Integer, A> f) { return countIterator_exclusive(a, b, f); } static IterableIterator<Integer> countIterator(int a, int b, int step) { return countIterator_exclusive_step(a, b, step); } static <A> IterableIterator<A> countIterator(double a, double b, double step, IF1<Double, A> f) { return countIterator_exclusive_step(a, b, step, f); } static <A> IterableIterator<Double> countIterator(double a, double b, double step) { return countIterator_exclusive_step(a, b, step); } static <A> IterableIterator<A> countIterator(IF1<Double, A> f, double a, double b, double step) { return countIterator(a, b, step, f); } static <A> IterableIterator<A> countIterator(IF1<Integer, A> f, int b) { return countIterator(f, 0, b); } static <A> IterableIterator<A> countIterator(IF1<Integer, A> f, int a, int b) { return countIterator_exclusive(a, b, f); } static void centerWindowWithin(Window w, Rect r) { if (w != null) { swing(() -> { w.setLocation( r.x+(r.w-w.getWidth())/2, r.y+(r.h-w.getHeight())/2); }); } } static int screenNrOfWindow(Window w) { if (w == null) return -1; Rect bounds = boundsAsRect(w); return indexOfMax(allScreenBounds(), screen -> area(intersectRects(bounds, screen))); } static void setTrayIconToolTip(TrayIcon trayIcon, String toolTip) { if (trayIcon != null) trayIcon.setToolTip(toolTip); } static Font getFont(JComponent c) { return c == null ? null : swing(new F0<Font>() { public Font get() { try { return c.getFont(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getFont();"; }}); } static <A> List<A> asSynchroList(Iterable<A> l) { return syncList(cloneList(l)); } static double toMinutes(long ms) { return ms/60000.0; } static JPanel vstack2(final Object... parts) { return swing(new F0<JPanel>() { 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; smartAddWithLayout(panel, gbc, parts); //gbc = (GridBagConstraints) gbc.clone(); //gbc.fill = GridBagConstraints.BOTH; gbc.weighty = 1; 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..."; }}); } // runnable can take an argument (Pt) static <A extends JComponent> A onLeftClick(final A c, final Object runnable) { { swing(() -> { c.addMouseListener(leftClickMouseAdapter(runnable)); }); } return c; } // runnable takes no args static TrayIcon onLeftClick(final TrayIcon c, final Object runnable) { { swing(() -> { c.addMouseListener(leftClickMouseAdapter_noPt(runnable)); }); } return c; } static <A extends JComponent> A onLeftClick(final Object runnable, A c) { return onLeftClick(c, runnable); } static <A extends JComponent> A onLeftClick(Runnable runnable, A c) { return onLeftClick((Object) runnable, c); } static <A extends JComponent> A onLeftClick(A c, Runnable r) { return onLeftClick(r, c); } static <A extends JComponent> A onLeftClick(A c, IVF1<Pt> r) { return onLeftClick(c, (Object) r); } static <A extends JComponent> A instaToolTip(A c, String toolTip) { return instaToolTip(toolTip, c); } static <A extends JComponent> A instaToolTip(IF0<String> toolTip, A c) { new InstantNeverHideToolTip(toolTip, c); return c; } static <A extends JComponent> A instaToolTip(String toolTip, A c) { new InstantNeverHideToolTip(toolTip, c); return c; } 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(() -> { l.setVerticalTextPosition(SwingConstants.BOTTOM); l.setHorizontalTextPosition(SwingConstants.CENTER); }); } return l; } static JPanel northAndCenter(Component n, Component c) { return centerAndNorth(c, n); } static int withBottomMargin_defaultWidth = 6; static JPanel withBottomMargin(Component c) { return withBottomMargin(withBottomMargin_defaultWidth, c); } static JPanel withBottomMargin(final int w, final Component c) { return swing(new F0<JPanel>() { public JPanel get() { try { JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createEmptyBorder(0, 0, w, 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 JLabel jLiveValueLabel(final LiveValue lv) { return bindJLabelToLiveValue(jlabel(), lv); } static JLabel jVarLabel(IVarWithNotify var) { return bindJLabelToVar(jlabel(), var); } static JSplitPane setSplitPaneOnFirstShowing(Component c, double value) { return setSplitPaneOnFirstShowing(c, value, 0); } static JSplitPane setSplitPaneOnFirstShowing(Component c, double value, int delay) { final JSplitPane sp = first(childrenOfType(c, JSplitPane.class)); if (sp != null) onFirstResize(sp, new Runnable() { public void run() { try { if (delay == 0) sp.setDividerLocation(value); else awtLater(delay, new Runnable() { public void run() { try { sp.setDividerLocation(value); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "sp.setDividerLocation(value);"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef setSplitPaneOnFirstShowing_debug\r\n printVars setSplitPaneOnFirst..."; }}); return sp; } static void setSCPComponent(SingleComponentPanel scp, Component c) { if (scp != null) scp.setComponent(c); } static long nanoTime() { return System.nanoTime(); } static double nanoSecondsToSeconds(double nanos) { return nanos*1e-9; } static <A extends JComponent> A applyDefaultMargin(final A c) { if (c != null) { swing(() -> { c.setBorder(BorderFactory.createEmptyBorder(withMargin_defaultWidth, withMargin_defaultWidth, withMargin_defaultWidth, withMargin_defaultWidth)); }); } return c; } static <A extends JComponent> A applyMargin(int top, int left, int bottom, int right, A c) { return addMargin(top, left, bottom, right, c); } static <A extends JComponent> A applyMargin(int w, A c) { return addMargin(w, c); } static Component horizontalStrut(int height) { return Box.createHorizontalStrut(height); } static <A extends JLabel> A setHorizontalAlignment(final int pos, final A a) { swingCall(a, "setHorizontalAlignment", pos); return a; } static <A extends AbstractButton> A setHorizontalAlignment(final int pos, final A a) { swingCall(a, "setHorizontalAlignment", pos); return a; } static <A extends JTextField> A setHorizontalAlignment(final int pos, final A a) { swingCall(a, "setHorizontalAlignment", pos); return a; } static JLabel jLabel(String text) { return jlabel(text); } static JLabel jLabel() { return jlabel(); } static Boolean grabbableIntPixels_succeeded; static boolean grabbableIntPixels_enable = true; static GrabbableIntPixels grabbableIntPixels(BufferedImage img) { if (img == null || !grabbableIntPixels_enable) return null; try { var result = grabbableIntPixels_impl(img); grabbableIntPixels_succeeded = result != null; return result; } catch (Throwable _e) { grabbableIntPixels_succeeded = false; throw rethrow(_e); } } static GrabbableIntPixels grabbableIntPixels_impl(BufferedImage img) { Raster raster = img.getRaster(); SampleModel _sampleModel = raster.getSampleModel(); if (!(_sampleModel instanceof SinglePixelPackedSampleModel)) return null; SinglePixelPackedSampleModel sampleModel = (SinglePixelPackedSampleModel) _sampleModel; DataBufferInt dataBuffer = (DataBufferInt) (raster.getDataBuffer()); assertEquals(1, dataBuffer.getNumBanks()); assertEquals(DataBuffer.TYPE_INT, dataBuffer.getDataType()); // Let's at this point assume the raster data is what we // think it is... (TODO: test on unusual platforms) int w = img.getWidth(), h = img.getHeight(); int scanlineStride = sampleModel.getScanlineStride(); int[] pixels = dataBuffer.getData(); int offset = dataBuffer.getOffset(); int translateX = raster.getSampleModelTranslateX(); int translateY = raster.getSampleModelTranslateY(); offset += -translateX-translateY*scanlineStride; return new GrabbableIntPixels(pixels, w, h, offset, scanlineStride); } static int colorToIntOpaque(Color c) { return c.getRGB() | 0xFF000000; } static String copyToClipboard(String text) { return copyTextToClipboard(text); } static <A extends Image> A copyToClipboard(A image) { copyImageToClipboard(image); return image; } static File copyToClipboard(File f) { return copyFileToClipboard(f); } static <A> List<A> uniquifyList(Collection<A> l) { if (l == null) return null; if (l(l) < 2) return asList(l); HashSet<A> set = new HashSet(); List<A> out = new ArrayList(); for (A a : l) if (set.add(a)) out.add(a); return out; } static TreeMap descTreeMap() { return revTreeMap(); } static <A, B> List<B> secondOfPairs(Collection<Pair<A, B>> l) { return lambdaMap(__134 -> secondOfPair(__134), l); } // TODO: clean up these type signatures? static <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(final Iterable<A> c, final F1<A, C> makeInnerIterator) { return nestedIterator(iterator(c), makeInnerIterator); } static <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(Collection<A> c, final IF1<A, C> makeInnerIterator) { return nestedIterator(iterator(c), makeInnerIterator); } static <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(Iterator<A> it1, IF1<A, C> makeInnerIterator) { return nestedIterator(it1, toF1(makeInnerIterator)); } static <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(final Iterator<A> it1, final F1<A, C> makeInnerIterator) { if (it1 == null || !it1.hasNext()) return emptyIterableIterator(); return iff(new F0() { A a; Iterator<B> innerIterator; { nextOuter(); } void nextOuter() { a = it1.next(); innerIterator = makeInnerIterator.get(a); } public Object get() { while (true) { ping(); if (innerIterator != null && innerIterator.hasNext()) return innerIterator.next(); if (!it1.hasNext()) return endMarker(); nextOuter(); } } }); } static <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(IF1<A, C> makeInnerIterator, Iterator<A> it1) { return nestedIterator(it1, makeInnerIterator); } static <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(IF1<A, C> makeInnerIterator, Collection<A> l) { return nestedIterator(l, makeInnerIterator); } static class mapI_It extends IterableIterator { Object f; Iterator i; mapI_It() {} mapI_It(Object f, Iterator i) { this.i = i; this.f = f;} public boolean hasNext() { return i.hasNext(); } public Object next() { return callF(f, i.next()); } public String toString() { return formatFunctionCall("mapI", f, i); } } // apply a function to an iterator static IterableIterator mapI(final Object f, final Iterator i) { return new mapI_It(f, i); } static IterableIterator mapI(IterableIterator i, Object f) { return mapI((Iterator) i, f); } static IterableIterator mapI(Object f, IterableIterator i) { return mapI((Iterator) i, f); } static IterableIterator mapI(Iterator i, Object f) { return mapI(f, i); } static <A, B> IterableIterator<B> mapI(Iterable<A> i, IF1<A, B> f) { return mapI(i, (Object) f); } static <A, B> IterableIterator<B> mapI(Iterator<A> i, IF1<A, B> f) { return mapI(f, i); } static <A, B> IterableIterator<B> mapI(IterableIterator<A> i, IF1<A, B> f) { return mapI((Iterator<A>) i, f); } static <A, B> IterableIterator<B> mapI(IF1<A, B> f, Iterable<A> i) { return mapI(i, (Object) f); } static IterableIterator mapI(Iterable i, Object f) { return mapI(f, i.iterator()); } static IterableIterator mapI(Object f, Iterable i) { return mapI(i, f); } static String assertPossibleGlobalID(String s) { if (!possibleGlobalID(s)) throw fail("Not an acceptable global ID: " + s); return s; } static char charPlus(char a, int b) { return (char) (((int) a) + b); } static char charPlus(int b, char a) { return charPlus(a, b); } // better modulo that gives positive numbers always static int mod(int n, int m) { return (n % m + m) % m; } static long mod(long n, long m) { return (n % m + m) % m; } static BigInteger mod(BigInteger n, int m) { return n.mod(bigint(m)); } static double mod(double n, double m) { return (n % m + m) % m; } static List<String> dropHTMLComments(List<String> tok) { List<String> 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("<!--")) >= 0) { int j = smartIndexOf(t, "-->", 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<String> dropTags(List<String> tok) { return dropAllTags(tok); } static List<String> regexpGetGroups(Matcher matcher) { int n = matcher.groupCount(); List<String> l = new ArrayList(); for (int i = 1; i <= n; i++) l.add(matcher.group(i)); return l; } // performs find() static List<String> regexpGetGroups(String pat, String s) { Matcher m = regexpMatcher(pat, s); if (m.find()) return regexpGetGroups(m); return null; } static String getFrameTitle(Component c) { JFrame f = getFrame(c); return f == null ? null : f.getTitle(); } static void closeAutoCloseables(Collection<AutoCloseable> l) { if (l != null) for (AutoCloseable c : l) { try { c.close(); } catch (Throwable __e) { printStackTrace(__e); }} } static <A> A nuInstance(Class<A> c) { return nuEmptyObject(c); } static <A extends AutoCloseable> A dm_ownResource(A resource) { dm_currentModuleMandatory().ownResource(resource); return resource; } static <A> AutoCloseable vmBus_onMessage(String msg, IVF1<A> onMessage) { return vmBus_onMessage(msg, ivf1ToVF1(onMessage)); } static <A> AutoCloseable vmBus_onMessage(final String msg, final VF1<A> onMessage) { Map<String, Set> 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<String, Object>() { 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 <A, B> AutoCloseable vmBus_onMessage(String msg, final VF2<A, B> onMessage) { return vmBus_onMessage(msg, new VF1<Object[]>() { 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 <A, B> AutoCloseable vmBus_onMessage(String msg, final IVF2<A, B> onMessage) { return vmBus_onMessage(msg, new VF1<Object[]>() { 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 String selectedItem(JList l) { return getSelectedItem(l); } static String selectedItem(JComboBox cb) { return getSelectedItem(cb); } static void internalFrameTitlePopupMenuItem(JInternalFrame f, final String name, final Object action) { internalFrameTitlePopupMenu(f, new VF1<JPopupMenu>() { 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 File toFile_nullOnEmpty(String s) { return empty(s) ? null : new File(s); } 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<ScannedBot> fullBotScan() { return fullBotScan(""); } static List<ScannedBot> fullBotScan(String searchPattern) { List<ScannedBot> 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<Number, String> 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); } // TODO: comparators!? static MultiMap similarEmptyMultiMap(MultiMap m) { if (m instanceof TreeMultiMap) return new TreeMultiMap((TreeMultiMap) 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; } // 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<Object> { 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 <A> Collection<A> concatCollections_conservative(Collection<A> a, Collection<A> b) { if (empty(a)) return b; if (empty(b)) return a; return concatLists(a, b); } static <A> Set<A> mergeTreeSets(Collection<A>... l) { TreeSet<A> set = new TreeSet(); for (Collection<A> o : l) if (o != null) set.addAll(o); return set; } 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 <A, B, C> Map<A, C> mapKeyAndFunction(Map<A, B> map, IF2<A, B, C> f) { HashMap map2 = new HashMap(); if (map != null) for (Map.Entry<? extends A, ? extends B> __0 : _entrySet( map)) { A key = __0.getKey(); B value = __0.getValue(); map2.put(key, callF(f, key, value)); } return map2; } static <A, B> Map<A, B> mapKeyAndFunction(Iterable<A> l, IF1<A, B> f) { return mapKeyAndFunction(f, l); } 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 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 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 int hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static int hashCode(long l) { return Long.hashCode(l); } static String filterChars(IF1<Character, Boolean> pred, String s) { if (empty(s)) return null; int n = l(s), i = 0; char c = s.charAt(i); boolean totality = pred.get(c); for (++i; i < n; i++) { c = s.charAt(i); boolean b = pred.get(c); if (b != totality) { // diverse case StringBuilder buf = new StringBuilder(); if (totality) buf.append(substring(s, 0, i)); while (true) { ping(); if (b) buf.append(c); if (++i >= n) break; c = s.charAt(i); b = pred.get(c); } return buf.toString(); } } return totality ? s : ""; } // this copies to RGBA static BufferedImage copyImage(Image img) { if (img == null) return null; if (img instanceof BufferedImage) return copyImage((BufferedImage) img); int w = img.getWidth(null), h = img.getHeight(null); BufferedImage bi = newBufferedImage(w, h); drawImage(bi, img); return bi; } // this one stays in color model. inconsistent i guess static BufferedImage copyImage(BufferedImage bi) { if (bi == null) return null; ColorModel cm = bi.getColorModel(); boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster()); return new BufferedImage(cm, raster, isAlphaPremultiplied, null); } static boolean isImageFile(File f) { return isImageFileName(fileName(f)) && isFile(f); } static byte[] intToBytes(int i) { return new byte[] { (byte) (i >>> 24), (byte) (i >>> 16), (byte) (i >>> 8), (byte) i}; } static <A> IterableIterator<A> iteratorFromFunction_f0(final F0<A> f) { class IFF2 extends IterableIterator<A> { 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 <A> IterableIterator<A> iteratorFromFunction_if0(final IF0<A> f) { class IFF2 extends IterableIterator<A> { 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 <A extends JComponent> A setFont(final Font font, final A a) { if (a != null) { swing(() -> { a.setFont(font); }); } return a; } static <A extends JComponent> A setFont(A a, Font font) { return setFont(font, a); } static <A extends JComponent> 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 String htitle_noEncode(String title) { return tag("title", title); } // Can a be converted to b? // score 0 = exact match // score Int.MAX_VALUE = no match // score 1 = conversion needed (boxing/unboxing or non-primitve widening) // score -2 = primitive widening needed static int typeConversionScoreWithUnboxing(Class a, Class b) { int score = typeConversionScore(a, b); if (score == 0 || score == 1) return score; Class aPrim = boxedToPrimitiveType(a); if (aPrim != null) { int score2 = typeConversionScore(aPrim, b); if (score2 == 0) return 1; else if (score2 != Integer.MAX_VALUE) return score2; } return score; } static Object convertPrimitiveIfNecessary(Object o, Class b) { if (b.isPrimitive()) { if (o instanceof Character) { char x = (char) (((Character) o)); if (b == int.class) return Integer.valueOf(x); if (b == long.class) return Long.valueOf(x); if (b == float.class) return Float.valueOf(x); if (b == double.class) return Double.valueOf(x); } else if (o instanceof Number) { if (b == char.class) return Character.valueOf((char) ((Number) o).intValue()); if (b == short.class) return ((Number) o).shortValue(); if (b == int.class) return ((Number) o).intValue(); if (b == long.class) return ((Number) o).longValue(); if (b == float.class) return ((Number) o).floatValue(); } } return o; } static _MethodCache getMethodCache(Class c) { return callOpt_getCache(c); } static boolean zipFileContains_falseOnError(File inZip, String fileName) { try { return zipFileContains(inZip, fileName); } catch (Throwable e) { return false; } } static File javaHome() { return envJavaHome(); } static File javaHome(String sub) { return newFile(envJavaHome(), sub); } // moduleName = e.g. "java.scripting" static File jigsawModuleFile(String moduleName) { return jigsawModuleFile(moduleName, javaHome()); } static File jigsawModuleFile(String moduleName, File javaHome) { return new File(javaHome, "jmods/" + moduleName + ".jmod"); } // from: https://stackoverflow.com/questions/14416107/int-array-to-bufferedimage // pixels are RGB pixels static BufferedImage intArrayToBufferedImage(int[] pixels, int w, int h) { int[] bitMasks = new int[]{0xFF0000, 0xFF00, 0xFF, 0xFF000000}; SinglePixelPackedSampleModel sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, bitMasks); DataBufferInt db = new DataBufferInt(pixels, pixels.length); WritableRaster wr = Raster.createWritableRaster(sm, db, new Point()); return new BufferedImage(ColorModel.getRGBdefault(), wr, false, null); } static void registerEscape_rootPane(JComponent rootPane, final Runnable r) { String name = "Escape"; Action action = abstractAction(name, r); KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); rootPane.getActionMap().put(name, action); rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, name); } static JPanel centerAndEast(final Component c, final Component e) { return swing(new F0<JPanel>() { 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 BufferedImage reconstructBufferedImage(BufferedImage img) { if (img == null) return null; RGBImage rgb = new RGBImage(img); rgb.uncacheBufferedImage(); return rgb.getBufferedImage(); } static List<CriticalAction> beginCriticalAction_inFlight = synchroList(); static class CriticalAction implements AutoCloseable { String description; CriticalAction() {} CriticalAction(String description) { this.description = description;} final public void close(){ done(); } public 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) { printStackTrace(__e); } } sleepInCleanUp(10); } } static File toFile(Object o) { if (o instanceof File) return (File) o; if (o instanceof String) return new File((String) o); throw fail("Not a file: " + o); } static <A extends JTextComponent> A moveCaretToEnd(A ta) { setCaretPosition(ta, textAreaTextLength(ta)); return ta; } static JComponent showTitledForm(String title, Object... _parts) { return showFormTitled(title, _parts); } static Rect intersectRects(Rect a, Rect b) { int x = max(a.x, b.x), y = max(a.y, b.y); int x2 = min(a.x+a.w, b.x+b.w), y2 = min(a.y+a.h, b.y+b.h); return new Rect(x, y, x2-x, y2-y); } static Rect intersectRects(Rect a, int x1, int y1, int w, int h) { if (a == null || a.x >= x1 && a.y >= y1 && a.x2() < x1+w && a.y2() < y1+h) return a; return rectFromPoints( max(a.x, x1), max(a.y, y1), min(a.x2(), x1+w), min(a.y2(), y1+h)); } static boolean rectEmpty(Rect r) { return r == null || r.w <= 0 || r.h <= 0; } static BufferedImage decodeImage(byte[] data) { try { if (empty(data)) return null; return ImageIO.read(new ByteArrayInputStream(data)); } catch (Exception __e) { throw rethrow(__e); } } static byte[] bytesFromDataURL(String url) { String pref = "base64,"; int i = indexOf(url, pref); if (i < 0) return null; return base64decode(substring(url, i+l(pref))); } static String shorten_str(Object o) { return shorten(str(o)); } static String shorten_str(Object o, int max) { return shorten(str(o), max); } static String shorten_str(int max, Object o) { return shorten_str(o, max); } static int menuItemCount(JMenu menu) { return menu == null ? 0 : swing(() -> menu.getItemCount()); } static void caretToHome(JTextComponent c) { setCaret(c, 0); } 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<JPanel>() { 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 JScrollPane withoutViewportBorder(JScrollPane sp) { if (sp != null) { swing(() -> { sp.setViewportBorder(null); }); } return sp; } static void setMetaAndVerify(IMeta o, Object key, Object value) { setMeta(o, key, value); assertSame(() -> str(key), value, metaGet(o, key)); } static void setMetaAndVerify(Object o, Object key, Object value) { setMeta(o, key, value); assertSame(() -> str(key), value, metaGet(o, key)); } static void setCaretPosition(final JTextComponent c, final int pos) { if (c != null) { swing(() -> { try { int _pos = max(0, min(l(c.getText()), pos)); c.setCaretPosition(_pos); } catch (Throwable __e) { printStackTrace(__e); } }); } } static JRootPane getRootPane(Component c) { Component f = (Component) getPossiblyInternalFrame(c); return f instanceof RootPaneContainer ? (JRootPane) swingCall(f, "getRootPane") : null; } static boolean isCurrentThread(Thread t) { return t != null && t == currentThread(); } static JButton jimageButtonScaledToWidth(int w, String imageID, Runnable action) { return jimageButtonScaledToWidth(w, imageID, "", action); } static JButton jimageButtonScaledToWidth(int w, String imageID, String toolTip, Runnable action) { return jimageButton(scaleImageToWidth(w, imageID), toolTip, action); } static String errorIconID() { return "#1101390"; } static JComponent showErrorFrame(Throwable e) { return showText("Error", renderStackTrace(/*getInnerException*/(e))); } static JLabel jlabel_noAutoToolTip() { return jlabel_noAutoToolTip(" "); } static JLabel jlabel_noAutoToolTip(String text) { var lbl = swingConstruct(BetterLabel.class, text); lbl.autoToolTip = false; return lbl; } static String exceptionType(Throwable e) { e = getInnerException(e); return shortClassName(e); } static <A> Set<A> synchroSet() { return synchroHashSet(); } static <A> Set<A> synchroSet(Set<A> set) { return Collections.synchronizedSet(set); } static void printStackTrace_inPossiblyCancelledThread(Throwable e) { AutoCloseable __1 = tempUncancelThread(); try { System.out.println(getStackTrace_noRecord(e)); } finally { _close(__1); }} static List beforeDelegatingToThread_operations = synchroList(); static void beforeDelegatingToThread(Thread t) { for (Object op : cloneList(beforeDelegatingToThread_operations)) pcallF(op, t); } static void beforeDelegatingToThread_do(Object f) { setAdd(beforeDelegatingToThread_operations, f); } static <A, B> Either<A, B> either2(B b) { return new Either(2, b); } static <A, B> Either<A, B> either1(A a) { return new Either(1, a); } static List afterDelegatingToThread_operations = synchroList(); static void afterDelegatingToThread(Thread t) { for (Object op : cloneList(afterDelegatingToThread_operations)) pcallF(op, t); } static void afterDelegatingToThread_do(Object f) { setAdd(afterDelegatingToThread_operations, f); } static boolean activateFrame(final Component c, Object... __) { return swing(new F0<Boolean>() { public Boolean get() { try { Frame f = getAWTFrame(c); if (f == null) return false; if (!f.isVisible()) f.setVisible(true); boolean windowsHack = optPar("windowsHack", __, true); boolean iconified = f.getState() == Frame.ICONIFIED; boolean maximize = boolPar("maximize", __); if (iconified) f.setState(maximize ? Frame.MAXIMIZED_BOTH : Frame.NORMAL); // My glorious Windows hack // See: https://stackoverflow.com/questions/309023/how-to-bring-a-window-to-the-front if (windowsHack && !iconified && isWindows()) { boolean fullscreen = f.getExtendedState() == Frame.MAXIMIZED_BOTH; f.setExtendedState(JFrame.ICONIFIED); f.setExtendedState(fullscreen ? JFrame.MAXIMIZED_BOTH : JFrame.NORMAL); } f.toFront(); return true; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Frame f = getAWTFrame(c);\r\n if (f == null) false;\r\n if (!f.isVisible())..."; }}); } static List<Integer> countIteratorToList(int b) { return countIteratorToList(0, b); } static List<Integer> countIteratorToList(int a, int b) { return asList(countIterator(a, b)); } static <A> List<A> countIteratorToList(int b, IF1<Integer, A> f) { return countIteratorToList(0, b, f); } static <A> List<A> countIteratorToList(int a, int b, IF1<Integer, A> f) { return asList(countIterator(a, b, f)); } static List<Integer> countIteratorToList(int a, int b, int step) { return asList(countIterator(a, b, step)); } static <A> List<A> countIteratorToList(double a, double b, double step, IF1<Double, A> f) { return asList(countIterator(a, b, step, f)); } static <A> List<Double> countIteratorToList(double a, double b, double step) { return asList(countIterator(a, b, step)); } static <A> List<A> countIteratorToList(IF1<Double, A> f, double a, double b, double step) { return asList(countIterator(f, a, b, step)); } static <A> List<A> countIteratorToList(IF1<Integer, A> f, int b) { return countIteratorToList(f, 0, b); } static <A> List<A> countIteratorToList(IF1<Integer, A> f, int a, int b) { return asList(countIterator(f, a, b)); } static IterableIterator<Integer> countIterator_exclusive(int b) { return countIterator_exclusive(0, b); } static IterableIterator<Integer> countIterator_exclusive(int a, int b) { return new IterableIterator<Integer>() { int i = a; public boolean hasNext() { return i < b; } public Integer next() { return i++; } }; } static <A> IterableIterator<A> countIterator_exclusive(int b, IF1<Integer, A> f) { return countIterator_exclusive(0, b, f); } static <A> IterableIterator<A> countIterator_exclusive(int a, int b, IF1<Integer, A> f) { return mapI_if1(f, countIterator_exclusive(a, b)); } static IterableIterator<Integer> countIterator_exclusive_step(final int a, final int b, final int step) { assertTrue("step > 0", step > 0); return new IterableIterator<Integer>() { int i = a; public boolean hasNext() { return i < b; } public Integer next() { var j = i; i += step; return j; } }; } static IterableIterator<Double> countIterator_exclusive_step(double a, double b, double step) { assertTrue("step > 0", step > 0); return new IterableIterator<Double>() { double i = a; public boolean hasNext() { return i < b; } public Double next() { var j = i; i += step; return j; } }; } static <A> IterableIterator<A> countIterator_exclusive_step(double a, double b, double step, IF1<Double, A> f) { return mapI_if1(f, countIterator_exclusive_step(a, b, step)); } static Rect boundsAsRect(Component c) { return boundsRect(c); } static <A> int indexOfMax(List<A> l, IF1<A, Comparable> f) { Best_comparable<Integer> best = new Best_comparable(); for (int i = 0; i < l(l); i++) best.put(i, f.get(l.get(i))); return or(best.get(), -1); } static List<Rect> allScreenBounds() { return map(__135 -> screenBounds(__135), screenDevices()); } static int area(Rect r) { return rectArea(r); } static double area(DoubleRect r) { return r == null ? 0 : r.w*r.h; } static MouseAdapter leftClickMouseAdapter(final Object r) { return new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getButton() == 1) pcallF(r, new Pt(e.getPoint())); } }; } static MouseAdapter leftClickMouseAdapter_noPt(final Object r) { return new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getButton() == 1) pcallF(r); } }; } static JPanel centerAndNorth(final Component c, final Component n) { return swing(new F0<JPanel>() { public JPanel get() { try { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.CENTER, wrap(c)); panel.add(BorderLayout.NORTH, wrap(n)); 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 <A extends JLabel> A bindJLabelToLiveValue(final A label, final LiveValue lv) { bindLiveValueListenerToComponent(label, lv, new Runnable() { public void run() { try { setText(label, strOrNull(lv.get())) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "setText(label, strOrNull(lv!))"; }}); return label; } static <A extends JLabel> A bindJLabelToVar(A label, IVarWithNotify var) { bindListenerToComponent(label, var, () -> { try { setText(label, strOrNull(var.get())); } catch (Throwable e) { printStackTrace(e); setText(label, "Error"); } }); return label; } static void onFirstResize(final Component c, final Object r) { if (c != null && r != null) { swing(() -> { c.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { c.removeComponentListener(this); pcallF(r); } }); }); } } static <A extends JComponent> A addMargin(final int top, final int left, final int bottom, final int right, final A c) { if (c != null) { swing(() -> { Border margin = BorderFactory.createEmptyBorder(top, left, bottom, right); c.setBorder(jCompoundBorder(c.getBorder(), margin)); }); } return c; } static <A extends JComponent> A addMargin(int w, A c) { return addMargin(w, w, w, w, c); } static File copyFileToClipboard(File f) { if (f == null) return null; Toolkit.getDefaultToolkit().getSystemClipboard().setContents( new FileTransferable(f), null); vmBus_send("newClipboardContents", f); return f; } static TreeMap revTreeMap() { return new TreeMap(reverseComparator()); } static <A, B> B secondOfPair(Pair<A, B> p) { return p == null ? null : p.b; } static F1 toF1(final Object f) { return functionToF1(f); } static IterableIterator emptyIterableIterator_instance = new IterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static <A> IterableIterator<A> emptyIterableIterator() { return emptyIterableIterator_instance; } static boolean possibleGlobalID(String s) { return l(s) == globalIDLength() && allLowerCaseCharacters(s); } static DynModule dm_currentModuleMandatory() { return dm_current_mandatory(); } static <A> A third(List<A> l) { return _get(l, 2); } static <A> A third(Iterable<A> l) { if (l == null) return null; Iterator<A> 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> A third(Producer<A> 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> A third(A[] bla) { return bla == null || bla.length <= 2 ? null : bla[2]; } static <A, B, C> C third(T3<A, B, C> t) { return t == null ? null : t.c; } static <A> VF1<A> runnableToVF1(final Runnable r) { return r == null ? null : new VF1<A>() { public void get(A a) { try { r.run() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "r.run()"; }}; } // changes & returns canvas static BufferedImage drawImage(BufferedImage canvas, Image img, Pt p) { return drawImageOnImage(img, canvas, p.x, p.y); } static void drawImage(BufferedImage g, Image img) { drawImage(graphics(g), img); } static void drawImage(Graphics2D g, Image img) { drawImage(g, img, 0, 0); } static void drawImage(Graphics2D g, Image img, Pt p) { drawImage(g, img, p.x, p.y); } static void drawImage(Graphics2D g, Image img, int x, int y) { { if (g != null) g.drawImage(img, x, y, null); } } static boolean isImageFileName(String s) { return eqicOneOf(fileExtension(s), ".png", ".jpg", ".jpeg", ".gif"); } static Map<String, Font> 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); } } // Can a be converted to b? // score 0 = exact match // score Int.MAX_VALUE = no match // score 1 = conversion needed (boxing/unboxing or non-primitve widening) // score -2 = primitive widening needed static int typeConversionScore(Class a, Class b) { if (a == b) return 0; if (b.isPrimitive()) { // target type is primitive if (a.isPrimitive()) { // both types are primitive // No widenings to bool if (b == boolean.class) return Integer.MAX_VALUE; // No widenings to byte if (b == byte.class) return Integer.MAX_VALUE; // byte can be converted to char if (b == char.class) return a == byte.class ? -2 : Integer.MAX_VALUE; // byte can be converted to short if (b == short.class) return a == byte.class ? -2 : Integer.MAX_VALUE; // byte, char and short can be converted to int if (b == int.class) return a == byte.class || a == char.class || a == short.class ? -2 : Integer.MAX_VALUE; // byte, char, short and int can be converted to long if (b == long.class) return a == byte.class || a == char.class || a == short.class || a == int.class ? -2 : Integer.MAX_VALUE; // byte, char, short and int can be converted to float if (b == float.class) return a == byte.class || a == char.class || a == short.class || a == int.class ? -2 : Integer.MAX_VALUE; // all primitive types except bool can be converted to double return a != boolean.class ? -2 : Integer.MAX_VALUE; } else { // source type is boxed - check if they're a match return primitiveToBoxedType(b) == a ? 1 : Integer.MAX_VALUE; } } else { // target type is not primitive // object type is primitive - check for exact match if (a.isPrimitive()) return primitiveToBoxedType(a) == b ? 1 : Integer.MAX_VALUE; // both types are not primitive return b.isAssignableFrom(a) ? 1 : Integer.MAX_VALUE; } } static Class boxedToPrimitiveType(Class type) { if (type == Boolean.class) return boolean.class; if (type == Integer.class) return int.class; if (type == Long.class) return long.class; if (type == Float.class) return float.class; if (type == Short.class) return short.class; if (type == Character.class) return char.class; if (type == Byte.class) return byte.class; if (type == Double.class) return double.class; return null; } static boolean zipFileContains(File inZip, String fileName) { try { ZipFile zip = new ZipFile(inZip); try { return zipFileContains(zip, fileName); } finally { zip.close(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean zipFileContains(ZipFile zip, String fileName) { try { return zip.getEntry(fileName) != null; } catch (Exception __e) { throw rethrow(__e); } } static File envJavaHome() { return newFile(System.getProperty("java.home")); } static int textAreaTextLength(JTextComponent ta) { return l(getText(ta)); } static Rect rectFromPoints(int x1, int y1, int x2, int y2) { return pointsRect(x1, y1, x2, y2); } static Rect rectFromPoints(Pt a, Pt b) { return pointsRect(a.x, a.y, b.x, b.y); } static void setCaret(final JTextComponent c, final int pos) { if (c != null) { swing(() -> { c.setCaretPosition(pos); }); } } static void setMeta(IMeta o, Object key, Object value) { metaMapPut(o, key, value); } static void setMeta(Object o, Object key, Object value) { metaMapPut(o, key, value); } static JButton jimageButton(String imageID, Object action) { JButton btn = jbutton("", action); btn.setIcon(imageIcon(imageID)); return btn; } // button without action static JButton jimageButton(String imageID) { return jimageButton(imageID, null); } static JButton jimageButton(Image img) { return jimageButton(img, null, null); } static JButton jimageButton(String imageID, String toolTip, Runnable action) { return jimageButton(imageIcon(imageID), toolTip, action); } static JButton jimageButton(Image img, String toolTip, Runnable action) { var btn = jbutton("", action); setButtonImage(btn, img); return setToolTip(toolTip, btn); } static JButton jimageButton(ImageIcon img, String toolTip, Runnable action) { var btn = jbutton("", action); setButtonImage(btn, img); return setToolTip(toolTip, btn); } static BufferedImage scaleImageToWidth(BufferedImage img, int newW) { return resizeImage(img, newW); } static BufferedImage scaleImageToWidth(int newW, BufferedImage img) { return scaleImageToWidth(img, newW); } static BufferedImage scaleImageToWidth(int newW, String imageID) { return scaleImageToWidth(newW, loadImage2(imageID)); } static AutoCloseable tempUncancelThread() { return tempRemove(ping_actions, Thread.currentThread()); } static class mapI_if1_It<A, B> extends IterableIterator<B> { IF1<A, B> f; Iterator<A> i; mapI_if1_It() {} mapI_if1_It(IF1<A, B> f, Iterator<A> i) { this.i = i; this.f = f;} public boolean hasNext() { return i.hasNext(); } public B next() { return f.get(i.next()); } public String toString() { return formatFunctionCall("mapI_if1", f, i); } } static <A, B> IterableIterator<B> mapI_if1(IF1<A, B> f, Iterable<A> i) { return new mapI_if1_It(f, i.iterator()); } static <A, B> IterableIterator<B> mapI_if1(Iterable<A> i, IF1<A, B> f) { return mapI_if1(f, i); } static Rect boundsRect(Component c) { return toRect(getBounds(c)); } static int rectArea(Rect r) { return r == null ? 0 : r.w*r.h; } static <A extends JComponent> A bindListenerToComponent(A component, IHasChangeListeners source, Runnable listener) { if (source != null && listener != null) bindToComponent(component, new Runnable() { public void run() { try { source.onChangeAndNow(listener); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "source.onChangeAndNow(listener);"; }}, new Runnable() { public void run() { try { source.removeChangeListener(listener) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "source.removeChangeListener(listener)"; }}); return component; } static Border jCompoundBorder(Border inner, Border outer) { if (inner == null) return outer; if (outer == null) return inner; return BorderFactory.createCompoundBorder(inner, outer); } static <A> Comparator<A> reverseComparator(Comparator<A> c) { return (a, b) -> c.compare(b, a); } static <A> Comparator<A> reverseComparator() { return (a, b) -> cmp(b, a); } static F1 functionToF1(final Object f) { if (isString(f)) return mainFunctionToF1((String) f); if (f instanceof F1) return (F1) f; return new F1<Object, Object>() { public Object get(Object a) { try { return callF(f, a); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ret callF(f, a);"; }}; } static boolean allLowerCaseCharacters(String s) { for (int i = 0; i < l(s); i++) if (Character.getType(s.charAt(i)) != Character.LOWERCASE_LETTER) return false; return true; } // changes & returns canvas static BufferedImage drawImageOnImage(Image img, BufferedImage canvas, int x, int y) { createGraphics(canvas).drawImage(img, x, y, null); return canvas; } static BufferedImage drawImageOnImage(Image img, BufferedImage canvas) { return drawImageOnImage(img, canvas, 0, 0); } static Graphics2D graphics(BufferedImage img) { return imageGraphics(img); } 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 Rect pointsRect(int x1, int y1, int x2, int y2) { return new Rect(x1, y1, x2-x1, y2-y1); } static JButton setButtonImage(Icon img, JButton btn) { btn.setIcon(img); return btn; } static JButton setButtonImage(Image img, JButton btn) { btn.setIcon(imageIcon(img)); return btn; } static <A extends AbstractButton> A setButtonImage(Image img, A btn) { btn.setIcon(imageIcon(img)); return btn; } static <A extends AbstractButton> A setButtonImage(A btn, Image img) { return setButtonImage(img, btn); } static <A extends AbstractButton> A setButtonImage(A btn, String imageID) { btn.setIcon(imageIcon(imageID)); return btn; } static JButton setButtonImage(JButton btn, Image img) { return setButtonImage(img, btn); } static JButton setButtonImage(JButton btn, Icon img) { return setButtonImage(img, btn); } static <A, B> AutoCloseable tempRemove(Map<A, B> map, A key) { if (map == null || !map.containsKey(key)) return null; B b = map.get(key); map.remove(key); return new AutoCloseable() { public String toString() { return "map.put(key, b);"; } public void close() throws Exception { map.put(key, b); }}; } static Rectangle getBounds(final Component c) { return c == null ? null : swing(new F0<Rectangle>() { public Rectangle get() { try { return c.getBounds(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getBounds();"; }}); } static F1 mainFunctionToF1(final String fname) { return new F1<Object, Object>() { public Object get(Object a) { try { return callMC(fname, a); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callMC(fname, a)"; }}; } static Map<BufferedImage, Object> createGraphics_modulators = synchroIdentityHashMap(); static Graphics2D createGraphics(BufferedImage img) { Graphics2D g = img.createGraphics(); Object mod = createGraphics_modulators.get(img); if (mod != null) callF(mod, g); return g; } // mod: voidfunc(Graphics2D) static void createGraphics_modulate(BufferedImage img, Object mod) { mapPut2(createGraphics_modulators, img, mod); } static ThreadLocal<Boolean> imageGraphics_antiAlias = new ThreadLocal(); static Graphics2D imageGraphics(BufferedImage img) { return !isFalse(imageGraphics_antiAlias.get()) ? antiAliasGraphics(img) : createGraphics(img); } static <A, B> Map<A, B> synchroIdentityHashMap() { return synchroMap(new IdentityHashMap()); } static <A, B> void mapPut2(Map<A, B> map, A key, B value) { if (map != null && key != null) if (value != null) map.put(key, value); else map.remove(key); } static Graphics2D antiAliasGraphics(BufferedImage img) { return antiAliasOn(createGraphics(img)); } static Graphics2D antiAliasOn(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); return g; } 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 class SingleThread { boolean running = false; void run(Object r) { go(r); } synchronized boolean go(final Object runnable) { if (running) return false; running = true; startThread("Single Thread", new Runnable() { public void run() { try { try { callF(runnable); } finally { _done(); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "try {\r\n callF(runnable);\r\n } finally {\r\n _done();\r\n }"; }}); return true; } synchronized void _done() { running = false; } boolean running() { return running; } } static class Best_comparable<A> { A best; Comparable score; boolean verboseNewBest, replaceIfSameScore; transient Object onChange; transient Object stringifier; // func(A) -> S synchronized boolean isNewBest(Comparable score) { return this.score == null || (replaceIfSameScore ? cmp(score, this.score) >= 0 : cmp(score, this.score) > 0); } synchronized Comparable bestScore() { return score; } Comparable score() { return bestScore(); } Comparable getScore() { return bestScore(); } synchronized float floatScoreOr(float defaultValue) { return best == null ? defaultValue : (float) score; } boolean put(Pair<? extends A, Comparable> p) { return p != null && put(p.a, p.b); } boolean put(Best<? extends A> b) { return b != null && put(b.get(), b.score); } boolean put(A a, Comparable score) { ping(); boolean change = false; if (a != null) synchronized(this) { if (isNewBest(score)) { best = a; this.score = score; change = true; } } if (change) { if (verboseNewBest) print("New best! " + this); pcallF(onChange); } return change; } synchronized A get() { return best; } synchronized boolean has() { return best != null; } synchronized Pair<A, Comparable> pair() { return main.pair(best, bestScore()); } synchronized A getIfScoreAbove(Comparable x) { return cmp(x, score()) >= 0 ? best : null; } public String toString() { return !has() ? "-" : "Score " + score + ": " + callStringifier(stringifier, best); } boolean putAndPrintIfNewBest(A a, Comparable score) { if (!put(a, score)) return false; { print(this); return true; } } synchronized void clear() { best = null; score = 0; } } // size: // 64 bytes for 0 to 1 elements // 96 bytes for 2 to 4 elements /* * #! * 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. * !# */ static class CompactHashMap<K, V> extends CompactAbstractMap<K, V> { final static int INITIAL_SIZE = 3; final static double LOAD_FACTOR = 0.6; // This object is used to represent null, should clients use that as final static Object nullObject = new Object(); /** * When a key is deleted this object is put into the hashtable in * its place, so that other entries with the same key (collisions) * further down the hashtable are not lost after we delete an object * in the collision chain. */ final static Object deletedObject = new Object(); int elements; int freecells; Object[] table; // key, value, key, value, ... //int modCount; CompactHashMap() { this(INITIAL_SIZE); } CompactHashMap(int size) { table = new Object[(size==0 ? 1 : size)*2]; elements = 0; freecells = tableSize(); //modCount = 0; } // TODO: allocate smarter CompactHashMap(Map<K, V> map) { this(0); if (map != null) putAll(map); } // ===== MAP IMPLEMENTATION ============================================= /** * Returns the number of key/value mappings in this map. */ public synchronized int size() { return elements; } /** * Returns <tt>true</tt> if this map contains no mappings. */ public synchronized boolean isEmpty() { return elements == 0; } /** * Removes all key/value mappings in the map. */ public synchronized void clear() { elements = 0; for (int ix = 0; ix < tableSize(); ix++) { key(ix, null); value(ix, null); } freecells = tableSize(); //modCount++; } /** * Returns <tt>true</tt> if this map contains the specified key. */ public synchronized boolean containsKey(Object k) { return key(findKeyIndex(k)) != null; } /** * Returns <tt>true</tt> if this map contains the specified value. */ public synchronized boolean containsValue(Object v) { if (v == null) v = (V)nullObject; for (int ix = 0; ix < tableSize(); ix++) if (value(ix) != null && value(ix).equals(v)) return true; return false; } /** * Returns a read-only set view of the map's keys. */ public synchronized Set<Entry<K, V>> entrySet() { throw new UnsupportedOperationException(); } /** * Removes the mapping with key k, if there is one, and returns its * value, if there is one, and null if there is none. */ public synchronized V remove(Object k) { int index = findKeyIndex(k); // we found the right position, now do the removal if (key(index) != null) { // we found the object // same problem here as with put V v = value(index); key(index, deletedObject); value(index, deletedObject); //modCount++; elements--; return v; } else // we did not find the key return null; } /** * Adds the specified mapping to this map, returning the old value for * the mapping, if there was one. */ public synchronized V put(K k, V v) { if (k == null) k = (K)nullObject; int hash = k.hashCode(); int index = (hash & 0x7FFFFFFF) % tableSize(); int offset = 1; int deletedix = -1; // search for the key (continue while !null and !this key) while(key(index) != null && !(key(index).hashCode() == hash && key(index).equals(k))) { // if there's a deleted mapping here we can put this mapping here, // provided it's not in here somewhere else already if (key(index) == deletedObject) deletedix = index; index = ((index + offset) & 0x7FFFFFFF) % tableSize(); offset = offset*2 + 1; if (offset == -1) offset = 2; } if (key(index) == null) { // wasn't present already if (deletedix != -1) // reusing a deleted cell index = deletedix; else freecells--; //modCount++; elements++; key(index, k); value(index, v); // rehash with increased capacity if (1 - (freecells / (double) tableSize()) > LOAD_FACTOR) rehash(tableSize()*2 + 1); return null; } else { // was there already //modCount++; V oldv = value(index); value(index, v); return oldv; } } /** * INTERNAL: Rehashes the hashmap to a bigger size. */ void rehash(int newCapacity) { int oldCapacity = tableSize(); Object[] newTable = new Object[newCapacity*2]; for (int ix = 0; ix < oldCapacity; ix++) { Object k = key(ix); if (k == null || k == deletedObject) continue; int hash = k.hashCode(); int index = (hash & 0x7FFFFFFF) % newCapacity; int offset = 1; // search for the key while(newTable[index*2] != null) { // no need to test for duplicates index = ((index + offset) & 0x7FFFFFFF) % newCapacity; offset = offset*2 + 1; if (offset == -1) offset = 2; } newTable[index*2] = k; newTable[index*2+1] = value(ix); } table = newTable; freecells = tableSize() - elements; } /** * Returns the value for the key k, if there is one, and null if * there is none. */ public synchronized V get(Object k) { return value(findKeyIndex(k)); } /** * Returns a virtual read-only collection containing all the values * in the map. */ public synchronized Collection<V> values() { return new ValueCollection(); } /** * Returns a virtual read-only set of all the keys in the map. */ public synchronized Set<K> keySet() { return new KeySet(); } // --- Internal utilities final int findKeyIndex(Object k) { if (k == null) k = nullObject; int hash = k.hashCode(); int index = (hash & 0x7FFFFFFF) % tableSize(); int offset = 1; // search for the key (continue while !null and !this key) while(key(index) != null && !(key(index).hashCode() == hash && key(index).equals(k))) { index = ((index + offset) & 0x7FFFFFFF) % tableSize(); offset = offset*2 + 1; if (offset == -1) offset = 2; } return index; } // --- Key set class KeySet<K> extends AbstractSet<K> { public synchronized int size() { return elements; } public synchronized boolean contains(Object k) { return containsKey(k); } public synchronized Iterator<K> iterator() { return new KeyIterator(); } } class KeyIterator<K> implements Iterator<K> { private int ix; private KeyIterator() { // walk up to first value, so that hasNext() and next() return // correct results for (; ix < tableSize(); ix++) if (value(ix) != null && key(ix) != deletedObject) break; } public synchronized boolean hasNext() { return ix < tableSize(); } public synchronized void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public synchronized K next() { if (ix >= tableSize()) throw new NoSuchElementException(); K key = (K) key(ix++); // walk up to next value for (; ix < tableSize(); ix++) if (key(ix) != null && key(ix) != deletedObject) break; // ix now either points to next key, or outside array (if no next) return key; } } // --- Value collection class ValueCollection<V> extends AbstractCollection<V> { public synchronized int size() { return elements; } public synchronized Iterator<V> iterator() { return new ValueIterator(); } public synchronized boolean contains(Object v) { return containsValue(v); } } class ValueIterator<V> implements Iterator<V> { private int ix; private ValueIterator() { // walk up to first value, so that hasNext() and next() return // correct results for (; ix < table.length/2; ix++) if (value(ix) != null && value(ix) != deletedObject) break; } public synchronized boolean hasNext() { return ix < tableSize(); } public synchronized void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public synchronized V next() { if (ix >= tableSize()) throw new NoSuchElementException(); V value = (V) value(ix++); // walk up to next value for (; ix < tableSize(); ix++) if (value(ix) != null && value(ix) != deletedObject) break; // ix now either points to next value, or outside array (if no next) return value; } } K key(int i) { return (K) table[i*2]; } void key(int i, Object key) { table[i*2] = key; } V value(int i) { return (V) table[i*2+1]; } void value(int i, Object value) { table[i*2+1] = value; } int tableSize() { return table.length/2; } } static class WidthAndHeightImpl extends Meta implements WidthAndHeight , IFieldsToList{ int width; int height; WidthAndHeightImpl() {} WidthAndHeightImpl(int width, int height) { this.height = height; this.width = width;}public Object[] _fieldsToList() { return new Object[] {width, height}; } public int getWidth() { return width; } public int getHeight() { return height; } public String toString() { return n2(width) + "*" + n2(height) + " px"; } } static class LeftAlignedLine extends JPanel { LeftAlignedLine(Component... components) { setLayout(LetterLayout.leftAlignedRow()); for (Component component : components) if (component != null) add(component); } public void add(String text) { add(new JLabel(text)); } } static class TreeMultiMap<A, B> extends MultiMap<A, B> { TreeMultiMap() { super(true); } TreeMultiMap(MultiMap<A, B> map) { this(); putAll(map); } } static class IntVar { int a; // you can access this directly if you use one thread public synchronized void set(int v) { if (v != a) { a = v; notifyAll(); } } public synchronized int get() { return a; } synchronized int waitForValue(int x) { try { while (a != x) wait(); return a; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return str(this.get()); } } static class DoubleRect { double x, y, w, h; DoubleRect() {} DoubleRect(Rectangle r) { x = r.x; y = r.y; w = r.width; h = r.height; } DoubleRect(double x, double y, double w, double h) { this.h = h; this.w = w; this.y = y; this.x = x;} // Huh. not implementing equals()/hashCode? Stefan is mysterious boolean eq(Object o) { if (!(o instanceof DoubleRect)) return false; if (o == this) return true; DoubleRect r = (DoubleRect) o; return x == r.x && y == r.y && w == r.w && h == r.h; } public String toString() { return x + "," + y + " / " + w + "," + h; } double x1() { return x; } double y1() { return y; } double x2() { return x + w; } double y2() { return y + h; } boolean contains(Pt p) { return contains(p.x, p.y); } boolean contains(double _x, double _y) { return _x >= x && _y >= y && _x < x+w && _y < y+h; } boolean empty() { return w <= 0 || h <= 0; } } static class Complex implements IFieldsToList{ static final String _fieldOrder = "re im"; double re; double im; Complex() {} Complex(double re, double im) { this.im = im; this.re = re;} public boolean equals(Object o) { if (!(o instanceof Complex)) return false; Complex __1 = (Complex) o; return re == __1.re && im == __1.im; } public int hashCode() { int h = -1679819632; h = boostHashCombine(h, _hashCode(re)); h = boostHashCombine(h, _hashCode(im)); return h; } public Object[] _fieldsToList() { return new Object[] {re, im}; } double abs() { return sqrt(re*re+im*im); } double re() { return re; } double im() { return im; } final double angle(){ return phase(); } double phase() { return Math.atan2(im, re); } double fracAngle() { return fracNonNeg(angle()/twoPi()); } // angle as 0 to 1 public String toString() { if (im != 0) return re == 0 ? im + "i" : re + plusPrefixUnlessMinus(str(im)) + "i"; else return str(re); } } static class GrabbableIntPixels implements IFieldsToList{ static final String _fieldOrder = "data w h offset scanlineStride"; int[] data; int w; int h; int offset; int scanlineStride; GrabbableIntPixels() {} GrabbableIntPixels(int[] data, int w, int h, int offset, int scanlineStride) { this.scanlineStride = scanlineStride; this.offset = offset; this.h = h; this.w = w; this.data = data;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + data + ", " + w + ", " + h + ", " + offset + ", " + scanlineStride + ")"; } public boolean equals(Object o) { if (!(o instanceof GrabbableIntPixels)) return false; GrabbableIntPixels __1 = (GrabbableIntPixels) o; return eq(data, __1.data) && w == __1.w && h == __1.h && offset == __1.offset && scanlineStride == __1.scanlineStride; } public int hashCode() { int h = -1183022196; h = boostHashCombine(h, _hashCode(data)); h = boostHashCombine(h, _hashCode(w)); h = boostHashCombine(h, _hashCode(h)); h = boostHashCombine(h, _hashCode(offset)); h = boostHashCombine(h, _hashCode(scanlineStride)); return h; } public Object[] _fieldsToList() { return new Object[] {data, w, h, offset, scanlineStride}; } int[] asPackedArray() { if (offset == 0 && data.length == w*h) return data; int[] pixels = new int[w*h]; for (int y = 0; y < h; y++) arrayCopy(data, offset+y*scanlineStride, pixels, y*h, w); return pixels; } } static class FileTransferable implements Transferable { List<File> files = new ArrayList(); FileTransferable(File f) { files.add(f); } FileTransferable(List<File> files) { this.files = files;} public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { DataFlavor.javaFileListFlavor }; } public boolean isDataFlavorSupported(DataFlavor flavor) { return eq(flavor, DataFlavor.javaFileListFlavor); } public Object getTransferData(DataFlavor flavor) { return files; } } static class TransferableImage implements Transferable { Image i; TransferableImage(Image i) { this.i = i;} public Object getTransferData( DataFlavor flavor ) throws UnsupportedFlavorException, IOException { if ( flavor.equals( DataFlavor.imageFlavor ) && i != null ) { return i; } else { throw new UnsupportedFlavorException( flavor ); } } public DataFlavor[] getTransferDataFlavors() { DataFlavor[] flavors = new DataFlavor[ 1 ]; flavors[ 0 ] = DataFlavor.imageFlavor; return flavors; } public boolean isDataFlavorSupported( DataFlavor flavor ) { DataFlavor[] flavors = getTransferDataFlavors(); for ( int i = 0; i < flavors.length; i++ ) { if ( flavor.equals( flavors[ i ] ) ) { return true; } } return false; } } // instantiate only once per component! static class InstantNeverHideToolTip extends MouseAdapter { JComponent component; JToolTip toolTip; Popup popup; int x, y; String text; IF0<String> calculateText; InstantNeverHideToolTip(IF0<String> calculateText, JComponent component) { this(component); this.calculateText = calculateText; } InstantNeverHideToolTip(String text, JComponent component) { this(component); this.text = text; } InstantNeverHideToolTip(JComponent component) { this.component = component; { swing(() -> { component.addMouseListener(InstantNeverHideToolTip.this); toolTip = component.createToolTip(); // Hide also when component is removed from hierarchy bindToComponent(component, null, () -> hideToolTip()); }); } } @Override public void mouseEntered(MouseEvent e) { x = e.getXOnScreen()+2; y = e.getYOnScreen()+2; showToolTip(); } void updateText() { swing(() -> { if (calculateText != null) setText(calculateText.get()); }); } public void showToolTip() { updateText(); if (empty(text)) return; toolTip.setTipText(text); popup = PopupFactory.getSharedInstance().getPopup(component, toolTip, x, y); popup.show(); } public void mouseExited(MouseEvent e) { hideToolTip(); } boolean toolTipShowing() { return popup != null; } void hideToolTip() { if (popup != null) { popup.hide(); popup = null; } } String getText() { return text; } void setText(String text) { swing(() -> { if (eq(text, getText())) return; InstantNeverHideToolTip.this.text = text; if (toolTipShowing()) { hideToolTip(); showToolTip(); } }); } } static class Either<A, B> { 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 + ")"; } } // -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<A> extends AbstractSet<A> { UnsynchronizedCompactHashSet<Entry<A>> entries = new UnsynchronizedCompactHashSet(); Entry<A> head, tail; static class Entry<A> { A value; Entry<A> 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<A> 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<A> 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<A> iterator() { return new IterableIterator<A>() { Entry<A> 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<A> e = entries.find(o); return e == null ? null : e.value; } public A prevElement(A a) { Entry<A> e = entries.find(a); if (e == null || e.prev == null) return null; return e.prev.value; } public A nextElement(A a) { Entry<A> 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 class NumPadFixingInputMap extends InputMap { public void put(KeyStroke keyStroke, Object actionMapKey) { super.put(keyStroke, actionMapKey); Integer code = rsyntaxTextArea_fixNumPad_map.get(keyStroke.getKeyCode()); if (code != null) put(KeyStroke.getKeyStroke(code, keyStroke.getModifiers()), actionMapKey); } void replaceOn(JComponent c) { InputMap im = c.getInputMap(); setParent(im.getParent()); for (KeyStroke key : im.keys()) put(key, im.get(key)); c.setInputMap(JComponent.WHEN_FOCUSED, this); } } abstract static class NotifyingSet<A> extends NotifyingCollection<A> implements Set<A> { Set<A> set; NotifyingSet() {} // for persistence NotifyingSet(Set<A> set) { super(set); this.set = set; } } // has equals semantics so passing it to onChange() etc is idempotent static class ChangeTrigger implements Runnable , IFieldsToList{ ChangeTriggerable target; ChangeTrigger() {} ChangeTrigger(ChangeTriggerable target) { this.target = target;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + target + ")"; } public boolean equals(Object o) { if (!(o instanceof ChangeTrigger)) return false; ChangeTrigger __1 = (ChangeTrigger) o; return eq(target, __1.target); } public int hashCode() { int h = -1239972920; h = boostHashCombine(h, _hashCode(target)); return h; } public Object[] _fieldsToList() { return new Object[] {target}; } public void run() { try { { if (target != null) target.change(); } } catch (Exception __e) { throw rethrow(__e); } } } static class TimeoutException extends RuntimeException implements IFieldsToList{ double timeoutSeconds; Object function; Thread thread; TimeoutException() {} TimeoutException(double timeoutSeconds, Object function, Thread thread) { this.thread = thread; this.function = function; this.timeoutSeconds = timeoutSeconds;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + timeoutSeconds + ", " + function + ", " + thread + ")"; }public Object[] _fieldsToList() { return new Object[] {timeoutSeconds, function, thread}; } @Override public String getMessage() { return "Timeout after " + iceil(timeoutSeconds) + "s by " + shorten_str(function); } } static class Stringable implements IFieldsToList{ IF0<?> f; Stringable() {} Stringable(IF0<?> f) { this.f = f;}public Object[] _fieldsToList() { return new Object[] {f}; } public String toString() { return str(f == null ? null : f.get()); } } abstract static class NotifyingCollection<E> extends AbstractCollection<E> implements Collection<E> { Collection<E> c; // Backing Collection NotifyingCollection() {} // for persistence NotifyingCollection(Collection<E> c) { this.c = c;} public int size() { synchronized (this) {return c.size();} } public boolean isEmpty() { synchronized (this) {return c.isEmpty();} } public boolean contains(Object o) { synchronized (this) {return c.contains(o);} } public Object[] toArray() { synchronized (this) {return c.toArray();} } public <T> T[] toArray(T[] a) { synchronized (this) {return c.toArray(a);} } public Iterator<E> iterator() { return c.iterator(); // caller should not use Iterator.remove() } public boolean add(E e) { synchronized (this) { beforeChange(); if (!c.add(e)) return false; } change(); return true; } public boolean remove(Object o) { synchronized (this) { beforeChange(); if (!c.remove(o)) return false;} change(); return true; } public boolean containsAll(Collection<?> coll) { synchronized (this) {return c.containsAll(coll);} } public boolean addAll(Collection<? extends E> coll) { synchronized (this) { beforeChange(); if (!c.addAll(coll)) return false;} change(); return true; } public boolean removeAll(Collection<?> coll) { synchronized (this) { beforeChange(); if (!c.removeAll(coll)) return false;} change(); return true; } public boolean retainAll(Collection<?> coll) { synchronized (this) { beforeChange(); if (!c.retainAll(coll)) return false;} change(); return true; } public void clear() { synchronized (this) { beforeChange(); c.clear();} change(); } public String toString() { synchronized (this) {return c.toString();} } /*public void forEach(Consumer<? super E> consumer) { synchronized (this) {c.forEach(consumer);} }*/ /*public boolean removeIf(Predicate<? super E> filter) { synchronized (this) {return c.removeIf(filter);} }*/ @Override public Spliterator<E> spliterator() { return c.spliterator(); // Must be manually synched by user! } /*public Stream<E> stream() { return c.stream(); // Must be manually synched by user! }*/ /*public Stream<E> parallelStream() { return c.parallelStream(); // Must be manually synched by user! }*/ void beforeChange() {} abstract void change(); Collection<E> unwrap() { return c; } } abstract static class CompactAbstractMap<K, V> implements Map<K, V> { public int size() { return entrySet().size(); } public boolean isEmpty() { return size() == 0; } public boolean containsValue(Object value) { Iterator<Entry<K, V>> i = entrySet().iterator(); if (value == null) { while (i.hasNext()) { Entry<K, V> e = i.next(); if (e.getValue() == null) return true; } } else { while (i.hasNext()) { Entry<K, V> e = i.next(); if (value.equals(e.getValue())) return true; } } return false; } public boolean containsKey(Object key) { Iterator<Entry<K, V>> i = entrySet().iterator(); if (key == null) { while (i.hasNext()) { Entry<K, V> e = i.next(); if (e.getKey() == null) return true; } } else { while (i.hasNext()) { Entry<K, V> e = i.next(); if (key.equals(e.getKey())) return true; } } return false; } public V get(Object key) { Iterator<Entry<K, V>> i = entrySet().iterator(); if (key == null) { while (i.hasNext()) { Entry<K, V> e = i.next(); if (e.getKey() == null) return e.getValue(); } } else { while (i.hasNext()) { Entry<K, V> e = i.next(); if (key.equals(e.getKey())) return e.getValue(); } } return null; } public V put(K key, V value) { throw new UnsupportedOperationException(); } public V remove(Object key) { Iterator<Entry<K, V>> i = entrySet().iterator(); Entry<K, V> correctEntry = null; if (key == null) { while (correctEntry == null && i.hasNext()) { Entry<K, V> e = i.next(); if (e.getKey() == null) correctEntry = e; } } else { while (correctEntry == null && i.hasNext()) { Entry<K, V> e = i.next(); if (key.equals(e.getKey())) correctEntry = e; } } V oldValue = null; if (correctEntry != null) { oldValue = correctEntry.getValue(); i.remove(); } return oldValue; } public void putAll(Map<? extends K, ? extends V> m) { for (Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } public void clear() { entrySet().clear(); } public Set<K> keySet() { return new AbstractSet<K>() { public Iterator<K> iterator() { return new Iterator<K>() { private Iterator<Entry<K, V>> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public K next() { return i.next().getKey(); } public void remove() { i.remove(); } }; } public int size() { return CompactAbstractMap.this.size(); } public boolean isEmpty() { return CompactAbstractMap.this.isEmpty(); } public void clear() { CompactAbstractMap.this.clear(); } public boolean contains(Object k) { return CompactAbstractMap.this.containsKey(k); } }; } public Collection<V> values() { return new AbstractCollection<V>() { public Iterator<V> iterator() { return new Iterator<V>() { private Iterator<Entry<K, V>> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public V next() { return i.next().getValue(); } public void remove() { i.remove(); } }; } public int size() { return CompactAbstractMap.this.size(); } public boolean isEmpty() { return CompactAbstractMap.this.isEmpty(); } public void clear() { CompactAbstractMap.this.clear(); } public boolean contains(Object v) { return CompactAbstractMap.this.containsValue(v); } }; } public abstract Set<Entry<K, V>> entrySet(); public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map<?, ?> m = (Map<?, ?>) o; if (m.size() != size()) return false; try { for (Entry<K, V> e : entrySet()) { K key = e.getKey(); V value = e.getValue(); if (value == null) { if (!(m.get(key) == null && m.containsKey(key))) return false; } else { if (!value.equals(m.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; } public int hashCode() { int h = 0; for (Entry<K, V> entry : entrySet()) h += entry.hashCode(); return h; } public String toString() { Iterator<Entry<K, V>> i = entrySet().iterator(); if (!i.hasNext()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); for (; ; ) { Entry<K, V> e = i.next(); K key = e.getKey(); V value = e.getValue(); sb.append(key == this ? "(this Map)" : key); sb.append('='); sb.append(value == this ? "(this Map)" : value); if (!i.hasNext()) return sb.append('}').toString(); sb.append(',').append(' '); } } protected Object clone() throws CloneNotSupportedException { CompactAbstractMap<?, ?> result = (CompactAbstractMap<?, ?>) super.clone(); return result; } public static class SimpleEntry<K, V> implements Entry<K, V>, java.io.Serializable { @java.io.Serial private static final long serialVersionUID = -8499721149061103585L; @SuppressWarnings("serial") private final K key; @SuppressWarnings("serial") private V value; public SimpleEntry(K key, V value) { this.key = key; this.value = value; } public SimpleEntry(Entry<? extends K, ? extends V> entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Entry<?, ?> e = (Entry<?, ?>) o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; } } public static class SimpleImmutableEntry<K, V> implements Entry<K, V>, java.io.Serializable { @java.io.Serial private static final long serialVersionUID = 7138329143949025153L; @SuppressWarnings("serial") private final K key; @SuppressWarnings("serial") private final V value; public SimpleImmutableEntry(K key, V value) { this.key = key; this.value = value; } public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { throw new UnsupportedOperationException(); } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Entry<?, ?> e = (Entry<?, ?>) o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; } } } /* * #! * 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<A> extends java.util.AbstractSet<A> { 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<A> c) { this(c.size()); addAll(c); } @Override public Iterator<A> iterator() { return new CompactHashIterator<A>(); } @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> 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<T> implements Iterator<T> { 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 Best<A> { A best; double score; boolean verboseNewBest, replaceIfSameScore; transient Object onChange; transient Object stringifier; // func(A) -> S synchronized boolean isNewBest(double score) { return best == null || !isNaN(score) && (replaceIfSameScore ? score >= this.score : score > this.score); } synchronized double bestScore() { return best == null ? minusInfinity() : score; } double score() { return bestScore(); } double getScore() { return bestScore(); } synchronized float floatScoreOr(float defaultValue) { return best == null ? defaultValue : (float) score; } boolean put(Pair<? extends A, Double> p) { return p != null && put(p.a, p.b); } boolean put(Best<? extends A> b) { return b != null && put(b.get(), b.score); } boolean put(A a, double score) { ping(); boolean change = false; if (a != null) synchronized(this) { if (isNewBest(score)) { best = a; this.score = score; change = true; } } if (change) { if (verboseNewBest) print("New best! " + this); pcallF(onChange); } return change; } synchronized A get() { return best; } synchronized boolean has() { return best != null; } synchronized Pair<A, Double> pair() { return main.pair(best, bestScore()); } synchronized Scored<A> scored() { return best == null ? null : new Scored<A>(best, bestScore()); } synchronized A getIfScoreAbove(double x) { return score() >= x ? best : null; } public String toString() { return "Score " + formatDouble_significant2(score, 4) + ": " + callStringifier(stringifier, best); } boolean putAndPrintIfNewBest(A a, double score) { if (!put(a, score)) return false; { print(this); return true; } } synchronized void clear() { best = null; score = 0; } } static class LetterLayout implements LayoutManager { private String[] lines; private Map<String, Component> map = new TreeMap(); private Map<Component, String> 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 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 JPanel jrightalignedline(Component... components) { return jrightAlignedLine(components); } static JPanel jrightalignedline(List<Component> components) { return jrightAlignedLine(components); } 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 GlobalID aGlobalIDObjUnlessLoading() { return dynamicObjectIsLoading() ? null : aGlobalIDObj(); } static Border getBorder(final JComponent c) { return c == null ? null : swing(new F0<Border>() { public Border get() { try { return c.getBorder(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getBorder();"; }}); } static <A> float getScore(Scored<A> s) { return s == null ? 0 : s.score; } static String callStringifier(Object stringifier, Object o) { return stringifier != null ? str(callF(stringifier, o)) : str(o); } static <A> Value<A> value(A a) { return new Value<A>(a); } static double fracNonNeg(double d) { return frac_nonNeg(d); } static double twoPi() { return Math.PI*2; } static String plusPrefixUnlessMinus(String s) { return startsWith(s, "-") ? s : "+" + s; } static <A, B> boolean isA(Either<A, B> e) { return eitherIsA(e); } 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> A find(Collection<A> c, Object... data) { for (A x : c) if (checkFields(x, data)) return x; return null; } static JComponent unwrap(JComponent c) { return c instanceof JScrollPane ? unwrapScrollPane((JScrollPane) c) : c; } static boolean isNaN(double d) { return Double.isNaN(d); } static boolean isNaN(float f) { return Float.isNaN(f); } static double minusInfinity() { return negativeInfinity(); } static <A> Scored<A> scored(A a, float score) { return new Scored(a, score); } static <A> Scored<A> scored(A a, double score) { return new Scored(a, (float) score); } static <A> Scored<A> scored(A a, Scored scored) { return new Scored(a, getScore(scored)); } static <A> Scored<A> scored(double score, A a) { return scored(a, score); } static JPanel withBorder(javax.swing.border.Border border, final Component c) { return swing(new F0<JPanel>() { public JPanel get() { try { JPanel p = new JPanel(new BorderLayout()); p.setBorder(border); 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(border);\r\n p.add..."; }}); } static int getMinimumHeight(final Component c) { return getMinimumSize(c).height; } 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 String form(Object contents, Object... params) { return hform(contents, params); } static JPanel centerAndSouth(final Component c, final Component s) { return swing(new F0<JPanel>() { 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<JPanel>() { 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 JPanel jrightAlignedLine(final Component... components) { return swing(new F0<RightAlignedLine>() { public RightAlignedLine get() { try { return new RightAlignedLine(components); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return RightAlignedLine(components);"; }}); } static JPanel jrightAlignedLine(List<? extends Component> components) { return jrightAlignedLine(asArray(Component.class, components)); } static JCheckBox jLiveValueCheckBox(String text, final SimpleLiveValue<Boolean> lv) { final JCheckBox cb = jCheckBox(text); bindCheckBoxToLiveValue(cb, lv); return cb; } // TODO: use weak reference to live value - or make each of them only once static SimpleLiveValue dm_fieldLiveValue(String fieldName) { return dm_fieldLiveValue(dm_current_mandatory(), fieldName); } static SimpleLiveValue dm_fieldLiveValue(DynModule module, 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 SimpleLiveValue dm_fieldLiveValue(String fieldName, DynModule module) { return dm_fieldLiveValue(module, fieldName); } static GlobalID aGlobalIDObj() { return asGlobalID(randomID(16)); } static GlobalID aGlobalIDObj(Random random) { return asGlobalID(randomID(random, 16)); } static double frac_nonNeg(double d) { return mod(d, 1); } static <A, B> boolean eitherIsA(Either<A, B> e) { return e != null && e.isA(); } static JComponent unwrapScrollPane(JScrollPane sp) { return sp == null ? null : (JComponent) sp.getViewport().getView(); } static double negativeInfinity() { return Double.NEGATIVE_INFINITY; } 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, Object onChange) { JCheckBox cb = jCheckBox(text, checked); 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 JCheckBox jCheckBox(boolean checked, String text, IVF1<Boolean> onChangeAndNow) { var cb = jCheckBox(text, checked); onChangeAndNow(cb, new Runnable() { public void run() { try { onChangeAndNow.get(isChecked(cb)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "onChangeAndNow.get(isChecked(cb));"; }}); return cb; } static <A extends JCheckBox> A bindCheckBoxToLiveValue(final A cb, final SimpleLiveValue<Boolean> 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 GlobalID asGlobalID(String id) { return id == null ? null : new GlobalID(id); } static class Value<A> implements IF0<A> , IFieldsToList{ A value; Value() {} Value(A value) { this.value = value;} public boolean equals(Object o) { if (!(o instanceof Value)) return false; Value __1 = (Value) o; return eq(value, __1.value); } public int hashCode() { int h = 82420049; h = boostHashCombine(h, _hashCode(value)); return h; } public Object[] _fieldsToList() { return new Object[] {value}; } public A get() { return value; } public String toString() { return str(get()); } } static class Dyn_FieldWatcher implements AutoCloseable { DynModule module; String field; Object value; Runnable action; Runnable changeListener = new Runnable() { public void run() { try { check(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "check();"; }}; VF1<String> fieldChangeListener = new VF1<String>() { 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();"; }}; transient IF1<Object, Object> cloneValue; Object cloneValue(Object o) { return cloneValue != null ? cloneValue.get(o) : cloneValue_base(o); } final Object cloneValue_fallback(IF1<Object, Object> _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(changeListener); module.onFieldChange(fieldChangeListener); } void check() { Object newValue = cloneValue(get(module, field)); if (eq(value, newValue)) return; value = newValue; dm_q(module, action); } public void close() { try { module.removeChangeListener(changeListener); module.removeFieldChangeListener(fieldChangeListener); } catch (Exception __e) { throw rethrow(__e); } } } static class RightAlignedLine extends JPanel { RightAlignedLine(Component... components) { setLayout(LetterLayout.rightAlignedRow()); for (Component component : components) if (component != null) add(component); } public void add(String text) { add(new JLabel(text)); } } 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> A dm_q(IF0<A> f) { return dm_evalInQ(if0ToF0(f)); } static void dm_inQ(Runnable r) { dm_q().add(r); } static void dm_inQ(DynModule mod, Runnable r) { dm_q(mod, r); } static <A> A dm_evalInQ(F0<A> f) { return dm_evalInQ(dm_current_mandatory(), f); } static <A> A dm_evalInQ(IF0<A> f) { return dm_evalInQ(dm_current_mandatory(), if0ToF0(f)); } static <A> A dm_evalInQ(DynModule module, F0<A> f) { return evalInQ(module.q(), f); } static <A> F0<A> if0ToF0(IF0<A> f) { return f == null ? null : new F0<A>() { public A get() { try { return f.get(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return f.get();"; }}; } static <A> A evalInQ(Q q, final F0<A> f) { if (isInQ(q)) return callF(f); final Var<Either<A, Throwable>> 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 <A, B> Either<A, B> eitherA(A a) { return new Either(1, a); } static <A, B> Either<A, B> eitherB(B b) { return new Either(2, b); } static <A> A returnOrThrow_either(Either<A, Throwable> e) { if (isEitherB(e)) throw rethrow(e.b()); return eitherAOpt(e); } static <A> A waitForVarToBeNotNull(Var<A> v) { try { synchronized(v) { while (!v.has()) v.wait(); return v.get(); } } catch (Exception __e) { throw rethrow(__e); } } static <A, B> boolean isEitherB(Either<A, B> e) { return eitherIsB(e); } static <A, B> A eitherAOpt(Either<A, B> e) { return e != null && e.isA() ? e.a() : null; } static <A, B> boolean eitherIsB(Either<A, B> e) { return e != null && e.isB(); } }