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.*; import java.time.Duration; import java.lang.invoke.VarHandle; import java.lang.invoke.MethodHandles; // TODO: different sorting methods, e.g. "widest SSIs" // Note: Blur is slow. e.g. 48ms for 2 screens // Posterization is fast, e.g. 8ms for 2 screens // Region making speed depends on how many regions there are. // TODO: Make regions incrementally by starting at random pixels import java.awt.geom.*; import javax.imageio.metadata.*; import javax.imageio.stream.*; import java.text.SimpleDateFormat; import java.nio.charset.Charset; import static x30_pkg.x30_util.DynamicObject; import javax.swing.Icon; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.*; import java.awt.dnd.*; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.DataFlavor; import javax.swing.event.AncestorListener; import javax.swing.event.AncestorEvent; import javax.swing.Timer; import java.text.*; import java.text.NumberFormat; import javax.swing.border.*; import javax.swing.undo.UndoManager; import java.util.TimeZone; import java.awt.datatransfer.UnsupportedFlavorException; class main { static class G22DataWrangler extends Stages2 implements IHasChangeListeners { transient Set onChange; public G22DataWrangler onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public G22DataWrangler removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void change() { if (onChange != null) for (var listener : onChange) pcallF_typed(listener); } final public G22DataWrangler setInputImage(BufferedImage inputImage){ return inputImage(inputImage); } public G22DataWrangler inputImage(BufferedImage inputImage) { this.inputImage = inputImage; return this; } final public BufferedImage getInputImage(){ return inputImage(); } public BufferedImage inputImage() { return inputImage; } BufferedImage inputImage; // try to re-use anything that is identical final public G22DataWrangler setStealingFrom(G22DataWrangler stealingFrom){ return stealingFrom(stealingFrom); } public G22DataWrangler stealingFrom(G22DataWrangler stealingFrom) { this.stealingFrom = stealingFrom; return this; } final public G22DataWrangler getStealingFrom(){ return stealingFrom(); } public G22DataWrangler stealingFrom() { return stealingFrom; } G22DataWrangler stealingFrom; final public G22DataWrangler setWithDiagonals(boolean withDiagonals){ return withDiagonals(withDiagonals); } public G22DataWrangler withDiagonals(boolean withDiagonals) { this.withDiagonals = withDiagonals; return this; } final public boolean getWithDiagonals(){ return withDiagonals(); } public boolean withDiagonals() { return withDiagonals; } boolean withDiagonals = true; public transient FieldVar varBlur_cache; public FieldVar varBlur() { if (varBlur_cache == null) varBlur_cache = varBlur_load(); return varBlur_cache;} public FieldVar varBlur_load() { return new FieldVar(this, "blur", () -> blur(), blur -> blur(blur)); } final public G22DataWrangler setBlur(int blur){ return blur(blur); } public G22DataWrangler blur(int blur) { if (!eq(this.blur, blur)) { this.blur = blur; change(); } return this; } final public int getBlur(){ return blur(); } public int blur() { return blur; } int blur = 0; // in pixels public transient FieldVar varColorsPerChannel_cache; public FieldVar varColorsPerChannel() { if (varColorsPerChannel_cache == null) varColorsPerChannel_cache = varColorsPerChannel_load(); return varColorsPerChannel_cache;} public FieldVar varColorsPerChannel_load() { return new FieldVar(this, "colorsPerChannel", () -> colorsPerChannel(), colorsPerChannel -> colorsPerChannel(colorsPerChannel)); } final public G22DataWrangler setColorsPerChannel(int colorsPerChannel){ return colorsPerChannel(colorsPerChannel); } public G22DataWrangler colorsPerChannel(int colorsPerChannel) { if (!eq(this.colorsPerChannel, colorsPerChannel)) { this.colorsPerChannel = colorsPerChannel; change(); } return this; } final public int getColorsPerChannel(){ return colorsPerChannel(); } public int colorsPerChannel() { return colorsPerChannel; } int colorsPerChannel = 2; public transient FieldVar varDrift_cache; public FieldVar varDrift() { if (varDrift_cache == null) varDrift_cache = varDrift_load(); return varDrift_cache;} public FieldVar varDrift_load() { return new FieldVar(this, "drift", () -> drift(), drift -> drift(drift)); } final public G22DataWrangler setDrift(float drift){ return drift(drift); } public G22DataWrangler drift(float drift) { if (!eq(this.drift, drift)) { this.drift = drift; change(); } return this; } final public float getDrift(){ return drift(); } public float drift() { return drift; } float drift; // brightness drift for gradients (-1 to 1) public transient FieldVar varAntiDrift_cache; public FieldVar varAntiDrift() { if (varAntiDrift_cache == null) varAntiDrift_cache = varAntiDrift_load(); return varAntiDrift_cache;} public FieldVar varAntiDrift_load() { return new FieldVar(this, "antiDrift", () -> antiDrift(), antiDrift -> antiDrift(antiDrift)); } final public G22DataWrangler setAntiDrift(float antiDrift){ return antiDrift(antiDrift); } public G22DataWrangler antiDrift(float antiDrift) { if (!eq(this.antiDrift, antiDrift)) { this.antiDrift = antiDrift; change(); } return this; } final public float getAntiDrift(){ return antiDrift(); } public float antiDrift() { return antiDrift; } float antiDrift; // brightness drift after rposterization gradients (-1 to 1) // kilobytes per compressed image (pessimistic estimate // counting 2 bytes for each int) final public G22DataWrangler setKilobytes(TargetAndActual < Double > kilobytes){ return kilobytes(kilobytes); } public G22DataWrangler kilobytes(TargetAndActual < Double > kilobytes) { this.kilobytes = kilobytes; return this; } final public TargetAndActual < Double > getKilobytes(){ return kilobytes(); } public TargetAndActual < Double > kilobytes() { return kilobytes; } TargetAndActual < Double > kilobytes = new TargetAndActual<>(250.0); final public G22DataWrangler setCoveredPixelsPercentage(TargetAndActual coveredPixelsPercentage){ return coveredPixelsPercentage(coveredPixelsPercentage); } public G22DataWrangler coveredPixelsPercentage(TargetAndActual coveredPixelsPercentage) { this.coveredPixelsPercentage = coveredPixelsPercentage; return this; } final public TargetAndActual getCoveredPixelsPercentage(){ return coveredPixelsPercentage(); } public TargetAndActual coveredPixelsPercentage() { return coveredPixelsPercentage; } TargetAndActual coveredPixelsPercentage = new TargetAndActual(); final public G22DataWrangler setDetailLevel(TargetAndActual detailLevel){ return detailLevel(detailLevel); } public G22DataWrangler detailLevel(TargetAndActual detailLevel) { this.detailLevel = detailLevel; return this; } final public TargetAndActual getDetailLevel(){ return detailLevel(); } public TargetAndActual detailLevel() { return detailLevel; } TargetAndActual detailLevel = new TargetAndActual(); final public G22DataWrangler setVectorize(boolean vectorize){ return vectorize(vectorize); } public G22DataWrangler vectorize(boolean vectorize) { this.vectorize = vectorize; return this; } final public boolean getVectorize(){ return vectorize(); } public boolean vectorize() { return vectorize; } boolean vectorize = true; final public G22DataWrangler setAllowPartialSSIs(boolean allowPartialSSIs){ return allowPartialSSIs(allowPartialSSIs); } public G22DataWrangler allowPartialSSIs(boolean allowPartialSSIs) { this.allowPartialSSIs = allowPartialSSIs; return this; } final public boolean getAllowPartialSSIs(){ return allowPartialSSIs(); } public boolean allowPartialSSIs() { return allowPartialSSIs; } boolean allowPartialSSIs = true; final public boolean getBlackAndWhiteMode(){ return blackAndWhiteMode(); } public boolean blackAndWhiteMode() { return blackAndWhiteMode; } boolean blackAndWhiteMode = false; final public G22DataWrangler setSortMode(SortMode sortMode){ return sortMode(sortMode); } public G22DataWrangler sortMode(SortMode sortMode) { this.sortMode = sortMode; return this; } final public SortMode getSortMode(){ return sortMode(); } public SortMode sortMode() { return sortMode; } SortMode sortMode = SortMode.compressibility; enum SortMode { compressibility, pixels } BufferedImage blurredImage; int maxLines, maxInts; List currentSSIs; // ssis at current stage List initialSSIs; AbstractSSIList sortedSSIs, cutSSIs, vectorizedSSIs, cutVectorizedSSIs; CutListToBudget cutter; // We store the posterized image as Hi15 Hi15Image posterizedImage; // Region maker + regions FastRegions_Hi15Image regionMaker; List> regions, regionsBySize; // Constructors G22DataWrangler() {} G22DataWrangler(BufferedImage inputImage) { this.inputImage = inputImage;} G22DataWrangler(MakesBufferedImage inputImage) { this.inputImage = inputImage.getBufferedImage(); } // Methods SinglePixelPosterizer posterizer() { return new SinglePixelPosterizer(colorsPerChannel()).drift(drift).antiDrift(antiDrift); } G22DataWrangler kb(TargetAndActual kb) { return kilobytes(kb); } TargetAndActual kb() { return kilobytes; } WidthAndHeight resolution() { return imageSize(inputImage); } double detailDivisor() { return areaRoot(inputImage); } int colors() { return blackAndWhiteMode ? 2 : cubed(colorsPerChannel()); } transient FieldVar varColors_cache; FieldVar varColors() { if (varColors_cache == null) varColors_cache = varColors_load(); return varColors_cache;} FieldVar varColors_load() { return new FieldVar(this, "colors", () -> colors(), __1 -> colors(__1)); } // choose number of colors for posterized image G22DataWrangler colors(int colors) { int perChannel = iceil(cbrt(colors)); blackAndWhiteMode = colors == 2; return colorsPerChannel(perChannel); } { stage("Steal", new Runnable() { public void run() { try { if (stealingFrom != null) { if (stealingFrom.inputImage != inputImage) { stealingFrom = null; return; } // Avoid keeping old data wranglers around through a stealingFrom chain stealingFrom.stealingFrom = null; } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (stealingFrom != null) {\r\n if (stealingFrom.inputImage != inputImage)..."; }}); } { stage("Blur", new Runnable() { public void run() { try { if (stealingFrom != null) if (stealingFrom.blur == blur) { blurredImage = stealingFrom.blurredImage; return; } else stealingFrom = null; blurredImage = blurBufferedImage(blur, inputImage); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (stealingFrom != null)\r\n if (stealingFrom.blur == blur)\r\n { bl..."; }}); } Stage posterizeStage = stage("Posterize", new Runnable() { public void run() { try { if (stealingFrom != null) if (eq(stealingFrom.posterizer(), posterizer())) { posterizedImage = stealingFrom.posterizedImage; return; } else stealingFrom = null; var image = blurredImage; if (blackAndWhiteMode) image = new BWImage(image).getBufferedImage(); //image = transformBufferedImageWithSimplePixelOp(image, rgb -> 0xFFFFFF, ...); posterizedImage = posterizeBufferedImageToHi15(image, posterizer()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (stealingFrom != null)\r\n if (eq(stealingFrom.posterizer(), posterizer..."; }}); Stage prepareRegionsStage = stage("Prepare regions", new Runnable() { public void run() { try { if (stealingFrom != null) if (stealingFrom.withDiagonals == withDiagonals) { regionMaker = stealingFrom.regionMaker; regions = stealingFrom.regions; return; } else stealingFrom = null; regionMaker = new FastRegions_Hi15Image(posterizedImage); regionMaker.withDiagonals(withDiagonals); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (stealingFrom != null)\r\n if (stealingFrom.withDiagonals == withDiagon..."; }}); Stage regionsStage = stage("Regions", new Runnable() { public void run() { try { regions = regionMaker.get(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "regions = regionMaker.get();"; }}); Stage biggestRegionsStage = stage("Sort regions", new Runnable() { public void run() { try { regions = regionsBySize = biggestRegionsFirst(regions); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "regions = regionsBySize = biggestRegionsFirst(regions);"; }}); { stage("SSIs", new Runnable() { public void run() { try { initialSSIs = new ArrayList(); for (var region : regions) initialSSIs.addAll(new G22_RegionToSSIs_v2(region).withDiagonals (withDiagonals).get()); currentSSIs = initialSSIs; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "initialSSIs = new ArrayList();\r\n for (var region : regions)\r\n initial..."; }}); } int initialSSILines() { return totalSSILines(initialSSIs); } { stage("Vector-Optimize", new Runnable() { public void run() { try { currentSSIs = vectorizedSSIs = vectorize ? new VectorOptimizedSSIList(currentSSIs) : new GeneralSSIList(currentSSIs); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "currentSSIs = vectorizedSSIs = vectorize\r\n ? new VectorOptimizedSSIList(..."; }}); } { stage("Sort SSIs", new Runnable() { public void run() { try { if (sortMode == SortMode.compressibility) sortedSSIs = new GeneralSSIList(sortedDesc(currentSSIs, (a, b) -> { int x = cmp(a.compressibility(), b.compressibility()); if (x != 0) return x; return cmp(a.numberOfPixels(), b.numberOfPixels()); })); else if (sortMode == SortMode.pixels) sortedSSIs = new GeneralSSIList(biggestSSIsFirst(currentSSIs)); else throw fail("Unknown sort mode"); currentSSIs = sortedSSIs; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (sortMode == SortMode.compressibility)\r\n sortedSSIs = new GeneralSSIL..."; }}); } { stage("Cut SSI List by detail level", new Runnable() { public void run() { try { maxLines = !detailLevel.hasTarget() ? Integer.MAX_VALUE : iround(detailDivisor()*detailLevel.target()); currentSSIs = cutSSIs = new GeneralSSIList(takeFirstNSSILines(maxLines, currentSSIs)); detailLevel.set(l(cutSSIs)/detailDivisor()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "maxLines = !detailLevel.hasTarget() ? Integer.MAX_VALUE\r\n : iround(detai..."; }}); } { stage("Cut Vector-Optimized SSIs by file size", new Runnable() { public void run() { try { maxInts = !kilobytes.hasTarget() ? Integer.MAX_VALUE : iround(kilobytes.target()*512); // assuming 16 bit ints cutter = new CutListToBudget(ssi -> (double) ssi.sizeInInts(), maxInts, (List) currentSSIs); if (allowPartialSSIs) cutter.allowPartial((ssi, budget) -> ssi.reduceToInts(iround(budget))); currentSSIs = cutVectorizedSSIs = new GeneralSSIList(cutter.get()); kilobytes.set(totalSizeInInts(cutVectorizedSSIs)/512.0); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "maxInts = !kilobytes.hasTarget() ? Integer.MAX_VALUE\r\n : iround(kilobyte..."; }}); } List> regions() { stepUntilStage(regionsStage); return regions; } List> regionsBySize() { stepUntilStage(biggestRegionsStage); return regions; } List regionsAsIBinaryImages() { return map(__32 -> regionToIBinaryImage(__32), regions()); } BlurAndPosterizeSettings bnpSettings() { return new BlurAndPosterizeSettings() .blur(blur) .colors(colors()) .colorDrift(new RGB(drift())) .antiDrift(new RGB(antiDrift)); } void importSettings(BlurAndPosterizeSettings bnp) { blur(bnp.blur); colors(bnp.colors); drift(bnp.colorDrift.brightness()); antiDrift(bnp.antiDrift.brightness()); } FastRegions_Hi15Image preparedRegionMaker() { stepUntilStage(prepareRegionsStage); return regionMaker; } FastRegions_Hi15Image regionMaker() { stepUntilStage(regionsStage); return regionMaker; } public String toString() { return commaCombine( shortClassName(this), imageSize(inputImage), "completed stages: " + or2(joinWithComma(completedStages()), "-") ); } Hi15Image posterizedImage() { stepUntilStage(posterizeStage); return posterizedImage; } } static Set createOrAddToSyncLinkedHashSet(Set set, A a) { if (set == null) set = syncLinkedHashSet(); set.add(a); return set; } static void remove(List l, int i) { if (l != null && i >= 0 && i < l(l)) l.remove(i); } static void remove(Collection l, A a) { if (l != null) l.remove(a); } static B remove(Map map, Object a) { return map == null ? null : map.remove(a); } static void remove(BitSet bs, int i) { bs.clear(i); } static A pcallF_typed(F0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { pcallFail(__e); } return null; } static B pcallF_typed(F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { pcallFail(__e); } return null; } static void pcallF_typed(VF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { pcallFail(__e); } } static void pcallF_typed(IVF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { pcallFail(__e); } } static void pcallF_typed(IVF2 f, A a, B b) { try { { if (f != null) f.get(a, b); } } catch (Throwable __e) { pcallFail(__e); } } static Object pcallF_typed(Runnable r) { try { { if (r != null) r.run(); } } catch (Throwable __e) { pcallFail(__e); } return null; } static A pcallF_typed(IF0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { pcallFail(__e); } return null; } static B pcallF_typed(IF1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { pcallFail(__e); } return null; } // externally supplied image static BufferedImage inputImage_image; static BufferedImage inputImage() { if (inputImage_image != null) return inputImage_image; final ImageChooser ic = new ImageChooser(); if (!showTitledForm_blocking("Please Choose Image To Work On", "", ic)) throw fail("No input image selected"); return ic.getImage(); } 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 void change() { //mainConcepts.allChanged(); // safe version for now cause function is sometimes included unnecessarily (e.g. by EGDiff) callOpt(getOptMC("mainConcepts"), "allChanged"); } static WidthAndHeight imageSize(BufferedImage img) { return img == null ? null : widthAndHeight(img.getWidth(), img.getHeight()); } static WidthAndHeight imageSize(WidthAndHeight img) { return img == null ? null : widthAndHeight(img.getWidth(), img.getHeight()); } static double areaRoot(Rect r) { return r == null ? 0 : sqrt(area(r)); } static double areaRoot(DoubleRect r) { return r == null ? 0 : sqrt(area(r)); } static double areaRoot(WidthAndHeight img) { return img == null ? 0 : sqrt(area(img)); } static double areaRoot(BufferedImage img) { return img == null ? 0 : sqrt(area(img)); } static int cubed(int i) { return i*i*i; } static double cubed(double d) { return pow(d, 3); } static int iceil(double d) { return (int) Math.ceil(d); } static double cbrt(double x) { return Math.cbrt(x); } 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 BufferedImage blurBufferedImage(BufferedImage img) { return blurFilterBufferedImage(img); } static BufferedImage blurBufferedImage(int hradius, BufferedImage img) { return blurBufferedImage(hradius, hradius, img); } static BufferedImage blurBufferedImage(int hradius, int vradius, BufferedImage img) { return blurFilterBufferedImage(hradius, vradius, img); } static BufferedImage blurBufferedImage(BufferedImage img, int hradius) { return blurBufferedImage(img, hradius, hradius); } static BufferedImage blurBufferedImage(BufferedImage img, int hradius, int vradius) { return blurFilterBufferedImage(hradius, vradius, img); } static Hi15Image posterizeBufferedImageToHi15(int brightnessLevels, MakesBufferedImage img) { return posterizeBufferedImageToHi15(brightnessLevels, toBufferedImage(img)); } static Hi15Image posterizeBufferedImageToHi15(int brightnessLevels, BufferedImage img) { PosterizeBufferedImageToHi15 x = new PosterizeBufferedImageToHi15(brightnessLevels, img); return x.get(); } static Hi15Image posterizeBufferedImageToHi15(IPosterizer posterizer, MakesBufferedImage img) { return posterizeBufferedImageToHi15(posterizer, toBufferedImage(img)); } static Hi15Image posterizeBufferedImageToHi15(IPosterizer posterizer, BufferedImage img) { PosterizeBufferedImageToHi15 x = new PosterizeBufferedImageToHi15(posterizer, img); return x.get(); } static Hi15Image posterizeBufferedImageToHi15(MakesBufferedImage img, IPosterizer posterizer) { return posterizeBufferedImageToHi15(toBufferedImage(img), posterizer); } static Hi15Image posterizeBufferedImageToHi15(BufferedImage img, IPosterizer posterizer) { return posterizeBufferedImageToHi15(posterizer, img); } static List> biggestRegionsFirst(Collection> l) { return sortByCalculatedFieldDesc(l, r -> r.numberOfPixels()); } static int totalSSILines(Iterable l) { int sum = 0; for (var ssi : unnullForIteration(l)) if (ssi != null) sum += ssi.height(); return sum; } static List sortedDesc(Collection c, Object comparator) { List l = cloneList(c); sort(l, makeReversedComparator(comparator)); return l; } static List sortedDesc(Collection c, Comparator comparator) { List l = cloneList(c); sort(l, makeReversedComparator(comparator)); return l; } // TODO: should use a reverse comparator to keep sort stable static List sortedDesc(Collection c) { List l = cloneList(c); sort(l); Collections.reverse(l); return l; } static int cmp(Number a, Number b) { return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue()); } static int cmp(double a, double b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(int a, int b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(long a, long b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(Object a, Object b) { if (a == null) return b == null ? 0 : -1; if (b == null) return 1; return ((Comparable) a).compareTo(b); } static List biggestSSIsFirst(Collection l) { return sortByCalculatedFieldDesc(l, __1 -> __1.numberOfPixels()); } 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 int iround(double d) { return (int) Math.round(d); } static int iround(Number n) { return iround(toDouble(n)); } static List takeFirstNSSILines(int maxLines, Iterable l) { if (maxLines == Integer.MAX_VALUE) return asList(l); int sum = 0; List out = new ArrayList(); for (var ssi : unnullForIteration(l)) { if (sum >= maxLines) break; int linesToCopy = min(ssi.height(), maxLines-sum); if (linesToCopy < ssi.height()) break; out.add(ssi); //out.add(linesToCopy == ssi.height() ? ssi : ssi.topPart(linesToCopy)); sum += ssi.height(); } return out; } 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(IMultiMap mm) { return mm == null ? 0 : mm.size(); } static int l(IntRange r) { return r == null ? 0 : r.length(); } static double l(DoubleRange r) { return r == null ? 0 : r.length(); } static int l(IntBuffer b) { return b == null ? 0 : b.size(); } static int l(LongBuffer b) { return b == null ? 0 : b.size(); } static int l(ShortBuffer b) { return b == null ? 0 : b.size(); } static int l(IntSize o) { return o == null ? 0 : o.size(); } static long totalSizeInInts(Iterable l) { long sum = 0; for (var a : unnullForIteration(l)) if (a != null) sum += a.sizeInInts(); return sum; } 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; } // map: func(key, value) -> list element static List map(Map map, Object f) { List x = new ArrayList(); if (map != null) for (Object _e : map.entrySet()) { ping(); Map.Entry e = (Map.Entry) _e; x.add(callF(f, e.getKey(), e.getValue())); } return x; } static List map(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); } static List map(Iterable l, F1 f) { return map(f, l); } static List map(F1 f, Iterable l) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(callF(f, o)); } return x; } static List map(IF1 f, Iterable l) { return map(l, f); } static List map(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) { var it = l.iterator(); if (it.hasNext()) { var pingSource = pingSource(); do { ping(pingSource); x.add(f.get(it.next())); } while (it.hasNext()); } } return x; } static List map(IF1 f, A[] l) { return map(l, f); } static List map(A[] l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map(Map map, IF2 f) { List x = new ArrayList(); if (map != null) for (Map.Entry e : map.entrySet()) { ping(); x.add(f.get(e.getKey(), e.getValue())); } return x; } // new magic alias for mapLL - does it conflict? static List map(IF1 f, A data1, A... moreData) { List x = emptyList(l(moreData)+1); x.add(f.get(data1)); if (moreData != null) for (A o : moreData) { ping(); x.add(f.get(o)); } return x; } static IBinaryImage regionToIBinaryImage(IImageRegion region) { Rect r = region.bounds(); return iBinaryImageFromFunction(r.w, r.h, (x, y) -> !region.contains(r.x+x, r.y+y)); } static String commaCombine(Object... l) { return joinNemptiesWithComma(flattenCollectionsAndArrays(l)); } 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 or2(String a, String b) { return nempty(a) ? a : b; } static String or2(String a, String b, String c) { return or2(or2(a, b), c); } static String joinWithComma(Iterable 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 Set syncLinkedHashSet() { return synchroLinkedHashSet(); } static void pcallFail(Throwable e) { pcallPolicyForThread().handlePcallFail(e); } static void pcallFail(String msg) { pcallFail(new Throwable(msg)); } static boolean showTitledForm_blocking(String title, Object... _parts) { assertNotOnAWTThread(); final Var var = new Var(); onFrameClose(showFormTitled(title, arrayPlus(_parts, new Runnable() { public void run() { try { var.set(true) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "var.set(true)"; }})), new Runnable() { public void run() { try { if (!var.has()) var.set(false) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!var.has()) var.set(false)"; }}); waitUntilVarSet(var); return var.get(); } static String str(Object o) { return o == null ? "null" : o.toString(); } static String str(char[] c) { return c == null ? "null" : new String(c); } static String str(char[] c, int offset, int count) { return new String(c, offset, count); } 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 WidthAndHeight widthAndHeight(BufferedImage image) { return image == null ? null : widthAndHeight(image.getWidth(), image.getHeight()); } static WidthAndHeight widthAndHeight(int w) { return widthAndHeight(w, w); } static WidthAndHeight widthAndHeight(int w, int h) { return new WidthAndHeightFinal(w, h); } static double sqrt(double x) { return Math.sqrt(x); } static int area(Rect r) { return rectArea(r); } static double area(DoubleRect r) { return r == null ? 0 : r.w*r.h; } static int area(WidthAndHeight img) { return img == null ? 0 : img.getWidth()*img.getHeight(); } static int area(BufferedImage img) { return img == null ? 0 : img.getWidth()*img.getHeight(); } static double pow(double a, double b) { return Math.pow(a, b); } static float pow(float a, float b) { return (float) Math.pow(a, b); } static BigInteger pow(BigInteger a, int b) { return a.pow(b); } static void _handleError(Error e) { //call(javax(), '_handleError, e); } static BufferedImage blurFilterBufferedImage(BufferedImage img) { return blurFilterBufferedImage(1, img); } static BufferedImage blurFilterBufferedImage(int hradius, BufferedImage img) { return blurFilterBufferedImage(hradius, hradius, img); } static BufferedImage blurFilterBufferedImage(int hradius, int vradius, BufferedImage img) { if (hradius <= 0 || vradius <= 0) return img; return new BoxBlurFilter(hradius, vradius).get(img); } static BufferedImage toBufferedImage(Object o) { return toBufferedImageOpt(o); } // f: A -> Comparable static List sortByCalculatedFieldDesc(Collection c, final Object f) { return sortByCalculatedFieldDesc_inPlace(cloneList(c), f); } static List sortByCalculatedFieldDesc(Object f, Collection c) { return sortByCalculatedFieldDesc(c, f); } static List sortByCalculatedFieldDesc(Iterable c, IF1 f) { List l = cloneList(c); sort(l, new Comparator() { public int compare(A a, A b) { return stdcompare(f.get(b), f.get(a)); } }); return l; } static List sortByCalculatedFieldDesc(IF1 f, Iterable c) { return sortByCalculatedFieldDesc(c, f); } static String unnullForIteration(String s) { return s == null ? "" : s; } static Collection unnullForIteration(Collection l) { return l == null ? immutableEmptyList() : l; } static List unnullForIteration(List l) { return l == null ? immutableEmptyList() : l; } static byte[] unnullForIteration(byte[] l) { return l == null ? emptyByteArray() : l; } static int[] unnullForIteration(int[] l) { return l == null ? emptyIntArray() : l; } static char[] unnullForIteration(char[] l) { return l == null ? emptyCharArray() : l; } static double[] unnullForIteration(double[] l) { return l == null ? emptyDoubleArray() : l; } static short[] unnullForIteration(short[] l) { return l == null ? emptyShortArray() : l; } static Map unnullForIteration(Map l) { return l == null ? immutableEmptyMap() : l; } static Iterable unnullForIteration(Iterable i) { return i == null ? immutableEmptyList() : i; } static A[] unnullForIteration(A[] a) { return a == null ? (A[]) emptyObjectArray() : a; } static BitSet unnullForIteration(BitSet b) { return b == null ? new BitSet() : b; } static Pt unnullForIteration(Pt p) { return p == null ? new Pt() : p; } //ifclass Symbol static Symbol unnullForIteration(Symbol s) { return s == null ? emptySymbol() : s; } //endif static Pair unnullForIteration(Pair p) { return p != null ? p : new Pair(null, null); } static long unnullForIteration(Long l) { return l == null ? 0L : l; } static ArrayList cloneList(Iterable l) { return l instanceof Collection ? cloneList((Collection) l) : asList(l); } static ArrayList cloneList(Collection l) { if (l == null) return new ArrayList(); synchronized(collectionMutex(l)) { return new ArrayList(l); } } static void sort(T[] a, Comparator c) { if (a != null) Arrays.sort(a, c); } static void sort(T[] a) { if (a != null) Arrays.sort(a); } static void sort(int[] a) { if (a != null) Arrays.sort(a); } static void sort(List a, Comparator c) { if (a != null) Collections.sort(a, c); } static void sort(List a) { if (a != null) Collections.sort(a); } static Comparator makeReversedComparator(Object f) { return new Comparator() { public int compare(Object a, Object b) { return (int) callF(f, b, a); } }; } static Comparator makeReversedComparator(Comparator c) { return (a, b) -> c.compare(b, a); } static RuntimeException asRuntimeException(Throwable t) { if (t instanceof Error) _handleError((Error) t); return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } 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); } // unclear semantics as to whether return null on null static ArrayList asList(A[] a) { return a == null ? new ArrayList() : new ArrayList(Arrays.asList(a)); } static ArrayList asList(char[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (var i : a) l.add(i); return l; } static ArrayList asList(byte[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (var i : a) l.add(i); return l; } static ArrayList asList(int[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (int i : a) l.add(i); return l; } static ArrayList asList(long[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (long i : a) l.add(i); return l; } static ArrayList asList(float[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (float i : a) l.add(i); return l; } static ArrayList asList(double[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (double i : a) l.add(i); return l; } static ArrayList asList(short[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (short i : a) l.add(i); return l; } static ArrayList asList(Iterator it) { ArrayList l = new ArrayList(); if (it != null) while (it.hasNext()) l.add(it.next()); return l; } // disambiguation static ArrayList asList(IterableIterator s) { return asList((Iterator) s); } static ArrayList asList(Iterable s) { if (s instanceof ArrayList) return (ArrayList) s; ArrayList l = new ArrayList(); if (s != null) for (A a : s) l.add(a); return l; } static ArrayList asList(Producer p) { ArrayList l = new ArrayList(); A a; if (p != null) while ((a = p.next()) != null) l.add(a); return l; } static ArrayList asList(Enumeration e) { ArrayList l = new ArrayList(); if (e != null) while (e.hasMoreElements()) l.add(e.nextElement()); return l; } static ArrayList asList(ReverseChain c) { return c == null ? emptyList() : c.toList(); } static List asList(Pair p) { return p == null ? null : ll(p.a, p.b); } 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 > A min(A a, A b) { return cmp(a, b) <= 0 ? a : b; } static int iteratorCount_int_close(Iterator i) { try { int n = 0; if (i != null) while (i.hasNext()) { i.next(); ++n; } if (i instanceof AutoCloseable) ((AutoCloseable) i).close(); return n; } catch (Exception __e) { throw rethrow(__e); } } static ArrayList emptyList() { return new ArrayList(); //ret Collections.emptyList(); } static ArrayList emptyList(int capacity) { return new ArrayList(max(0, capacity)); } // Try to match capacity static ArrayList emptyList(Iterable l) { return l instanceof Collection ? emptyList(((Collection) l).size()) : emptyList(); } static ArrayList emptyList(Object[] l) { return emptyList(l(l)); } // get correct type at once static ArrayList emptyList(Class c) { return new ArrayList(); } // legacy mode //sbool ping_actions_shareable = true; static volatile boolean ping_pauseAll = false; static int ping_sleep = 100; // poll pauseAll flag every 100 static volatile boolean ping_anyActions = false; static Map ping_actions = newWeakHashMap(); static ThreadLocal ping_isCleanUpThread = new ThreadLocal(); // ignore pingSource if not PingV3 static boolean ping(PingSource pingSource) { return ping(); } // 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> callF_cache = newDangerousWeakHashMap(); static A callF(F0 f) { return f == null ? null : f.get(); } static B callF(F1 f, A a) { return f == null ? null : f.get(a); } static A callF(IF0 f) { return f == null ? null : f.get(); } static B callF(IF1 f, A a) { return f == null ? null : f.get(a); } static B callF(A a, IF1 f) { return f == null ? null : f.get(a); } static C callF(IF2 f, A a, B b) { return f == null ? null : f.get(a, b); } static void callF(VF1 f, A a) { if (f != null) f.get(a); } static void callF(A a, IVF1 f) { if (f != null) f.get(a); } static void callF(IVF1 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 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 callF_makeCache(Class c) { ArrayList l = new ArrayList(); Class _c = c; do { for (Method m : _c.getDeclaredMethods()) if (m.getName().equals("get")) { makeAccessible(m); l.add(m); } if (!l.isEmpty()) break; _c = _c.getSuperclass(); } while (_c != null); callF_cache.put(c, l); return l; } // assumptions: // -pingSource() stays constant within a stack frame (so you can cache it) // -all changes happen with tempSetPingSource // -multiple threads can share a PingSource static PingSource pingSource() { return pingSource_tl().get(); } static PingSource pingSource(Thread thread) { return pingSource_tl().get(thread); } static IBinaryImage iBinaryImageFromFunction(IIntIntPred f, int w, int h) { return new IBinaryImage() { public int getWidth() { return w; } public int getHeight() { return h; } public boolean getBoolPixel(int x, int y) { return f.get(x, y); } }; } static IBinaryImage iBinaryImageFromFunction(int w, int h, IIntIntPred f) { return iBinaryImageFromFunction(f, w, h); } static String joinNemptiesWithComma(Object... strings) { return joinNempties(", ", strings); } static String joinNemptiesWithComma(Iterable strings) { return joinNempties(", ", strings); } 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 List flattenCollectionsAndArrays(Object... whatever) { return flattenCollectionsAndArrays(ll(whatever)); } 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 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(IMultiMap mm) { return mm != null && mm.size() != 0; } static boolean nempty(Object o) { return !empty(o); } static boolean nempty(IntRange r) { return !empty(r); } static boolean nempty(IntBuffer b) { return b != null && !b.isEmpty(); } static boolean nempty(LongBuffer b) { return b != null && !b.isEmpty(); } static boolean nempty(Rect r) { return r != null && r.w != 0 && r.h != 0; } static boolean nempty(IntSize l) { return l != null && l.size() != 0; } public static String join(String glue, Iterable strings) { if (strings == null) return ""; if (strings instanceof Collection) { if (((Collection) strings).size() == 1) return strOrEmpty(first((Collection) strings)); } StringBuilder buf = new StringBuilder(); Iterator i = strings.iterator(); if (i.hasNext()) { buf.append(strOrEmpty(i.next())); while (i.hasNext()) buf.append(glue).append(strOrEmpty(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 String join(Iterable strings) { return join("", strings); } static String join(Iterable strings, String glue) { return join(glue, strings); } public static String join(String[] strings) { return join("", strings); } static String join(String glue, Pair p) { return p == null ? "" : str(p.a) + glue + str(p.b); } // BREAKING CHANGE! // Also NOTE: Iterators of these sync-wrapped collections // after generally NOT thread-safe! // TODO: change that? static Set synchroLinkedHashSet() { return synchronizedSet(new CompactLinkedHashSet()); } static PCallPolicy pcallPolicyForThread() { var policy = pcallPolicyForThread_tl().get(); if (policy != null) return policy; return defaultPCallPolicy(); } static void assertNotOnAWTThread() { assertFalse("Can't do this in AWT thread", isAWTThread()); } static void onFrameClose(Component c, final Runnable r) { JFrame f = getFrame(c); if (f != null) f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { try { r.run(); } catch (Throwable __e) { messageBox(__e); } } }); } 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(() -> { JPanel panel = showForm_makePanel(false, _parts); showForm_makeFrame(title, panel); return panel; }); } static JPanel showForm_makePanel(Boolean internalFrame, Object... _parts) { List out = showForm_arrange1(showForm_makeComponents(internalFrame, _parts)); return vstackWithSpacing(out, showForm_defaultGap); } // one array plus more elements static Object[] arrayPlus(Object[] a1, Object... a2) { return concatArrays(a1, a2); } static A waitUntilVarSet(Var v) { try { synchronized(v) { while (!v.has()) v.wait(); return v.get(); } } catch (Exception __e) { throw rethrow(__e); } } static A waitUntilVarSet(Var v, int timeout) { try { synchronized(v) { long time = nanoTime(); while (!v.has() && (nanoTime()-time) < timeout*900000) v.wait(timeout); // XXX-??? return v.get(); } } 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 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 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 Class mc() { return main.class; } static int rectArea(Rect r) { return r == null ? 0 : r.w*r.h; } 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(); 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"); return null; } // f: A -> Comparable static List sortByCalculatedFieldDesc_inPlace(List l, final Object f) { sort(l, new Comparator() { public int compare(A b, A a) { return stdcompare((Object) callF(f, a), (Object) callF(f, b)); } }); return l; } static List sortByCalculatedFieldDesc_inPlace(Object f, List c) { return sortByCalculatedFieldDesc_inPlace(c, f); } static int stdcompare(Number a, Number b) { return cmp(a, b); } static int stdcompare(String a, String b) { return cmp(a, b); } static int stdcompare(long a, long b) { return a < b ? -1 : a > b ? 1 : 0; } static int stdcompare(Object a, Object b) { return cmp(a, b); } static List immutableEmptyList() { return Collections.emptyList(); } static byte[] emptyByteArray_a = new byte[0]; static byte[] emptyByteArray() { return emptyByteArray_a; } 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 short[] emptyShortArray = new short[0]; static short[] emptyShortArray() { return emptyShortArray; } static Map immutableEmptyMap() { return Collections.emptyMap(); } 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; } // 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 double parseDouble(String s) { return empty(s) ? 0.0 : Double.parseDouble(s); } static List ll(A... a) { ArrayList l = new ArrayList(a.length); if (a != null) for (A x : a) l.add(x); return l; } static int 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 > A max (Iterable l) { A max = null; var it = iterator(l); if (it.hasNext()) { max = it.next(); while (it.hasNext()) { A a = it.next(); if (cmp(a, max) > 0) max = a; } } return max; } /*Nah. static int max(Collection c) { int x = Integer.MIN_VALUE; for (int i : c) x = max(x, i); ret 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 max(A a, A b) { return cmp(a, b) >= 0 ? a : b; } static Map 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(); } } // 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 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 void failIfUnlicensed() { assertTrue("license off", licensed()); } static Thread currentThread() { return Thread.currentThread(); } static Map newDangerousWeakHashMap() { return _registerDangerousWeakMap(synchroMap(new WeakHashMap())); } // initFunction: voidfunc(Map) - is called initially, and after clearing the map static Map newDangerousWeakHashMap(Object initFunction) { return _registerDangerousWeakMap(synchroMap(new WeakHashMap()), initFunction); } static String getClassName(Object o) { return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName(); } 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 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 BetterThreadLocal pingSource_tl_var = new BetterThreadLocal() { @Override public PingSource initialValue() { return ping_v3_pingSourceMaker().get(); } }; static BetterThreadLocal pingSource_tl() { return pingSource_tl_var; } static String joinNempties(String sep, Object... strings) { return joinStrings(sep, strings); } static String joinNempties(String sep, Iterable strings) { return joinStrings(sep, strings); } static int lastIndexOf(String a, String b) { return a == null || b == null ? -1 : a.lastIndexOf(b); } static int lastIndexOf(String a, char b) { return a == null ? -1 : a.lastIndexOf(b); } // starts searching from i-1 static int lastIndexOf(List l, int i, A a) { if (l == null) return -1; for (i = min(l(l), i)-1; i >= 0; i--) if (eq(l.get(i), a)) return i; return -1; } static int lastIndexOf(List l, A a) { if (l == null) return -1; for (int i = l(l)-1; i >= 0; i--) if (eq(l.get(i), a)) return i; return -1; } 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 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(IMultiMap mm) { return mm == null || mm.size() == 0; } static boolean empty(File f) { return getFileSize(f) == 0; } static boolean empty(IntRange r) { return r == null || r.empty(); } static boolean empty(DoubleRange r) { return r == null || r.isEmpty(); } static boolean empty(IntBuffer b) { return b == null || b.isEmpty(); } static boolean empty(LongBuffer b) { return b == null || b.isEmpty(); } static boolean empty(DoubleBuffer b) { return b == null || b.isEmpty(); } static boolean empty(Rect r) { return !(r != null && r.w != 0 && r.h != 0); } static boolean empty(Chain c) { return c == null; } static boolean empty(AppendableChain c) { return c == null; } static boolean empty(IntSize l) { return l == null || l.size() == 0; } static String strOrEmpty(Object o) { return o == null ? "" : str(o); } static Object first(Object list) { return first((Iterable) list); } static A first(List list) { return empty(list) ? null : list.get(0); } static A first(A[] bla) { return bla == null || bla.length == 0 ? null : bla[0]; } static Pair first(Map map) { return mapEntryToPair(first(entrySet(map))); } static Pair first(MultiMap 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 first(IterableIterator i) { return first((Iterator) i); } static A first(Iterator i) { return i == null || !i.hasNext() ? null : i.next(); } static A first(Iterable i) { if (i == null) return null; Iterator it = i.iterator(); return it.hasNext() ? it.next() : null; } static Character first(String s) { return empty(s) ? null : s.charAt(0); } static Character first(CharSequence s) { return empty(s) ? null : s.charAt(0); } static A first(Pair p) { return p == null ? null : p.a; } static Byte first(byte[] l) { return empty(l) ? null : l[0]; } static Double first(double[] l) { return empty(l) ? null : l[0]; } static int first(IntBuffer buf) { return buf.get(0); } static byte first(ByteBuffer buf) { return buf.get(0); } static A first(A[] l, IF1 pred) { return firstThat(l, pred); } static A first(Iterable l, IF1 pred) { return firstThat(l, pred); } static A first(IF1 pred, Iterable l) { return firstThat(pred, l); } static A first(AppendableChain a) { return a == null ? null : a.element; } static Set synchronizedSet() { return synchroHashSet(); } static Set synchronizedSet(Set set) { return Collections.synchronizedSet(set); } static ThreadLocal pcallPolicyForThread_tl_tl = new ThreadLocal(); static ThreadLocal pcallPolicyForThread_tl() { return pcallPolicyForThread_tl_tl; } static PCallPolicy defaultPCallPolicy = __1 -> printStackTrace(__1); static PCallPolicy defaultPCallPolicy() { return defaultPCallPolicy; } static void defaultPCallPolicy_set(PCallPolicy policy) { defaultPCallPolicy = policy; } 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 JFrame getFrame(final Object _o) { return swing(new F0() { public JFrame get() { try { Object o = _o; if (o instanceof ButtonGroup) o = first(buttonsInGroup((ButtonGroup) o)); if (!(o instanceof Component)) return null; Component c = (Component) o; while (c != null) { if (c instanceof JFrame) return (JFrame) c; c = c.getParent(); } return null; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O o = _o;\r\n if (o instanceof ButtonGroup) o = first(buttonsInGroup((Button..."; }}); } static void messageBox(final String msg) { print(msg); { swing(() -> { JOptionPane.showMessageDialog(null, msg, "JavaX", JOptionPane.INFORMATION_MESSAGE); }); } } static void messageBox(Throwable e) { //showConsole(); printStackTrace(e); messageBox(hideCredentials(innerException2(e))); } 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 Object swing(Object f) { return swingAndWait(f); } static void swing(Runnable f) { swingAndWait(f); } static A swing(F0 f) { return (A) swingAndWait(f); } static A swing(IF0 f) { return (A) swingAndWait(f); } static IVF2 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 _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 List showForm_arrange1(List> l) { int minW = showForm_leftWidth(l); List out = new ArrayList(); for (List row : l) out.add(westAndCenter(withRightMargin(showForm_gapBetweenColumns, jMinWidthAtLeast(minW, first(row))), second(row))); return out; } static List> showForm_makeComponents(Boolean internalFrame, Object... _parts) { IVF1 closeFrame = isTrue(internalFrame) ? __1 -> disposeInternalFrame(__1) : isFalse(internalFrame) ? __2 -> disposeFrame(__2) : null; return showForm_makeComponents(closeFrame, _parts); } static List> showForm_makeComponents(IVF1 closeFrame, Object... _parts) { List> l = new ArrayList(); List parts = asList(_parts); JButton submitButton = null; for (int i = 0; i < l(parts); i++) if (parts.get(i) instanceof Swingable) parts.set(i, ((Swingable) parts.get(i)).visualize()); for (int i = 0; i < l(parts); i++) { 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(__33 -> wrapForSmartAdd_jComponent(__33), 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(__34 -> wrapForSmartAdd_jComponent(__34), 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 (closeFrame != null) closeFrame.get(heldInstance(JButton.class)); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Object result = call(o);\r\n print(\"Result of form runnable: \" + result ..."; }}))); else print("showForm: Unknown element type: " + getClassName(o)); } if (submitButton != null) { final JButton _submitButton = submitButton; onEnterInAllTextFields(concatLists(l), new Runnable() { public void run() { try { clickButton(_submitButton) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "clickButton(_submitButton)"; }}); } // massage labels for (List row : l) { JComponent left = first(row); if (left instanceof JLabel) makeBold((JLabel) left).setVerticalAlignment(JLabel.TOP); } return l; } static int vstackWithSpacing_default = 10; static JPanel vstackWithSpacing(final List parts) { return vstackWithSpacing(parts, vstackWithSpacing_default); } static JPanel vstackWithSpacing(final List parts, final int spacing) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.weightx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.insets = new Insets(spacing/2, 0, (spacing+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 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 long nanoTime() { return System.nanoTime(); } static final Map callOpt_cache = newDangerousWeakHashMap(); static Object callOpt_cached(Object o, String methodName, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); // TODO: (super-rare) case where method exists static and non-static // with different args Method me = cache.findMethod(methodName, args); if (me == null || (me.getModifiers() & Modifier.STATIC) == 0) return null; return invokeMethod(me, null, args); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(methodName, args); if (me == null) return null; return invokeMethod(me, o, args); } } catch (Exception __e) { throw rethrow(__e); } } // no longer synchronizes! (see #1102990) static _MethodCache callOpt_getCache(Class c) { _MethodCache cache = callOpt_cache.get(c); if (cache == null) callOpt_cache.put(c, cache = new _MethodCache(c)); return cache; } static Object[] massageArgsForVarArgsCall(Executable m, Object[] args) { Class[] types = m.getParameterTypes(); int n = types.length-1, nArgs = l(args); 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 final Map> getOpt_cache = newDangerousWeakHashMap(f getOpt_special_init); static class getOpt_Map extends WeakHashMap { getOpt_Map() { if (getOpt_special == null) getOpt_special = new HashMap(); clear(); } public void clear() { super.clear(); //print("getOpt clear"); put(Class.class, getOpt_special); put(String.class, getOpt_special); } } static final Map> getOpt_cache = _registerDangerousWeakMap(synchroMap(new getOpt_Map())); static HashMap getOpt_special; // just a marker /*static void getOpt_special_init(Map map) { map.put(Class.class, getOpt_special); map.put(S.class, getOpt_special); }*/ static Map getOpt_getFieldMap(Object o) { Class c = _getClass(o); HashMap map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); return map; } static Object getOpt_cached(Object o, String field) { try { if (o == null) return null; Map map = getOpt_getFieldMap(o); if (map == getOpt_special) { if (o instanceof Class) return getOpt((Class) o, field); /*if (o instanceof S) ret getOpt(getBot((S) o), field);*/ if (o instanceof Map) return ((Map) o).get(field); } Field f = map.get(field); if (f != null) return f.get(o); if (o instanceof DynamicObject) return syncMapGet2(((DynamicObject) o).fieldValues, field); return null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap getOpt_makeCache(Class c) { HashMap map; if (isSubtypeOf(c, Map.class)) map = getOpt_special; else { map = new HashMap(); if (!reflection_classesNotToScan().contains(c.getName())) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) { makeAccessible(f); String name = f.getName(); if (!map.containsKey(name)) map.put(name, f); } _c = _c.getSuperclass(); } while (_c != null); } } if (getOpt_cache != null) getOpt_cache.put(c, map); return map; } static 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; } // 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 eqOneOf(Object o, Object... l) { if (l != null) for (Object x : l) if (eq(o, x)) return true; return false; } 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 WeakHasherMap symbol_map = new WeakHasherMap(new Hasher() { public int hashCode(Symbol symbol) { return symbol.text.hashCode(); } public boolean equals(Symbol a, Symbol b) { if (a == null) return b == null; return b != null && eq(a.text, b.text); } }); static Symbol symbol(String s) { if (s == null) return null; synchronized(symbol_map) { // TODO: avoid object creation by passing the string to findKey Symbol symbol = new Symbol(s, true); Symbol existingSymbol = symbol_map.findKey(symbol); if (existingSymbol == null) symbol_map.put(existingSymbol = symbol, true); return existingSymbol; } } static Symbol symbol(CharSequence s) { if (s == null) return null; if (s instanceof Symbol) return (Symbol) s; if (s instanceof String) return symbol((String) s); return symbol(str(s)); } static Symbol symbol(Object o) { return symbol((CharSequence) o); } static String className(Object o) { return getClassName(o); } static Iterator iterator(Iterable c) { return c == null ? emptyIterator() : c.iterator(); } static List _registerWeakMap_preList; static A _registerWeakMap(A map) { if (javax() == null) { // We're in class init if (_registerWeakMap_preList == null) _registerWeakMap_preList = synchroList(); _registerWeakMap_preList.add(map); return map; } try { call(javax(), "_registerWeakMap", map); } catch (Throwable e) { printException(e); print("Upgrade JavaX!!"); } return map; } static void _onLoad_registerWeakMap() { assertNotNull(javax()); if (_registerWeakMap_preList == null) return; for (Object o : _registerWeakMap_preList) _registerWeakMap(o); _registerWeakMap_preList = null; } static Map synchroMap() { return synchroHashMap(); } static Map synchroMap(Map map) { return Collections.synchronizedMap(map); } static x30_pkg.x30_util.BetterThreadLocal newPing_actionTL; static x30_pkg.x30_util.BetterThreadLocal newPing_actionTL() { if (newPing_actionTL == null) newPing_actionTL = vm_generalMap_getOrCreate("newPing_actionTL", () -> { Runnable value = (Runnable) (callF_gen(vm_generalMap_get("newPing_valueForNewThread"))); var tl = new x30_pkg.x30_util.BetterThreadLocal(); tl.set(value); return tl; }); return newPing_actionTL; } 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 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 boolean assertTrue(Scorer scorer, boolean b) { scorer.add(b); return b; } 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 List _registerDangerousWeakMap_preList; static A _registerDangerousWeakMap(A map) { return _registerDangerousWeakMap(map, null); } static A _registerDangerousWeakMap(A map, Object init) { callF(init, map); if (init instanceof String) { final String f = (String) init; init = new VF1() { public void get(Map map) { try { callMC(f, map) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callMC(f, map)"; }}; } if (javax() == null) { // We're in class init if (_registerDangerousWeakMap_preList == null) _registerDangerousWeakMap_preList = synchroList(); _registerDangerousWeakMap_preList.add(pair(map, init)); return map; } call(javax(), "_registerDangerousWeakMap", map, init); return map; } static void _onLoad_registerDangerousWeakMap() { assertNotNull(javax()); if (_registerDangerousWeakMap_preList == null) return; for (Pair p : _registerDangerousWeakMap_preList) _registerDangerousWeakMap(p.a, p.b); _registerDangerousWeakMap_preList = null; } static Throwable getExceptionCause(Throwable e) { Throwable c = e.getCause(); return c != null ? c : e; } static String joinWithSpace(Iterable c) { return join(" ", c); } static String joinWithSpace(Object... c) { return join(" ", c); } static List classNames(Collection l) { return getClassNames(l); } static List classNames(Object[] l) { return getClassNames(asList(l)); } 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 print_byThread; // special handling by thread - prefers F1 static volatile Object print_allThreads; static volatile Object print_preprocess; static void print() { print(""); } static A print(String s, A o) { print(combinePrintParameters(s, o)); return o; } // slightly overblown signature to return original object... static A print(A o) { ping_okInCleanUp(); if (print_silent) return o; String s = o + "\n"; print_noNewLine(s); return o; } static void print_noNewLine(String s) { try { Object f = getThreadLocal(print_byThread_dontCreate()); if (f == null) f = print_allThreads; if (f != null) // We do need the general callF machinery here as print_byThread is sometimes shared between modules if (isFalse( f instanceof F1 ? ((F1) f).get(s) : callF(f, s))) return; } catch (Throwable e) { System.out.println(getStackTrace(e)); } print_raw(s); } static void print_raw(String s) { if (print_preprocess != null) s = (String) callF(print_preprocess, s); s = fixNewLines(s); Appendable loc = local_log; Appendable buf = print_log; int loc_max = print_log_max; if (buf != loc && buf != null) { print_append(buf, s, print_log_max); loc_max = local_log_max; } if (loc != null) print_append(loc, s, loc_max); if (printAlsoToSystemOut) System.out.print(s); vmBus_send("printed", mc(), s); } static void print_autoRotate() { } 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 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 IF0 ping_v3_pingSourceMaker_cache; static IF0 ping_v3_pingSourceMaker() { if (ping_v3_pingSourceMaker_cache == null) ping_v3_pingSourceMaker_cache = ping_v3_pingSourceMaker_load(); return ping_v3_pingSourceMaker_cache;} static IF0 ping_v3_pingSourceMaker_load() { return or((IF0) vm_generalMap_get("ping_v3_pingSourceMaker"), () -> null); } 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 int strL(String s) { return s == null ? 0 : s.length(); } static int lCharSequence(CharSequence s) { return s == null ? 0 : s.length(); } 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 Pair mapEntryToPair(Map.Entry e) { return e == null ? null : pair(e.getKey(), e.getValue()); } static Set> entrySet(Map map) { return _entrySet(map); } static Pair pair(A a, B b) { return new Pair(a, b); } static Pair pair(A a) { return new Pair(a, a); } static A firstThat(Iterable l, IF1 pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static A firstThat(A[] l, IF1 pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static A firstThat(IF1 pred, Iterable l) { return firstThat(l, pred); } static A firstThat(IF1 pred, A[] l) { return firstThat(l, pred); } static AutoCloseable tempInterceptPrintIfNotIntercepted(F1 f) { return print_byThread().get() == null ? tempInterceptPrint(f) : null; } static Set synchroHashSet() { return synchronizedSet(new HashSet()); } static 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)); } // get purpose 1: access a list/array/map (safer version of x.get(y)) static A get(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } // seems to conflict with other signatures /*static B get(Map map, A key) { ret map != null ? map.get(key) : null; }*/ static A get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } // default to false static boolean get(boolean[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : false; } // get purpose 2: access a field by reflection or a map static Object get(Object o, String field) { try { if (o == null) return null; if (o instanceof Class) return get((Class) o, field); if (o instanceof Map) return ((Map) o).get(field); Field f = getOpt_findField(o.getClass(), field); if (f != null) { makeAccessible(f); return f.get(o); } if (o instanceof DynamicObject) return getOptDynOnly(((DynamicObject) o), field); } catch (Exception e) { throw asRuntimeException(e); } throw new RuntimeException("Field '" + field + "' not found in " + o.getClass().getName()); } static Object get_raw(String field, Object o) { return get_raw(o, field); } static Object get_raw(Object o, String field) { try { if (o == null) return null; Field f = get_findField(o.getClass(), field); makeAccessible(f); return f.get(o); } catch (Exception __e) { throw rethrow(__e); } } static Object get(Class c, String field) { try { Field f = get_findStaticField(c, field); makeAccessible(f); return f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } static Field get_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field get_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static Object get(String field, Object o) { return get(o, field); } static boolean get(BitSet bs, int idx) { return bs != null && bs.get(idx); } static List buttonsInGroup(ButtonGroup g) { if (g == null) return ll(); return asList(g.getElements()); } static String hideCredentials(URL url) { return url == null ? null : hideCredentials(str(url)); } static String hideCredentials(String url) { try { if (startsWithOneOf(url, "http://", "https://") && isAGIBlueDomain(hostNameFromURL(url))) return url; } catch (Throwable e) { print("HideCredentials", e); } return url.replaceAll("([&?])(_pass|key|cookie)=[^&\\s\"]*", "$1$2="); } static String hideCredentials(Object o) { return hideCredentials(str(o)); } static Throwable innerException2(Throwable e) { if (e == null) return null; while (empty(e.getMessage()) && e.getCause() != null) e = e.getCause(); return e; } 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 void swingAndWait(Runnable r) { try { if (isAWTThread()) r.run(); else { r = addThreadInfoToRunnable(r); executingSwingCode(r); EventQueue.invokeAndWait(r); } } catch (Exception __e) { throw rethrow(__e); } } static Object swingAndWait(Object f) { if (isAWTThread()) return callF(f); else { 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 JFrame handleEscapeKey(final JFrame frame) { KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); frame.getRootPane().registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { frame.dispose(); } }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); return frame; } static JFrame minFrameWidth(JFrame frame, int w) { if (frame != null && frame.getWidth() < w) frame.setSize(w, frame.getHeight()); return frame; } static JFrame minFrameWidth(int w, JFrame frame) { return minFrameWidth(frame, w); } static JFrame showPackedFrame(String title, Component contents) { return packFrame(showFrame(title, contents)); } static JFrame showPackedFrame(Component contents) { return packFrame(showFrame(contents)); } static int withMargin_defaultWidth = 6; static JPanel withMargin(Component c) { return withMargin(withMargin_defaultWidth, c); } static JPanel withMargin(int w, Component c) { return withMargin(w, w, c); } static JPanel withMargin(int w, int h, Component c) { return withMargin(w, h, w, h, c); } static JPanel withMargin(final int top, final int left, final int bottom, final int right, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel p = 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 int showForm_leftWidth_safetyMargin = 10; static int showForm_leftWidth(List> l) { forEachLevel2(l, x -> vmBus_send("updateLayoutNow", x)); int minW = 0; for (List row : l) minW = max(minW, getMinimumSize(first(row)).width); return minW + or((Integer) vmBus_query("formSafetyMargin"), showForm_leftWidth_safetyMargin); } static JPanel westAndCenter(final Component w, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.WEST, wrap(w)); panel.add(BorderLayout.CENTER, wrap(c)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new BorderLayout);\r\n panel.add(BorderLayout.WEST..."; }}); } static int withRightMargin_defaultWidth = 6; static JPanel withRightMargin(Component c) { return withRightMargin(withRightMargin_defaultWidth, c); } static JPanel withRightMargin(final int w, final Component c) { return swing(new F0() { public JPanel get() { try { JPanel p = 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 jMinWidthAtLeast(int w, final A c) { if (c == null) return null; return swing(new F0() { public A get() { try { Dimension size = c.getMinimumSize(); Dimension d = new Dimension(max(w, size.width), size.height); c.setMinimumSize(d); return jPreferWidth(d.width, c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Dimension size = c.getMinimumSize();\r\n Dimension d = new Dimension(max(w, ..."; }}); } static A second(List l) { return get(l, 1); } static A second(Iterable l) { if (l == null) return null; Iterator it = iterator(l); if (!it.hasNext()) return null; it.next(); return it.hasNext() ? it.next() : null; } static A second(A[] bla) { return bla == null || bla.length <= 1 ? null : bla[1]; } static B second(Pair p) { return p == null ? null : p.b; } static A second(Producer p) { if (p == null) return null; if (p.next() == null) return null; return p.next(); } static char second(String s) { return charAt(s, 1); } static 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 boolean isFalse(Object o) { return eq(false, o); } static void disposeFrame(final Component c) { disposeWindow(c); } static void setComponentID(Component c, String id) { if (c != null) componentID_map.put(c, id); } static List mapLL(Object f, Object... data) { return map(f, ll(data)); } static List mapLL(IF1 f, A... data) { return map(f, ll(data)); } static JComponent wrapForSmartAdd_jComponent(Object o) { return componentToJComponent(wrapForSmartAdd(o)); } static Map humanizeFormLabel_replacements = litmap("id" , "ID", "md5" , "MD5"); static String humanizeFormLabel(String s) { if (!isIdentifier(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 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 showFormSubmitButtonName() { return "Submit"; } static A heldInstance(Class c) { List l = holdInstance_l.get(); for (int i = l(l)-1; i >= 0; i--) { Object o = l.get(i); if (isInstanceOf(o, c)) return (A) o; } throw fail("No instance of " + className(c) + " held"); } static boolean neq(Object a, Object b) { return !eq(a, b); } 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 List concatLists(Iterable... lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) addAll(l, list); return l; } static List concatLists(Collection> lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) addAll(l, list); return l; } static void clickButton(final JButton b) { if (b != null) { swing(() -> { if (b.isEnabled()) b.doClick(); }); } } static 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))); } // 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 List nonNulls(Iterable l) { return withoutNulls(l); } static List nonNulls(A[] l) { return withoutNulls(l); } static Map nonNulls(Map map) { return withoutNulls(map); } static List nonNulls(Iterable l, IF1 f) { return mapNonNulls(l, f); } static Component jrigid() { return javax.swing.Box.createRigidArea(new Dimension(0, 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 void clear(Collection c) { if (c != null) c.clear(); } static void clear(Map map) { if (map != null) map.clear(); } static void put(Map map, A a, B b) { if (map != null) map.put(a, b); } static void put(List l, int i, A a) { if (l != null && i >= 0 && i < l(l)) l.set(i, a); } static Class _getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; // could optimize this } } static Class _getClass(Object o) { return o == null ? null : o instanceof Class ? (Class) o : o.getClass(); } static Class _getClass(Object realm, String name) { try { return classLoaderForObject(realm).loadClass(classNameToVM(name)); } catch (ClassNotFoundException e) { return null; // could optimize this } } static B syncMapGet2(Map map, A a) { if (map == null) return null; synchronized(collectionMutex(map)) { return map.get(a); } } static B syncMapGet2(A a, Map map) { return syncMapGet2(map, a); } static boolean isSubtypeOf(Class a, Class b) { return a != null && b != null && b.isAssignableFrom(a); // << always hated that method, let's replace it! } static Set reflection_classesNotToScan_value = litset( "jdk.internal.loader.URLClassPath" ); static Set reflection_classesNotToScan() { return reflection_classesNotToScan_value; } // 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); } static BufferedImage newBufferedImage(WidthAndHeight size, Color color) { return newBufferedImage(size.w(), size.h(), color); } // This one is with alpha... static BufferedImage newBufferedImage(int w, int h, int[] pixels) { return intArrayToBufferedImage(pixels, w, h); } // 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 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 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 + "(" + formatArgumentClasses(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 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 + "(" + formatArgumentClasses(args) + ") not found"); } } catch (Exception __e) { throw rethrow(__e); } } static Iterator emptyIterator() { return Collections.emptyIterator(); } static Class javax() { return getJavaX(); } static List synchroList() { return synchroList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } static A printException(A e) { printStackTrace(e); return e; } static A assertNotNull(A a) { assertTrue(a != null); return a; } static A assertNotNull(String msg, A a) { assertTrue(msg, a != null); return a; } static A assertNotNull(Scorer scorer, String msg, A a) { if (scorer == null) return assertNotNull(msg, a); if (a == null) { print("BAD - " + msg + " is null: " + a); scorer.add(false); } else { print("OK, " + msg + " not null: " + a); scorer.add(true); } return a; } static Map synchroHashMap() { return synchronizedMap(new HashMap()); } static A vm_generalMap_getOrCreate(Object key, F0 create) { return vm_generalMap_getOrCreate(key, f0ToIF0(create)); } static A vm_generalMap_getOrCreate(Object key, IF0 create) { Map generalMap = vm_generalMap(); if (generalMap == null) return null; // must be x30 init synchronized(generalMap) { // should switch to locks here A a = (A) (vm_generalMap_get(key)); if (a == null) vm_generalMap_put(key, a = create == null ? null : create.get()); return a; } } static A callF_gen(F0 f) { return f == null ? null : f.get(); } static B callF_gen(F1 f, A a) { return f == null ? null : f.get(a); } static A callF_gen(IF0 f) { return f == null ? null : f.get(); } static B callF_gen(IF1 f, A a) { return f == null ? null : f.get(a); } static B callF_gen(A a, IF1 f) { return f == null ? null : f.get(a); } static C callF_gen(IF2 f, A a, B b) { return f == null ? null : f.get(a, b); } static void callF_gen(VF1 f, A a) { { if (f != null) f.get(a); } } static void callF_gen(A a, IVF1 f) { { if (f != null) f.get(a); } } static void callF_gen(IVF1 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 Object vm_generalMap_get(Object key) { return vm_generalMap().get(key); } static void ping_okInCleanUp() { if (ping_pauseAll || ping_anyActions) ping_impl(true); } static HashMap> callMC_cache = new HashMap(); static String callMC_key; static Method callMC_value; // varargs assignment fixer for a single string array argument static Object callMC(String method, String[] arg) { return callMC(method, new Object[] {arg}); } static Object callMC(String method, Object... args) { try { Method me; if (callMC_cache == null) callMC_cache = new HashMap(); // initializer time workaround synchronized(callMC_cache) { me = method == callMC_key ? callMC_value : null; } if (me != null) try { return invokeMethod(me, null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e); } List m; synchronized(callMC_cache) { m = callMC_cache.get(method); } if (m == null) { if (callMC_cache.isEmpty()) { callMC_makeCache(); m = callMC_cache.get(method); } if (m == null) throw fail("Method named " + method + " not found in main"); } int n = m.size(); if (n == 1) { me = m.get(0); synchronized(callMC_cache) { callMC_key = method; callMC_value = me; } try { return invokeMethod(me, null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e); } } for (int i = 0; i < n; i++) { me = m.get(i); if (call_checkArgs(me, args, false)) return invokeMethod(me, null, args); } throw fail("No method called " + method + " with arguments (" + joinWithComma(getClasses(args)) + ") found in main"); } catch (Exception __e) { throw rethrow(__e); } } static void callMC_makeCache() { synchronized(callMC_cache) { callMC_cache.clear(); Class _c = (Class) mc(), c = _c; while (c != null) { for (Method m : c.getDeclaredMethods()) if ((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) { makeAccessible(m); multiMapPut(callMC_cache, m.getName(), m); } c = c.getSuperclass(); } } } static List getClassNames(Collection l) { List out = new ArrayList(); if (l != null) for (Object o : l) out.add(o == null ? null : getClassName(o)); return out; } static 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 getThreadLocal(ThreadLocal tl) { return tl == null ? null : tl.get(); } static A getThreadLocal(ThreadLocal tl, A defaultValue) { return or(getThreadLocal(tl), defaultValue); } static ThreadLocal print_byThread_dontCreate() { return print_byThread; } static 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 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 Object vmBus_wrapArgs(Object... args) { return empty(args) ? null : l(args) == 1 ? args[0] : args; } static void pcallFAll_minimalExceptionHandling(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) { ping(); pcallF_minimalExceptionHandling(f, args); } } static void pcallFAll_minimalExceptionHandling(Iterator it, Object... args) { while (it.hasNext()) { ping(); pcallF_minimalExceptionHandling(it.next(), args); } } static Set vm_busListeners_live_cache; static Set vm_busListeners_live() { if (vm_busListeners_live_cache == null) vm_busListeners_live_cache = vm_busListeners_live_load(); return vm_busListeners_live_cache;} static Set vm_busListeners_live_load() { return vm_generalIdentityHashSet("busListeners"); } static Map vm_busListenersByMessage_live_cache; static Map vm_busListenersByMessage_live() { if (vm_busListenersByMessage_live_cache == null) vm_busListenersByMessage_live_cache = vm_busListenersByMessage_live_load(); return vm_busListenersByMessage_live_cache;} static Map vm_busListenersByMessage_live_load() { return vm_generalHashMap("busListenersByMessage"); } static A or(A a, A b) { return a != null ? a : b; } static String unnull(String s) { return s == null ? "" : s; } static Collection unnull(Collection l) { return l == null ? emptyList() : l; } static List unnull(List l) { return l == null ? emptyList() : l; } static int[] unnull(int[] l) { return l == null ? emptyIntArray() : l; } static char[] unnull(char[] l) { return l == null ? emptyCharArray() : l; } static double[] unnull(double[] l) { return l == null ? emptyDoubleArray() : l; } static float[] unnull(float[] l) { return l == null ? emptyFloatArray() : l; } static Map unnull(Map l) { return l == null ? emptyMap() : l; } static Iterable unnull(Iterable i) { return i == null ? emptyList() : i; } static A[] unnull(A[] a) { return a == null ? (A[]) emptyObjectArray() : a; } static BitSet unnull(BitSet b) { return b == null ? new BitSet() : b; } static Pt unnull(Pt p) { return p == null ? new Pt() : p; } //ifclass Symbol static Symbol unnull(Symbol s) { return s == null ? emptySymbol() : s; } //endif static Pair unnull(Pair p) { return p != null ? p : new Pair(null, null); } static int unnull(Integer i) { return i == null ? 0 : i; } static long unnull(Long l) { return l == null ? 0L : l; } static double unnull(Double l) { return l == null ? 0.0 : l; } static String strOrNull(Object o) { return o == null ? null : str(o); } static Set> _entrySet(Map map) { return map == null ? Collections.EMPTY_SET : map.entrySet(); } static Map componentID_map = weakHashMap(); static String componentID(Component c) { return c == null ? null : componentID_map.get(c); } static ThreadLocal> holdInstance_l = new ThreadLocal(); static AutoCloseable holdInstance(Object o) { if (o == null) return null; listThreadLocalAdd(holdInstance_l, o); return new AutoCloseable() { public void close() { listThreadLocalPopLast(holdInstance_l); } }; } static ThreadLocal print_byThread() { synchronized(print_byThread_lock) { if (print_byThread == null) print_byThread = new ThreadLocal(); } return print_byThread; } // f can return false to suppress regular printing // call print_raw within f to actually print something static AutoCloseable tempInterceptPrint(F1 f) { return tempSetThreadLocal(print_byThread(), f); } static Object getOptDynOnly(DynamicObject o, String field) { if (o == null || o.fieldValues == null) return null; return o.fieldValues.get(field); } 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; } 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 ThreadLocal addInternalFrame_dontSelect = new ThreadLocal(); static ThreadLocal addInternalFrame_layer = new ThreadLocal(); static ThreadLocal addInternalFrame_toBack = new ThreadLocal(); static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final int x, final int y, final int w, final int h) { return addInternalFrame(desktop, title, x, y, w, h, null); } static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final int x, final int y, final int w, final int h, final Component contents) { return addInternalFrame(desktop, title, rect(x, y, w, h), contents); } static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final Component contents) { return addInternalFrame(desktop, title, null, contents); } static JInternalFrame addInternalFrame(final JDesktopPane desktop, final String title, final Rect r, final Component contents) { final boolean dontSelect = isTrue(optParam(addInternalFrame_dontSelect)); final boolean toBack = isTrue(optParam(addInternalFrame_toBack)); final Integer layer = optParam(addInternalFrame_layer); return swing(new F0() { public JInternalFrame get() { try { JInternalFrame frame; if (contents instanceof JInternalFrame) frame = (JInternalFrame) contents; else { frame = jInternalFrame(title); setInternalFrameContents(frame, contents); } frame.setVisible(true); desktop.add(frame, layer); if (r != null) setBounds(frame, r); else internalFrameDefaultPosition(frame); if (dontSelect) if (toBack) frame.toBack(); else frame.toFront(); else frame.setSelected(true); return fixInternalFrame(frame); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JInternalFrame frame;\r\n if (contents instanceof JInternalFrame)\r\n fra..."; }}); } static JInternalFrame addInternalFrame(JDesktopPane desktop, String title) { return addInternalFrame(desktop, title, jpanel()); } static JInternalFrame minInternalFrameWidth(final JInternalFrame frame, final int w) { { swing(() -> { 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 packInternalFrameVertically(A c) { return packInternalFrameVertically(-1, c); } static A packInternalFrameVertically(int width, A c) { final JInternalFrame win = getInternalFrame(c); if (win == null) return c; final int w = width < 0 ? win.getWidth() : width; { swing(() -> { 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 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 event executingSwingCode(Runnable code); static transient Set> onExecutingSwingCode; public static void onExecutingSwingCode(IVF1 f) { onExecutingSwingCode = createOrAddToSyncLinkedHashSet(onExecutingSwingCode, f); } public static void removeExecutingSwingCodeListener(IVF1 f) { main.remove(onExecutingSwingCode, f); } public static void executingSwingCode(Runnable code) { if (onExecutingSwingCode != null) for (var listener : onExecutingSwingCode) pcallF_typed(listener, code); } static int packFrame_minw = 150, packFrame_minh = 50; static 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 JPanel marginPanel() { return jtransparent(borderLayoutPanel()); } static > void forEachLevel2(Iterable l, IVF1 f) { if (l != null) for (B b : l) forEach(b, f); } static > void forEachLevel2(IVF1 f, Iterable l) { forEachLevel2(f, l); } static Dimension getMinimumSize(final Component c) { return c == null ? null : swing(new F0() { public Dimension get() { try { return c.getMinimumSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getMinimumSize();"; }}); } 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); } // 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 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 A jPreferWidth(A c, int w) { return jPreferWidth(w, c); } static char charAt(String s, int i) { return s != null && i >= 0 && i < s.length() ? s.charAt(i) : '\0'; } static JInternalFrame getInternalFrame(final Object _o) { return _o == null ? null : swing(new F0() { public JInternalFrame get() { try { Object o = _o; if (o instanceof ButtonGroup) o = first(buttonsInGroup((ButtonGroup) o)); if (!(o instanceof Component)) return null; Component c = (Component) o; while (c != null) { if (c instanceof JInternalFrame) return (JInternalFrame) c; c = c.getParent(); } return null; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O o = _o;\r\n if (o instanceof ButtonGroup) o = first(buttonsInGroup((Button..."; }}); } static 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 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 Component wrapForSmartAdd(Object o) { if (o == null) return jpanel(); if (o instanceof String) return jlabel((String) o); return wrap(o); } static HashMap litmap(Object... x) { HashMap map = new HashMap(); litmap_impl(map, x); return map; } static void litmap_impl(Map map, Object... x) { if (x != null) for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) map.put(x[i], x[i+1]); } static boolean isIdentifier(String s) { return isJavaIdentifier(s); } static String firstToUpper(String s) { if (empty(s)) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } static List replaceElementsUsingMap(Iterable l, final Map map) { return map(l, new F1() { public A get(A a) { try { return getOrKeep(map, a); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "getOrKeep(map, a)"; }}); } static List splitCamelCase(String s) { return ai_splitCamelCase(s); } static boolean hasMethod(Object o, String method, Object... args) { return findMethod_cached(o, method, args) != null; } static boolean newButton_autoToolTip = true; static boolean newButton_autoToolTip() { return newButton_autoToolTip; } // action can be Runnable or a function name static JButton newButton(final String text, final Object action) { return swing(new F0() { public JButton get() { try { String text2 = dropPrefix("[disabled] ", text); 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 swingNu(final Class c, final Object... args) { return swingConstruct(c, args); } static boolean isInstanceOf(Object o, Class type) { return type.isInstance(o); } static List 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 void addAll(Collection c, Iterable b) { if (c != null && b != null) for (A a : b) c.add(a); } static boolean addAll(Collection c, Collection b) { return c != null && b != null && c.addAll(b); } static boolean addAll(Collection c, B... b) { return c != null && b != null && c.addAll(Arrays.asList(b)); } static Map addAll(Map a, Map b) { if (a != null && b != null) a.putAll(b); return a; } static A addAll(A c, Collection components) { return addComponents(c, components); } static A addAll(A c, Component... components) { return addComponents(c, components); } 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 List withoutNulls(Iterable l) { if (l instanceof List) if (!containsNulls((List) l)) return ((List) l); List l2 = new ArrayList(); for (A a : l) if (a != null) l2.add(a); return l2; } static Map withoutNulls(Map map) { Map map2 = similarEmptyMap(map); for (A a : keys(map)) if (a != null) { B b = map.get(a); if (b != null) map2.put(a, b); } return map2; } static List withoutNulls(A[] l) { List l2 = new ArrayList(); if (l != null) for (A a : l) if (a != null) l2.add(a); return l2; } static List mapNonNulls(Iterable l, Object f) { return mapNonNulls(f, l); } static List mapNonNulls(Object f, Iterable l) { List x = new ArrayList(); if (l != null) for (Object o : l) addIfNotNull(x, callF(f, o)); return x; } static List mapNonNulls(Object f, Object[] l) { List x = new ArrayList(); if (l != null) for (Object o : l) addIfNotNull(x, callF(f, o)); return x; } static List mapNonNulls(Iterable l, F1 f) { return mapNonNulls(f, l); } static List mapNonNulls(F1 f, Iterable l) { List x = new ArrayList(); if (l != null) for (Object o : l) addIfNotNull(x, callF(f, o)); return x; } static List mapNonNulls(A[] l, IF1 f) { return mapNonNulls(f, l); } static List mapNonNulls(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) addIfNotNull(x, f.get(o)); return x; } static List mapNonNulls(IF1 f, Iterable l) { return mapNonNulls(l, f); } 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 HashSet litset(A... items) { return lithashset(items); } // from: https://stackoverflow.com/questions/14416107/int-array-to-bufferedimage // pixels are RGB pixels static BufferedImageWithMeta intArrayToBufferedImage(int[] pixels, int w) { return intArrayToBufferedImage(pixels, w, pixels.length/w); } static BufferedImageWithMeta intArrayToBufferedImage(int[] pixels, int w, int h) { if (w == 0 || h == 0) return null; 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 BufferedImageWithMeta(ColorModel.getRGBdefault(), wr, false, null); } static BufferedImageWithMeta intArrayToBufferedImage(int w, int... pixels) { return intArrayToBufferedImage(pixels, w); } // 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 boolean isStaticMethod(Method m) { return methodIsStatic(m); } static String formatArgumentClasses(Object[] args) { return joinWithComma(map(__35 -> getClassName(__35), args)); } static Class __javax; static Class getJavaX() { try { return __javax; } catch (Exception __e) { throw rethrow(__e); } } static void __setJavaX(Class j) { __javax = j; _onJavaXSet(); } static Map synchronizedMap() { return synchroMap(); } static Map synchronizedMap(Map map) { return synchroMap(map); } static IF0 f0ToIF0(F0 f) { return f == null ? null : () -> f.get(); } 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 List getClasses(Object[] array) { List l = emptyList(l(array)); for (Object o : array) l.add(_getClass(o)); return l; } static void multiMapPut(Map> map, A a, B b) { List l = map.get(a); if (l == null) map.put(a, l = new ArrayList()); l.add(b); } static void multiMapPut(MultiMap mm, A key, B value) { if (mm != null && key != null && value != null) mm.put(key, value); } static boolean endsWithLetterOrDigit(String s) { return s != null && s.length() > 0 && Character.isLetterOrDigit(s.charAt(s.length()-1)); } // 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 int indexOf(List l, A a, int startIndex) { if (l == null) return -1; int n = l(l); for (int i = startIndex; i < n; i++) if (eq(l.get(i), a)) return i; return -1; } static int indexOf(List l, int startIndex, A a) { return indexOf(l, a, startIndex); } static int indexOf(List l, A a) { if (l == null) return -1; return l.indexOf(a); } static int indexOf(String a, String b) { return a == null || b == null ? -1 : a.indexOf(b); } static int indexOf(String a, String b, int i) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, char b) { return a == null ? -1 : a.indexOf(b); } static int indexOf(String a, int i, char b) { return indexOf(a, b, i); } static int indexOf(String a, char b, int i) { return a == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, int i, String b) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(A[] x, A a) { int n = l(x); for (int i = 0; i < n; i++) if (eq(x[i], a)) return i; return -1; } static int indexOf(Iterable 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 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 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 float[] emptyFloatArray = new float[0]; static float[] emptyFloatArray() { return emptyFloatArray; } static Map emptyMap() { return new HashMap(); } static Map myFrames_list = weakHashMap(); static List myFrames() { return swing(new F0>() { public List get() { try { return keysList(myFrames_list); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return keysList(myFrames_list);"; }}); } static Map weakHashMap() { return newWeakHashMap(); } static void listThreadLocalAdd(ThreadLocal> tl, A a) { List l = tl.get(); if (l == null) tl.set(l = new ArrayList()); l.add(a); } static A listThreadLocalPopLast(ThreadLocal> tl) { List l = tl.get(); if (l == null) return null; A a = popLast(l); if (empty(l)) tl.set(null); return a; } static AutoCloseable tempSetThreadLocal(final ThreadLocal tl, A a) { if (tl == null) return null; final A prev = setThreadLocal(tl, a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static 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 boolean domainIsUnder(String domain, String mainDomain) { return eqic(domain, mainDomain) || ewic(domain, "." + mainDomain); } static String theAGIBlueDomain() { return "agi.blue"; } 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 A optParam(ThreadLocal tl, A defaultValue) { return optPar(tl, defaultValue); } static A optParam(ThreadLocal tl) { return optPar(tl); } static Object optParam(String name, Map params) { return mapGet(params, name); } // now also takes a map as single array entry static A optParam(Object[] opt, String name, A defaultValue) { int n = l(opt); if (n == 1 && opt[0] instanceof Map) { Map map = (Map) (opt[0]); return map.containsKey(name) ? (A) map.get(name) : defaultValue; } if (!even(l(opt))) throw fail("Odd parameter length"); for (int i = 0; i < l(opt); i += 2) if (eq(opt[i], name)) return (A) opt[i+1]; return defaultValue; } static Object optParam(Object[] opt, String name) { return optParam(opt, name, null); } static Object optParam(String name, Object[] params) { return optParam(params, name); } static boolean jInternalFrame_iconifiable = true; static JInternalFrame jInternalFrame() { return jInternalFrame(""); } static JInternalFrame jInternalFrame(final String title) { return swing(new F0() { public JInternalFrame get() { try { JInternalFrame f = new JInternalFrame(title, true, true, true, jInternalFrame_iconifiable); f.setVisible(true); return f; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JInternalFrame f = new JInternalFrame(title, true, true, true, jInternalFrame..."; }}); } static void setInternalFrameContents(final Component c, final Object contents) { { swing(() -> { 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 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 setBounds(A a, Rect r) { if (a != null && r != null) { swing(() -> { a.setBounds(toRectangle(r)); }); } return a; } static A setBounds(A a, Rectangle r) { if (a != null && r != null) { swing(() -> { a.setBounds(r); }); } return a; } static A setBounds(Rect r, A a) { return setBounds(a, r); } static A setBounds(A a, int x, int y, int w, int h) { return setBounds(x, y, w, h, a); } static void internalFrameDefaultPosition(JInternalFrame f) { f.setSize(500, 300); centerInternalFrame(f); } static int fixInternalFrame_borderTopLeft = 0; static int fixInternalFrame_borderBottomRight = 40; // for title bar static JInternalFrame fixInternalFrame(final JInternalFrame f) { return swing(new F0() { public JInternalFrame get() { try { Container c = f.getParent(); if (c == null) return f; Rect r = toRect(f.getBounds()); int a = fixInternalFrame_borderTopLeft, b = fixInternalFrame_borderBottomRight; Rect outer = new Rect(a, a, c.getWidth()-b, c.getHeight()-b); if (!rectContains(outer, r)) f.setLocation( max(a, min(r.x, outer.x2())), max(a, min(r.y, outer.y2()))); if (r.w > c.getWidth() || r.h > c.getHeight()) f.setSize(c.getWidth()-a, c.getHeight()-a); return f; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Container c = f.getParent();\r\n if (c == null) ret f;\r\n Rect r = toRect(..."; }}); } static JPanel jpanel(LayoutManager layout, Object... components) { return jpanel(layout, asList(components)); } static JPanel jpanel(LayoutManager layout, List components) { return smartAdd(jpanel(layout), components); } static JPanel jpanel(LayoutManager layout) { return swing(() -> new JPanel(layout)); } static JPanel jpanel() { return swing(() -> new JPanel()); } static List> _threadInfo_makers = synchroList(); static Object _threadInfo() { if (empty(_threadInfo_makers)) return null; HashMap map = new HashMap(); pcallFAll(_threadInfo_makers, map); return map; } static Runnable asRunnable(Object o) { return toRunnable(o); } static void _inheritThreadInfo(Object info) { _threadInheritInfo(info); } static Class main() { return getMainClass(); } 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 int getScreenWidth() { return getScreenSize().width; } static int getScreenHeight() { return getScreenSize().height; } static String makeFrame_defaultIcon; static boolean makeFrame_hideConsole = false; static ThreadLocal> makeFrame_post = new ThreadLocal(); static JFrame makeFrame() { return makeFrame((Component) null); } static JFrame makeFrame(Object content) { return makeFrame(programTitle(), content); } static JFrame makeFrame(String title) { return makeFrame(title, null); } static JFrame makeFrame(String title, Object content) { return makeFrame(title, content, true); } static JFrame makeFrame(final String title, final Object content, final boolean showIt) { final VF1 post = optParam(makeFrame_post); return swing(new F0() { public JFrame get() { try { if (getFrame(content) != null) return getFrame(setFrameTitle((Component) content, title)); final JFrame frame = new JFrame(title); if (makeFrame_defaultIcon != null) setFrameIconLater(frame, makeFrame_defaultIcon); _initFrame(frame); Component wrapped = wrap(content); if (wrapped != null) frame.getContentPane().add(wrapped); frame.setBounds(defaultNewFrameBounds()); callF(post, frame); if (showIt) frame.setVisible(true); //callOpt(content, "requestFocus"); //exitOnFrameClose(frame); if (showIt && makeFrame_hideConsole) { hideConsole(); makeFrame_hideConsole = false; } return frame; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (getFrame(content) != null)\r\n ret getFrame(setFrameTitle((Component) ..."; }}); } static 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 jtransparent(final A a) { { swing(() -> { a.setOpaque(false); }); } return a; } static JPanel borderLayoutPanel() { return jpanel(new BorderLayout()); } static void forEach(Iterable l, IVF1 f) { if (f != null && l != null) for (A a : l) callF(f, a); } static void forEach(IVF1 f, Iterable l) { forEach(l, f); } static void forEach(A[] l, IVF1 f) { if (f != null && l != null) for (A a : l) callF(f, a); } static void forEach(IVF1 f, A[] l) { forEach(l, f); } static void forEach(Map map, IVF2 f) { for (Map.Entry __0 : _entrySet( map)) { A a = __0.getKey(); B b = __0.getValue(); f.get(a, b); } } 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 A _recordNewSwingComponent(A c) { if (c != null) callF((Object) vm_generalMap_get("newSwingComponentRegistry"), (Object) c); return c; } static JScrollPane jscroll(Component c) { return swing(() -> { return c instanceof JScrollPane ? ((JScrollPane) c) : new JScrollPane(c); }); } static JLabel jlabel(final String text) { return swingConstruct(BetterLabel.class, text); } static JLabel jlabel() { return jlabel(" "); } 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 A getOrKeep(Map map, A a) { if (map == null) return a; A v = map.get(a); return v != null ? v : a; } static List ai_splitCamelCase(String s) { int j = 0; List l = new ArrayList(); // new addition if (isAllUpperCase(s)) { l.add(s); return l; } for (int i = 0; i < l(s); i++) if (i > j && isUpperCaseLetter(s.charAt(i))) { l.add(substring(s, j, i)); j = i; } if (j < l(s)) l.add(substring(s, j)); return l; } static Method findMethod_cached(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { _MethodCache cache = callOpt_getCache((Class) o); List methods = cache.cache.get(method); if (methods != null) for (Method m : methods) if (isStaticMethod(m) && findMethod_checkArgs(m, args, false)) return m; return null; } else { _MethodCache cache = callOpt_getCache(o.getClass()); List methods = cache.cache.get(method); if (methods != null) for (Method m : methods) if (findMethod_checkArgs(m, args, false)) return m; return null; } } catch (Exception __e) { throw rethrow(__e); } } static String dropPrefix(String prefix, String s) { return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s; } static JButton basicJButton(String text) { return swing(() -> new JButton(text)); } 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 A swingConstruct(final Class c, final Object... args) { return swing(new F0() { public A get() { try { return nuObject(c, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return nuObject(c, args);"; }}); } static List childrenOfType(Component c, Class theClass) { List l = new ArrayList(); scanForComponents(c, theClass, l); return l; } static List childrenOfType(Class theClass, Component c) { return childrenOfType(c, theClass); } static 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 A addComponents(A c, Collection components) { if (nempty(components)) { swing(() -> { for (Component comp : components) if (comp != null) c.add(comp); revalidate(c); }); } return c; } static A addComponents(A c, Component... components) { return addComponents(c, asList(components)); } static Object[] asObjectArray(Collection l) { return toObjectArray(l); } static boolean containsNulls(Collection c) { return contains(c, null); } static Map similarEmptyMap(Map m) { if (m instanceof TreeMap) return new TreeMap(((TreeMap) m).comparator()); if (m instanceof LinkedHashMap) return new LinkedHashMap(); // default to a hash map return new HashMap(); } static Map similarEmptyMap(Iterable m) { if (m instanceof TreeSet) return new TreeMap(((TreeSet) m).comparator()); if (m instanceof LinkedHashSet) return new LinkedHashMap(); return new HashMap(); } static Set keys(Map map) { return map == null ? new HashSet() : map.keySet(); } // convenience shortcut for keys_gen static Set keys(Object map) { return keys((Map) map); } static Set keys(IMultiMap mm) { return mm.keySet(); } static boolean addIfNotNull(Collection l, A a) { return a != null && l != null & l.add(a); } static HashSet lithashset(A... items) { HashSet set = new HashSet(); for (A a : items) set.add(a); return set; } static Map 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 imageGraphics_antiAlias = new ThreadLocal(); static Graphics2D imageGraphics(BufferedImage img) { return !isFalse(imageGraphics_antiAlias.get()) ? antiAliasGraphics(img) : createGraphics(img); } static boolean methodIsStatic(Method m) { return (m.getModifiers() & Modifier.STATIC) != 0; } static void _onJavaXSet() {} static B mapPutOrRemove(Map map, A key, B value) { if (map != null && key != null) if (value != null) return map.put(key, value); else return map.remove(key); return null; } static PersistableThrowable persistableThrowable(Throwable e) { return e == null ? null : new PersistableThrowable(e); } static Object callFunction(Object f, Object... args) { return callF(f, args); } static Throwable _storeException_value; static void _storeException(Throwable e) { _storeException_value = e; } static Set syncIdentityHashSet() { return (Set) synchronizedSet(identityHashSet()); } static Map syncHashMap() { return synchroHashMap(); } static List keysList(Map map) { return cloneListSynchronizingOn(keys(map), map); } static A popLast(List l) { return liftLast(l); } static List popLast(int n, List l) { return liftLast(n, l); } static double popLast(DoubleBuffer l) { return l.popLast(); } static A setThreadLocal(ThreadLocal tl, A value) { if (tl == null) return null; A old = tl.get(); tl.set(value); return old; } static boolean nemptyString(String s) { return s != null && s.length() > 0; } static int listL(Collection l) { return l == null ? 0 : l.size(); } 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 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 A optPar(ThreadLocal tl, A defaultValue) { A a = tl.get(); if (a != null) { tl.set(null); return a; } return defaultValue; } static A optPar(ThreadLocal tl) { return optPar(tl, null); } static Object optPar(Object[] params, String name) { return optParam(params, name); } static Object optPar(String name, Object[] params) { return optParam(params, name); } static Object optPar(String name, Map params) { return optParam(name, params); } static A optPar(Object[] params, String name, A defaultValue) { return optParam(params, name, defaultValue); } static A optPar(String name, Object[] params, A defaultValue) { return optParam(params, name, defaultValue); } static B mapGet(Map map, A a) { return map == null || a == null ? null : map.get(a); } static B mapGet(A a, Map map) { return map == null || a == null ? null : map.get(a); } 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 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 Rectangle toRectangle(Rect r) { return r == null ? null : r.getRectangle(); } static Rect toRect(Rectangle r) { return r == null ? null : new Rect(r); } static Rect toRect(RectangularShape r) { return r == null ? null : toRect(r.getBounds()); } static Rect toRect(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 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 JPanel smartAdd(JPanel panel, List parts) { for (Object o : parts) addToContainer(panel, wrapForSmartAdd(o)); return panel; } static JPanel smartAdd(JPanel panel, Object... parts) { return smartAdd(panel, asList(parts)); } 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 Runnable toRunnable(final Object o) { if (o == null) return null; 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 List> _threadInheritInfo_retrievers = synchroList(); static void _threadInheritInfo(Object info) { if (info == null) return; pcallFAll(_threadInheritInfo_retrievers, (Map) info); } 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 Dimension getScreenSize() { return Toolkit.getDefaultToolkit().getScreenSize(); } static Dimension getScreenSize(int iScreen) { return toDimension(screenBounds(iScreen).widthAndHeight()); } static String programTitle() { return getProgramName(); } static A setFrameTitle(A c, final String title) { final Frame f = getAWTFrame(c); if (f != null) { swing(() -> { f.setTitle(title); }); } return c; } static A setFrameTitle(String title, A c) { return setFrameTitle(c, title); } // magically find a field called "frame" in main class :-) static JFrame setFrameTitle(String title) { Object f = getOpt(mc(), "frame"); if (f instanceof JFrame) return setFrameTitle((JFrame) f, title); return null; } static JFrame setFrameIconLater(Component c, final String imageID) { final JFrame frame = getFrame(c); if (frame != null) startThread("Loading Icon", new Runnable() { public void run() { try { final Image i = imageIcon(or2(imageID, "#1005557")).getImage(); swingLater(new Runnable() { public void run() { try { frame.setIconImage(i); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "frame.setIconImage(i);"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final Image i = imageIcon(or2(imageID, \"#1005557\")).getImage();\r\n swingL..."; }}); return frame; } static void _initFrame(JFrame f) { myFrames_list.put(f, Boolean.TRUE); standardTitlePopupMenu(f); } static Rectangle defaultNewFrameBounds_r = new Rectangle(300, 100, 500, 400); static Rectangle defaultNewFrameBounds() { return swing(new F0() { public Rectangle get() { try { defaultNewFrameBounds_r.translate(60, 20); 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 Object pcallF(Object f, Object... args) { return pcallFunction(f, args); } static A pcallF(F0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { pcallFail(__e); } return null; } static B pcallF(F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { pcallFail(__e); } return null; } static void pcallF(VF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { pcallFail(__e); } } static Object pcallF(Runnable r) { try { { if (r != null) r.run(); } } catch (Throwable __e) { pcallFail(__e); } return null; } static A pcallF(IF0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { pcallFail(__e); } return null; } static B pcallF(IF1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { pcallFail(__e); } return null; } static boolean isAllUpperCase(String s) { return hasLettersAllUpperCase(s); } static boolean isUpperCaseLetter(char c) { return Character.isUpperCase(c); } 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 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 Object nuObject(String className, Object... args) { try { return nuObject(classForName(className), args); } catch (Exception __e) { throw rethrow(__e); } } // too ambiguous - maybe need to fix some callers /*static O nuObject(O realm, S className, O... args) { ret nuObject(_getClass(realm, className), args); }*/ static A nuObject(Class c, Object... args) { try { if (args == 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 : getDeclaredConstructors_cached(c)) { 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 void scanForComponents(final Component c, final Class theClass, final List 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 KeyListener enterKeyListener(final Object action) { return new KeyAdapter() { public void keyPressed(KeyEvent ke) { if (ke.getKeyCode() == KeyEvent.VK_ENTER) pcallF(action); } }; } static Runnable rCallOnSelectedListItem(final JList list, final Object action) { return new Runnable() { public void run() { try { pcallF(action, getSelectedItem(list)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(action, getSelectedItem(list))"; }}; } static AbstractAction abstractAction(String name, final Object runnable) { return new AbstractAction(name) { public void actionPerformed(ActionEvent evt) { pcallF(runnable); } }; } static boolean contains(Collection c, Object o) { return c != null && c.contains(o); } static boolean contains(Iterable it, Object a) { if (it != null) for (Object o : it) if (eq(a, o)) return true; return false; } static boolean contains(Object[] x, Object o) { if (x != null) for (Object a : x) if (eq(a, o)) return true; return false; } static boolean contains(String s, char c) { return s != null && s.indexOf(c) >= 0; } static boolean contains(String s, String b) { return s != null && s.indexOf(b) >= 0; } static boolean contains(BitSet bs, int i) { return bs != null && bs.get(i); } static boolean contains(Producer p, A a) { if (p != null && a != null) while (true) { A x = p.next(); if (x == null) break; if (eq(x, a)) return true; } return false; } static boolean contains(Rect r, Pt p) { return rectContains(r, p); } static Map synchroIdentityHashMap() { return synchroMap(new IdentityHashMap()); } static void mapPut2(Map 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 Set identityHashSet() { return Collections.newSetFromMap(new IdentityHashMap()); } static ArrayList cloneListSynchronizingOn(Collection l, Object mutex) { if (l == null) return new ArrayList(); synchronized(mutex) { return new ArrayList(l); } } static A liftLast(List l) { if (empty(l)) return null; int i = l(l)-1; A a = l.get(i); l.remove(i); return a; } static List liftLast(int n, List l) { int i = l(l)-n; List part = cloneSubList(l, i); removeSubList(l, i); return part; } static String asString(Object o) { return o == null ? null : o.toString(); } 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 void addToContainer(Container a, Component... b) { if (a == null) return; { swing(() -> { for (Component c : unnullForIteration(b)) if (c != null) a.add(c); }); } } 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 Dimension toDimension(WidthAndHeight wh) { return wh == null ? null : new Dimension(wh.getWidth(), wh.getHeight()); } static Rect screenBounds(GraphicsDevice screen) { return screen == null ? null : toRect(screen.getDefaultConfiguration().getBounds()); } static Rect screenBounds(int iScreen) { return screenBounds(get(screenDevices(), iScreen)); } 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 Frame getAWTFrame(final Object _o) { return swing(new F0() { public Frame get() { try { Object o = _o; /* ifdef HaveProcessing if (o instanceof PApplet) o = ((PApplet) o).getSurface(); endifdef */ if (o instanceof ButtonGroup) o = first(buttonsInGroup((ButtonGroup) o)); if (!(o instanceof Component)) return null; Component c = (Component) o; while (c != null) { if (c instanceof Frame) return (Frame) c; c = c.getParent(); } return null; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "O o = _o;\r\n /*\r\n ifdef HaveProcessing\r\n if (o instanceof PApplet) ..."; }}); } static 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; } static int imageIcon_cacheSize = 10; static boolean imageIcon_verbose = false; static Map imageIcon_cache; static Lock imageIcon_lock = lock(); static ThreadLocal imageIcon_fixGIF = new ThreadLocal(); // not going through BufferedImage preserves animations static ImageIcon imageIcon(String imageID) { try { if (imageID == null) return null; Lock __0 = imageIcon_lock; lock(__0); try { if (imageIcon_cache == null) imageIcon_cache = new MRUCache(imageIcon_cacheSize); imageID = fsI(imageID); ImageIcon ii = imageIcon_cache.get(imageID); if (ii == null) { if (imageIcon_verbose) print("Loading image icon: " + imageID); File f = loadBinarySnippet(imageID); Boolean b = imageIcon_fixGIF.get(); if (!isFalse(b)) ii = new ImageIcon(loadBufferedImageFixingGIFs(f)); else ii = new ImageIcon(f.toURI().toURL()); } else imageIcon_cache.remove(imageID); // move to front of cache on access imageIcon_cache.put(imageID, ii); return ii; } finally { unlock(__0); } } catch (Exception __e) { throw rethrow(__e); } } // doesn't fix GIFs static ImageIcon imageIcon(File f) { try { return new ImageIcon(f.toURI().toURL()); } catch (Exception __e) { throw rethrow(__e); } } static ImageIcon imageIcon(Image img) { return img == null ? null : new ImageIcon(img); } static ImageIcon imageIcon(RGBImage img) { return imageIcon(img.getBufferedImage()); } 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) { var runnable = toRunnable(r); executingSwingCode(runnable); SwingUtilities.invokeLater(runnable); } static void standardTitlePopupMenu(final JFrame frame) { // standard right-click behavior on titles if (!isSubstanceLAF()) return; titlePopupMenu(frame, new VF1() { public void get(JPopupMenu menu) { try { boolean alwaysOnTop = frame.isAlwaysOnTop(); menu.add(jmenuItem("Restart Program", 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 Rect preferredScreenBounds() { return screenBounds_safe(preferredScreen()); } 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 Object pcallFunction(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable __e) { pcallFail(__e); } return null; } static boolean hasLettersAllUpperCase(String s) { return hasLetters(s) && !containsLowerCase(s); } static Map classForName_cache = synchroHashMap(); static Class classForName(String name) { return classForName(name, null); } static Class classForName(String name, Object classFinder) { // first clause is when we're in class init if (classForName_cache == null || classFinder != null) return classForName_uncached(name, classFinder); Class c = classForName_cache.get(name); if (c == null) classForName_cache.put(name, c = classForName_uncached(name, null)); return c; } static Class classForName_uncached(String name, Object classFinder) { try { if (classFinder != null) return (Class) callF(classFinder, name); return Class.forName(name); } catch (Exception __e) { throw rethrow(__e); } } static Map nuObjectWithoutArguments_cache = newDangerousWeakHashMap(); static Object nuObjectWithoutArguments(String className) { try { return nuObjectWithoutArguments(classForName(className)); } catch (Exception __e) { throw rethrow(__e); } } static A nuObjectWithoutArguments(Class c) { try { if (nuObjectWithoutArguments_cache == null) // in class init return (A) nuObjectWithoutArguments_findConstructor(c).newInstance(); Constructor m = nuObjectWithoutArguments_cache.get(c); if (m == null) nuObjectWithoutArguments_cache.put(c, m = nuObjectWithoutArguments_findConstructor(c)); return (A) m.newInstance(); } catch (Exception __e) { throw rethrow(__e); } } static Constructor nuObjectWithoutArguments_findConstructor(Class c) { for (Constructor m : getDeclaredConstructors_cached(c)) if (empty(m.getParameterTypes())) { makeAccessible(m); return m; } throw fail("No default constructor found in " + c.getName()); } // TODO: convert to regularly cleared normal map static Map getDeclaredConstructors_cached_cache = newDangerousWeakHashMap(); static Constructor[] getDeclaredConstructors_cached(Class c) { Constructor[] ctors; synchronized(getDeclaredConstructors_cached_cache) { ctors = getDeclaredConstructors_cached_cache.get(c); if (ctors == null) { getDeclaredConstructors_cached_cache.put(c, ctors = c.getDeclaredConstructors()); for (var ctor : ctors) makeAccessible(ctor); } } return ctors; } static String getSelectedItem(JList l) { return (String) l.getSelectedValue(); } static String getSelectedItem(JComboBox cb) { return strOrNull(cb.getSelectedItem()); } 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 List cloneSubList(List l, int startIndex, int endIndex) { return newSubList(l, startIndex, endIndex); } static List cloneSubList(List l, int startIndex) { return newSubList(l, startIndex); } static void removeSubList(List l, int from, int to) { if (l != null) subList(l, from, to).clear(); } static void removeSubList(List l, int from) { if (l != null) subList(l, from).clear(); } static boolean regionMatchesIC(String a, int offsetA, String b, int offsetB, int len) { return a != null && a.regionMatches(true, offsetA, b, offsetB, len); } static List screenDevices() { return asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()); } static Lock downloadLock_lock = fairLock(); static Lock downloadLock() { return downloadLock_lock; } 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 String getSnippetTitleOpt(String s) { try { return isSnippetID(s) ? getSnippetTitle(s) : s; } catch (Throwable __e) { pcallFail(__e); } return s; } static String programID() { return getProgramID(); } static String programID(Object o) { return getProgramID(o); } static void unlock(Lock lock, String msg) { if (lock == null) return; lock.unlock(); vmBus_send("unlocked", lock, "thread" , currentThread()); print("Unlocked: " + msg); // print afterwards to make sure the lock is always unlocked } static void unlock(Lock lock) { if (lock == null) return; lock.unlock(); vmBus_send("unlocked", lock, "thread" , currentThread()); } static String 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 Map _registerThread_threads; static Object _onRegisterThread; // voidfunc(Thread) static Thread _registerThread(Thread t) { if (_registerThread_threads == null) _registerThread_threads = newWeakHashMap(); _registerThread_threads.put(t, true); vm_generalWeakSubMap("thread2mc").put(t, weakRef(mc())); callF(_onRegisterThread, t); return t; } static void _registerThread() { _registerThread(Thread.currentThread()); } static String fsI(String id) { return formatSnippetID(id); } static String fsI(long id) { return formatSnippetID(id); } static File loadBinarySnippet(String snippetID) { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadLibrary(snippetID); return loadBinarySnippet_noResourceLoader(snippetID); } static File loadBinarySnippet_noResourceLoader(String snippetID) { try { long id = parseSnippetID(snippetID); if (isImageServerSnippet(id)) return loadImageAsFile(snippetID); File f = DiskSnippetCache_getLibrary(id); if (fileSize(f) == 0) f = loadDataSnippetToFile_noResourceLoader(snippetID); return f; } catch (Exception __e) { throw rethrow(__e); } } static boolean loadBufferedImageFixingGIFs_debug = false; static ThreadLocal> loadBufferedImageFixingGIFs_output = new ThreadLocal(); static Image loadBufferedImageFixingGIFs(File file) { try { if (!file.exists()) return null; // Load anything but GIF the normal way if (!isGIF(file)) return ImageIO.read(file); if (loadBufferedImageFixingGIFs_debug) print("loadBufferedImageFixingGIFs" + ": checking gif"); // Get GIF reader ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next(); // Give it the stream to decode from reader.setInput(ImageIO.createImageInputStream(file)); int numImages = reader.getNumImages(true); // Get 'metaFormatName'. Need first frame for that. IIOMetadata imageMetaData = reader.getImageMetadata(0); String metaFormatName = imageMetaData.getNativeMetadataFormatName(); // Find out if GIF is bugged boolean foundBug = false; for (int i = 0; i < numImages && !foundBug; i++) { // Get metadata IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName); // Find GraphicControlExtension node int nNodes = root.getLength(); for (int j = 0; j < nNodes; j++) { org.w3c.dom.Node node = root.item(j); if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) { // Get delay value String delay = ((IIOMetadataNode)node).getAttribute("delayTime"); // Check if delay is bugged if (Integer.parseInt(delay) == 0) { foundBug = true; } break; } } } if (loadBufferedImageFixingGIFs_debug) print("loadBufferedImageFixingGIFs" + ": " + f2s(file) + " foundBug=" + foundBug); // Load non-bugged GIF the normal way Image image; if (!foundBug) { image = Toolkit.getDefaultToolkit().createImage(f2s(file)); } else { // Prepare streams for image encoding ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); { ImageOutputStream ios = ImageIO.createImageOutputStream(baoStream); try { // Get GIF writer that's compatible with reader ImageWriter writer = ImageIO.getImageWriter(reader); // Give it the stream to encode to writer.setOutput(ios); writer.prepareWriteSequence(null); for (int i = 0; i < numImages; i++) { // Get input image BufferedImage frameIn = reader.read(i); // Get input metadata IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName); // Find GraphicControlExtension node int nNodes = root.getLength(); for (int j = 0; j < nNodes; j++) { org.w3c.dom.Node node = root.item(j); if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) { // Get delay value String delay = ((IIOMetadataNode)node).getAttribute("delayTime"); // Check if delay is bugged if (Integer.parseInt(delay) == 0) { // Overwrite with a valid delay value ((IIOMetadataNode)node).setAttribute("delayTime", "10"); } break; } } // Create output metadata IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(frameIn), null); // Copy metadata to output metadata metadata.setFromTree(metadata.getNativeMetadataFormatName(), root); // Create output image IIOImage frameOut = new IIOImage(frameIn, null, metadata); // Encode output image writer.writeToSequence(frameOut, writer.getDefaultWriteParam()); } writer.endWriteSequence(); } finally { _close(ios); }} // Create image using encoded data byte[] data = baoStream.toByteArray(); setVar(loadBufferedImageFixingGIFs_output.get(), data); if (loadBufferedImageFixingGIFs_debug) print("Data size: " + l(data)); image = Toolkit.getDefaultToolkit().createImage(data); } return image; } catch (Exception __e) { throw rethrow(__e); } } static 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 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 boolean jmenuItem_newThreads = false; static JMenuItem jmenuItem(final String text) { return jMenuItem(text, null); } static JMenuItem jmenuItem(final String text, final Object r) { return swing(new F0() { public JMenuItem get() { try { Pair p = jmenu_autoMnemonic(dropPrefix("[disabled] ", text)); JMenuItem mi = new JMenuItem(p.a); if (startsWith(text, "[disabled] ")) disableMenuItem(mi); if (p.b != 0) mi.setMnemonic(p.b); mi.addActionListener(jmenuItem_newThreads ? actionListenerInNewThread(r) : actionListener(r)); return mi; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Pair p = jmenu_autoMnemonic(dropPrefix(\"[disabled] \", text));\r\n JM..."; }}); } static void restart() { Object j = getJavaX(); call(j, "cleanRestart", get(j, "fullArgs")); } 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 r) { return jCheckBoxMenuItem(text, checked, (Object) r); } static void toggleAlwaysOnTop(Window frame) { if (frame == null) return; { swing(() -> { frame.setAlwaysOnTop(!frame.isAlwaysOnTop()); }); } } static Rect screenBounds_safe(int iScreen) { return screenBounds(min(iScreen, screenCount()-1)); } static IF0 preferredScreen; static int preferredScreen() { return preferredScreen != null ? preferredScreen.get() : preferredScreen_base(); } final static int preferredScreen_fallback(IF0 _f) { return _f != null ? _f.get() : preferredScreen_base(); } static int preferredScreen_base() { return 0; } static Integer rectCenterX(Rect r) { return r == null ? null : r.x+r.w/2; } static Random defaultRandomizer() { return defaultRandomGenerator(); } static int random(int n) { return random(n, defaultRandomGenerator()); } static int random(int n, Random r) { return random(r, n); } static long random(long n) { return random(n, defaultRandomGenerator()); } static long random(long n, Random r) { return random(r, n); } static int random(Random r, int n) { return n <= 0 ? 0 : getRandomizer(r).nextInt(n); } static long random(Random r, long n) { return n <= 0 ? 0 : getRandomizer(r).nextLong(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); } // min <= value < max static long random(long min, long max) { return min+random(max-min); } static int random(int min, int max, Random r) { return random(r, min, max); } static int random(Random r, int min, int max) { return min+random(r, max-min); } static A random(List l) { return oneOf(l); } static A random(Collection c) { if (c instanceof List) return random((List) c); int i = random(l(c)); return collectionGet(c, i); } static int random(IntRange r) { return random(r.start, r.end); } static double random(DoubleRange r) { return random(r.start, r.end); } static Pair random(Map map) { return entryToPair(random(entries(map))); } 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 List newSubList(List l, int startIndex, int endIndex) { return cloneList(subList(l, startIndex, endIndex)); } static List newSubList(List l, int startIndex) { return cloneList(subList(l, startIndex)); } static List subList(List l, int startIndex) { return subList(l, startIndex, l(l)); } static List subList(int startIndex, List l) { return subList(l, startIndex); } static List subList(int startIndex, int endIndex, List l) { return subList(l, startIndex, endIndex); } static List subList(List l, int startIndex, int endIndex) { if (l == null) return null; int n = l(l); startIndex = Math.max(0, startIndex); endIndex = Math.min(n, endIndex); if (startIndex > endIndex) return ll(); if (startIndex == 0 && endIndex == n) return l; return l.subList(startIndex, endIndex); } static List subList(List l, IntRange r) { return subList(l, r.start, r.end); } static ReentrantLock fairLock() { return new ReentrantLock(true); } static Map 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); } } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } 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 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 Object dm_current_generic() { return getWeakRef(dm_current_generic_tl().get()); } static Object rcall(String method, Object o, Object... args) { return call_withVarargs(o, method, args); } 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(); Runnable r2 = r; if (info != null || mod == null) r2 = 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 ..."; }}; r2 = rPcall(r2); return r2; } 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 WeakReference weakRef(A a) { return newWeakReference(a); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } static IResourceLoader vm_getResourceLoader() { return proxy(IResourceLoader.class, vm_generalMap_get("_officialResourceLoader")); } 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 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 long fileSize(String path) { return getFileSize(path); } static long fileSize(File f) { return getFileSize(f); } static File loadDataSnippetToFile(String snippetID) { try { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadLibrary(snippetID); return loadDataSnippetToFile_noResourceLoader(snippetID); } catch (Exception __e) { throw rethrow(__e); } } static File loadDataSnippetToFile_noResourceLoader(String snippetID) { try { snippetID = fsI(snippetID); File f = DiskSnippetCache_file(parseSnippetID(snippetID)); List urlsTried = new ArrayList(); List errors = new ArrayList(); try { URL url = addAndReturn(urlsTried, new URL(dataSnippetLink(snippetID))); print("Loading library: " + hideCredentials(url)); try { loadBinaryPageToFile(openConnection(url), f); if (fileSize(f) == 0) throw fail(); } catch (Throwable e) { errors.add(e); url = addAndReturn(urlsTried, new URL(tb_mainServer() + "/blobs/" + psI(snippetID))); print(e); print("Trying other server: " + hideCredentials(url)); loadBinaryPageToFile(openConnection(url), f); print("Got bytes: " + fileSize(f)); } // TODO: check if we hit the "LOADING" message if (fileSize(f) == 0) throw fail(); System.err.println("Bytes loaded: " + fileSize(f)); } catch (Throwable e) { //printStackTrace(e); errors.add(e); throw fail("Binary snippet " + snippetID + " not found or not public. URLs tried: " + allToString(urlsTried) + ", errors: " + allToString(errors)); } return f; } catch (Exception __e) { throw rethrow(__e); } } static 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 String f2s(File f) { return f == null ? null : f.getAbsolutePath(); } static String f2s(String s) { return f2s(newFile(s)); } static String f2s(java.nio.file.Path p) { return p == null ? null : f2s(p.toFile()); } static void setVar(IVar v, A value) { if (v != null) v.set(value); } static IVF1 setVar(IVar v) { return a -> { if (v != null) v.set(a); }; } static int parseInt(String s) { return emptyString(s) ? 0 : Integer.parseInt(s); } static int parseInt(char c) { return Integer.parseInt(str(c)); } static int boolToInt(boolean b) { return b ? 1 : 0; } static boolean substanceLookAndFeelEnabled() { return startsWith(getLookAndFeel(), "org.pushingpixels."); } static JComponent getTitlePaneComponent(RootPaneContainer window) { if (window instanceof JInternalFrame) return getInternalFrameTitlePaneComponent((JInternalFrame) window); if (!substanceLookAndFeelEnabled() || window == null) return null; JRootPane rootPane = window.getRootPane(); if (rootPane != null) { Object /*SubstanceRootPaneUI*/ ui = rootPane.getUI(); return (JComponent) call(ui, "getTitlePane"); } return null; } static RootPaneContainer getPossiblyInternalFrame(Component c) { JInternalFrame f = getInternalFrame(c); if (f != null) return f; return optCast(RootPaneContainer.class, getWindow(c)); } static ThreadLocal componentPopupMenu_mouseEvent; static void componentPopupMenu_init() { { swing(() -> { if (componentPopupMenu_mouseEvent == null) componentPopupMenu_mouseEvent = (ThreadLocal) vm_generalMap_get("mouseEvent"); if (componentPopupMenu_mouseEvent == null) vm_generalMap_put("componentPopupMenu_mouseEvent" , componentPopupMenu_mouseEvent = new ThreadLocal()); }); } } static void componentPopupMenu(final JComponent component, IVF1 menuMaker) { componentPopupMenu(component, (Object) menuMaker); } // 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() { 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 JMenuItem jMenuItem(final String text) { return jmenuItem(text); } static JMenuItem jMenuItem(String text, Object r) { return jmenuItem(text, r); } static Pair jmenu_autoMnemonic(String s) { int i = indexOf(s, '&'); if (i >= 0 && i < l(s) && isLetterOrDigit(s.charAt(i+1))) return pair(substring(s, 0, i) + substring(s, i+1), (int) s.charAt(i+1)); return pair(s, 0); } static JMenuItem disableMenuItem(final JMenuItem mi) { if (mi != null) { swing(() -> { 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 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 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(); } // Try to get the quoting right... static String smartJoin(String[] args) { if (empty(args)) return ""; if (args.length == 1) return args[0]; String[] a = new String[args.length]; for (int i = 0; i < a.length; i++) a[i] = !isJavaIdentifier(args[i]) && !isQuoted(args[i]) ? quote(args[i]) : args[i]; return join(" ", a); } static String smartJoin(List args) { return smartJoin(toStringArray(args)); } static 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() { 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() { 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 int screenCount() { return l(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()); } 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 oneOf(List 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 A oneOf(A... l) { return oneOf(asList(l)); } static A collectionGet(Collection c, int idx) { if (c == null || idx < 0 || idx >= l(c)) return null; if (c instanceof List) return listGet((List) c, idx); Iterator it = c.iterator(); for (int i = 0; i < idx; i++) if (it.hasNext()) it.next(); else return null; return it.hasNext() ? it.next() : null; } static Pair entryToPair(Map.Entry e) { return mapEntryToPair(e); } static Set> entries(Map map) { return _entrySet(map); } static boolean isLowerCase(char c) { return Character.isLowerCase(c); } 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 boolean isLocalSnippetID(String snippetID) { return isSnippetID(snippetID) && isLocalSnippetID(psI(snippetID)); } static boolean isLocalSnippetID(long snippetID) { return snippetID >= 1000 && snippetID <= 9999; } 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 imageServerURL() { return or2(trim(loadTextFile(javaxDataDir("image-server-url.txt"))), "http://botcompany.de/images/raw/"); } static String muricaCredentialsQuery() { return htmlQuery(muricaCredentials()); } static boolean isGeneralFileServerSnippet(long id) { return id >= 1400000 && id < 1500000; } 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_noCookies() { return standardCredentials() + "&noCookies=1"; } static int loadPage_defaultTimeout = 60000; static ThreadLocal loadPage_charset = new ThreadLocal(); static boolean loadPage_allowGzip = true, loadPage_debug; static boolean loadPage_anonymous = false; // don't send computer ID static int loadPage_verboseness = 100000; static int loadPage_retries = 1; //60; // seconds static ThreadLocal loadPage_silent = new ThreadLocal(); static volatile int loadPage_forcedTimeout; // ms static ThreadLocal loadPage_forcedTimeout_byThread = new ThreadLocal(); // ms static ThreadLocal>> loadPage_responseHeaders = new ThreadLocal(); static ThreadLocal> loadPage_extraHeaders = new ThreadLocal(); static ThreadLocal loadPage_sizeLimit = new ThreadLocal(); public static String loadPageSilently(String url) { try { return loadPageSilently(new URL(loadPage_preprocess(url))); } catch (Exception __e) { throw rethrow(__e); } } public static String loadPageSilently(URL url) { try { if (!networkAllowanceTest(str(url))) throw fail("Not allowed: " + url); IOException e = null; for (int tries = 0; tries < loadPage_retries; tries++) try { URLConnection con = loadPage_openConnection(url); return loadPage(con, url); } catch (IOException _e) { e = _e; if (loadPage_debug) print(exceptionToStringShort(e)); if (tries < loadPage_retries-1) sleepSeconds(1); } throw e; } catch (Exception __e) { throw rethrow(__e); } } static String loadPage_preprocess(String url) { if (url.startsWith("tb/")) // don't think we use this anymore url = tb_mainServer() + "/" + url; if (url.indexOf("://") < 0) url = "http://" + url; return url; } static String loadPage(String url) { try { url = loadPage_preprocess(url); if (!isTrue(loadPage_silent.get())) printWithTime("Loading: " + hideCredentials(url)); return loadPageSilently(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } static String loadPage(URL url) { return loadPage(url.toExternalForm()); } static String loadPage(URLConnection con, URL url) throws IOException { return loadPage(con, url, true); } static String loadPage(URLConnection con, URL url, boolean addHeaders) { try { Map extraHeaders = getAndClearThreadLocal(loadPage_extraHeaders); 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 return loadPage(con); } catch (Exception __e) { throw rethrow(__e); } } // just download as string, no shenanigans or extra headers or ANYTHING static String loadPage(URLConnection con) { try { Long limit = optPar(loadPage_sizeLimit); URL url = con.getURL(); 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(); } } catch (Exception __e) { throw rethrow(__e); } } 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 boolean saveTextFileIfChanged(File f, String contents) { return saveTextFileIfDifferent(f, contents); } static File snippetTitle_cacheFile(String snippetID) { return javaxCachesDir("Snippet Titles/" + psI(snippetID)); } static String exceptionToStringShort(Throwable e) { try { 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; } catch (Throwable _e) { printStackTrace(_e); return "Error in exceptionToStringShort"; } } static String formatSnippetIDOpt(String s) { return isSnippetID(s) ? formatSnippetID(s) : s; } static A getWeakRef(Reference ref) { return ref == null ? null : ref.get(); } static x30_pkg.x30_util.BetterThreadLocal dm_current_generic_tl; static x30_pkg.x30_util.BetterThreadLocal dm_current_generic_tl() { if (dm_current_generic_tl == null) dm_current_generic_tl = vm_generalMap_getOrCreate("currentModule", () -> new x30_pkg.x30_util.BetterThreadLocal()); return dm_current_generic_tl; } static Runnable rPcall(Runnable r) { return r == null ? null : () -> { try { r.run(); } catch (Throwable __e) { pcallFail(__e); } }; } static Map newWeakMap() { return newWeakHashMap(); } static WeakReference newWeakReference(A a) { return a == null ? null : new WeakReference(a); } static A proxy(Class intrface, final Object target) { if (target == null) return null; if (isInstance(intrface, target)) return (A) target; return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, new proxy_InvocationHandler(target)); } static A proxy(Object target, Class intrface) { return proxy(intrface, target); } static 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 boolean isURL(String s) { return startsWithOneOf(s, "http://", "https://", "file:"); } static File imageSnippetCacheFile(String snippetID) { File dir = imageSnippetsCacheDir(); if (!loadBufferedImage_useImageCache) return null; return new File(dir, parseSnippetID(snippetID) + ".png"); } static String snippetImageURL_noHttps(String snippetID) { return snippetImageURL_noHttps(snippetID, "png"); } static String snippetImageURL_noHttps(String snippetID, String contentType) { return snippetImageURL(snippetID, contentType) .replace("https://www.botcompany.de:8443/", "http://www.botcompany.de:8080/") .replace("https://botcompany.de/", "http://botcompany.de/"); } static ThreadLocal>> loadBinaryPage_responseHeaders = new ThreadLocal(); static ThreadLocal> loadBinaryPage_extraHeaders = new ThreadLocal(); static byte[] loadBinaryPage(String url) { try { print("Loading " + url); return loadBinaryPage(loadPage_openConnection(new URL(url))); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage(URLConnection con) { try { Map extraHeaders = getAndClearThreadLocal(loadBinaryPage_extraHeaders); setHeaders(con); for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key)); return loadBinaryPage_noHeaders(con); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage_noHeaders(URLConnection con) { try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); loadBinaryPage_responseHeaders.set(con.getHeaderFields()); long len = 0; try { len = con.getContentLength/*Long*/(); } catch (Throwable e) { printStackTrace(e); } int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) println(" " + n + (len != 0 ? "/" + len : "") + " bytes loaded."); } inputStream.close(); return buf.toByteArray(); } catch (Exception __e) { throw rethrow(__e); } } /** writes safely (to temp file, then rename) */ public static byte[] saveBinaryFile(String fileName, byte[] contents) { try { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; try (FileOutputStream fileOutputStream = newFileOutputStream(tempFileName)) { fileOutputStream.write(contents); } if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); vmBus_send("wroteFile", file); return contents; } catch (Exception __e) { throw rethrow(__e); } } static byte[] saveBinaryFile(File fileName, byte[] contents) { return saveBinaryFile(fileName.getPath(), contents); } static File getGlobalCache() { File file = new File(javaxCachesDir(), "Binary Snippets"); file.mkdirs(); return file; } static long psI(String snippetID) { return parseSnippetID(snippetID); } 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 A addAndReturn(Collection c, A a) { if (c != null) c.add(a); return a; } static A addAndReturn(List c, int idx, A a) { if (c != null) c.add(idx, 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 { assertNotNull(file); 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 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 List allToString(Iterable c) { List l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static List allToString(Object[] c) { List l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static 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 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 boolean emptyString(String s) { return s == null || s.length() == 0; } static String getLookAndFeel() { return getClassName(UIManager.getLookAndFeel()); } static JComponent getInternalFrameTitlePaneComponent(JInternalFrame f) { return (JComponent) childWithClassNameEndingWith(f, "InternalFrameTitlePane"); } static A optCast(Class c, Object o) { return isInstance(c, o) ? (A) o : null; } static A _get(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } static Object _get(Object o, String field) { return get(o, field); } static Object _get(String field, Object o) { return get(o, field); } static A _get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } static MouseListener findComponentPopupMenuListener_gen(final JComponent c) { return c == null ? null : swing(() -> firstWithClassShortNamed("componentPopupMenu_Adapter", c.getMouseListeners()) ); } static boolean internalFrameActive(Component c) { final JInternalFrame f = getInternalFrame(c); return f != null && swing(new F0() { public Boolean get() { try { return f.isSelected(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return f.isSelected();"; }}); } static AutoCloseable tempSetTL(ThreadLocal tl, A a) { return tempSetThreadLocal(tl, a); } static AutoCloseable tempSetTL(BetterThreadLocal tl, A a) { return tempSetThreadLocalIfNecessary(tl, a); } static boolean isLetterOrDigit(char c) { return Character.isLetterOrDigit(c); } static Class hotwireOnce(String programID) { return hotwireCached(programID, false); } // supports the usual quotings (", variable length double brackets) except ' quoting static boolean isQuoted(String s) { if (isNormalQuoted(s)) return true; // use the exact version return isMultilineQuoted(s); } static String 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 String[] toStringArray(Collection c) { String[] a = new String[l(c)]; Iterator it = c.iterator(); for (int i = 0; i < l(a); i++) a[i] = it.next(); return a; } static String[] toStringArray(Object o) { if (o instanceof String[]) return (String[]) o; else if (o instanceof Collection) return toStringArray((Collection) o); else throw fail("Not a collection or array: " + getClassName(o)); } static Random customRandomizerForThisThread() { return customRandomizerForThisThread_tl().get(); } static A listGet(List l, int idx) { return l != null && idx >= 0 && idx < l.size() ? 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 { if (!isFile(file)) return null; return ImageIO.read(file); } catch (Exception __e) { throw rethrow(__e); } } static File localSnippetFile(long snippetID) { return localSnippetsDir(snippetID + ".text"); } static File localSnippetFile(String snippetID) { return localSnippetFile(parseSnippetID(snippetID)); } 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 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 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 String htmlQuery(Map params) { return empty(params) ? "" : "?" + makePostData(params); } static String htmlQuery(Object... data) { return empty(data) ? "" : "?" + makePostData(data); } static Object[] muricaCredentials() { String pass = muricaPassword(); return nempty(pass) ? new Object[] {"_pass", pass } : new Object[0]; } static File getProgramFile(String progID, String fileName) { if (new File(fileName).isAbsolute()) return new File(fileName); return new File(getProgramDir(progID), fileName); } static File getProgramFile(String fileName) { return getProgramFile(getProgramID(), fileName); } static String standardCredentials() { String user = standardCredentialsUser(); String pass = standardCredentialsPass(); if (nempty(user) && nempty(pass)) return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass); return ""; } static boolean networkAllowanceTest(String url) { return isAllowed("networkAllowanceTest", url); } static void sleepSeconds(double s) { if (s > 0) sleep(round(s*1000)); } static A printWithTime(A a) { return printWithTime("", a); } static A printWithTime(String s, A a) { print(hmsWithColons() + ": " + s, a); return a; } static A getAndClearThreadLocal(ThreadLocal tl) { A a = tl.get(); tl.set(null); return a; } static 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 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); } } return unquoteSingleOrDoubleQuotes(s); } 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 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 boolean saveTextFileIfDifferent(File f, String contents) { if (eq(loadTextFile(f), contents)) return false; // TODO: optimize { saveTextFile(f, contents); return true; } } 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 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 boolean isInstance(Class type, Object arg) { return type.isInstance(arg); } 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 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 A println(A a) { return print(a); } 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 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 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 byte[] loadBinaryFilePart(File file, long start, long end) { try { if (file == null) return null; 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 A firstWithClassShortNamed(String shortName, Iterable l) { if (l != null) for (A o : l) if (eq(shortClassName(o), shortName)) return o; return null; } static A firstWithClassShortNamed(String shortName, A[] l) { if (l != null) for (A o : l) if (eq(shortClassName(o), shortName)) return o; return null; } static AutoCloseable tempSetThreadLocalIfNecessary(ThreadLocal tl, A a) { if (tl == null) return null; A prev = tl.get(); if (eq(prev, a)) return null; tl.set(a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static AutoCloseable tempSetThreadLocalIfNecessary(BetterThreadLocal tl, A a) { if (tl == null) return null; A prev = tl.get(); if (eq(prev, a)) return null; tl.set(a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static TreeMap hotwireCached_cache = new TreeMap(); static Lock hotwireCached_lock = lock(); static Class hotwireCached(String programID) { return hotwireCached(programID, true); } static Class hotwireCached(String programID, boolean runMain) { return hotwireCached(programID, runMain, false); } static Class hotwireCached(String programID, boolean runMain, boolean dependent) { Lock __0 = hotwireCached_lock; lock(__0); try { programID = formatSnippetID(programID); Class c = hotwireCached_cache.get(programID); if (c == null) { c = hotwire(programID); if (dependent) makeDependent(c); if (runMain) callMain(c); hotwireCached_cache.put(programID, c); } return c; } finally { unlock(__0); } } static 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, n = s.length(); while (i < n && s.charAt(i) == '=') ++i; return i < n && s.charAt(i) == '['; } static ThreadLocal customRandomizerForThisThread_tl = new ThreadLocal(); static ThreadLocal customRandomizerForThisThread_tl() { return customRandomizerForThisThread_tl; } static String replacePrefix(String prefix, String replacement, String s) { if (!startsWith(s, prefix)) return s; return replacement + substring(s, l(prefix)); } static boolean isFile(File f) { return f != null && f.isFile(); } static boolean isFile(String path) { return isFile(newFile(path)); } static File localSnippetsDir() { return javaxDataDir("Personal Programs"); } static File localSnippetsDir(String sub) { return newFile(localSnippetsDir(), sub); } static String getOneLineFileInfoField(File f, String field) { File infoFile = associatedInfosFile(f); List lines = lines(loadTextFile(infoFile)); return firstStartingWithIC_drop(lines, field + ": "); } static File fileInSameDir(File f, String newName) { return newFile(parentFile(f), newName); } static List takeFirst(List l, int n) { return l(l) <= n ? l : newSubListOrSame(l, 0, n); } static List takeFirst(int n, List l) { return takeFirst(l, n); } static String takeFirst(int n, String s) { return substring(s, 0, n); } static String takeFirst(String s, int n) { return substring(s, 0, n); } static CharSequence takeFirst(int n, CharSequence s) { return subCharSequence(s, 0, n); } static List takeFirst(int n, Iterator it) { if (it == null) return null; List l = new ArrayList(); for (int _repeat_0 = 0; _repeat_0 < n; _repeat_0++) { if (it.hasNext()) l.add(it.next()); else break; } return l; } static List takeFirst(int n, Iterable i) { if (i == null) return null; return i == null ? null : takeFirst(n, i.iterator()); } static List takeFirst(int n, IterableIterator i) { return takeFirst(n, (Iterator) i); } static int[] takeFirst(int n, int[] a) { return takeFirstOfIntArray(n, a); } static 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 int smartLastIndexOf(String s, char c) { if (s == null) return 0; int i = s.lastIndexOf(c); return i >= 0 ? i : l(s); } static int smartLastIndexOf(List l, A sub) { int i = lastIndexOf(l, sub); return i < 0 ? l(l) : i; } static ThreadLocal> checkFileNotTooBigToRead_tl = new ThreadLocal(); static void checkFileNotTooBigToRead(File f) { callF(checkFileNotTooBigToRead_tl.get(), f); } 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 makePostData(Map map) { StringBuilder buf = new StringBuilder(); for (Map.Entry e : castMapToMapO(map).entrySet()) { String key = (String) (e.getKey()); Object val = e.getValue(); if (val != null) { String value = str(val); if (nempty(buf)) buf.append("&"); buf.append(urlencode(key)).append("=").append(urlencode(/*escapeMultichars*/(value))); } } return str(buf); } static String makePostData(Object... params) { StringBuilder buf = new StringBuilder(); int n = l(params); for (int i = 0; i+1 < n; i += 2) { String key = (String) (params[i]); Object val = params[i+1]; if (val != null) { String value = str(val); if (nempty(buf)) buf.append("&"); buf.append(urlencode(key)).append("=").append(urlencode(/*escapeMultichars*/(value))); } } return str(buf); } static File getProgramDir() { return programDir(); } static File getProgramDir(String snippetID) { return programDir(snippetID); } 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 String urlencode(String x) { try { return URLEncoder.encode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } 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 volatile boolean sleep_noSleep = false; static void sleep(long ms) { ping(); if (ms < 0) return; // allow spin locks if (isAWTThread() && ms > 100) throw fail("Should not sleep on AWT thread"); try { Thread.sleep(ms); } catch (Exception e) { throw new RuntimeException(e); } } static void sleep() { try { if (sleep_noSleep) throw fail("nosleep"); print("Sleeping."); sleepQuietly(); } catch (Exception __e) { throw rethrow(__e); } } static 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 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 String unquoteSingleOrDoubleQuotes(String s) { if (s == null) return null; 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 } 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 byte[] toUtf8(String s) { try { return s.getBytes(utf8charset()); } catch (Exception __e) { throw rethrow(__e); } } static Map compileRegexp_cache = syncMRUCache(10); static java.util.regex.Pattern compileRegexp(String pat) { java.util.regex.Pattern p = compileRegexp_cache.get(pat); if (p == null) { compileRegexp_cache.put(pat, p = java.util.regex.Pattern.compile(pat)); } return p; } /** 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()); try { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); printWriter.print(contents); printWriter.close(); } finally { _close(fileOutputStream); }} 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 Throwable getException(Runnable r) { try { callF(r); return null; } catch (Throwable e) { return e; } } static String dropSuffix(String suffix, String s) { return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } 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; } 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 void _registerIO(Object object, String path, boolean opened) { } static File muricaPasswordFile() { return new File(javaxSecretDir(), "murica/muricaPasswordFile"); } // TODO: optimize to x-(x%n) in case that's the same thing // (or x-mod(x,n)?) static int roundDownTo(int n, int x) { return x/n*n; } static long roundDownTo(long n, long x) { return x/n*n; } static double roundDownTo(double n, double x) { return floor(x/n)*n; } 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 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 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 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 List getComponents(final Component c) { return !(c instanceof Container) ? emptyList() : asList(swing(new F0() { public Component[] get() { try { return ((Container) c).getComponents(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return ((Container) c).getComponents();"; }})); } // custom mainClass only works with hotwire_here static Class hotwire(String src) { return hotwire(src, __1 -> mainClassNameForClassLoader(__1)); } static Class hotwire(String src, IF1 calculateMainClass) { assertFalse(_inCore()); Class j = getJavaX(); if (isAndroid()) { synchronized(j) { // hopefully this goes well... List libraries = new ArrayList(); File srcDir = (File) call(j, "transpileMain", src, libraries); if (srcDir == null) throw fail("transpileMain returned null (src=" + quote(src) + ")"); Object androidContext = get(j, "androidContext"); return (Class) call(j, "loadx2android", srcDir, src); } } else { Class c = (Class) (call(j, "hotwire", src)); hotwire_copyOver(c); return c; } } static Object makeDependent_postProcess; static void makeDependent(Object c) { if (c == null) return; assertTrue("Not a class", c instanceof Class); dependentClasses(); // cleans up the list hotwire_classes.add(new WeakReference(c)); Object local_log = getOpt(mc(), "local_log"); if (local_log != null) setOpt(c, "local_log", local_log); /*if (isTrue(getOpt(c, 'ping_actions_shareable))) setOpt(c, +ping_actions);*/ Object print_byThread = getOpt(mc(), "print_byThread"); if (print_byThread != null) setOpt(c, "print_byThread", print_byThread); callF(makeDependent_postProcess, c); } static A callMain(A c, String... args) { callOpt(c, "main", new Object[] {args}); return c; } static void callMain() { callMain(mc()); } static char lastChar(String s) { return empty(s) ? '\0' : s.charAt(l(s)-1); } static File associatedInfosFile(File f) { return replaceExtension(f, ".infos"); } static String lines(Iterable lines) { return fromLines(lines); } static String lines(Object[] lines) { return fromLines(asList(lines)); } static List lines(String s) { return toLines(s); } // convenience map call static String lines(Iterable l, IF1 f) { return mapToLines(l, f); } static String firstStartingWithIC_drop(Collection l, final String prefix) { for (String s : unnull(l)) if (swic(s, prefix)) return substring(s, l(prefix)); return null; } static String firstStartingWithIC_drop(String prefix, Collection l) { return firstStartingWithIC_drop(l, prefix); } static File parentFile(File f) { return dirOfFile(f); } static List newSubListOrSame(List l, int startIndex) { return newSubListOrSame(l, startIndex, l(l)); } static List newSubListOrSame(List l, int startIndex, int endIndex) { if (l == null) return null; int n = l(l); startIndex = max(0, startIndex); endIndex = min(n, endIndex); if (startIndex >= endIndex) return ll(); if (startIndex == 0 && endIndex == n) return l; return cloneList(l.subList(startIndex, endIndex)); } static List newSubListOrSame(List l, IntRange r) { return newSubListOrSame(l, r.start, r.end); } static 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 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 Map castMapToMapO(Map map) { return map; } 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 oneOfTheFiles(String... paths) { if (paths != null) for (String path : paths) if (fileExists(path)) return newFile(path); return null; } static File oneOfTheFiles(File... files) { return oneOfTheFiles(asList(files)); } static File oneOfTheFiles(Iterable files) { if (files != null) for (File f : files) if (fileExists(f)) return f; return null; } static 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 Object sleepQuietly_monitor = new Object(); static void sleepQuietly() { try { assertFalse(isAWTThread()); synchronized(sleepQuietly_monitor) { sleepQuietly_monitor.wait(); } } catch (Exception __e) { throw rethrow(__e); } } static String roundBracket(String s) { return "(" + s + ")"; } static String roundBracket(Object s) { return roundBracket(str(s)); } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } 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 _registerIOWrap(A wrapper, Object wrapped) { return wrapper; } 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 Map syncMRUCache(int size) { return synchroMap(new MRUCache(size)); } 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 boolean possibleMD5(String s) { return isMD5(s); } static double floor(double d) { return Math.floor(d); } 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 A[] dropLast(A[] a) { return dropLast(a, 1); } static A[] dropLast(A[] a, int n) { if (a == null) return null; n = Math.min(n, a.length); A[] b = arrayOfSameType(a, a.length-n); System.arraycopy(a, 0, b, 0, b.length); return b; } static List dropLast(List l) { return subList(l, 0, l(l)-1); } static List dropLast(int n, List l) { return subList(l, 0, l(l)-n); } static List dropLast(Iterable l) { return dropLast(asList(l)); } static String dropLast(String s) { return substring(s, 0, l(s)-1); } static String dropLast(String s, int n) { return substring(s, 0, l(s)-n); } static String dropLast(int n, String s) { return dropLast(s, n); } static boolean _inCore() { return false; } static List hotwire_copyOver_after = synchroList(); static void hotwire_copyOver(Class c) { // TODO: make a mechanism for making such "inheritable" fields for (String field : ll("print_log", "print_silent", "androidContext", "_userHome")) setOptIfNotNull(c, field, getOpt(mc(), field)); setOptIfNotNull(c, "mainBot" , getMainBot()); setOpt(c, "creator_class" , new WeakReference(mc())); pcallFAll(hotwire_copyOver_after, c); } static List dependentClasses() { return cleanUpAndGetWeakReferencesList(hotwire_classes); } static Field setOpt_findField(Class c, String field) { HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } return map.get(field); } static void setOpt(Object o, String field, Object value) { try { if (o == null) return; Class c = o.getClass(); HashMap map; if (getOpt_cache == null) map = getOpt_makeCache(c); // in class init else synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } if (map == getOpt_special) { if (o instanceof Class) { setOpt((Class) o, field, value); return; } // It's probably a subclass of Map. Use raw method. 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 List> hotwire_classes = synchroList(); static Class hotwireDependent(String src) { Class c = hotwire(src); makeDependent(c); return c; } 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); } // usually L static String fromLines(Iterable lines) { StringBuilder buf = new StringBuilder(); if (lines != null) for (Object line : lines) buf.append(str(line)).append('\n'); return buf.toString(); } static String fromLines(String... lines) { return fromLines(asList(lines)); } static IterableIterator toLines(File f) { return linesFromFile(f); } static List toLines(String s) { List lines = new ArrayList(); if (s == null) return lines; int start = 0; while (true) { int i = toLines_nextLineBreak(s, start); if (i < 0) { if (s.length() > start) lines.add(s.substring(start)); break; } lines.add(s.substring(start, i)); if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n') i += 2; else ++i; start = i; } return lines; } static int toLines_nextLineBreak(String s, int start) { int n = s.length(); for (int i = start; i < n; i++) { char c = s.charAt(i); if (c == '\r' || c == '\n') return i; } return -1; } static List mapToLines(Map map) { List l = new ArrayList(); for (Object key : keys(map)) l.add(str(key) + " = " + str(map.get(key))); return l; } static String mapToLines(Map map, Object f) { return lines(map(map, f)); } static String mapToLines(Object f, Map map) { return lines(map(map, f)); } static String mapToLines(Object f, Iterable l) { return lines(map(f, l)); } static String mapToLines(Iterable l, IF1 f) { return mapToLines((Object) f, l); } static String mapToLines(IF1 f, Iterable l) { return mapToLines((Object) f, l); } static String mapToLines(Map map, IF2 f) { return lines(map(map, f)); } static String mapToLines(IF1 f, A data1, A... moreData) { return lines(map(f, data1, moreData)); } static 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 File dirOfFile(File f) { return f == null ? null : f.getParentFile(); } 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 boolean sameSnippetID(String a, String b) { if (!isSnippetID(a) || !isSnippetID(b)) return false; return parseSnippetID(a) == parseSnippetID(b); } static volatile String caseID_caseID; static String caseID() { return caseID_caseID; } static void caseID(String id) { caseID_caseID = id; } static boolean fileExists(String path) { return path != null && new File(path).exists(); } static boolean fileExists(File f) { return f != null && f.exists(); } static File computerIDFile() { return javaxDataDir("Basic Info/computer-id.txt"); } 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 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 isMD5(String s) { return l(s) == 32 && isLowerHexString(s); } static char stringToChar(String s) { if (l(s) != 1) throw fail("bad stringToChar: " + s); return firstChar(s); } static A[] arrayOfSameType(A[] a, int n) { return newObjectArrayOfSameType(a, n); } static void setOptIfNotNull(Object o, String field, Object value) { if (value != null) setOpt(o, field, value); } static Object mainBot; static Object getMainBot() { return mainBot; } static List cleanUpAndGetWeakReferencesList(List> l) { if (l == null) return null; synchronized(l) { List out = new ArrayList(); for (int i = 0; i < l(l); i++) { A a = l.get(i).get(); if (a == null) l.remove(i--); else out.add(a); } return out; } } // 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 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; } throw e; } } static A setDyn(A o, String key, Object value) { setDynObjectValue(o, key, value); return o; } static void setDyn(IMeta o, String key, Object value) { metaMapPut(o, key, value); } static String fileExtension(File f) { if (f == null) return null; return fileExtension(f.getName()); } static String fileExtension(String s) { return substring(s, smartLastIndexOf(s, '.')); } static String 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 CloseableIterableIterator linesFromFile(File f) { return linesFromFile(f, null); } static CloseableIterableIterator linesFromFile(File f, IResourceHolder resourceHolder) { try { if (!fileExists(f)) return emptyCloseableIterableIterator(); if (ewic(f.getName(), ".gz")) return linesFromReader(utf8bufferedReader(newGZIPInputStream(f)), resourceHolder); return linesFromReader(utf8bufferedReader(f), resourceHolder); } catch (Exception __e) { throw rethrow(__e); } } static CloseableIterableIterator linesFromFile(String path) { return linesFromFile(path, null); } static CloseableIterableIterator linesFromFile(String path, IResourceHolder resourceHolder) { return linesFromFile(newFile(path), resourceHolder); } static boolean startsWithIgnoreCase(String a, String b) { return regionMatchesIC(a, 0, b, 0, b.length()); } 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 char firstChar(String s) { return s.charAt(0); } static A[] newObjectArrayOfSameType(A[] a) { return newObjectArrayOfSameType(a, a.length); } static A[] newObjectArrayOfSameType(A[] a, int n) { return (A[]) Array.newInstance(a.getClass().getComponentType(), n); } static boolean isTrueOrYes(Object o) { return isTrueOpt(o) || o instanceof String && (eqicOneOf(((String) o), "1", "t", "true") || isYes(((String) o))); } static LinkedHashMap asLinkedHashMap(Map map) { if (map instanceof LinkedHashMap) return (LinkedHashMap) map; LinkedHashMap m = new LinkedHashMap(); if (map != null) synchronized(collectionMutex(map)) { m.putAll(map); } return m; } static void setDynObjectValue(DynamicObject o, String field, Object value) { dynamicObject_setRawFieldValue(o, field, value); } static void metaMapPut(IMeta o, Object key, Object value) { { if (o != null) 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 String addPrefixIfNotEmpty2(String prefix, String s) { return empty(s) ? "" : addPrefix(prefix, s); } static CloseableIterableIterator emptyCloseableIterableIterator_instance = new CloseableIterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static CloseableIterableIterator emptyCloseableIterableIterator() { return emptyCloseableIterableIterator_instance; } static CloseableIterableIterator linesFromReader(Reader r) { return linesFromReader(r, null); } static CloseableIterableIterator linesFromReader(Reader r, IResourceHolder resourceHolder) { final BufferedReader br = bufferedReader(r); return holdResource(resourceHolder, iteratorFromFunction_f0_autoCloseable(new F0() { public String get() { try { return readLineFromReaderWithClose(br); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return readLineFromReaderWithClose(br);"; }}, _wrapIOCloseable(r))); } static BufferedReader utf8bufferedReader(InputStream in) { try { return in == null ? null : bufferedReader(_registerIOWrap(new InputStreamReader(in, "UTF-8"), in)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedReader utf8bufferedReader(File f) { try { return utf8bufferedReader(newFileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static 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 boolean eqicOneOf(String s, String... l) { for (String x : l) if (eqic(s, x)) return true; return false; } static List isYes_yesses = litlist("y", "yes", "yeah", "y", "yup", "yo", "corect", "sure", "ok", "afirmative"); // << collapsed words, so "corect" means "correct" static boolean isYes(String s) { return isYes_yesses.contains(collapseWord(toLowerCase(firstWord2(s)))); } static 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 IMeta initIMeta(Object o) { if (o == null) return null; if (o instanceof IMeta) return ((IMeta) o); if (o instanceof JComponent) return initMetaOfJComponent((JComponent) o); // This is not really used. Try to use BufferedImageWithMeta instead if (o instanceof BufferedImage) return optCast(IMeta.class, ((BufferedImage) o).getProperty("meta")); return null; } static String addPrefix(String prefix, String s) { return s.startsWith(prefix) ? s : prefix + s; } 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 BufferedReader bufferedReader(File f) { return utf8bufferedReader(f); } static A holdResource(IResourceHolder holder, A a) { { if (holder != null) holder.add(a); } return a; } static CloseableIterableIterator iteratorFromFunction_f0_autoCloseable(final F0 f, final AutoCloseable closeable) { class IFF2 extends CloseableIterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } public void close() throws Exception { if (closeable != null) closeable.close(); } }; return new IFF2(); } static String readLineFromReaderWithClose(BufferedReader r) { try { String s = r.readLine(); if (s == null) r.close(); return s; } catch (Exception __e) { throw rethrow(__e); } } static AutoCloseable _wrapIOCloseable(final AutoCloseable c) { return c == null ? null : new AutoCloseable() { public String toString() { return "c.close();\r\n _registerIO(c, null, false);"; } public void close() throws Exception { c.close(); _registerIO(c, null, false); }}; } static 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 ArrayList litlist(A... a) { ArrayList l = new ArrayList(a.length); for (A x : a) l.add(x); return l; } static String collapseWord(String s) { if (s == null) return ""; StringBuilder buf = new StringBuilder(); for (int i = 0; i < l(s); i++) if (i == 0 || !charactersEqualIC(s.charAt(i), s.charAt(i-1))) buf.append(s.charAt(i)); return buf.toString(); } static List toLowerCase(List strings) { List x = new ArrayList(); for (String s : strings) x.add(s.toLowerCase()); return x; } static String[] toLowerCase(String[] strings) { String[] x = new String[l(strings)]; for (int i = 0; i < l(strings); i++) x[i] = strings[i].toLowerCase(); return x; } static String toLowerCase(String s) { return s == null ? "" : s.toLowerCase(); } static String firstWord2(String s) { s = xltrim(s); if (empty(s)) return ""; if (isLetterOrDigit(first(s))) return takeCharsWhile(__36 -> isLetterOrDigit(__36), s); else return "" + first(s); } static LinkedHashMap syncMapPut2_createLinkedHashMap(LinkedHashMap map, A key, B value) { if (key != null) if (value != null) { if (map == null) map = new LinkedHashMap(); synchronized(collectionMutex(map)) { map.put(key, value); } } else if (map != null) synchronized(collectionMutex(map)) { map.remove(key); } return map; } static 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; } 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 f, String s) { return takeCharsWhile(s, f); } 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 abstract class VF1 implements IVF1 { public abstract void get(A a); } // Meta - a "minimal" approach to adding meta-level to Java objects static class Meta implements IMeta { // 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(); } final boolean scaffolded(){ return scaffoldingEnabled(); } boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); } boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); } // Implementing setMetaToString String toString_base() { return super.toString(); } public String toString() { Object o = metaGet("toString", this); if (o instanceof String) return ((String) o); if (o instanceof IF1) return str(((IF1) o).get(this)); return toString_base(); } } static class Hi15Image extends WAndHImpl implements MakesBufferedImage { short[] pixels; Hi15Image() {} // for persistence Hi15Image(int w, int h, short[] pixels) { this.pixels = pixels; this.h = h; this.w = w;} // TODO: optimize! Hi15Image(BufferedImage img) { this(new RGBImage(img)); } Hi15Image(RGBImage img) { w = img.getWidth(); h = img.getHeight(); pixels = new short[w*h]; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) pixels[y*w+x] = rgbIntToHi15(img.getInt(x, y)); } Hi15Image(MakesBufferedImage img) { this(toBufferedImage(img)); } RGBImage toRGB() { RGBImage img = new RGBImage(w, h); int i = 0, w = this.w, h = this.h; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) img.setPixel(x, y, hi15ToRGBInt(pixels[i++])); return img; } public BufferedImage getBufferedImage() { return toRGB().getBufferedImage(); } // 0 to 32767 final short getHi15Color(int x, int y){ return getHi15Pixel(x, y); } short getHi15Pixel(int x, int y) { return pixels[y*w+x]; } // idx = index in pixel array public short getHi15Pixel_noRangeCheck(int idx) { return pixels[idx]; } static int perChannelBitCountMask(int bitCount) { int mask = ((~0x1F) >> bitCount) & 0x1F; int mask2 = (mask << 10) | (mask << 5) | mask; return mask2; } void reducePerChannelBitCount(int bitCount) { if (bitCount >= 5) return; int mask = perChannelBitCountMask(bitCount); short[] pixels = this.pixels; for (int i = 0; i < pixels.length; i++) pixels[i] = (short) (pixels[i] & mask); } void reduceColorCount(int numColors) { assertPowerOfTwo(numColors); int bits = dualLog(numColors); assertDivisibleBy(3, bits); int bitsPerChannel = bits/3; reducePerChannelBitCount(bitsPerChannel); } } static class Var implements IVar, ISetter { Var() {} Var(A v) { this.v = v;} A v; // you can access this directly if you use one thread public synchronized void set(A a) { if (v != a) { v = a; notifyAll(); } } public synchronized A get() { return v; } public synchronized boolean has() { return v != null; } public void clear() { set(null); } public synchronized A getAndSet(A a) { var value = v; set(a); return value; } public IF0 getter() { return () -> get(); } public IVF1 setter() { return __60 -> set(__60); } public String toString() { return str(this.get()); } } static class Scorer { double score, total; List successes, errors; // set to non-null if you want them filled boolean verboseFailures, verboseAll; final void add(double score){ addZeroToOne(score); } void addZeroToOne(double score) { ++total; this.score += clamp(score, 0, 1); } void addZeroToOneError(double error) { addZeroToOne(1-error); } void addError() { add(false); } void addError(A info) { add(false, info); } void error(A info) { addError(info); } void addOK() { add(true); } void addOK(A info) { add(true, info); } void ok() { addOK(); } void ok(A info) { addOK(info); } boolean add(boolean correct) { ++total; if (correct) ++score; return correct; } boolean add(boolean correct, A info) { main.add(correct ? successes : errors, info); if (verboseAll || verboseFailures && !correct) _print((correct ? "[GOOD] " : "[BAD] ") + info); return add(correct); } // works if you use Scorer or Scorer void eq(Object a, Object b) { if (_eq(a, b)) add(true); else add(false, (A) (a + " != " + b)); } void print() { main.print(toString()); } public String toString() { return formatDouble(ratioToPercent(score, total), 1) + "% correct (n=" + formatDouble(total, 1) + ")"; } double get() { return ratioToPercent(score, total); } double percentScore() { return get(); } double score() { return get(); } boolean allCorrect() { return score == total; } void add(Scorer scorer) { if (scorer == null) return; total += scorer.total; score += scorer.score; addAll(successes, scorer.successes); addAll(errors, scorer.errors); } void collectErrors() { errors = new ArrayList(); } void collectSuccesses() { successes = new ArrayList(); } } // Cut a list off at a certain point in order to stay within a // defined "budget" (according to a user-defined function giving // a price for each element) static class CutListToBudget { final public CutListToBudget setMaxPrice(double maxPrice){ return maxPrice(maxPrice); } public CutListToBudget maxPrice(double maxPrice) { this.maxPrice = maxPrice; return this; } final public double getMaxPrice(){ return maxPrice(); } public double maxPrice() { return maxPrice; } double maxPrice; final public CutListToBudget setInputList(Iterable inputList){ return inputList(inputList); } public CutListToBudget inputList(Iterable inputList) { this.inputList = inputList; return this; } final public Iterable getInputList(){ return inputList(); } public Iterable inputList() { return inputList; } Iterable inputList; final public List getOutputList(){ return outputList(); } public List outputList() { return outputList; } List outputList; final public double getFinalPrice(){ return finalPrice(); } public double finalPrice() { return finalPrice; } double finalPrice; // cut up last element to fill remaining budget? final public CutListToBudget setAllowPartial(boolean allowPartial){ return allowPartial(allowPartial); } public CutListToBudget allowPartial(boolean allowPartial) { this.allowPartial = allowPartial; return this; } final public boolean getAllowPartial(){ return allowPartial(); } public boolean allowPartial() { return allowPartial; } boolean allowPartial = false; // last element of outputList before reduction // (if there was a reduction) final public A getFullLastElement(){ return fullLastElement(); } public A fullLastElement() { return fullLastElement; } A fullLastElement; transient IF1 getPrice; double getPrice(A element) { return getPrice != null ? getPrice.get(element) : getPrice_base(element); } final double getPrice_fallback(IF1 _f, A element) { return _f != null ? _f.get(element) : getPrice_base(element); } double getPrice_base(A element) { throw unimplemented(); } // reduce an element to fit a budget // can return null if no reduced element is possible transient IF2 reduceElement; A reduceElement(A element, double budget) { return reduceElement != null ? reduceElement.get(element, budget) : reduceElement_base(element, budget); } final A reduceElement_fallback(IF2 _f, A element, double budget) { return _f != null ? _f.get(element, budget) : reduceElement_base(element, budget); } A reduceElement_base(A element, double budget) { return null; } CutListToBudget(IF1 getPrice) { this.getPrice = getPrice;} CutListToBudget(IF1 getPrice, double maxPrice, Iterable inputList) { this.inputList = inputList; this.maxPrice = maxPrice; this.getPrice = getPrice;} public void run() { try { outputList = new ArrayList(); finalPrice = 0; for (var element : unnullForIteration(inputList)) { double price = getPrice(element); if (finalPrice + price > maxPrice) { if (allowPartial) { A partial = reduceElement(element, maxPrice-finalPrice); if (partial != null) { fullLastElement = element; finalPrice += getPrice(partial); outputList.add(partial); if (finalPrice > maxPrice) throw fail("reduceElement failure (over budget)"); } } break; } finalPrice += price; outputList.add(element); } } catch (Exception __e) { throw rethrow(__e); } } List get() { if (outputList == null) run(); return outputList; } CutListToBudget allowPartial(IF2 reduceElement) { this.reduceElement = reduceElement; return allowPartial(true); } A lastElement() { return last(get()); } Percent lastElementKeptPercentage() { return fullLastElement == null ? null : new Percent(getPrice(lastElement())/getPrice(fullLastElement)*100); } } static class GeneralSSIList extends AbstractSSIList { GeneralSSIList() { init(); } GeneralSSIList(Collection l) { super(l); } @Override AbstractSSI importSSI(AbstractSSI ssi) { return ssi; } } interface IImageRegion extends INumberOfPixels, IPixelSet { // REQUIRED METHODS // smallest rectangle that all of the region's pixels are contained in public Rect bounds(); public IterableIterator pixelIterator(); public boolean contains(int x, int y); // OPTIONAL METHODS // get whole image that the region refers to public default Img image() { return null; } // which object made this public default Object creator() { return null; } public default int indexInCreator() { return 0; } // was diagonal walking enabled? public default Boolean createdWithDiagonals() { return null; } public default int nPixels(){ return numberOfPixels(); } public default int numberOfPixels() { return l(pixelIterator()); } public default Pt firstPixel() { return first(pixelIterator()); } // gets the region's color (what exactly this means is defined // by the creator of this object) public default Color color() { return null; } public default int brightness() { return -1; } public default OnePathWithOrigin outline() { return g22_regionOutline(this); } default Image2B toImage2B() { return new RegionToImage2B(this).get(); } default int[] pixelsAsIntArray() { throw unimplemented(); } // CONVENIENCE/ORTHOGONALITY METHODS default IPixelSet pixelSet() { return this; } } static final class BWImage extends Meta implements MakesBufferedImage, IBWImage { int width, height; byte[] pixels; // color returned when getPixel is called with a position outside the actual image float borderColor = 0.0f; // for unstructure() BWImage() {} // BLACK! BWImage(int width, int height) { this.height = height; this.width = width; pixels = new byte[width*height]; } BWImage(int width, int height, float brightness) { this.height = height; this.width = width; pixels = new byte[width*height]; fillArrayUnlessZero(pixels, _toByte(brightness)); } BWImage(int width, int height, float[] pixels) { this.pixels = new byte[pixels.length]; this.height = height; this.width = width; for (int i = 0; i < pixels.length; i++) this.pixels[i] = _toByte(pixels[i]); } public BWImage(int width, int height, byte[] pixels) { this.height = height; this.width = width; this.pixels = pixels; } public BWImage(BWImage image) { width = image.getWidth(); height = image.getHeight(); byte[] pixels = this.pixels = new byte[width*height]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) pixels[y*width+x] = image.getByte(x, y); } // TODO: optimize! BWImage(RGBImage image) { width = image.getWidth(); height = image.getHeight(); byte[] pixels = this.pixels = new byte[height*width]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { RGB rgb = image.getRGB(x, y); pixels[y*width+x] = BWImage._toByte(rgb.getBrightness()); } } /*public BWImage(BufferedImage image) { this(new RGBImage(image)); }*/ BWImage(BufferedImage image) { try { width = image.getWidth(); height = image.getHeight(); int[] pixels = new int[width*height]; byte[] bytePixels = this.pixels = new byte[width*height]; PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width); if (!pixelGrabber.grabPixels()) throw fail("Could not grab pixels"); int n = width*height; for (int i = 0; i < n; i++) { //bytePixels[i] = pixelToByte(pixels[i]); int packed = pixels[i]; /*float r = ((packed >> 16) & 0xFF)/255f; float g = ((packed >> 8) & 0xFF)/255f; float b = (packed & 0xFF)/255f; bytePixels[i] = (byte) iround((r+g+b)/3.0f*255f);*/ int r = ((packed >> 16) & 0xFF); int g = ((packed >> 8) & 0xFF); int b = (packed & 0xFF); bytePixels[i] = (byte) ((r+g+b+1)/3); } } catch (Exception __e) { throw rethrow(__e); } } // TODO: does it exactly match the other method? (asRGB+getBrightness+_toByte) static byte pixelToByte(int packed) { /*int r = (packed >> 16) & 0xFF; int g = (packed >> 8) & 0xFF; int b = packed & 0xFF; ret (byte) ((r+g+b)/3.0f);*/ float r = ((packed >> 16) & 0xFF)/255f; float g = ((packed >> 8) & 0xFF)/255f; float b = (packed & 0xFF)/255f; return (byte) ((r+g+b)/3.0f*255f); } public byte getByte(int x, int y) { return inRange(x, y) ? getByte_noRangeCheck(x, y) : _toByte(borderColor); } // pretty bad function name // gets brightness (0 to 255) at pixel public int getInt(int x, int y) { return ubyteToInt(getByte(x, y)); } // idx = index in pixel array public int getInt_noRangeCheck(int idx) { return ubyteToInt(pixels[idx]); } // gets brightness (0 to 255) at pixel with default if out of image public int getInt(int x, int y, int defaultValue) { return inRange(x, y) ? getInt(x, y) : defaultValue; } public double averageBrightness() { double sum = 0; int n = width*height; for (int i = 0; i < n; i++) sum += getInt_noRangeCheck(i); return sum/n; } public float minimumBrightness() { float min = 1; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) min = Math.min(min, getPixel(x, y)); return min; } public float maximumBrightness() { float max = 0; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) max = Math.max(max, getPixel(x, y)); return max; } float getPixel(int x, int y) { return inRange(x, y) ? _toFloat(getByte(x,y )) : borderColor; } public float getFloatPixel(int x, int y) { return getPixel(x, y); } float getPixel(Pt p) { return getPixel(p.x, p.y); } static byte _toByte(float pixel) { return (byte) (pixel*255f); } static float _toFloat(byte pixel) { return (((int) pixel) & 255)/255f; } private boolean inRange(int x, int y) { return x >= 0 && x < width && y >= 0 && y < height; } public int getWidth() { return width; } public int getHeight() { return height; } public RGBImage toRGB() { int[] rgbs = new int[width*height]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { int b = getByte(x, y) & 0xFF; rgbs[y*width+x] = 0xFF000000 | b*0x010101; } return new RGBImage(width, height, rgbs); } public RGBImage toRGB_slow() { RGB[] rgbs = new RGB[width*height]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { float p = getPixel(x, y); rgbs[y*width+x] = new RGB(p, p, p); } return new RGBImage(width, height, rgbs); } public BWImage clip(int x, int y, int w, int h) { return clip(new Rectangle(x, y, w, h)); } private Rectangle fixClipRect(Rectangle r) { return r.intersection(new Rectangle(0, 0, width, height)); } BWImage clip(Rect r) { return clip(r.getRectangle()); } /** this should be multithread-safe */ public BWImage clip(Rectangle r) { r = fixClipRect(r); byte[] newPixels = new byte[r.height*r.width]; for (int y = 0; y < r.height; y++) for (int x = 0; x < r.width; x++) newPixels[y*r.width+x] = getByte(r.x+x, r.y+y); return new BWImage(r.width, r.height, newPixels); } public void setPixel(int x, int y, float brightness) { setByte(x, y, _toByte(fixPixel(brightness))); } // i = 0 to 255 public void setInt(int x, int y, int i) { setByte(x, y, (byte) limitToUByte(i)); } public void setInt(Pt p, int i) { setInt(p.x, p.y, i); } public void setByte(int x, int y, byte b) { if (x >= 0 && x < width && y >= 0 && y < height) pixels[y*width+x] = b; } byte getByte_noRangeCheck(int x, int y) { return pixels[y*width+x]; } public void setByte(int x, int y, int brightness) { setByte(x, y, (byte) brightness); } private float fixPixel(float pixel) { return Math.max(0, Math.min(1, pixel)); } public float getBorderColor() { return borderColor; } public void setBorderColor(float borderColor) { this.borderColor = borderColor; } public boolean anyPixelBrighterThan(double threshold) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) if (getPixel(x, y) > threshold) return true; return false; } int[] getRGBPixels() { int n = width*height; int[] out = new int[n]; for (int i = 0; i < n; i++) { var b = ubyteToInt(pixels[i]); b |= (b << 8) | (b << 16); out[i] = b | 0xFF000000; } return out; } public BufferedImage getBufferedImage() { return bufferedImage(getRGBPixels(), width, height); } byte[] getBytes() { return pixels; } } static class SinglePixelPosterizer implements IPosterizer { int brightnessLevels; double factor1, factor2; final public SinglePixelPosterizer setDrift(double drift){ return drift(drift); } public SinglePixelPosterizer drift(double drift) { this.drift = drift; return this; } final public double getDrift(){ return drift(); } public double drift() { return drift; } double drift; // between -1 and 1, normally final public SinglePixelPosterizer setAntiDrift(double antiDrift){ return antiDrift(antiDrift); } public SinglePixelPosterizer antiDrift(double antiDrift) { this.antiDrift = antiDrift; return this; } final public double getAntiDrift(){ return antiDrift(); } public double antiDrift() { return antiDrift; } double antiDrift; // between -1 and 1 SinglePixelPosterizer(int brightnessLevels) { this.brightnessLevels = brightnessLevels; factor1 = doubleRatio(brightnessLevels, 256); factor2 = doubleRatio(255, brightnessLevels-1); } // 0..255 to 0..255 public int get(int brightness) { var inner = brightness*factor1+drift; var posterized = ifloor(inner)+antiDrift; return clampUByteInt(iround(posterized*factor2)); } public boolean equals(Object o) { return stdEq2(this, o); } public int hashCode() { return stdHash2(this); } } static class BlurAndPosterizeSettings extends MetaWithChangeListeners { final public BlurAndPosterizeSettings setBlur(int blur){ return blur(blur); } public BlurAndPosterizeSettings blur(int blur) { this.blur = blur; return this; } final public int getBlur(){ return blur(); } public int blur() { return blur; } int blur = 0; final public BlurAndPosterizeSettings setColors(int colors){ return colors(colors); } public BlurAndPosterizeSettings colors(int colors) { this.colors = colors; return this; } final public int getColors(){ return colors(); } public int colors() { return colors; } int colors = 64; final public BlurAndPosterizeSettings setColorDrift(RGB colorDrift){ return colorDrift(colorDrift); } public BlurAndPosterizeSettings colorDrift(RGB colorDrift) { this.colorDrift = colorDrift; return this; } final public RGB getColorDrift(){ return colorDrift(); } public RGB colorDrift() { return colorDrift; } RGB colorDrift = noColorDrift(); final public BlurAndPosterizeSettings setAntiDrift(RGB antiDrift){ return antiDrift(antiDrift); } public BlurAndPosterizeSettings antiDrift(RGB antiDrift) { this.antiDrift = antiDrift; return this; } final public RGB getAntiDrift(){ return antiDrift(); } public RGB antiDrift() { return antiDrift; } RGB antiDrift = noColorDrift(); static RGB noColorDrift() { return new RGB(0f); } BlurAndPosterizeSettings cloneMe() { return shallowClone(this) .colorDrift(colorDrift.cloneMe()) .antiDrift(antiDrift.cloneMe()); } public String toString() { /*if (eq(colorDrift, noColorDrift())) ret stdToStringWithFields(this, "blur", "colors"); else*/ return stdToString(this); } @Override public boolean equals(Object o) { return stdEq_nonTransient(this, o); } @Override public int hashCode() { return stdHash_nonTransient(this); } } static class FastRegions_Hi15Image extends AbstractFastRegions { int getColor(int pos) { return image.getHi15Pixel_noRangeCheck(pos); } RGB rgbForRegion(ImageRegion r) { return hi15ToRGB((short) r.firstPixelLogicalColor()); } FastRegions_Hi15Image(Hi15Image img) { super(img); } FastRegions_Hi15Image(BufferedImage img) { this(new Hi15Image(img)); } } // In the newest pinging system (with flag PingV3), a ping source // is the object that "allows" some code to run. // When that code calls ping(), the ping source's action (if defined) // is triggered. // This allows randomly interrupting code execution, for example. static class PingSource { // returns true if it slept final public PingSource setAction(IF0 action){ return action(action); } public PingSource action(IF0 action) { this.action = action; return this; } final public IF0 getAction(){ return action(); } public IF0 action() { return action; } volatile IF0 action; // optional description of this ping source String text; // optional thread pool that this ping source likes to run in ThreadPool threadPool; PingSource() {} PingSource(ThreadPool threadPool) { this.threadPool = threadPool;} PingSource(ThreadPool threadPool, String text) { this.text = text; this.threadPool = threadPool;} PingSource(IF0 action) { this.action = action;} // returns true if it slept final boolean get() { var a = action; return a != null && a.get(); } final void ping() { var a = action; if (a != null) a.get(); } void cancel() { action = new Cancelled(); } class Cancelled implements IF0 { public Boolean get() { throw new PingSourceCancelledException(PingSource.this); } } class Encapsulated implements Runnable , IFieldsToList{ Runnable r; Encapsulated() {} Encapsulated(Runnable r) { this.r = r;}public Object[] _fieldsToList() { return new Object[] {r}; } public void run() { try { //System.out.println("Encapsulated running: " + r); try { pingSource_tl().set(PingSource.this); //System.out.println("Ping source set"); ping(); r.run(); //System.out.println("Done running"); } finally { //System.out.println("Finally"); pingSource_tl().set(null); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return PingSource.this + ": " + r; } } void dO(Runnable r) { if (r == null) return; threadPool.acquireThreadOrQueue(new Encapsulated(r)); } public String toString() { String t = text; return nempty(t) ? t : super.toString(); } ISleeper_v2 sleeper() { return threadPool.sleeper(); } } 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 fillMenu){ return fillMenu(fillMenu); } public PopupMenuMaker fillMenu(IVF1 fillMenu) { this.fillMenu = fillMenu; return this; } final public IVF1 getFillMenu(){ return fillMenu(); } public IVF1 fillMenu() { return fillMenu; } IVF1 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 fillMenu) { this.fillMenu = fillMenu; this.event = event;} public void run() { swing(() -> { if (existingMenu != null) { fix(); 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); } }); } static void fix() { JPopupMenu.setDefaultLightWeightPopupEnabled(false); } } static class G22_RegionToSSIs_v2 extends Meta implements IFieldsToList{ IImageRegion region; G22_RegionToSSIs_v2() {} G22_RegionToSSIs_v2(IImageRegion region) { this.region = region;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; } Color color; List ssis; List growingSSIs = new ArrayList(); final public G22_RegionToSSIs_v2 setWithDiagonals(boolean withDiagonals){ return withDiagonals(withDiagonals); } public G22_RegionToSSIs_v2 withDiagonals(boolean withDiagonals) { this.withDiagonals = withDiagonals; return this; } final public boolean getWithDiagonals(){ return withDiagonals(); } public boolean withDiagonals() { return withDiagonals; } boolean withDiagonals = false; final public G22_RegionToSSIs_v2 setCheckCoherence(boolean checkCoherence){ return checkCoherence(checkCoherence); } public G22_RegionToSSIs_v2 checkCoherence(boolean checkCoherence) { this.checkCoherence = checkCoherence; return this; } final public boolean getCheckCoherence(){ return checkCoherence(); } public boolean checkCoherence() { return checkCoherence; } boolean checkCoherence = true; final public G22_RegionToSSIs_v2 setCheckStreaks(boolean checkStreaks){ return checkStreaks(checkStreaks); } public G22_RegionToSSIs_v2 checkStreaks(boolean checkStreaks) { this.checkStreaks = checkStreaks; return this; } final public boolean getCheckStreaks(){ return checkStreaks(); } public boolean checkStreaks() { return checkStreaks; } boolean checkStreaks = true; private int x1, y; class GrowingSSI { final public GrowingSSI setY1(int y1){ return y1(y1); } public GrowingSSI y1(int y1) { this.y1 = y1; return this; } final public int getY1(){ return y1(); } public int y1() { return y1; } int y1; ShortBuffer data = new ShortBuffer(); int y2() { return y1+l(data)/2; } SSI finish() { return addAndReturn(ssis, new SSI(y1, y2()).data(data.toArray()) .color(color)); } boolean isEmpty() { return data.isEmpty(); } int lastx1() { return data.get(l(data)-2); } int lastx2() { return data.get(l(data)-1); } IntRange lastRange() { return isEmpty() ? null : intRange(lastx1(), lastx2()); } // can add this line without breaking coherence? boolean canAdd(IntRange r) { return isEmpty() || intRangesOverlapNempty(r.start, r.end, lastx1()-diag(), lastx2()+diag()); } void add(IntRange r) { if (checkCoherence() && !canAdd(r)) throw fail("Coherence fail", "lastx1", lastx1(), "lastx2", lastx2(), "r", r); data.add(toShort_enforce(r.start)); data.add(toShort_enforce(r.end)); } } void finishSSI(int iSSI) { growingSSIs.remove(iSSI).finish(); } private GrowingSSI startSSI(IntRange range) { return startSSI(l(growingSSIs), range); } private GrowingSSI startSSI(int iSSI, IntRange range) { if (scaffoldingEnabled()) printVars("startSSI", "iSSI", iSSI, "range", range); var ssi = new GrowingSSI().y1(y); ssi.add(range); return addAndReturn(growingSSIs, iSSI, ssi); } List lastStreaks() { return reallyLazyMap(growingSSIs, __1 -> __1.lastRange()); } int diag() { return withDiagonals ? 1 : 0; } List get() { if (region == null) return null; color = region.color(); ssis = new ArrayList(); Rect r = region.bounds(); int x1 = this.x1 = r.x1(), y1 = r.y1(), y2 = r.y2(), h = y2-y1, w = r.w; boolean scaff = scaffoldingEnabled(); for (y = y1; y < y2; y++) { var _y_2 = y; List streaks = shiftIntRanges(x1, genericStreaks(w, x -> region.contains(x1+x, _y_2))); var lastStreaks = lastStreaks(); if (checkStreaks()) assertProperStreaks(lastStreaks); // advance iSSI & iStreak simultaneously int iStreak = 0, iSSI = 0; int nStreaks = l(streaks); if (scaff) printVars("y", _y_2, "lastStreaks", lastStreaks, "streaks", streaks); while (iStreak < nStreaks) { ping(); var range = streaks.get(iStreak); var ssi = _get(growingSSIs, iSSI); if (scaff) printVars("y", _y_2, "iStreak", iStreak, "iSSI", iSSI, "range", range, "ssi" , ssi == null ? null : ssi.lastRange()); // case 1: // ------ // ------- // // case 2: // // ------- // (left of current SSI or to the right of everything) if (ssi == null || range.end <= ssi.lastx1()-diag()) { startSSI(iSSI++, range); ++iStreak; continue; } // Now we know that we have another SSI. // case 3: // ------- // ------ // (Just end current SSI and do streak again.) if (range.start >= ssi.lastx2()+diag()) { finishSSI(iSSI); continue; } // Now we know the streak and the SSI overlap. // Find out if we have a choice on the top or on the bottom. // Check choices for streak int jStreak = iStreak+1; while (jStreak < nStreaks && ssi.canAdd(streaks.get(jStreak))) ++jStreak; // Check choices for SSI int jSSI = iSSI+1; while (jSSI < l(growingSSIs) && growingSSIs.get(jSSI).canAdd(range)) ++jSSI; int nSSI = jSSI-iSSI, nStreak = jStreak-iStreak; if (scaff) printVars("nSSI", nSSI, "nStreak", nStreak); Best best = new Best(); if (nSSI > 1) { if (nStreak > 1) throw fail("ANOMALY", "nSSI", nSSI, "nStreak", nStreak, "ssis" , subList(lastStreaks(), iSSI, jSSI), "streaks" , subList(streaks, iStreak, jStreak)); // Choose longest SSI for (int idx = iSSI; idx < jSSI; idx++) best.put(idx, l(growingSSIs.get(idx).lastRange())); jSSI = best.get(); while (jSSI-- > iSSI) finishSSI(iSSI); } else if (nStreak > 1) { // Choose longest streak for (int idx = iStreak; idx < jStreak; idx++) best.put(idx, l(streaks.get(idx))); jStreak = best.get(); while (iStreak < jStreak) startSSI(iSSI++, streaks.get(iStreak++)); } // add line to SSI growingSSIs.get(iSSI++).add(streaks.get(iStreak++)); } // remaining SSIs are unpaired, close them while (iSSI < l(growingSSIs)) finishSSI(iSSI); } // finish all remaining SSIs for (var ssi : cloneAndClear(growingSSIs)) ssi.finish(); return ssis; } } // same brightness level count for each channel for now static class PosterizeBufferedImageToHi15 { BufferedImage img; Hi15Image result; IPosterizer posterizer; PosterizeBufferedImageToHi15(int brightnessLevels, BufferedImage img) { this.img = img; if (brightnessLevels >= 256 || img == null) posterizer = new DummyPosterizer(); posterizer = new SinglePixelPosterizer(brightnessLevels); } PosterizeBufferedImageToHi15(IPosterizer posterizer, BufferedImage img) { this.img = img; this.posterizer = posterizer;} public void run() { try { if (img == null) return; if (posterizer instanceof DummyPosterizer) { result = new Hi15Image(img); return; } GrabbableRGBBytePixels gp = grabbableRGBBytePixels(img); if (gp != null) { run(gp); return; } GrabbableIntPixels gp2 = grabbableIntPixels_fastOrSlow(img); run(gp2); } catch (Exception __e) { throw rethrow(__e); } } void run(GrabbableIntPixels gp) { int w = img.getWidth(), h = img.getHeight(), n = w*h; int[] pixels = gp.data; short[] pixels2 = new short[n]; byte[] cache = cachePosterizer(); int extraStride = gp.scanlineStride-w; int iOut = 0, iIn = gp.offset; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int rgb = pixels[iIn++]; pixels2[iOut++] = (short) ((cache[(rgb >> 16) & 0xFF] << 10) | (cache[(rgb >> 8) & 0xFF] << 5) | cache[rgb & 0xFF]); } iIn += extraStride; } result = new Hi15Image(w, h, pixels2); } void run(GrabbableRGBBytePixels gp) { int w = img.getWidth(), h = img.getHeight(), n = w*h; byte[] pixels = gp.data; short[] pixels2 = new short[n]; byte[] cache = cachePosterizer(); int extraStride = gp.scanlineStride-w*3; int iOut = 0, iIn = gp.offset; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int r = pixels[iIn++] & 0xFF; int g = pixels[iIn++] & 0xFF; int b = pixels[iIn++] & 0xFF; pixels2[iOut++] = (short) ((cache[r] << 10) | (cache[g] << 5) | cache[b]); } iIn += extraStride; } result = new Hi15Image(w, h, pixels2); } public BufferedImage pixelPreservingSrcImage() { return img; } Hi15Image get() { if (result == null) run(); return result; } byte[] cachePosterizer() { byte[] cache = new byte[256]; for (int i = 0; i < 256; i++) cache[i] = (byte) (posterizer.get(i) >> 3); return cache; } } interface IntSize { int size(); default boolean isEmpty() { return size() == 0; } } static class TargetAndActual extends MetaWithChangeListeners { TargetAndActual() {} public transient FieldVar varTarget_cache; public FieldVar varTarget() { if (varTarget_cache == null) varTarget_cache = varTarget_load(); return varTarget_cache;} public FieldVar varTarget_load() { return new FieldVar(this, "target", () -> target(), target -> target(target)); } final public TargetAndActual setTarget(A target){ return target(target); } public TargetAndActual target(A target) { if (!eq(this.target, target)) { this.target = target; change(); } return this; } final public A getTarget(){ return target(); } public A target() { return target; } A target; public transient FieldVar varActual_cache; public FieldVar varActual() { if (varActual_cache == null) varActual_cache = varActual_load(); return varActual_cache;} public FieldVar varActual_load() { return new FieldVar(this, "actual", () -> actual(), actual -> actual(actual)); } final public TargetAndActual setActual(A actual){ return actual(actual); } public TargetAndActual actual(A actual) { if (!eq(this.actual, actual)) { this.actual = actual; change(); } return this; } final public A getActual(){ return actual(); } public A actual() { return actual; } A actual; TargetAndActual(A target, A actual) { this.actual = actual; this.target = target;} TargetAndActual(A target) { this.target = target;} public String toString() { return commaCombine( target == null ? null : "Target: " + target, actual == null ? null : "Actual: " + actual); } final A get(){ return value(); } A value() { return actual; } boolean hasTarget() { return target != null; } final void value(A actual){ set(actual); } void set(A actual) { actual(actual); } } static interface IIntIntPred { boolean get(int a, int b); } static class FieldVar extends VarWithNotify { IHasChangeListeners containingObject; String fieldName; IF0 getter; IVF1 setter; FieldVar(IHasChangeListeners containingObject, String fieldName, IF0 getter, IVF1 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 onChange(IVF1 r) { if (r != null) onChange(() -> r.get(get())); return this; } } static class Stage implements Runnable { String name; boolean done = false; Object result; PersistableThrowable error; //long started, done; // timestamps //S log; IF0 code; static String _fieldOrder = "name done result"; Stage() {} Stage(String name) { this.name = name;} Stage(String name, IF0 code) { this.code = code; this.name = name;} transient Set onComputed; public Stage onComputed(Runnable r) { onComputed = createOrAddToSyncLinkedHashSet(onComputed, r); return this; } public Stage removeComputedListener(Runnable r) { main.remove(onComputed, r); return this; } public void computed() { if (onComputed != null) for (var listener : onComputed) pcallF_typed(listener); } public void run() { try { if (done || code == null) return; try { result = code.get(); computed(); } catch (Throwable e) { printStackTrace(e); error = persistableThrowable(e); } finally { done = true; } } catch (Exception __e) { throw rethrow(__e); } } } // -has fast nextElement() and prevElement() // -design allows for more functions like reordering the list // -Saves up to 34% in space over LinkedHashSet // (e.g. 22% for a set of 1,000 Ints) static class CompactLinkedHashSet extends AbstractSet { UnsynchronizedCompactHashSet> entries = new UnsynchronizedCompactHashSet(); Entry head, tail; static class Entry { A value; Entry prev, next; public int hashCode() { return _hashCode(value); } // "magic" equals function for CompactHashSet lookup without temp object public boolean equals(Object o) { return o == this || eq(value, o); } } public boolean add(A a) { if (entries.contains(a)) return false; Entry n = new Entry(); n.value = a; n.prev = tail; if (tail != null) tail.next = n; tail = n; if (head == null) head = n; entries.add(n); return true; } public boolean remove(Object a) { return remove(entries.find(a)); } public boolean remove(Entry node) { if (node == null) return false; if (node.next != null) node.next.prev = node.prev; else tail = node.prev; if (node.prev != null) node.prev.next = node.next; else head = node.next; entries.remove(node); return true; } public int size() { return entries.size(); } public IterableIterator iterator() { return new IterableIterator() { Entry entry = head, prev = null; public boolean hasNext() { return entry != null; } public A next() { A a = entry.value; prev = entry; entry = entry.next; return a; } // untested public void remove() { if (prev == null) throw new IllegalStateException(); CompactLinkedHashSet.this.remove(prev); prev = null; } }; } public void clear() { entries.clear(); head = tail = null; } public boolean contains(Object a) { return entries.contains(a); } public A find(Object o) { Entry e = entries.find(o); return e == null ? null : e.value; } public A prevElement(A a) { Entry e = entries.find(a); if (e == null || e.prev == null) return null; return e.prev.value; } public A nextElement(A a) { Entry e = entries.find(a); if (e == null || e.next == null) return null; return e.next.value; } public A first() { return head == null ? null : head.value; } public A last() { return tail == null ? null : tail.value; } boolean removeIfSame(Object o) { A value = find(o); if (value == o) { remove(value); return true; } return false; } } static 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", getMessage()); } public String getMessage() { return commaCombine(getCause(), objects); } } static class Rect implements WidthAndHeight , 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; } final Rectangle getRectangle() { return new Rectangle(x, y, w, h); } public String toString() { return x + "," + y + " / " + w + "," + h; } final int x1() { return x; } final int y1() { return y; } final int x2() { return x + w; } final int y2() { return y + h; } final boolean contains(Pt p) { return contains(p.x, p.y); } final boolean contains(int _x, int _y) { return _x >= x && _y >= y && _x < x+w && _y < y+h; } final boolean contains(Rectangle r) { return rectContains(this, r); } final boolean empty() { return w <= 0 || h <= 0; } final public int getWidth() { return w; } final public int getHeight() { return h; } final public int area() { return w*h; } final public int x() { return x; } final public int y() { return y; } WidthAndHeight widthAndHeight() { return main.widthAndHeight(w, h); } } static class Pt implements Comparable, 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); } public Pt minus(Pt p) { return ptMinus(this, p); } public double x_double() { return x; } public double y_double() { return y; } } static class BufferedImageWithMeta extends BufferedImage implements IMeta { BufferedImageWithMeta(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable properties) { super(cm, raster, isRasterPremultiplied, properties); } // 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(); } final boolean scaffolded(){ return scaffoldingEnabled(); } boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); } boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); } // Implementing setMetaToString String toString_base() { return super.toString(); } public String toString() { Object o = metaGet("toString", this); if (o instanceof String) return ((String) o); if (o instanceof IF1) return str(((IF1) o).get(this)); return toString_base(); } } static class VectorOptimizedSSIList extends AbstractSSIList { VectorOptimizedSSIList() { init(); } VectorOptimizedSSIList(Collection l) { super(l); } static VectorOptimizedSSIList alreadyOptimized(List l) { VectorOptimizedSSIList list = new VectorOptimizedSSIList(); list.addAll(l); return list; } @Override AbstractSSI importSSI(AbstractSSI ssi) { return vectorizeSSIIfBeneficial(ssi.toSSI()); } } static abstract class F0 { abstract A get(); } static abstract class F1 { abstract B get(A a); } // you still need to implement hasNext() and next() static abstract class IterableIterator implements Iterator, Iterable { public Iterator iterator() { return this; } public void remove() { unsupportedOperation(); } } static class RGB { // usually in range [0, 1] public float r, g, b; // can't be final cause persistence RGB() {} public RGB(float brightness) { r = g = b = brightness; } 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) { r = rgbRed(rgb)/255f; g = rgbGreen(rgb)/255f; b = rgbBlue(rgb)/255f; } public RGB(double brightness) { this((float) brightness); } public RGB(Color color) { r = color.getRed()/255f; g = color.getGreen()/255f; 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(clampZeroToOne(r), clampZeroToOne(g), clampZeroToOne(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; } final public float brightness(){ return getBrightness(); } 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; } // This is hyperprecise boolean isGrayscale() { return r == g && g == b; } public String toString() { //return getHexString(); return isGrayscale() ? formatChannel(r) : spaceCombine("RGB", formatChannel(r), formatChannel(g), formatChannel(b)); } String formatChannel(float value) { return formatDouble(value, 2); } 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; } RGB cloneMe() { return new RGB(r, g, b); } } static interface MakesBufferedImage extends WidthAndHeight { BufferedImage getBufferedImage(); public default void drawAt(Graphics2D g, int x, int y) { g.drawImage(getBufferedImage(), x, y, null); } default int getWidth() { return getBufferedImage().getWidth(); } default int getHeight() { return getBufferedImage().getHeight(); } } static final class WidthAndHeightFinal extends Meta implements WidthAndHeight , ByteIO , IFieldsToList{ static final String _fieldOrder = "width height"; int width; int height; WidthAndHeightFinal() {} WidthAndHeightFinal(int width, int height) { this.height = height; this.width = width;} public boolean equals(Object o) { if (!(o instanceof WidthAndHeightFinal)) return false; WidthAndHeightFinal __0 = (WidthAndHeightFinal) o; return width == __0.width && height == __0.height; } public int hashCode() { int h = -1177452162; h = boostHashCombine(h, _hashCode(width)); h = boostHashCombine(h, _hashCode(height)); return h; } public Object[] _fieldsToList() { return new Object[] {width, height}; } public int getWidth() { return width; } public int getHeight() { return height; } WidthAndHeightFinal(int wAndH) { this(wAndH, wAndH); } public String toString() { return n2(width) + "*" + n2(height) + " px"; } public void readWrite(ByteHead head) { head.exchangeInt(() -> width(), w -> width = w); head.exchangeInt(() -> height(), h -> height = h); } } interface PCallPolicy { void handlePcallFail(Throwable e); } public static interface IF0 { A get(); } static interface Hasher { int hashCode(A a); boolean equals(A a, A b); } static abstract class CloseableIterableIterator extends IterableIterator implements AutoCloseable { public void close() throws Exception {} } static interface IF2 { C get(A a, B b); } static interface IF1 { B get(A a); } interface IPosterizer extends IF1_IntToInt {} abstract static class AbstractSSIList extends NotifyingList implements StringIO { AbstractSSIList() { init(); } AbstractSSIList(Collection l) { initAndAddAll(importSSIs(l)); } List importSSIs(Collection l) { return map(__37 -> importSSI(__37), l); } abstract MySSI importSSI(AbstractSSI ssi); BufferedImage render(WidthAndHeight size) { return renderAll(this, size); } BufferedImage render(BufferedImage canvas) { return renderAll(this, canvas); } BufferedImage renderOutlines(WidthAndHeight size) { return toSSIList().renderOutlines(size); } SSIList toSSIList() { return new SSIList(map(this, __1 -> __1.toSSI())); } public void readWrite(StringHead head) { head.exchangeAll(this); } List vectorSSIs() { return instancesOf(VectorSSI.class, this); } final List literalSSIs(){ return directSSIs(); } List directSSIs() { return instancesOf(SSI.class, this); } Percent percentageVectorized() { return Percent.ratio(l(vectorSSIs()), l(this)); } } static interface IVF1 { void get(A a); } static interface IVF2 { void get(A a, B b); } static interface IVar extends IF0 { void set(A a); A get(); // reified type of value (if available) default Class getType() { return null; } default IF0 getter() { return () -> get(); } default IVF1 setter() { return __1 -> set(__1); } default boolean has() { return get() != null; } default void clear() { set(null); } } // immutable, has strong refs // Do not run in a synchronized block - it goes wrong in the presence // of elaborate classloaders (like in Gazelle BEA) // see #1102990 and #1102991 final static class _MethodCache { final Class c; final HashMap> cache = new HashMap(); _MethodCache(Class c) { this.c = c; _init(); } void _init() { Class _c = c; 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 m = cache.get(method); if (m == null) return null; int n = m.size(); for (int i = 0; i < n; i++) { Method me = m.get(i); if (call_checkArgs(me, args, false)) return me; } return null; } catch (Exception __e) { throw rethrow(__e); } } Method findStaticMethod(String method, Object[] args) { try { List m = cache.get(method); if (m == null) return null; int n = m.size(); for (int i = 0; i < n; i++) { Method me = m.get(i); if (isStaticMethod(me) && call_checkArgs(me, args, false)) return me; } return null; } catch (Exception __e) { throw rethrow(__e); } } //Cl allMethods() { ret allValues(cache); } } 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); } } static class ImageChooser extends SingleComponentPanel { ImageSurface is = new ImageSurface(); JTextField tfPath = new JTextField(); ImageChooser(BufferedImage img) { this(); setImage(img); } ImageChooser(RGBImage img) { this(img.getBufferedImage()); } ImageChooser() { bindToComponent(this, new Runnable() { public void run() { try { is.zoomToWindow() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "is.zoomToWindow()"; }}); is.loadFromClipboard(); //setComponent(withLabel("Path to image or snippet ID:", tfPath)); setComponent(jMinSize(100, 100, jscroll(is))); componentPopupMenu(is, new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, "Screenshot in 3", new Runnable() { public void run() { try { startThread("Screenshot in 3", new Runnable() { public void run() { try { sleep(3000); final BufferedImage img = shootScreen2(); swingLater(new Runnable() { public void run() { try { is.setImage(img); infoMessage("Screenshot taken (" + img.getWidth() + "*" + img.getHeight() + " px)", 2); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "is.setImage(img);\r\n infoMessage(\"Screenshot taken (\" + img.getWidt..."; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "sleep(3000);\r\n final BufferedImage img = shootScreen2();\r\n ..."; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "thread \"Screenshot in 3\" {\r\n sleep(3000);\r\n final BufferedI..."; }}); addMenuItem(menu, "Load snippet or file...", new Runnable() { public void run() { try { final JTextField tf = new JTextField(); showFormTitled("Load image from snippet or file", "Path or snippet ID", tf, new Runnable() { public void run() { try { is.setImage(loadImage2(tf.getText().trim())); is.zoomToDisplaySize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "is.setImage(loadImage2(tf.getText().trim()));\r\n is.zoomToDisplaySi..."; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final new JTextField tf;\r\n showFormTitled(\"Load image from snippet or ..."; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, \"Screenshot in 3\", r {\r\n thread \"Screenshot in 3\" {\r..."; }}); } boolean hasImage() { return is.getImage() != null && is.getImage().getWidth()*is.getImage().getHeight() > 1; } BWImage getBWImage() { return is.getImage() == null ? null : new BWImage(is.getImage()); } BufferedImage getImage() { return hasImage() ? is.getImage() : null; } void setImage(BufferedImage img) { is.setImage(img); is.zoomToWindow(); } void setTitleForUpload(String title) { is.titleForUpload = title; } } // 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); } } // 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; } } abstract static class AbstractSSI extends AbstractHasHi15Color implements G2Drawable, HasBounds, SizeInInts, StringIO, INumberOfPixels { AbstractSSI() {} final SSI render(){ return toSSI(); } abstract SSI toSSI(); void copyAbstractSSI(AbstractSSI dest) { dest.hi15color(hi15color); } double compressibility() { return doubleRatio(numberOfPixels(), sizeInInts()); } AbstractSSI reduceToInts(int ints) { return null; } } 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; } } // records its full size (total value count) in a field now static class MultiMap implements IMultiMap { Map> data = new HashMap>(); int fullSize; MultiMap() {} MultiMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); } MultiMap(MultiMap map) { putAll(map); } MultiMap(Map> data) { this.data = data;} void put(A key, B value) { synchronized(data) { List list = data.get(key); if (list == null) data.put(key, list = _makeEmptyList()); list.add(value); ++fullSize; }} void add(A key, B value) { put(key, value); } void addAll(A key, Collection values) { putAll(key, values); } void addAllIfNotThere(A key, Collection values) { synchronized(data) { for (B value : values) setPut(key, value); }} void setPut(A key, B value) { synchronized(data) { if (!containsPair(key, value)) put(key, value); }} boolean containsPair(A key, B value) { synchronized(data) { return get(key).contains(value); }} void putAll(Collection keys, B value) { synchronized(data) { for (A key : unnullForIteration(keys)) put(key, value); }} void putAll(A key, Collection values) { synchronized(data) { if (nempty(values)) getActual(key).addAll(values); }} void putAll(Iterable> pairs) { synchronized(data) { for (Pair p : unnullForIteration(pairs)) put(p.a, p.b); }} void removeAll(A key, Collection values) { synchronized(data) { for (B value : values) remove(key, value); }} public List get(A key) { synchronized(data) { List list = data.get(key); return list == null ? Collections. emptyList() : list; }} List getOpt(A key) { synchronized(data) { return data.get(key); }} List getAndClear(A key) { synchronized(data) { List l = cloneList(data.get(key)); remove(key); return l; }} // returns actual mutable live list // creates the list if not there List getActual(A key) { synchronized(data) { List list = data.get(key); if (list == null) data.put(key, list = _makeEmptyList()); return list; }} void clean(A key) { synchronized(data) { List list = data.get(key); if (list != null && list.isEmpty()) { fullSize -= l(list); data.remove(key); } }} final public Set keys(){ return keySet(); } public Set keySet() { synchronized(data) { return data.keySet(); }} void remove(A key) { synchronized(data) { fullSize -= l(this.getOpt(key)); data.remove(key); }} final void remove(Pair p){ removePair(p); } void removePair(Pair p) { if (p != null) remove(p.a, p.b); } void remove(A key, B value) { synchronized(data) { List list = data.get(key); if (list != null) { if (list.remove(value)) fullSize--; if (list.isEmpty()) data.remove(key); } }} void clear() { synchronized(data) { data.clear(); }} boolean containsKey(A key) { synchronized(data) { return data.containsKey(key); }} B getFirst(A key) { synchronized(data) { List list = get(key); return list.isEmpty() ? null : list.get(0); }} void addAll(MultiMap map) { putAll(map); } void putAll(MultiMap map) { synchronized(data) { for (A key : map.keySet()) putAll(key, map.get(key)); }} void putAll(Map map) { synchronized(data) { if (map != null) for (Map.Entry e : map.entrySet()) put(e.getKey(), e.getValue()); }} final public int keyCount(){ return keysSize(); } public int keysSize() { synchronized(data) { return l(data); }} final public int fullSize(){ return size(); } public int size() { synchronized(data) { return fullSize; }} // expensive operation List reverseGet(B b) { synchronized(data) { List l = new ArrayList(); for (A key : data.keySet()) if (data.get(key).contains(b)) l.add(key); return l; }} Map> asMap() { synchronized(data) { return cloneMap(data); }} boolean isEmpty() { synchronized(data) { return data.isEmpty(); }} // override in subclasses List _makeEmptyList() { return new ArrayList(); } // returns live lists Collection> allLists() { synchronized(data) { return new ArrayList(data.values()); } } Collection> values() { return allLists(); } List allValues() { return concatLists(data.values()); } Object mutex() { return data; } public String toString() { return "mm" + str(data); } Map> innerMap() { return data; } } interface IHasChangeListeners { IHasChangeListeners onChange(Runnable r); IHasChangeListeners removeChangeListener(Runnable r); default IHasChangeListeners onChangeAndNow(Runnable l) { onChange(l); callF(l); return this; } } // Note: This does have the values problem (complicated values can cause memory leaks) static class BetterThreadLocal { Map map = newWeakHashMap(); BetterThreadLocal() {} BetterThreadLocal(A value) { set(value); } boolean isSet() { return map.containsKey(currentThread()); } A get() { if (map.containsKey(currentThread())) return map.get(currentThread()); A value = initialValue(); set(value); return value; } A get(Thread thread) { return thread == null ? null : map.get(thread); } void set(A a) { map.put(currentThread(), a); } public A initialValue() { return null; } } 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 extends AbstractMap implements Map { private Hasher hasher = null; /*@Pure*/ private boolean keyEquals(Object k1, Object k2) { return (hasher==null ? k1.equals(k2) : hasher.equals(k1, k2)); } /*@Pure*/ private int keyHashCode(Object k1) { return (hasher==null ? k1.hashCode() : hasher.hashCode(k1)); } // The WeakKey class can't be static because it depends on the hasher. // That in turn means that its methods can't be static. // However, I need to be able to call the methods such as create() that // were static in the original version of this code. // This finesses that. private /*@Nullable*/ WeakKey WeakKeyCreate(K k) { if (k == null) return null; else return new WeakKey(k); } private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue q) { if (k == null) return null; else return new WeakKey(k, q); } // Cannot be a static class: uses keyHashCode() and keyEquals() private final class WeakKey extends WeakReference { private int hash; /* Hashcode of key, stored here since the key may be tossed by the GC */ private WeakKey(K k) { super(k); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k) { if (k == null) return null; else return new WeakKey(k); } private WeakKey(K k, ReferenceQueue q) { super(k, q); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k, ReferenceQueue q) { if (k == null) return null; else return new WeakKey(k, q); } /* A WeakKey is equal to another WeakKey iff they both refer to objects that are, in turn, equal according to their own equals methods */ /*@Pure*/ @Override public boolean equals(/*@Nullable*/ Object o) { if (o == null) return false; // never happens if (this == o) return true; // This test is illegal because WeakKey is a generic type, // so use the getClass hack below instead. // if (!(o instanceof WeakKey)) return false; if (!(o.getClass().equals(WeakKey.class))) return false; Object t = this.get(); @SuppressWarnings("unchecked") Object u = ((WeakKey)o).get(); if ((t == null) || (u == null)) return false; if (t == u) return true; return keyEquals(t, u); } /*@Pure*/ @Override public int hashCode() { return hash; } } /* Hash table mapping WeakKeys to values */ private HashMap hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue queue = new ReferenceQueue(); /* Remove all invalidated entries from the map, that is, remove all entries whose keys have been discarded. This method should be invoked once by each public mutator in this class. We don't invoke this method in public accessors because that can lead to surprising ConcurrentModificationExceptions. */ @SuppressWarnings("unchecked") private void processQueue() { WeakKey wk; while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast hash.remove(wk); } } /* -- Constructors -- */ /** * Constructs a new, empty WeakHashMap with the given * initial capacity and the given load factor. * * @param initialCapacity the initial capacity of the * WeakHashMap * * @param loadFactor the load factor of the WeakHashMap * * @throws IllegalArgumentException If the initial capacity is less than * zero, or if the load factor is * nonpositive */ public WeakHasherMap(int initialCapacity, float loadFactor) { hash = new HashMap(initialCapacity, loadFactor); } /** * Constructs a new, empty WeakHashMap with the given * initial capacity and the default load factor, which is * 0.75. * * @param initialCapacity the initial capacity of the * WeakHashMap * * @throws IllegalArgumentException If the initial capacity is less than * zero */ public WeakHasherMap(int initialCapacity) { hash = new HashMap(initialCapacity); } /** * Constructs a new, empty WeakHashMap with the default * capacity and the default load factor, which is 0.75. */ public WeakHasherMap() { hash = new HashMap(); } /** * Constructs a new, empty WeakHashMap with the default * capacity and the default load factor, which is 0.75. * The WeakHashMap uses the specified hasher for hashing * keys and comparing them for equality. * @param h the Hasher to use when hashing values for this map */ public WeakHasherMap(Hasher h) { hash = new HashMap(); hasher = h; } /* -- Simple queries -- */ /** * Returns the number of key-value mappings in this map. * Note: In contrast to most implementations of the * Map interface, the time required by this operation is * linear in the size of the map. */ /*@Pure*/ @Override public int size() { return entrySet().size(); } /** * Returns true if this map contains no key-value mappings. */ /*@Pure*/ @Override public boolean isEmpty() { return entrySet().isEmpty(); } /** * Returns true if this map contains a mapping for the * specified key. * * @param key the key whose presence in this map is to be tested */ /*@Pure*/ @Override public boolean containsKey(Object key) { @SuppressWarnings("unchecked") K kkey = (K) key; return hash.containsKey(WeakKeyCreate(kkey)); } /* -- Lookup and modification operations -- */ /** * Returns the value to which this map maps the specified key. * If this map does not contain a value for this key, then return * null. * * @param key the key whose associated value, if any, is to be returned */ /*@Pure*/ @Override public /*@Nullable*/ V get(Object key) { // type of argument is Object, not K @SuppressWarnings("unchecked") K kkey = (K) key; return hash.get(WeakKeyCreate(kkey)); } /** * Updates this map so that the given key maps to the given * value. If the map previously contained a mapping for * key then that mapping is replaced and the previous value is * returned. * * @param key the key that is to be mapped to the given * value * @param value the value to which the given key is to be * mapped * * @return the previous value to which this key was mapped, or * null if if there was no mapping for the key */ @Override public V put(K key, V value) { processQueue(); return hash.put(WeakKeyCreate(key, queue), value); } /** * Removes the mapping for the given key from this map, if * present. * * @param key the key whose mapping is to be removed * * @return the value to which this key was mapped, or null if * there was no mapping for the key */ @Override public V remove(Object key) { // type of argument is Object, not K processQueue(); @SuppressWarnings("unchecked") K kkey = (K) key; return hash.remove(WeakKeyCreate(kkey)); } /** * Removes all mappings from this map. */ @Override public void clear() { processQueue(); hash.clear(); } /* -- Views -- */ /* Internal class for entries */ // This can't be static, again because of dependence on hasher. @SuppressWarnings("TypeParameterShadowing") private final class Entry implements Map.Entry { private Map.Entry ent; private K key; /* Strong reference to key, so that the GC will leave it alone as long as this Entry exists */ Entry(Map.Entry ent, K key) { this.ent = ent; this.key = key; } /*@Pure*/ @Override public K getKey() { return key; } /*@Pure*/ @Override public V getValue() { return ent.getValue(); } @Override public V setValue(V value) { return ent.setValue(value); } /*@Pure*/ private boolean keyvalEquals(K o1, K o2) { return (o1 == null) ? (o2 == null) : keyEquals(o1, o2); } /*@Pure*/ private boolean valEquals(V o1, V o2) { return (o1 == null) ? (o2 == null) : o1.equals(o2); } /*@Pure*/ @SuppressWarnings("NonOverridingEquals") public boolean equals(Map.Entry e /* Object o*/) { // if (! (o instanceof Map.Entry)) return false; // Map.Entry e = (Map.Entry)o; return (keyvalEquals(key, e.getKey()) && valEquals(getValue(), e.getValue())); } /*@Pure*/ @Override public int hashCode() { V v; return (((key == null) ? 0 : keyHashCode(key)) ^ (((v = getValue()) == null) ? 0 : v.hashCode())); } } /* Internal class for entry sets */ private final class EntrySet extends AbstractSet> { Set> hashEntrySet = hash.entrySet(); @Override public Iterator> iterator() { return new Iterator>() { Iterator> hashIterator = hashEntrySet.iterator(); Map.Entry next = null; @Override public boolean hasNext() { while (hashIterator.hasNext()) { Map.Entry ent = hashIterator.next(); WeakKey wk = ent.getKey(); K k = null; if ((wk != null) && ((k = wk.get()) == null)) { /* Weak key has been cleared by GC */ continue; } next = new Entry(ent, k); return true; } return false; } @Override public Map.Entry next() { if ((next == null) && !hasNext()) throw new NoSuchElementException(); Map.Entry e = next; next = null; return e; } @Override public void remove() { hashIterator.remove(); } }; } /*@Pure*/ @Override public boolean isEmpty() { return !(iterator().hasNext()); } /*@Pure*/ @Override public int size() { int j = 0; for (Iterator> i = iterator(); i.hasNext(); i.next()) j++; return j; } @Override public boolean remove(Object o) { processQueue(); if (!(o instanceof Map.Entry)) return false; @SuppressWarnings("unchecked") Map.Entry e = (Map.Entry)o; // unchecked cast Object ev = e.getValue(); WeakKey wk = WeakKeyCreate(e.getKey()); Object hv = hash.get(wk); if ((hv == null) ? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) { hash.remove(wk); return true; } return false; } /*@Pure*/ @Override public int hashCode() { int h = 0; for (Iterator> i = hashEntrySet.iterator(); i.hasNext(); ) { Map.Entry ent = i.next(); WeakKey wk = ent.getKey(); Object v; if (wk == null) continue; h += (wk.hashCode() ^ (((v = ent.getValue()) == null) ? 0 : v.hashCode())); } return h; } } private /*@Nullable*/ Set> entrySet = null; /** * Returns a Set view of the mappings in this map. */ /*@SideEffectFree*/ @Override public Set> entrySet() { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } // find matching key K findKey(Object key) { processQueue(); K kkey = (K) key; // TODO: use replacement for HashMap to avoid reflection WeakKey wkey = WeakKeyCreate(kkey); WeakKey found = hashMap_findKey(hash, wkey); return found == null ? null : found.get(); } } static class 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 Pair implements Comparable> { final public Pair setA(A a){ return a(a); } public Pair a(A a) { this.a = a; return this; } final public A getA(){ return a(); } public A a() { return a; } A a; final public Pair setB(B b){ return b(b); } public Pair b(B b) { this.b = b; return this; } final public B getB(){ return b(); } public B b() { return b; } B b; Pair() {} Pair(A a, B b) { this.b = b; this.a = a;} public int hashCode() { return hashCodeFor(a) + 2*hashCodeFor(b); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Pair)) return false; Pair t = (Pair) o; return eq(a, t.a) && eq(b, t.b); } public String toString() { return "<" + a + ", " + b + ">"; } public int compareTo(Pair p) { if (p == null) return 1; int i = ((Comparable) a).compareTo(p.a); if (i != 0) return i; return ((Comparable) b).compareTo(p.b); } } static interface IResourceHolder { A add(A a); Collection takeAll(); } static interface Swingable { JComponent visualize(); } interface SizeInInts { long sizeInInts(); } // goes well with the stage {} macro (see tok_stages) static class Stages2 implements Steppable, Runnable , IFieldsToList{ static final String _fieldOrder = "stages iStage stage timings"; List stages; Stages2() {} Stages2(List stages) { this.stages = stages;} public boolean equals(Object o) { if (!(o instanceof Stages2)) return false; Stages2 __1 = (Stages2) o; return eq(stages, __1.stages); } public int hashCode() { int h = -232873603; h = boostHashCombine(h, _hashCode(stages)); return h; } public Object[] _fieldsToList() { return new Object[] {stages}; } int iStage; Stage stage; // set this to record timing for each stage final public Stages2 setTimings(FunctionTimings timings){ return timings(timings); } public Stages2 timings(FunctionTimings timings) { this.timings = timings; return this; } final public FunctionTimings getTimings(){ return timings(); } public FunctionTimings timings() { return timings; } transient FunctionTimings timings; { stages = new ArrayList(); } class Stage implements IFieldsToList{ String name; Runnable body; Stage() {} Stage(String name, Runnable body) { this.body = body; this.name = name;}public Object[] _fieldsToList() { return new Object[] {name, body}; } public void run() { try { if (timings != null) timings.dO(name, body); else callF(body); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return or2(name, "Unnamed stage"); } } public boolean step() { if (iStage >= l(stages)) return false; var stage = stages.get(iStage++); stage.run(); return true; } Stage stage(Stage stage) { stages.add(stage); return stage; } Stage stage(String name, Runnable body) { return stage(new Stage(name, body)); } public void run() { try { stepAll(this); } catch (Exception __e) { throw rethrow(__e); } } int indexOfStage(Stage stage) { return stages.indexOf(stage); } void stepUntilStage(Stage stage) { int index = indexOfStage(stage); while (iStage <= index && step()) {} } List completedStages() { return cloneTakeFirst(stages, iStage); } List remainingStages() { return cloneDropFirst(stages, iStage); } } // from http://www.jhlabs.com/ip/blurring.html /* Copyright 2006 Jerry Huxtable 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 BoxBlurFilter extends AbstractBufferedImageOp implements IF1 { int hRadius; int vRadius; int iterations = 1; BoxBlurFilter() {} BoxBlurFilter(int radius) { hRadius = vRadius = radius; } BoxBlurFilter(int hRadius, int vRadius) { this.vRadius = vRadius; this.hRadius = hRadius;} public BufferedImage get(BufferedImage src) { return filter(src, null); } public BufferedImage filter(BufferedImage src, BufferedImage dst) { int width = src.getWidth(); int height = src.getHeight(); if ( dst == null ) dst = createCompatibleDestImage( src, null ); int[] inPixels = new int[width*height]; int[] outPixels = new int[width*height]; getRGB( src, 0, 0, width, height, inPixels ); for (int i = 0; i < iterations; i++ ) { blur( inPixels, outPixels, width, height, hRadius ); blur( outPixels, inPixels, height, width, vRadius ); } setRGB( dst, 0, 0, width, height, inPixels ); return dst; } static void blur( int[] in, int[] out, int width, int height, int radius ) { int widthMinus1 = width-1; int tableSize = 2*radius+1; int divide[] = new int[256*tableSize]; for ( int i = 0; i < 256*tableSize; i++ ) divide[i] = i/tableSize; int inIndex = 0; for ( int y = 0; y < height; y++ ) { int outIndex = y; int ta = 0, tr = 0, tg = 0, tb = 0; for ( int i = -radius; i <= radius; i++ ) { int rgb = in[inIndex + clamp(i, 0, width-1)]; ta += (rgb >> 24) & 0xff; tr += (rgb >> 16) & 0xff; tg += (rgb >> 8) & 0xff; tb += rgb & 0xff; } for ( int x = 0; x < width; x++ ) { out[ outIndex ] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb]; int i1 = x+radius+1; if ( i1 > widthMinus1 ) i1 = widthMinus1; int i2 = x-radius; if ( i2 < 0 ) i2 = 0; int rgb1 = in[inIndex+i1]; int rgb2 = in[inIndex+i2]; ta += ((rgb1 >> 24) & 0xff)-((rgb2 >> 24) & 0xff); tr += ((rgb1 & 0xff0000)-(rgb2 & 0xff0000)) >> 16; tg += ((rgb1 & 0xff00)-(rgb2 & 0xff00)) >> 8; tb += (rgb1 & 0xff)-(rgb2 & 0xff); outIndex += height; } inIndex += width; } } } // SSI - Simple Scanline Image // A partial image consisting of exactly one horizontal line per row. // All pixels are of the same color. // The important values are y1 and data static class SSI extends AbstractSSI implements ByteIO, StringIO { SSI() {} // core value (mandatory): first row occupied final public SSI setY1(short y1){ return y1(y1); } public SSI y1(short y1) { this.y1 = y1; return this; } final public short getY1(){ return y1(); } public short y1() { return y1; } short y1; // core value (mandatory): x coordinates per row // x_left is inclusive, x_right is exclusive // all x coordinates are absolute in image // // Order in array: // x_left at y1, x_right at y1, // x_left at y1+1, x_right at y1+1, ,,, final public SSI setData(short[] data){ return data(data); } public SSI data(short[] data) { this.data = data; return this; } final public short[] getData(){ return data(); } public short[] data() { return data; } short[] data; SSI(int y1, int y2) { init(y1, y2); } void init(int y1, int y2) { this.y1 = toShort_enforce(y1); data = newShortArrayOrNull((y2-y1)*2); } // all coordinates passed in are absolute void set(int y, int xLeft, int xRight) { data[(y-y1)*2] = toShort_enforce(xLeft); data[(y-y1)*2+1] = toShort_enforce(xRight); } // all coordinates passed in and out are absolute int getLeft (int y) { return data[(y-y1)*2]; } int getRight(int y) { return data[(y-y1)*2+1]; } // bounds can be calculated quickly from core values & are then cached public Rect bounds_cache; public Rect bounds() { if (bounds_cache == null) bounds_cache = bounds_load(); return bounds_cache;} public Rect bounds_load() { int x1 = Integer.MAX_VALUE, x2 = 0; for (int i = 0; i < l(data); i += 2) { x1 = min(x1, data[i]); x2 = max(x2, data[i+1]); } return rectFromPoints(x1, y1, x2, y2()); } // same with number of pixels int numberOfPixels_cache = -1; public int numberOfPixels() { if (numberOfPixels_cache < 0) { int n = 0; for (int i = 0; i < l(data); i += 2) n += max(0, data[i+1]-data[i]); numberOfPixels_cache = n; } return numberOfPixels_cache; } public int getHeight() { return l(data)/2; } int y2() { return y1+height(); } public boolean contains(Pt p) { return contains(p.x, p.y); } public boolean contains(int x, int y) { if (y < y1) return false; int i = (y-y1)*2; if (i >= l(data)) return false; return x >= data[i] && x < data[i+1]; } SSI color(Color color) { super.color(color); return this; } public SSI hi15color(short hi15color) { super.hi15color(hi15color); return this; } public void drawOn(Graphics2D g) { g.setColor(color()); int h = height(); for (int y = 0; y < h; y++) { int x1 = data[y*2], x2 = data[y*2+1]; if (x1 < x2) g.drawLine(x1, y1+y, x2-1, y1+y); } } // doesn't set a color // TODO public void drawOutline(Graphics2D g) { int h = height(); IntRange last = intRange(0, 0); for (int y = 0; y < h; y++) { IntRange r = intRange(data[y*2], data[y*2+1]); // anything to draw in this line? if (nempty(r)) { if (empty(last)) // draw full line if just starting g.drawLine(r.start, y1+y, r.end-1, y1+y); else { // We make a line from the left boundary that if possible goes only to last.start but is at least 1 pixel wide int xleft = max(r.start, last.start-1); // Same from the right boundary int xright = min(r.end-1, last.end); g.drawLine(r.start, y1+y, xleft, y1+y); g.drawLine(xright, y1+y, r.end-1, y1+y); } } last = r; } } SSI topPart(int nLines) { if (nLines <= 0) return null; SSI ssi = new SSI(y1, toShort_enforce(y1+nLines)).hi15color(hi15color); arrayCopy(data, ssi.data, 0, l(ssi.data)); return ssi; } // an SSI is coherent if all scanlines are non-empty // and the SSI is one region boolean coherent() { return coherent(false); } boolean coherent(boolean withDiagonals) { int h = height(); for (int y = 0; y < h; y++) { int i = y*2; int x1 = data[i], x2 = data[i+1]; if (x1 >= x2) return false; if (y > 0) { int lastx1 = data[i-2], lastx2 = data[i-1]; if (withDiagonals) { lastx1--; lastx2++; } if (!intRangesOverlap(x1, x2, lastx1, lastx2)) return false; } } return true; } public void readWrite(ByteHead head) { head.exchangeShort(() -> hi15color, color -> hi15color = color); head.exchangeShort(() -> y1, y1 -> this.y1 = y1); head.exchangeShort(() -> toShort_enforce(y2()), y2 -> init(y1, y2)); for (int i = 0; i < l(data); i++) { var _i_2 = i; head.exchangeShort(() -> data[_i_2], x -> data[_i_2] = x); } } SSI toSSI() { return this; } public long sizeInInts() { // "s", color, y1, height, data return 4+height()*2; } SSI reduceToInts(int ints) { return topPart((ints-4)/2); } public void readWrite(StringHead head) { head.exchangeLine(() -> toLine(), __1 -> fromLine(__1)); } String toLine() { return spaceCombine("s", colorToString(), y1, height(), toList(data)); } void fromLine(String line) { Iterator it = splitAtSpaceIterator(line); assertEquals("s", nextFromIterator(it)); int y1 = parseInt(nextFromIterator(it)); int h = parseInt(nextFromIterator(it)); init(y1, y1+h); for (int i = 0; i < l(data); i++) data[i] = toShort_enforce(parseInt(nextFromIterator(it))); } } static class BetterLabel extends JLabel { boolean autoToolTip = true; BetterLabel() { // Listeners given out to componentPopupMenu must not directly // reference the outer object (-> weak map problem). final WeakReference < BetterLabel > me = new WeakReference<>(this); componentPopupMenu(this, BetterLabel_menuItems(me)); } BetterLabel(String text) { this(); this.setText(text); } public void setText(String text) { super.setText(text); if (autoToolTip) if (!swic(text, "")) // HTML labels make super-huge, confusing tool tips setToolTipText(nullIfEmpty(text)); } } // moved outside of class for GC reasons (see above) static VF1 BetterLabel_menuItems(final WeakReference me) { return new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, "Copy text to clipboard", new Runnable() { public void run() { try { copyTextToClipboard(me.get().getText()); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyTextToClipboard(me.get().getText());"; }}); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, \"Copy text to clipboard\", r {\r\n copyTextToClipboard(me..."; }}; } // elements are put to front when added (not when accessed) static class MRUCache extends LinkedHashMap { int maxSize = 10; MRUCache() {} MRUCache(int maxSize) { this.maxSize = maxSize;} protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxSize; } Object _serialize() { return ll(maxSize, cloneLinkedHashMap(this)); } static MRUCache _deserialize(List l) { MRUCache m = new MRUCache(); m.maxSize = (int) first(l); m.putAll((LinkedHashMap) second(l)); return m; } } // TODO: subclass RuntimeException and use Meta instead of DynamicObject static class PersistableThrowable extends DynamicObject { String className; String msg; String stacktrace; final public PersistableThrowable setActualThrowable(Throwable actualThrowable){ return actualThrowable(actualThrowable); } public PersistableThrowable actualThrowable(Throwable actualThrowable) { this.actualThrowable = actualThrowable; return this; } final public Throwable getActualThrowable(){ return actualThrowable(); } public Throwable actualThrowable() { return actualThrowable; } transient Throwable actualThrowable; PersistableThrowable() {} PersistableThrowable(Throwable e) { actualThrowable = 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() { if (actualThrowable != null) return main.asRuntimeException(actualThrowable); return new Fail(this); } } static interface WidthAndHeight { default int w(){ return getWidth(); } default int width(){ return getWidth(); } int getWidth(); default int h(){ return getHeight(); } default int height(){ return getHeight(); } int getHeight(); public default Rect bounds() { return rect(0, 0, getWidth(), getHeight()); } default int area() { return toInt(areaAsLong()); } default long areaAsLong() { return longMul(w(), h()); } } static interface IBinaryImage extends MakesBufferedImage { boolean getBoolPixel(int x, int y); default void setPixel(int x, int y, boolean b) { throw unimplemented(); } default boolean getBoolPixel(Pt p) { return getBoolPixel(p.x, p.y); } default boolean getBoolPixel_noRangeCheck(int x, int y) { return getBoolPixel(x, y); } public default BufferedImage getBufferedImage() { return toImage2B().getBufferedImage(); } default Image2B toImage2B() { return this instanceof Image2B ? (Image2B) this : iBinaryImageToImage2B(this); } } static class PingSourceCancelledException extends RuntimeException implements IFieldsToList{ PingSource pingSource; PingSourceCancelledException() {} PingSourceCancelledException(PingSource pingSource) { this.pingSource = pingSource;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + pingSource + ")"; }public Object[] _fieldsToList() { return new Object[] {pingSource}; } } static class DummyPosterizer implements IPosterizer { public int get(int brightness) { return brightness; } } // VectorSSI - SSI with straight line segments static class VectorSSI extends AbstractSSI { VectorSSI() {} // core values (mandatory): first column and row occupied final public VectorSSI setX1(short x1){ return x1(x1); } public VectorSSI x1(short x1) { this.x1 = x1; return this; } final public short getX1(){ return x1(); } public short x1() { return x1; } short x1; final public VectorSSI setY1(short y1){ return y1(y1); } public VectorSSI y1(short y1) { this.y1 = y1; return this; } final public short getY1(){ return y1(); } public short y1() { return y1; } short y1; // Curves of x start and x end, relative to (x1, y1) // Both have same length final public VectorSSI setLeftCurve(InterpolatedDoubleArray leftCurve){ return leftCurve(leftCurve); } public VectorSSI leftCurve(InterpolatedDoubleArray leftCurve) { this.leftCurve = leftCurve; return this; } final public InterpolatedDoubleArray getLeftCurve(){ return leftCurve(); } public InterpolatedDoubleArray leftCurve() { return leftCurve; } InterpolatedDoubleArray leftCurve; final public VectorSSI setRightCurve(InterpolatedDoubleArray rightCurve){ return rightCurve(rightCurve); } public VectorSSI rightCurve(InterpolatedDoubleArray rightCurve) { this.rightCurve = rightCurve; return this; } final public InterpolatedDoubleArray getRightCurve(){ return rightCurve(); } public InterpolatedDoubleArray rightCurve() { return rightCurve; } InterpolatedDoubleArray rightCurve; VectorSSI(SSI ssi) { ssi.copyAbstractSSI(this); Rect r = ssi.bounds(); x1(toShort_enforce(r.x1())); y1(toShort_enforce(r.y1())); int h = r.h(); int[] left = new int[h], right = new int[h]; for (int y = 0; y < h; y++) { left[y] = ssi.getLeft(y1+y)-x1; right[y] = ssi.getRight(y1+y)-x1; } leftCurve(new CompressToInterpolatedDoubleArray(left).get()); rightCurve(new CompressToInterpolatedDoubleArray(right).get()); } final public int h(){ return height(); } public int height() { return leftCurve.length(); } int y2() { return y1+h(); } final SSI get(){ return toSSI(); } SSI toSSI() { int h = h(); var ssi = new SSI(y1, y2()); copyAbstractSSI(ssi); int[] left = leftCurve.rounded(); int[] right = rightCurve.rounded(); for (int y = 0; y < h; y++) ssi.set(y1+y, x1+left[y], x1+right[y]); return ssi; } public void drawOn(Graphics2D g) { toSSI().drawOn(g); } // How many points define the shape of this vector SSI // (left + right border) int nPoints() { return leftCurve.nPillars() + rightCurve.nPillars(); } public long sizeInInts() { // "v", color, x1, y1, height(), leftCurve, rightCurve return 5+leftCurve.nInts/*_withoutFirstAndLastIndex*/()+rightCurve.nInts/*_withoutFirstAndLastIndex*/(); } // TODO /*VectorSSI reduceToInts(int ints) { ints -= 5; // constant part int nPillars = ints/2; // max pillars we can afford (left+right) }*/ // optimizable public Rect bounds_cache; public Rect bounds() { if (bounds_cache == null) bounds_cache = bounds_load(); return bounds_cache;} public Rect bounds_load() { return toSSI().bounds(); } public Integer numberOfPixels_cache; public int numberOfPixels() { if (numberOfPixels_cache == null) numberOfPixels_cache = numberOfPixels_load(); return numberOfPixels_cache;} public int numberOfPixels_load() { return toSSI().numberOfPixels(); } public void readWrite(StringHead head) { head.exchangeLine(() -> toLine(), __1 -> fromLine(__1)); } String toLine() { List elements = ll("v", colorToString(), x1, y1, height()); addAll(elements, asList(iroundAll(leftCurve.indicesAndValues/*_withoutFirstAndLastIndex*/()))); addAll(elements, asList(iroundAll(rightCurve.indicesAndValues/*_withoutFirstAndLastIndex*/()))); return spaceCombine(elements); } void fromLine(String line) { Iterator it = splitAtSpaceIterator(line); assertEquals("v", nextFromIterator(it)); x1 = toShort_enforce(parseInt(nextFromIterator(it))); y1 = toShort_enforce(parseInt(nextFromIterator(it))); // TODO /*int h = parseInt(nextFromIterator(it)); init(y1, y1+h); for i over data: data[i] = toX(parseInt(nextFromIterator(it)));*/ } } // The idea is to leave max as the actual number of cores the system // has (numberOfCores()), and in case of being fully booked, raise an // alert (customerMustWaitAlert) which can be handled by a strategy // object (different reactions are possible). // If nothing is done in such an event, clients are processed serially // (no guarantees of order), split up among the available threads. /* SYNChronisation order: 1. PooledThread 2. ThreadPool */ static class ThreadPool implements AutoCloseable { int max = numberOfCores(); List all = new ArrayList(); Set used = new HashSet(); Set free = new HashSet(); boolean verbose, retired; // our own ping surce so we can start threads & keep them running class InternalPingSource extends PingSource {} InternalPingSource internalPingSource = new InternalPingSource(); MultiSleeper sleeper = new MultiSleeper(); ThreadPool() {} ThreadPool(int max) { this.max = max;} synchronized int maxSize() { return max; } synchronized int total() { return l(used)+l(free); } transient Set onCustomerMustWaitAlert; public ThreadPool onCustomerMustWaitAlert(Runnable r) { onCustomerMustWaitAlert = createOrAddToSyncLinkedHashSet(onCustomerMustWaitAlert, r); return this; } public ThreadPool removeCustomerMustWaitAlertListener(Runnable r) { main.remove(onCustomerMustWaitAlert, r); return this; } public void customerMustWaitAlert() { if (onCustomerMustWaitAlert != null) for (var listener : onCustomerMustWaitAlert) pcallF_typed(listener); } void fireCustomerMustWaitAlert() { vmBus_send("customerMustWaitAlert", this, currentThread()); customerMustWaitAlert(); } // DOESN'T WAIT. adds action to a thread's queue if nothing is // available immediately. PooledThread acquireThreadOrQueue(Runnable action) { if (action == null) return null; PooledThread t; synchronized(this) { if (_hasFreeAfterCreating()) { t = _firstFreeThread(); markUsed(t); } else t = _anyThread(); } t.addWork(action); // will move it from free to used return t; } // run in synchronized block boolean _hasFreeAfterCreating() { checkNotRetired(); if (nempty(free)) return true; if (total() < max) { PooledThread t = newThread(); all.add(t); free.add(t); return true; } return false; } // WAITS until thread is available PooledThread acquireThreadOrWait(Runnable action) { try { if (action == null) return null; PooledThread t; while (true) { synchronized(this) { if (_hasFreeAfterCreating()) { t = _firstFreeThread(); break; } else _waitWaitWait(); } } t.addWork(action); return t; } catch (Exception __e) { throw rethrow(__e); } } PooledThread _firstFreeThread() { return first(free); } PooledThread _anyThread() { return random(used); } class PooledThread extends Thread { PooledThread(String name) { super(name); } AppendableChain q; synchronized Runnable _grabWorkOrSleep() { try { Runnable r = first(q); if (r == null) { markFree(this); if (verbose) print("Thread sleeps"); synchronized(this) { wait(); } if (verbose) print("Thread woke up"); return null; } q = popFirst(q); return r; } catch (Exception __e) { throw rethrow(__e); } } public void run() { try { pingSource_tl().set(internalPingSource); while (!retired()) { ping(); Runnable r = _grabWorkOrSleep(); if (verbose) print(this + " work: " + r); if (r != null) try { if (verbose) print(this + " running: " + r); r.run(); pingSource_tl().set(internalPingSource); if (verbose) print(this + " done"); } catch (Throwable e) { pingSource_tl().set(internalPingSource); if (verbose) print(this + " error"); printStackTrace(e); } finally { pingSource_tl().set(internalPingSource); if (verbose) print("ThreadPool finally"); } } } catch (Exception __e) { throw rethrow(__e); } } synchronized boolean isEmpty() { return empty(q); } // append to q (do later) void addWork(Runnable r) { if (verbose) print("Added work to " + this + ": " + r); synchronized(this) { q = chainPlus(q, r); notifyAll(); } } } PooledThread newThread() { PooledThread t = new PooledThread("Thread Pool Inhabitant " + n2(total()+1)); t.start(); return t; } synchronized void markFree(PooledThread t) { used.remove(t); free.add(t); notifyAll(); } synchronized void markUsed(PooledThread t) { free.remove(t); used.add(t); } synchronized public String toString() { return retired() ? "Retired ThreadPool" : "ThreadPool " + roundBracket(commaCombine( n2(used) + " used out of " + n2(total()), max <= total() ? null : "could grow to " + n2(max))); } synchronized boolean retired() { return retired; } synchronized void retire() { if (verbose) print("ThreadPool Retiring"); retired = true; for (var thread : free) syncNotifyAll(thread); // wake it up so it exits } void checkNotRetired() { if (retired()) throw fail("retired"); } // We could do a soft-close here (stop the idle threads, let running threads finish, then end those too, stop accepting new orders) // or a hard close (interrupt all threads, stop accepting new orders) synchronized public void close() { try { retire(); } catch (Exception __e) { throw rethrow(__e); } } // run in synchronized block void _waitWaitWait() { try { do { fireCustomerMustWaitAlert(); wait(); checkNotRetired(); } while (empty(free)); } catch (Exception __e) { throw rethrow(__e); } } void dO(String text, Runnable r) { if (r == null) return; new PingSource(this, text).dO(r); } ISleeper_v2 sleeper() { return sleeper; } } static class ImageSurface extends Surface { BufferedImage image; double zoomX = 1, zoomY = 1, zoomFactor = 1.5; private Rectangle selection; List tools = new ArrayList(); // use overlays now Object overlay; // voidfunc(Graphics2D) List 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 setAllowPaste(boolean allowPaste){ return allowPaste(allowPaste); } public ImageSurface allowPaste(boolean allowPaste) { this.allowPaste = allowPaste; return this; } final public boolean getAllowPaste(){ return allowPaste(); } public boolean allowPaste() { return allowPaste; } boolean allowPaste = false; 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; // use imageChanged event instead 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 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() { if (onMousePositionChanged != null) for (var listener : onMousePositionChanged) pcallF_typed(listener); } transient Set onImageChanged; public ImageSurface onImageChanged(Runnable r) { onImageChanged = createOrAddToSyncLinkedHashSet(onImageChanged, r); return this; } public ImageSurface removeImageChangedListener(Runnable r) { main.remove(onImageChanged, r); return this; } public void imageChanged() { if (onImageChanged != null) for (var listener : onImageChanged) pcallF_typed(listener); } transient Set> onUserModifiedImage; public ImageSurface onUserModifiedImage(IVF1 f) { onUserModifiedImage = createOrAddToSyncLinkedHashSet(onUserModifiedImage, f); return this; } public ImageSurface removeUserModifiedImageListener(IVF1 f) { main.remove(onUserModifiedImage, f); return this; } public void userModifiedImage(BufferedImage image) { if (onUserModifiedImage != null) for (var listener : onUserModifiedImage) pcallF_typed(listener, image); } public ImageSurface() { this(dummyImage()); } static BufferedImage dummyImage() { return whiteImage(1); } ImageSurface(File file) { setImage(file); } ImageSurface(MakesBufferedImage image) { this(image != null ? image.getBufferedImage() : dummyImage()); } ImageSurface(BufferedImage image) { setImage(image); clearSurface = false; // perform auto-zoom when shown, resized or when parent resized bindToComponent(this, () -> performAutoZoom(), null); onResize(this, () -> performAutoZoom()); onEnclosingScrollPaneResize(this, () -> performAutoZoom()); componentPopupMenu2(this, ImageSurface_popupMenuMaker()); new ImageSurfaceSelector(this); jHandleFileDrop(this, new VF1() { 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); menu.add(jMenuItemStayCheckedOnClick("Zoom to window", () -> autoZoomToDisplay, () -> setAutoZoomToDisplay(true))); 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() { 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() { 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 || allowPaste) addMenuItem(menu, "Paste image from clipboard", new Runnable() { public void run() { try { loadFromClipboard() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "loadFromClipboard()"; }}); if (!specialPurposed) addMenuItem(menu, "Load image snippet...", new Runnable() { public void run() { try { selectImageSnippet(new VF1() { 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() { 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; setUserModifiedImage(img); } void setUserModifiedImage(BufferedImage img) { setImage(img); userModifiedImage(img); } void loadFromClipboard() { BufferedImage img = getImageFromClipboard(); if (img != null) setUserModifiedImage(img); } transient IF0 defaultImageDir; File defaultImageDir() { return defaultImageDir != null ? defaultImageDir.get() : defaultImageDir_base(); } final File defaultImageDir_fallback(IF0 _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 } transient IVF3 drawBackground; void drawBackground(int w, int h, Graphics2D g) { if (drawBackground != null) drawBackground.get(w, h, g); else drawBackground_base(w, h, g); } final void drawBackground_fallback(IVF3 _f, int w, int h, Graphics2D g) { if (_f != null) _f.get(w, h, g); else drawBackground_base(w, h, g); } void drawBackground_base(int w, int h, Graphics2D g) { g.setColor(or(getBackground(), Color.white)); g.fillRect(0, 0, w, h); } public void render(int w, int h, Graphics2D g) { if (verbose) _print("render"); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolationMode); drawBackground(w, h, g); BufferedImage image = or(imageToDraw, this.image); if (!hasImage()) drawBackground(w, h, g); else { boolean alpha = !noAlpha && hasTransparency(image); if (alpha) drawBackground(w, h, g); 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(cloneGraphics(g)); } catch (Throwable __e) { pcallFail(__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); } void setImage(File file) { setFile(file); setImage(loadImage2(file)); } 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(); imageChanged(); } 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 overlay) { this.overlay = overlay; } boolean hasImage() { return image != null; } public int w() { return image.getWidth(); } public int h() { return image.getHeight(); } final void pixelate(boolean b){ setPixelated(b); } void setPixelated(boolean b) { assertTrue(b); imageSurface_pixelated(this); } final ImageSurface autoZoomToDisplay(boolean b){ return setAutoZoomToDisplay(b); } 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); } }); } final void clearTools(){ removeAllTools(); } 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 setOverlay(G2Drawable overlay) { clearOverlays(); if (overlay != null) addOverlay(overlay); } void loadImage(File f) { setImage(loadImage2(f)); } JComponent visualize() { return jscroll_center_borderless(this); } void standardZoom() { setZoom(1.0); } A print(A a) { return print("", a); } A print(String msg, A a) { return _print(msg, a); } } // end of ImageSurface // static function allows garbage collection static VF2 ImageSurface_popupMenuMaker() { return new VF2() { 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..."; }}; } abstract static class AbstractHasHi15Color { AbstractHasHi15Color() {} // Color in 15 bit final public AbstractHasHi15Color setHi15color(short hi15color){ return hi15color(hi15color); } public AbstractHasHi15Color hi15color(short hi15color) { this.hi15color = hi15color; return this; } final public short getHi15color(){ return hi15color(); } public short hi15color() { return hi15color; } short hi15color; Color color() { return main.hi15color(hi15color); } AbstractHasHi15Color color(Color color) { return hi15color(colorToHi15(color)); } String colorToString() { return lower(colorToHex(color())); } } interface StringIO { void readWrite(StringHead head); default String saveToString() { return saveToString(new StringHead()); } default String saveToString(StringHead head) { var writer = stringWriter(); head.writer(writer); readWrite(head); head.finish(); return str(writer); } default File saveToTextFile(File file) { Writer out = bufferedFileWriter(file); try { var head = new StringHead(out); readWrite(head); head.finish(); return file; } finally { _close(out); }} default StringIO fromString(String data){ return load(data); } default StringIO load(String data) { readWrite(new StringHead(stringReader(data))); return this; } default StringIO loadTextFile(File file) { var in = bufferedReader(file); try { readWrite(new StringHead(in)); return this; } finally { _close(in); }} } // TODO: base on AbstractList to speed up subList().clear() // Note: We just assume the wrapped list is RandomAccess; // technically we should have 2 separate classes. abstract static class NotifyingList extends NotifyingCollection implements List, RandomAccess { List list; NotifyingList() {} // for persistence NotifyingList(List list) { super(list); this.list = list; } public List subList(int i, int j) { throw todo(); } protected void setInnerList(List list) { c = this.list = list; } void init() { setInnerList(new ArrayList()); } void initAndAddAll(Collection l) { init(); addAll(l); } public boolean equals(Object o) { if (this == o) return true; synchronized (this) {return list.equals(o);} } public int hashCode() { synchronized (this) {return list.hashCode();} } public A get(int index) { synchronized (this) {return list.get(index);} } public A set(int index, A element) { synchronized (this) { beforeChange(); A a = list.set(index, element); change(); return a; } } public void add(int index, A element) { synchronized (this) { beforeChange(); list.add(index, element);} change(); } public A remove(int index) { A e; synchronized (this) { beforeChange(); e = list.remove(index); } change(); return e; } public int indexOf(Object o) { synchronized (this) {return list.indexOf(o);} } public int lastIndexOf(Object o) { synchronized (this) {return list.lastIndexOf(o);} } public boolean addAll(int index, Collection c) { synchronized (this) { beforeChange(); if (!list.addAll(index, c)) return false;} change(); return true; } public ListIterator listIterator() { return list.listIterator(); // doesn't implement all methods } public ListIterator listIterator(int index) { return list.listIterator(index); // doesn't implement all methods } /*public void replaceAll(UnaryOperator operator) { synchronized (this) {list.replaceAll(operator);} }*/ public void sort(Comparator c) { synchronized (this) { beforeChange(); list.sort(c);} change(); } } static class WAndHImpl extends Meta implements WidthAndHeight , IFieldsToList{ int w; int h; WAndHImpl() {} WAndHImpl(int w, int h) { this.h = h; this.w = w;}public Object[] _fieldsToList() { return new Object[] {w, h}; } public int getWidth() { return w; } public int getHeight() { return h; } public String toString() { return n2(w) + "*" + n2(h) + " px"; } void setSize(int w, int h) { this.w = w; this.h = h; } } interface INumberOfPixels { int numberOfPixels(); } static class OnePathWithOrigin extends OnePath { final public OnePathWithOrigin setOrigin(Pt origin){ return origin(origin); } public OnePathWithOrigin origin(Pt origin) { this.origin = origin; return this; } final public Pt getOrigin(){ return origin(); } public Pt origin() { return origin; } Pt origin = pt(0, 0); OnePathWithOrigin() {} OnePathWithOrigin(String path) { super(path); } OnePathWithOrigin(Pt origin, String path) { super(path); this.origin = origin; } OnePathWithOrigin(int originX, int originY, String path) { super(path); origin = pt(originX, originY); } OnePathWithOrigin(Iterable points, boolean close) { var l = asList(points); origin = first(l); fromPoints(l, close); } OnePathWithOrigin(OnePathWithOrigin path) { super(path); origin = path.origin; } public String toString() { return "Origin (" + origin + "), path: " + super.toString(); } OnePathWithOrigin reversed() { return new OnePathWithOrigin(reversedList(pointList()), false); } } // NotifyingList is our new go-to way to wrap a list // with additional functions. static class SSIList extends NotifyingList implements ByteIO { SSIList() { init(); } SSIList(Collection l) { initAndAddAll(l); } public void readWrite(ByteHead head) { head.exchangeInt(() -> size(), size -> addAll(repF(size, () -> new SSI()))); head.exchangeAll(this); } BufferedImage render(WidthAndHeight size) { return renderAll(this, size); } BufferedImage render(BufferedImage canvas) { return renderAll(this, canvas); } BufferedImage renderOutlines(WidthAndHeight size) { return main.render(size, g -> { g.setColor(Color.black); for (var ssi : this) ssi.drawOutline(g); }); } } abstract static class AbstractBufferedImageOp implements BufferedImageOp, Cloneable { public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { if ( dstCM == null ) dstCM = src.getColorModel(); return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null); } public Rectangle2D getBounds2D( BufferedImage src ) { return new Rectangle(0, 0, src.getWidth(), src.getHeight()); } public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) { if ( dstPt == null ) dstPt = new Point2D.Double(); dstPt.setLocation( srcPt.getX(), srcPt.getY() ); return dstPt; } public RenderingHints getRenderingHints() { return null; } /** * A convenience method for getting ARGB pixels from an image. This tries to avoid the performance * penalty of BufferedImage.getRGB unmanaging the image. * @param image a BufferedImage object * @param x the left edge of the pixel block * @param y the right edge of the pixel block * @param width the width of the pixel arry * @param height the height of the pixel arry * @param pixels the array to hold the returned pixels. May be null. * @return the pixels * @see #setRGB */ public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) { int type = image.getType(); if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB ) return (int [])image.getRaster().getDataElements( x, y, width, height, pixels ); return image.getRGB( x, y, width, height, pixels, 0, width ); } /** * A convenience method for setting ARGB pixels in an image. This tries to avoid the performance * penalty of BufferedImage.setRGB unmanaging the image. * @param image a BufferedImage object * @param x the left edge of the pixel block * @param y the right edge of the pixel block * @param width the width of the pixel arry * @param height the height of the pixel arry * @param pixels the array of pixels to set * @see #getRGB */ public void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) { int type = image.getType(); if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB ) image.getRaster().setDataElements( x, y, width, height, pixels ); else image.setRGB( x, y, width, height, pixels, 0, width ); } public Object clone() { try { return super.clone(); } catch ( CloneNotSupportedException e ) { return null; } } } interface IMultiMap { public Set keySet(); public Collection get(A a); public int size(); public int keyCount(); } // Note for implementors: It is allowed to call step() again // after it returned false (usually it should just return false again) static interface Steppable { public boolean step(); // return false if done } 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); } final Component get(){ return getComponent(); } Component getComponent() { return swing( () -> getComponentCount() == 0 ? null : getComponent(0)); } boolean isEmpty() { return getComponent() == null; } boolean hasComponent() { return getComponent() != null; } } static class Best implements IBest { A best; double score; boolean verboseNewBest, replaceIfSameScore; final public Best setLowerIsBetter(boolean lowerIsBetter){ return lowerIsBetter(lowerIsBetter); } public Best lowerIsBetter(boolean lowerIsBetter) { this.lowerIsBetter = lowerIsBetter; return this; } final public boolean getLowerIsBetter(){ return lowerIsBetter(); } public boolean lowerIsBetter() { return lowerIsBetter; } boolean lowerIsBetter = false; transient Object onChange; transient Object stringifier; // func(A) -> S synchronized boolean isNewBest(double score) { return best == null || !isNaN(score) && (replaceIfSameScore ? compareScores(score, this.score) >= 0 : compareScores(score, this.score) > 0); } double worstScore() { return lowerIsBetter ? infinity() : minusInfinity(); } int compareScores(double a, double b) { return lowerIsBetter ? -cmp(a, b) : cmp(a, b); } synchronized double bestScore() { return best == null ? worstScore() : score; } double score() { return bestScore(); } double getScore() { return bestScore(); } synchronized float floatScoreOr(float defaultValue) { return best == null ? defaultValue : (float) score; } boolean put(Scored s) { return s != null && put(s.get(), s.score()); } boolean put(Pair p) { return p != null && put(p.a, p.b); } boolean put(Best b) { return b != null && put(b.get(), b.score); } public 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 pair() { return main.pair(best, bestScore()); } synchronized Scored scored() { return best == null ? null : new Scored(best, bestScore()); } synchronized A getIfScoreAbove(double x) { return compareScores(score(), x) >= 0 ? 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; } } interface G2Drawable { void drawOn(Graphics2D g); default void drawOn(BufferedImage img) { drawOn(img.createGraphics()); } } static interface IF1_IntToInt { int get(int i); } // function type is A (e.g. String) static class FunctionTimings { Map data = syncMap(); final void timeCtex(A function, RunnableWithExceptions r){ doCtex(function, r); } void doCtex(A function, RunnableWithExceptions r) { try { if (r == null) return; long time = nanoTime(); try { r.run(); } finally { time = nanoTime()-time; saveTiming(function, time); } } catch (Exception __e) { throw rethrow(__e); } } B get(A function, IF0 f) { if (f == null) return null; Var var = new Var(); time(function, () -> var.set(f.get())); return var.get(); } final void time(A function, Runnable r){ dO(function, r); } void dO(A function, Runnable r) { if (r == null) return; long time = nanoTime(); try { r.run(); } finally { time = nanoTime()-time; saveTiming(function, time); } } void saveTiming(A function, long time) { Average avg = syncMapGetOrCreate(data, function, () -> new Average()); avg.add(time); } Map get() { return cloneMap(data); } final void clear(){ reset(); } void reset() { data.clear(); } // as multi-line string, sorted alphabetically final public String toString(){ return render(); } public String render() { return lines(renderedEntries()); } List renderedEntries() { return ciSorted(map(get(), (f, avg) -> functionToString(f) + ": " + n2(iround(nsToMicroseconds(avg.get()))) + " " + microSymbol() + "s (" + n2(iround(avg.n())) + ")")); } String toStringSingleLine() { return joinWithComma(renderedEntries()); } String functionToString(A f) { return firstToUpper(str(f)); } } static interface IBWImage extends MakesBufferedImage, IRGBImage { float getFloatPixel(int x, int y); // usually between 0 and 1 default int getInt(int x, int y) { return iround(getFloatPixel(x, y)*255f); } // implementing IRGBImage default int getIntPixel(int x, int y) { return rgbIntFromGrayscale(getInt(x, y)); } default float getFloatPixel(Pt p) { return getFloatPixel(p.x, p.y); } default float getFloatPixel(int index) { int w = w(); return getFloatPixel(index % w, index / w); } default float[] toFloatArray() { float[] data = new float[w()*h()]; for (int i = 0; i < l(data); i++) data[i] = getFloatPixel(i); return data; } public default BufferedImage getBufferedImage() { return grayImageFromIBWImage(this); } default BWImage toBWImage() { return this instanceof BWImage ? (BWImage) this : iBWImageToBWImage(this); } } /** * A class that provides scrolling capabilities to a long menu dropdown or * popup menu. A number of items can optionally be frozen at the top and/or * bottom of the menu. *

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

* * @version 1.5.0 04/05/12 * @author Darryl * https://tips4java.wordpress.com/2009/02/01/menu-scroller/ */ static class JMenuScroller { VF1 fillMenu; private JPopupMenu menu; private Component[] menuItems; private MenuScrollItem upItem; private MenuScrollItem downItem; private final MenuScrollListener menuListener = new MenuScrollListener(); private int scrollCount; private int interval; private int topFixedCount; private int bottomFixedCount; private int firstIndex = 0; private int keepVisibleIndex = -1; /** * Registers a menu to be scrolled with the default number of items to * display at a time and the default scrolling interval. * * @param menu the menu * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JMenu menu) { return new JMenuScroller(menu); } /** * Registers a popup menu to be scrolled with the default number of items to * display at a time and the default scrolling interval. * * @param menu the popup menu * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JPopupMenu menu) { return new JMenuScroller(menu); } /** * Registers a menu to be scrolled with the default number of items to * display at a time and the specified scrolling interval. * * @param menu the menu * @param scrollCount the number of items to display at a time * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount is 0 or negative */ public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount) { return new JMenuScroller(menu, scrollCount); } /** * Registers a popup menu to be scrolled with the default number of items to * display at a time and the specified scrolling interval. * * @param menu the popup menu * @param scrollCount the number of items to display at a time * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount is 0 or negative */ public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) { return new JMenuScroller(menu, scrollCount); } /** * Registers a menu to be scrolled, with the specified number of items to * display at a time and the specified scrolling interval. * * @param menu the menu * @param scrollCount the number of items to be displayed at a time * @param interval the scroll interval, in milliseconds * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval) { return new JMenuScroller(menu, scrollCount, interval); } /** * Registers a popup menu to be scrolled, with the specified number of items to * display at a time and the specified scrolling interval. * * @param menu the popup menu * @param scrollCount the number of items to be displayed at a time * @param interval the scroll interval, in milliseconds * @return the JMenuScroller * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) { return new JMenuScroller(menu, scrollCount, interval); } /** * Registers a menu to be scrolled, with the specified number of items * to display in the scrolling region, the specified scrolling interval, * and the specified numbers of items fixed at the top and bottom of the * menu. * * @param menu the menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0. * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { return new JMenuScroller(menu, scrollCount, interval, topFixedCount, bottomFixedCount); } /** * Registers a popup menu to be scrolled, with the specified number of items * to display in the scrolling region, the specified scrolling interval, * and the specified numbers of items fixed at the top and bottom of the * popup menu. * * @param menu the popup menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0 * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative * @return the JMenuScroller */ public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { return new JMenuScroller(menu, scrollCount, interval, topFixedCount, bottomFixedCount); } /** * Constructs a JMenuScroller that scrolls a menu with the * default number of items to display at a time, and default scrolling * interval. * * @param menu the menu */ public JMenuScroller(JMenu menu) { this(menu, 15); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * default number of items to display at a time, and default scrolling * interval. * * @param menu the popup menu */ public JMenuScroller(JPopupMenu menu) { this(menu, 15); } /** * Constructs a JMenuScroller that scrolls a menu with the * specified number of items to display at a time, and default scrolling * interval. * * @param menu the menu * @param scrollCount the number of items to display at a time * @throws IllegalArgumentException if scrollCount is 0 or negative */ public JMenuScroller(JMenu menu, int scrollCount) { this(menu, scrollCount, 150); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * specified number of items to display at a time, and default scrolling * interval. * * @param menu the popup menu * @param scrollCount the number of items to display at a time * @throws IllegalArgumentException if scrollCount is 0 or negative */ public JMenuScroller(JPopupMenu menu, int scrollCount) { this(menu, scrollCount, 150); } /** * Constructs a JMenuScroller that scrolls a menu with the * specified number of items to display at a time, and specified scrolling * interval. * * @param menu the menu * @param scrollCount the number of items to display at a time * @param interval the scroll interval, in milliseconds * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public JMenuScroller(JMenu menu, int scrollCount, int interval) { this(menu, scrollCount, interval, 0, 0); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * specified number of items to display at a time, and specified scrolling * interval. * * @param menu the popup menu * @param scrollCount the number of items to display at a time * @param interval the scroll interval, in milliseconds * @throws IllegalArgumentException if scrollCount or interval is 0 or negative */ public JMenuScroller(JPopupMenu menu, int scrollCount, int interval) { this(menu, scrollCount, interval, 0, 0); } /** * Constructs a JMenuScroller that scrolls a menu with the * specified number of items to display in the scrolling region, the * specified scrolling interval, and the specified numbers of items fixed at * the top and bottom of the menu. * * @param menu the menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0 * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative */ public JMenuScroller(JMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { this(menu.getPopupMenu(), scrollCount, interval, topFixedCount, bottomFixedCount); } /** * Constructs a JMenuScroller that scrolls a popup menu with the * specified number of items to display in the scrolling region, the * specified scrolling interval, and the specified numbers of items fixed at * the top and bottom of the popup menu. * * @param menu the popup menu * @param scrollCount the number of items to display in the scrolling portion * @param interval the scroll interval, in milliseconds * @param topFixedCount the number of items to fix at the top. May be 0 * @param bottomFixedCount the number of items to fix at the bottom. May be 0 * @throws IllegalArgumentException if scrollCount or interval is 0 or * negative or if topFixedCount or bottomFixedCount is negative */ public JMenuScroller(JPopupMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) { if (scrollCount <= 0 || interval <= 0) { throw new IllegalArgumentException("scrollCount and interval must be greater than 0"); } if (topFixedCount < 0 || bottomFixedCount < 0) { throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative"); } upItem = new MenuScrollItem(UP, -1); downItem = new MenuScrollItem(DOWN, +1); setScrollCount(scrollCount); setInterval(interval); setTopFixedCount(topFixedCount); setBottomFixedCount(bottomFixedCount); this.menu = menu; menu.addPopupMenuListener(menuListener); } /** * Returns the scroll interval in milliseconds * * @return the scroll interval in milliseconds */ public int getInterval() { return interval; } /** * Sets the scroll interval in milliseconds * * @param interval the scroll interval in milliseconds * @throws IllegalArgumentException if interval is 0 or negative */ public void setInterval(int interval) { if (interval <= 0) { throw new IllegalArgumentException("interval must be greater than 0"); } upItem.setInterval(interval); downItem.setInterval(interval); this.interval = interval; } /** * Returns the number of items in the scrolling portion of the menu. * * @return the number of items to display at a time */ public int getscrollCount() { return scrollCount; } /** * Sets the number of items in the scrolling portion of the menu. * * @param scrollCount the number of items to display at a time * @throws IllegalArgumentException if scrollCount is 0 or negative */ public void setScrollCount(int scrollCount) { if (scrollCount <= 0) { throw new IllegalArgumentException("scrollCount must be greater than 0"); } this.scrollCount = scrollCount; // XXX the following line closes all menus then this menu is made. // That doesn't seem right. // MenuSelectionManager.defaultManager().clearSelectedPath(); } /** * Returns the number of items fixed at the top of the menu or popup menu. * * @return the number of items */ public int getTopFixedCount() { return topFixedCount; } /** * Sets the number of items to fix at the top of the menu or popup menu. * * @param topFixedCount the number of items */ public void setTopFixedCount(int topFixedCount) { if (firstIndex <= topFixedCount) { firstIndex = topFixedCount; } else { firstIndex += (topFixedCount - this.topFixedCount); } this.topFixedCount = topFixedCount; } /** * Returns the number of items fixed at the bottom of the menu or popup menu. * * @return the number of items */ public int getBottomFixedCount() { return bottomFixedCount; } /** * Sets the number of items to fix at the bottom of the menu or popup menu. * * @param bottomFixedCount the number of items */ public void setBottomFixedCount(int bottomFixedCount) { this.bottomFixedCount = bottomFixedCount; } /** * Scrolls the specified item into view each time the menu is opened. Call this method with * null to restore the default behavior, which is to show the menu as it last * appeared. * * @param item the item to keep visible * @see #keepVisible(int) */ public void keepVisible(JMenuItem item) { if (item == null) { keepVisibleIndex = -1; } else { int index = menu.getComponentIndex(item); keepVisibleIndex = index; } } /** * Scrolls the item at the specified index into view each time the menu is opened. Call this * method with -1 to restore the default behavior, which is to show the menu as * it last appeared. * * @param index the index of the item to keep visible * @see #keepVisible(javax.swing.JMenuItem) */ public void keepVisible(int index) { keepVisibleIndex = index; } /** * Removes this JMenuScroller from the associated menu and restores the * default behavior of the menu. */ public void dispose() { if (menu != null) { menu.removePopupMenuListener(menuListener); menu = null; } } /** * Ensures that the dispose method of this JMenuScroller is * called when there are no more refrences to it. * * @exception Throwable if an error occurs. * @see JMenuScroller#dispose() */ @Override public void finalize() throws Throwable { dispose(); } private void refreshMenu() { if (menuItems != null && menuItems.length > 0) { firstIndex = Math.max(topFixedCount, firstIndex); firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex); upItem.setEnabled(firstIndex > topFixedCount); downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount); menu.removeAll(); for (int i = 0; i < topFixedCount; i++) { menu.add(menuItems[i]); } if (topFixedCount > 0) { menu.addSeparator(); } menu.add(upItem); for (int i = firstIndex; i < scrollCount + firstIndex; i++) { menu.add(menuItems[i]); } menu.add(downItem); if (bottomFixedCount > 0) { menu.addSeparator(); } for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) { menu.add(menuItems[i]); } JComponent parent = (JComponent) upItem.getParent(); parent.revalidate(); parent.repaint(); } } private class MenuScrollListener implements PopupMenuListener { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { if (fillMenu != null) { clearPopupMenu(menu); callF(fillMenu, menu); } setMenuItems(); } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { if (fillMenu != null) clearPopupMenu(menu); else restoreMenuItems(); } @Override public void popupMenuCanceled(PopupMenuEvent e) { if (fillMenu != null) clearPopupMenu(menu); else restoreMenuItems(); } private void setMenuItems() { menuItems = menu.getComponents(); if (keepVisibleIndex >= topFixedCount && keepVisibleIndex <= menuItems.length - bottomFixedCount && (keepVisibleIndex > firstIndex + scrollCount || keepVisibleIndex < firstIndex)) { firstIndex = Math.min(firstIndex, keepVisibleIndex); firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1); } if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) { refreshMenu(); } } private void restoreMenuItems() { menu.removeAll(); for (Component component : menuItems) { menu.add(component); } } } private class MenuScrollTimer extends javax.swing.Timer { public MenuScrollTimer(final int increment, int interval) { super(interval, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { firstIndex += increment; refreshMenu(); } }); } } private class MenuScrollItem extends JMenuItem implements ChangeListener { private MenuScrollTimer timer; public MenuScrollItem(MenuIcon icon, int increment) { setIcon(icon); setDisabledIcon(icon); timer = new MenuScrollTimer(increment, interval); addChangeListener(this); } public void setInterval(int interval) { timer.setDelay(interval); } @Override public void stateChanged(ChangeEvent e) { if (isArmed() && !timer.isRunning()) { timer.start(); } if (!isArmed() && timer.isRunning()) { timer.stop(); } } } static MenuIcon UP = new MenuIcon(9, 1, 9); static MenuIcon DOWN = new MenuIcon(1, 9, 1); private static class MenuIcon implements Icon { final int[] xPoints = {1, 5, 9}; final int[] yPoints; MenuIcon(int... yPoints) { this.yPoints = yPoints; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { Dimension size = c.getSize(); Graphics g2 = g.create(size.width / 2 - 5, size.height / 2 - 5, 10, 10); g2.setColor(Color.GRAY); g2.drawPolygon(xPoints, yPoints, 3); if (c.isEnabled()) { g2.setColor(Color.BLACK); g2.fillPolygon(xPoints, yPoints, 3); } g2.dispose(); } @Override public int getIconWidth() { return 0; } @Override public int getIconHeight() { return 10; } } } static class PtInComponent 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;} } static class Percent implements IFieldsToList{ double percent; Percent() {} Percent(double percent) { this.percent = percent;} public boolean equals(Object o) { if (!(o instanceof Percent)) return false; Percent __1 = (Percent) o; return percent == __1.percent; } public int hashCode() { int h = 985725989; h = boostHashCombine(h, _hashCode(percent)); return h; } public Object[] _fieldsToList() { return new Object[] {percent}; } static Percent fromRatio(double ratio) { return new Percent(ratio*100); } static Percent fromRatio(double x, double y) { return fromRatio(doubleRatio(x, y)); } void set(double percent) { percent = percent; } double get() { return percent; } public String toString() { return renderValue(percent) + " %"; } String renderValue(double percent) { return formatDouble(percent, 1); } final String withDecimals(int decimals){ return renderWithDecimals(decimals); } String renderWithDecimals(int decimals) { return formatDouble(percent, decimals) + " %"; } String withExactDecimals(int decimals) { return formatDoubleX(percent, decimals) + " %"; } String oneDecimal() { return withDecimals(1); } String exactlyOneDecimal() { return withExactDecimals(1); } static Percent ratio(double x, double y) { return new Percent(doubleRatio(x, y)*100); } } static class Image2B implements IBinaryImage { Image2B() {} int w, h; byte[] pixels; Image2B(int w, int h, byte[] pixels) { this.pixels = pixels; this.h = h; this.w = w; cleanPixelArray(); } Image2B(Image2B img) { w = img.getWidth(); h = img.getHeight(); pixels = cloneByteArray(img.pixels); } Image2B(RGBImage img) { w = img.getWidth(); h = img.getHeight(); pixels = new byte[(w*h+7)/8]; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) if (img.getPixel(x, y).getBrightness() >= 0.5f) { int i = y*w+x; pixels[i/8] |= 1 << (i & 7); } } Image2B(BWImage img) { this(img, 128); } // >= threshold Image2B(BWImage img, int threshold) { w = img.w(); h = img.h(); int n = w*h; int nOut = (n+7)/8; byte[] pixels = this.pixels = new byte[nOut]; byte[] bwPixels = img.pixels; int iIn = 0; // do the bulk for (int iOut = 0; iOut < nOut-1; iOut++) { int value = 0; for (int bit = 0; bit < 8; bit++) { value >>= 1; if (ubyteToInt(bwPixels[iIn++]) >= threshold) value |= 0x80; } pixels[iOut] = (byte) value; } // do last (up to 7) bits for (; iIn < n; iIn++) if (ubyteToInt(bwPixels[iIn]) >= threshold) pixels[nOut-1] |= 1 << (iIn & 7); } Image2B(BufferedImage img) { this(img, 128); } Image2B(BufferedImage img, int threshold) { this(new BWImage(img), threshold); } // initializes with black Image2B(int w, int h) { this.h = h; this.w = w; pixels = new byte[(w*h+7)/8]; } RGBImage toRGB() { RGBImage img = new RGBImage(w, h, Color.black); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int i = y*w+x; if ((pixels[i/8] & (1 << (i & 7))) != 0) img.setPixel(x, y, Color.white); } return img; } BWImage toBW() { BWImage img = new BWImage(w, h, 0f); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int i = y*w+x; if ((pixels[i/8] & (1 << (i & 7))) != 0) img.setPixel(x, y, 1f); } return img; } public BufferedImage getBufferedImage() { return toBW().getBufferedImage(); } // x and y must be inside the image final public boolean getBoolPixel(int x, int y){ return getPixel(x, y); } public boolean getPixel(int x, int y) { int i = y*w+x; return (pixels[i/8] & (1 << (i & 7))) != 0; } // defaultColor is color outside of image final public boolean getBoolPixel(int x, int y, boolean defaultColor){ return getPixel(x, y, defaultColor); } public boolean getPixel(int x, int y, boolean defaultColor) { if (x < 0 || y < 0 || x >= w || y >= h) return defaultColor; return getPixel(x, y); } public void setPixel(int x, int y, boolean b) { int i = y*w+x; byte val = pixels[i/8], shifted = (byte) (1 << (i & 7)); val = (byte) (b ? val | shifted : val & ~shifted); pixels[i/8] = val; } void setPixel(int x, int y) { int i = y*w+x; pixels[i/8] |= 1 << (i & 7); } public int getWidth() { return w; } public int getHeight() { return h; } public String toString() { return "Image2B " + str_px(w, h); } // clear unused bits in pixel array after we received // a possibly dirty array void cleanPixelArray() { int n = w*h; if ((n & 7) != 0) pixels[n/8] &= (1 << (n & 7))-1; } public double averageBrightness() { return doubleRatio(bitCount(pixels), w*h); } } // Abstract base class for finding all connected regions of same color // in an image using flood filling abstract static class AbstractFastRegions implements Runnable, IImageRegions , IFieldsToList{ Img image; AbstractFastRegions() {} AbstractFastRegions(Img image) { this.image = image;}public Object[] _fieldsToList() { return new Object[] {image}; } int w, h; // Image size int runner; // Position in image currently being scanned final public int getSize(){ return size(); } public int size() { return size; } int size; // =w*h IntBuffer stack = new IntBuffer(); // locations as y*w+x final public int[] getRegionMatrix(){ return regionMatrix(); } public int[] regionMatrix() { return regionMatrix; } int[] regionMatrix; // for each pixel: region index (starting at 1) IntBuffer regionPixels = new IntBuffer(); // collect all pixels for regions final public AbstractFastRegions setWithDiagonals(boolean withDiagonals){ return withDiagonals(withDiagonals); } public AbstractFastRegions withDiagonals(boolean withDiagonals) { this.withDiagonals = withDiagonals; return this; } final public boolean getWithDiagonals(){ return withDiagonals(); } public boolean withDiagonals() { return withDiagonals; } boolean withDiagonals = false; // also walk diagonally? // initialize these to use them IntBuffer regionFirstPixel = new IntBuffer(); // for each region: index of first pixel found in regionPixels IntBuffer regionSize = new IntBuffer(); // for each region: number of pixels IntBuffer regionBounds = new IntBuffer(); // for each region: bounds (x1, y1, x2, y2) // index = dual log of region size, value = region index List regionsBySize = new ArrayList(); int regionCounter; boolean verbose = false; // temporary int currentColor; double regionStep = .1; // for rendering in regionsImage int x(int pos) { return pos % w; } int y(int pos) { return pos / w; } int pos(int x, int y) { return y*w+x; } Pt pt(int pos) { return new Pt(x(pos), y(pos)); } boolean validPos(int x, int y) { return x >= 0 && y >= 0 && x < w && y < h; } abstract int getColor(int pos); boolean initialized() { return regionMatrix != null; } void init() { if (initialized()) return; w = image.getWidth(); h = image.getHeight(); size = w*h; regionMatrix = new int[size]; // 0 entries are unused { if (regionFirstPixel != null) regionFirstPixel.add(0); } { if (regionSize != null) regionSize.add(0); } { if (regionPixels != null) regionPixels.setSize(size); } } // Search for all regions in scanline order public void run() { try { if (initialized()) return; init(); while (runner < size) { if (regionMatrix[runner] == 0) makeRegion(); ++runner; } } catch (Exception __e) { throw rethrow(__e); } } // returns index of new region int makeRegion() { // make a new region, get color int region = ++regionCounter; { if (regionFirstPixel != null) regionFirstPixel.add(regionPixels != null ? l(regionPixels) : runner); } currentColor = getColor(runner); stack.add(runner); int rsize = 0, x1 = w, y1 = h, x2 = 0, y2 = 0; // flood-fill region while (nempty(stack)) { int pos = stack.popLast(); if (regionMatrix[pos] != 0) continue; // check again if set in the meantime (color has been checked before) // new pixel found! int x = x(pos), y = y(pos); // march to the left int lineStart = pos-x; int xLeft = x; while (xLeft > 0 && addable(lineStart+xLeft-1)) --xLeft; // march to the right int xRight = x+1; while (xRight < w && addable(lineStart+xRight)) ++xRight; // Now we know [xLeft; xRight) are our pixels // mark them in matrix for (x = xLeft; x < xRight; x++) { regionMatrix[lineStart+x] = region; { if (regionPixels != null) regionPixels.add(lineStart+x); } } // increase region size rsize += xRight-xLeft; // update bounds if (xLeft < x1) x1 = xLeft; if (xRight-1 > x2) x2 = xRight-1; if (y < y1) y1 = y; if (y > y2) y2 = y; // explore above & below int xLeft2 = withDiagonals ? max(0, xLeft-1) : xLeft; int xRight2 = withDiagonals ? min(w, xRight+1) : xRight; if (y > 0) addStreaks(lineStart-w, xLeft2, xRight2); if (y < h-1) addStreaks(lineStart+w, xLeft2, xRight2); } { if (regionSize != null) regionSize.add(rsize); } { if (regionBounds != null) regionBounds.addAll(x1, y1, x2+1, y2+1); } if (regionsBySize != null) { int iBucket = dualLog(rsize); var buffer = listGetOrCreate(regionsBySize, iBucket, () -> new IntBuffer()); buffer.add(region); } return region; } boolean addable(int pos) { if (regionMatrix[pos] != 0) return false; // touching myself (or someone else) if (getColor(pos) != currentColor) return false; // wrong color return true; } boolean addToStack(int pos) { if (!addable(pos)) return false; stack.add(pos); return true; } void addStreaks(int lineStart, int xLeft, int xRight) { int x = xLeft; while (x < xRight) { if (addToStack(lineStart+x)) while (x+1 < xRight && addable(lineStart+x+1)) ++x; ++x; } } IBWImage regionsImage() { return iBWImageFromFunction((x, y) -> { var region = regionMatrix[pos(x, y)]; return ((region-1)*regionStep) % (1.0+regionStep-0.0001); }, w, h); } final public int nRegions(){ return regionCount(); } public int regionCount() { return regionCounter; } abstract class RegionIterator { int pos; abstract boolean next(); int pos() { return pos; } int x() { return AbstractFastRegions.this.x(pos); } int y() { return AbstractFastRegions.this.y(pos); } int[] pixelsAsIntArray() { throw todo(); } } // returns points in no particular order class FloodRegionIterator extends RegionIterator { int region; IntBuffer stack = new IntBuffer(); // locations as y*w+x BitSet seen = new BitSet(size); FloodRegionIterator(int region) { this.region = region; int pos = regionFirstPixel.get(region); printVars("region", region, "pos", pos); seen.set(pos); stack.add(pos); } // flood-fill region boolean next() { if (empty(stack)) return false; pos = stack.popLast(); // explore neighborhood int x = x(), y = y(); if (x > 0) tryPosition(pos-1); if (x < w-1) tryPosition(pos+1); if (y > 0) tryPosition(pos-w); if (y < h-1) tryPosition(pos+w); return true; } private void tryPosition(int p) { if (!seen.get(p) && regionMatrix[p] == region) { seen.set(p); stack.add(p); } } } class CachedRegionIterator extends RegionIterator { int i, to; CachedRegionIterator(int region) { i = regionFirstPixel.get(region); to = region+1 < l(regionFirstPixel) ? regionFirstPixel.get(region+1) : l(regionPixels); } boolean next() { if (i >= to) return false; pos = regionPixels.get(i++); return true; } int[] pixelsAsIntArray() { return regionPixels.subArray(i, to); } } int regionSize(int iRegion) { return regionSize.get(iRegion); } final Pt firstPixel(int iRegion){ return samplePixel(iRegion); } Pt samplePixel(int iRegion) { return pt(firstPixelPos(iRegion)); } int firstPixelPos(int iRegion) { int i = regionFirstPixel.get(iRegion); return regionPixels != null ? regionPixels.get(i) : i; } boolean inRegion(int iRegion, int x, int y) { return validPos(x, y) && regionMatrix[pos(x, y)] == iRegion; } Rect regionBounds(int iRegion) { return rectFromPoints( regionBounds.get((iRegion-1)*4), regionBounds.get((iRegion-1)*4+1), regionBounds.get((iRegion-1)*4+2), regionBounds.get((iRegion-1)*4+3)); } int regionAt(Pt p) { return regionAt(p.x, p.y); } int regionAt(int x, int y) { return !validPos(x, y) ? 0 : regionMatrix[pos(x, y)]; } int regionAt(int pos) { return regionMatrix[pos]; } RegionIterator regionIterator(int iRegion) { return regionPixels != null ? new CachedRegionIterator(iRegion) : new FloodRegionIterator(iRegion); } List regionPixels(int iRegion) { var it = regionIterator(iRegion); List l = new ArrayList(); while (it.next()) l.add(new Pt(it.x(), it.y())); return l; } // select extra features before regions are made // (not necessary anymore) void collectFirstPixels() { /*regionFirstPixel = new IntBuffer;*/ } void collectBounds() { /*regionBounds = new IntBuffer;*/ } Matrix regionBitMatrix(int iRegion) { return new AbstractMatrix(w, h) { public Boolean get(int x, int y) { return inRegion(iRegion, x, y); } }; } void markRegionInPixelArray(int[] pixels, int iRegion, int rgba) { if (iRegion <= 0) return; for (int i = 0; i < l(pixels); i++) if (regionAt(i) == iRegion) pixels[i] = rgba; } List regionIndices() { return virtualCountList(1, nRegions()+1); } IterableIterator regionsRoughlyByDecreasingSize() { return nestedIterator(countIterator_inclusive_backwards(regionsBySize.size()-1, 0), iBucket -> iterator(regionsBySize.get(iBucket))); } final public IImageRegion get(int iRegion){ return getRegion(iRegion); } public IImageRegion getRegion(int iRegion) { return new ImageRegion(iRegion); } final public List> get(){ return regions(); } public List> regions() { run(); return listFromFunction(i -> getRegion(i+1), nRegions()); } class ImageRegion implements IImageRegion , IFieldsToList{ int iRegion; ImageRegion() {} ImageRegion(int iRegion) { this.iRegion = iRegion;}public Object[] _fieldsToList() { return new Object[] {iRegion}; } public int hashCode() { return iRegion; } public boolean equals(Object o) { if (!(o instanceof AbstractFastRegions.ImageRegion)) return false; return ((AbstractFastRegions.ImageRegion) o).creator() == creator() && ((AbstractFastRegions.ImageRegion) o).iRegion == iRegion; } public Img image() { return image; } public Object creator() { return AbstractFastRegions.this; } public int indexInCreator() { return iRegion; } public Boolean createdWithDiagonals() { return withDiagonals; } public Rect bounds() { return regionBounds(iRegion); } public int numberOfPixels() { return regionSize(iRegion); } public Pt firstPixel() { return pt(firstPixelPos()); } public int firstPixelPos() { return AbstractFastRegions.this.firstPixelPos(iRegion); } public IterableIterator pixelIterator() { var it = regionIterator(iRegion); return iteratorFromFunction(() -> it.next() ? pt(it.pos()) : null); } public int[] pixelsAsIntArray() { return regionIterator(iRegion).pixelsAsIntArray(); } public boolean contains(int x, int y) { return inRegion(iRegion, x, y); } public Color color() { return toColor(rgbForRegion(this)); } final public int firstPixelLogicalColor(){ return brightness(); } public int brightness() { return getColor(firstPixelPos()); } public String toString() { return renderRecordVars("Region", "brightness", brightness(), "color", color(), "pixels" , numberOfPixels(), "bounds", bounds()); } } abstract RGB rgbForRegion(ImageRegion r); public Img image() { return image; } // called after scan was performed externally void markDone() { runner = size; } } // result image is as large as the region's bounds // with black pixels inside and white pixels outside the region static class RegionToImage2B implements IFieldsToList{ IPixelSet region; RegionToImage2B() {} RegionToImage2B(IPixelSet region) { this.region = region;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; } final public RegionToImage2B setBounds(Rect bounds){ return bounds(bounds); } public RegionToImage2B bounds(Rect bounds) { this.bounds = bounds; return this; } final public Rect getBounds(){ return bounds(); } public Rect bounds() { return bounds; } Rect bounds; Image2B image; public void run() { try { if (bounds == null) bounds = region.bounds(); int x1 = bounds.x, y1 = bounds.y, w = bounds.w, h = bounds.h, i = 0; byte[] pixels = new byte[(w*h+7)/8]; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { if (!region.contains(x1+x, y1+y)) pixels[i/8] |= 1 << (i & 7); i++; } image = new Image2B(w, h, pixels); } catch (Exception __e) { throw rethrow(__e); } } Image2B get() { if (image == null) run(); return image; } } static interface ISetter { void set(A a); } // 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 + "]"; } } static interface IAutoCloseableF0 extends IF0, AutoCloseable {} static class GrabbableRGBBytePixels implements IFieldsToList{ static final String _fieldOrder = "data w h offset scanlineStride pixelStride"; byte[] data; int w; int h; int offset; int scanlineStride; int pixelStride; GrabbableRGBBytePixels() {} GrabbableRGBBytePixels(byte[] data, int w, int h, int offset, int scanlineStride, int pixelStride) { this.pixelStride = pixelStride; 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 + ", " + pixelStride + ")"; } public boolean equals(Object o) { if (!(o instanceof GrabbableRGBBytePixels)) return false; GrabbableRGBBytePixels __1 = (GrabbableRGBBytePixels) o; return eq(data, __1.data) && w == __1.w && h == __1.h && offset == __1.offset && scanlineStride == __1.scanlineStride && pixelStride == __1.pixelStride; } public int hashCode() { int h = 330924690; 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)); h = boostHashCombine(h, _hashCode(pixelStride)); return h; } public Object[] _fieldsToList() { return new Object[] {data, w, h, offset, scanlineStride, pixelStride}; } } /* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ // modified by Stefan Reich // Implements the Set interface more compactly than // java.util.HashSet by using a closed hashtable. // Note: equals is always called on the _stored_ object, not the one // passed as an argument to find(), contains() etc. // (In case you want to put special magic in your equals() function) static class UnsynchronizedCompactHashSet extends java.util.AbstractSet { protected final static int INITIAL_SIZE = 3; public final static double LOAD_FACTOR = 0.75; protected final static Object nullObject = new Object(); protected final static Object deletedObject = new Object(); protected int elements; protected int freecells; protected A[] objects; protected int modCount; UnsynchronizedCompactHashSet() { this(INITIAL_SIZE); } UnsynchronizedCompactHashSet(int size) { // NOTE: If array size is 0, we get a // "java.lang.ArithmeticException: / by zero" in add(Object). objects = (A[]) new Object[(size==0 ? 1 : size)]; elements = 0; freecells = objects.length; modCount = 0; } UnsynchronizedCompactHashSet(Collection c) { this(c.size()); addAll(c); } @Override public Iterator iterator() { return new CompactHashIterator(); } @Override public int size() { return elements; } @Override public boolean isEmpty() { return elements == 0; } @Override public boolean contains(Object o) { return find(o) != null; } A find(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } return objects[index]; } boolean removeIfSame(Object o) { A value = find(o); if (value == o) { remove(value); return true; } return false; } @Override public boolean add(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; int deletedix = -1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { // if there's a deleted object here we can put this object here, // provided it's not in here somewhere else already if (objects[index] == deletedObject) deletedix = index; index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } if (objects[index] == null) { // wasn't present already if (deletedix != -1) // reusing a deleted cell index = deletedix; else freecells--; modCount++; elements++; // here we face a problem regarding generics: // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' // so adding an empty object is a problem here // If (! o instanceof A) : This will cause a class cast exception // If (o instanceof A) : This will work fine objects[index] = (A) o; // do we need to rehash? if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) rehash(); return true; } else // was there already return false; } @Override public boolean remove(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } // we found the right position, now do the removal if (objects[index] != null) { // we found the object // same problem here as with add objects[index] = (A) deletedObject; modCount++; elements--; return true; } else // we did not find the object return false; } @Override public void clear() { elements = 0; for (int ix = 0; ix < objects.length; ix++) objects[ix] = null; freecells = objects.length; modCount++; } @Override public Object[] toArray() { Object[] result = new Object[elements]; Object[] objects = this.objects; int pos = 0; for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) { if (objects[i] == nullObject) result[pos++] = null; else result[pos++] = objects[i]; } // unchecked because it should only contain A return result; } // not sure if this needs to have generics @Override public T[] toArray(T[] a) { int size = elements; if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); A[] objects = this.objects; int pos = 0; for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) { if (objects[i] == nullObject) a[pos++] = null; else a[pos++] = (T) objects[i]; } return a; } protected void rehash() { int garbagecells = objects.length - (elements + freecells); if (garbagecells / (double) objects.length > 0.05) // rehash with same size rehash(objects.length); else // rehash with increased capacity rehash(objects.length*2 + 1); } protected void rehash(int newCapacity) { int oldCapacity = objects.length; @SuppressWarnings("unchecked") A[] newObjects = (A[]) new Object[newCapacity]; for (int ix = 0; ix < oldCapacity; ix++) { Object o = objects[ix]; if (o == null || o == deletedObject) continue; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % newCapacity; int offset = 1; // search for the object while(newObjects[index] != null) { // no need to test for duplicates index = ((index + offset) & 0x7FFFFFFF) % newCapacity; offset = offset*2 + 1; if (offset == -1) offset = 2; } newObjects[index] = (A) o; } objects = newObjects; freecells = objects.length - elements; } private class CompactHashIterator implements Iterator { private int index; private int lastReturned = -1; private int expectedModCount; @SuppressWarnings("empty-statement") public CompactHashIterator() { for (index = 0; index < objects.length && (objects[index] == null || objects[index] == deletedObject); index++) ; expectedModCount = modCount; } @Override public boolean hasNext() { return index < objects.length; } @SuppressWarnings("empty-statement") @Override public T next() { /*if (modCount != expectedModCount) throw new ConcurrentModificationException();*/ int length = objects.length; if (index >= length) { lastReturned = -2; throw new NoSuchElementException(); } lastReturned = index; for (index += 1; index < length && (objects[index] == null || objects[index] == deletedObject); index++) ; if (objects[lastReturned] == nullObject) return null; else return (T) objects[lastReturned]; } @Override public void remove() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (lastReturned == -1 || lastReturned == -2) throw new IllegalStateException(); // delete object if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) { objects[lastReturned] = (A) deletedObject; elements--; modCount++; expectedModCount = modCount; // this is expected; we made the change } } } int capacity() { return objects.length; } // returns true if there was a shrink boolean shrinkToFactor(double factor) { if (factor > LOAD_FACTOR) throw fail("Shrink factor must be equal to or smaller than load factor: " + factor + " / " + LOAD_FACTOR); int newCapacity = max(INITIAL_SIZE, iround(size()/factor)); if (newCapacity >= capacity()) return false; rehash(newCapacity); return true; } } // We use big-endian as DataOutputStream does static class ByteHead /*is DataOutput*/ implements AutoCloseable { final public ByteHead setReadMode(boolean readMode){ return readMode(readMode); } public ByteHead readMode(boolean readMode) { this.readMode = readMode; return this; } final public boolean getReadMode(){ return readMode(); } public boolean readMode() { return readMode; } boolean readMode = false; final public ByteHead setWriteMode(boolean writeMode){ return writeMode(writeMode); } public ByteHead writeMode(boolean writeMode) { this.writeMode = writeMode; return this; } final public boolean getWriteMode(){ return writeMode(); } public boolean writeMode() { return writeMode; } boolean writeMode = false; final public InputStream getInputStream(){ return inputStream(); } public InputStream inputStream() { return inputStream; } InputStream inputStream; final public OutputStream getOutputStream(){ return outputStream(); } public OutputStream outputStream() { return outputStream; } OutputStream outputStream; final public ByteHead setByteCounter(long byteCounter){ return byteCounter(byteCounter); } public ByteHead byteCounter(long byteCounter) { this.byteCounter = byteCounter; return this; } final public long getByteCounter(){ return byteCounter(); } public long byteCounter() { return byteCounter; } long byteCounter; final public boolean getEof(){ return eof(); } public boolean eof() { return eof; } boolean eof = false; ByteHead() {} ByteHead(InputStream inputStream) { inputStream(inputStream); } ByteHead(OutputStream outputStream) { outputStream(outputStream); } ByteHead inputStream(InputStream inputStream) { this.inputStream = inputStream; readMode(true); return this; } ByteHead outputStream(OutputStream outputStream) { this.outputStream = outputStream; writeMode(true); return this; } void write(byte[] data) { try { ensureWriteMode(); { if (outputStream != null) outputStream.write(data); } byteCounter += data.length; } catch (Exception __e) { throw rethrow(__e); } } void writeFloat(float f) { writeInt(Float.floatToIntBits(f)); } void writeLong(long l) { writeInt((int) (l >> 32)); writeInt((int) l); } void writeInt(int i) { write(i >> 24); write(i >> 16); write(i >> 8); write(i); } void writeShort(int i) { write(i >> 8); write(i); } final void write(int i){ writeByte(i); } void writeByte(int i) { try { ensureWriteMode(); { if (outputStream != null) outputStream.write(i); } byteCounter++; } catch (Exception __e) { throw rethrow(__e); } } void writeASCII(char c) { write(toASCII(c)); } void writeASCII(String s) { write(toASCII(s)); } // write/verify constant ASCII text void exchangeASCII(String s) { exchangeConstantBytes(toASCII(s)); } void exchangeConstantBytes(byte[] data) { for (int i = 0; i < l(data); i++) exchangeByte(data[i]); } long readLong() { long i = ((long) readInt()) << 32; return i | (readInt() & 0xFFFFFFFFL); } float readFloat() { return Float.intBitsToFloat(readInt()); } int readInt() { int i = read() << 24; i |= read() << 16; i |= read() << 8; return i | read(); } short readShort() { int i = read() << 8; return (short) (i | read()); } byte[] readBytes(int n) { byte[] data = new byte[n]; for (int i = 0; i < n; i++) { int b = read(); if (b < 0) throw fail("EOF"); data[i] = (byte) b; } return data; } String readString() { int n = readInt(); if (eof()) return null; return fromUtf8(readBytes(n)); } // null is written as empty string // writes UTF8 length (4 bytes) plus string as UTF8 void writeString(String s) { byte[] utf8 = toUtf8(unnull(s)); writeInt(l(utf8)); write(utf8); } // -1 for EOF final int read(){ return readByte(); } int readByte() { try { ensureReadMode(); ++byteCounter; int b = inputStream.read(); if (b < 0) eof = true; return b; } catch (Exception __e) { throw rethrow(__e); } } void ensureReadMode() { if (!readMode) throw fail("Not in read mode"); } void ensureWriteMode() { if (!writeMode) throw fail("Not in write mode"); } // exchange = read or write depending on mode void exchangeByte(byte getter, IVF1 setter) { exchangeByte(() -> getter, setter); } void exchangeByte(IF0 getter, IVF1 setter) { if (writeMode()) writeByte(getter.get()); if (readMode()) setter.get(toUByte(readByte())); } void exchangeShort(IF0 getter, IVF1 setter) { if (writeMode()) writeShort(getter.get()); if (readMode()) setter.get(readShort()); } void exchangeLong(IVar var) { exchangeLong(var.getter(), var.setter()); } void exchangeLong(IF0 getter, IVF1 setter) { if (writeMode()) writeLong(getter.get()); if (readMode()) setter.get(readLong()); } void exchangeByte(byte i) { exchangeByte(() -> i, j -> assertEquals(i, j)); } void exchangeInt(int i) { exchangeInt(() -> i, j -> assertEquals(i, j)); } void exchangeInt(IF0 getter, IVF1 setter) { if (writeMode()) writeInt(getter.get()); if (readMode()) setter.get(readInt()); } void exchange(ByteIO writable) { if (writable != null) writable.readWrite(this); } void exchangeAll(Iterable writables) { if (writables != null) for (var writable : writables) exchange(writable); } // write size in bytes of element first (as int), // then the element itself. // upon reading, size is actually ignored. void exchangeWithSize(ByteIO writable) { if (writeMode()) { byte[] data = writable.saveToByteArray(); writeInt(l(data)); write(data); } if (readMode()) { int n = readInt(); writable.readWrite(this); } } void finish() {} public void close() { main.close(inputStream); main.close(outputStream); finish(); } } 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; } } /* Typical way to implement (from WidthAndHeightImpl): ifclass ByteHead public void readWrite(ByteHead head) { head.exchangeInt(l0 width, w -> width = w); head.exchangeInt(l0 height, h -> height = h); } endif */ interface ByteIO { void readWrite(ByteHead head); default byte[] saveAsByteArray(){ return saveToByteArray(); } default byte[] toByteArray(){ return saveToByteArray(); } default byte[] saveToByteArray() { return saveToByteArray(new ByteHead()); } default byte[] saveAsByteArray(ByteHead head){ return saveToByteArray(head); } default byte[] toByteArray(ByteHead head){ return saveToByteArray(head); } default byte[] saveToByteArray(ByteHead head) { var baos = byteArrayOutputStream(); head.outputStream(baos); readWrite(head); head.finish(); return baos.toByteArray(); } default String toHexString() { return main.toHexString(toByteArray()); } default File saveToFile(File file) { OutputStream out = bufferedFileOutputStream(file); try { var head = new ByteHead(out); readWrite(head); head.finish(); return file; } finally { _close(out); }} default ByteIO fromByteArray(byte[] data){ return load(data); } default ByteIO load(byte[] data) { readWrite(new ByteHead(new ByteArrayInputStream(data))); return this; } default ByteIO load(File file) { InputStream in = bufferedInputStream(file); try { readWrite(new ByteHead(in)); return this; } finally { _close(in); }} default long byteIOLength() { JustCountingOutputStream out = new JustCountingOutputStream(); var head = new ByteHead(out); readWrite(head); head.finish(); return out.get(); } } interface IPixelSet extends HasBounds { public boolean contains(int x, int y); default boolean contains(Pt p) { return contains(p.x, p.y); } default public IterableIterator pixelIterator() { return filterI(main.pixelIterator(bounds()), p -> contains(p)); } } static interface IFieldsToList { Object[] _fieldsToList(); } interface ISleeper_v2 { default Sleeping doLater(long targetTime, Runnable r) { return doLater(sysTimeToTimestamp(targetTime), r); } Sleeping doLater(Timestamp targetTime, Runnable r); public default Sleeping doAfter(double seconds, Runnable r) { return doLater(tsNow().plusSeconds(seconds), r); } } static class MetaWithChangeListeners extends Meta implements IHasChangeListeners, ChangeTriggerable { transient Set 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() { if (onChange != null) for (var listener : onChange) pcallF_typed(listener); } } static class StringHead implements Closeable { final public StringHead setReadMode(boolean readMode){ return readMode(readMode); } public StringHead readMode(boolean readMode) { this.readMode = readMode; return this; } final public boolean getReadMode(){ return readMode(); } public boolean readMode() { return readMode; } boolean readMode = false; final public StringHead setWriteMode(boolean writeMode){ return writeMode(writeMode); } public StringHead writeMode(boolean writeMode) { this.writeMode = writeMode; return this; } final public boolean getWriteMode(){ return writeMode(); } public boolean writeMode() { return writeMode; } boolean writeMode = false; final public BufferedReader getReader(){ return reader(); } public BufferedReader reader() { return reader; } BufferedReader reader; final public Writer getWriter(){ return writer(); } public Writer writer() { return writer; } Writer writer; StringHead() {} StringHead(Reader reader) { reader(reader); } StringHead(Writer writer) { writer(writer); } StringHead reader(Reader reader) { this.reader = bufferedReader(reader); readMode(true); return this; } StringHead writer(Writer writer) { this.writer = writer; writeMode(true); return this; } void finish() {} String readLine() { String line = readLineFromReader(reader); if (line == null) close(); return line; } void writeLine(String line) { try { writer.write(line); writer.write('\n'); } catch (Exception __e) { throw rethrow(__e); } } void exchangeLine(IF0 getter, IVF1 setter) { if (writeMode()) writeLine(getter.get()); if (readMode()) setter.get(readLine()); } public void close() { try { { cleanUp(reader); reader = null; } { cleanUp(writer); writer = null; } } catch (Exception __e) { throw rethrow(__e); } } void exchange(StringIO writable) { if (writable != null) writable.readWrite(this); } void exchangeAll(Iterable writables) { if (writables != null) for (var writable : writables) exchange(writable); } } static class VarWithNotify extends Var implements IVarWithNotify { transient Set onChange; public VarWithNotify onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public VarWithNotify removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void fireChange() { if (onChange != null) for (var listener : onChange) pcallF_typed(listener); } 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(); } } interface IDoublePt { public double x_double(); public double y_double(); } 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; } // idx = index in pixel array public int getIntPixel_noRangeCheck(int idx) { return pixels[idx]; } 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; } } static class ShortBuffer implements Iterable { short[] data; int size; ShortBuffer() {} ShortBuffer(int size) { if (size != 0) data = new short[size]; } ShortBuffer(Iterable l) { if (l instanceof Collection) allocate(((Collection) l).size()); addAll(l); } void add(short i) { if (size >= lShortArray(data)) { data = resizeShortArray(data, Math.max(1, toShort(Math.min(maximumSafeArraySize(), lShortArray(data)*2L)))); if (size >= data.length) throw fail("ShortBuffer too large: " + size); } data[size++] = i; } void allocate(int n) { data = resizeShortArray(data, max(n, size())); } void setSize(int n) { data = resizeShortArray(data, n); size = min(l(data), size); } void addAll(Iterable l) { if (l != null) for (var i : l) add(i); } void addAll(short... l) { if (l != null) for (short i : l) add(i); } // Note: may return the internal array final short[] toShortArray(){ return toArray(); } short[] toArray() { return size == 0 ? null : resizeShortArray(data, size); } // abandoned version /*L toList() { ret intArrayToList(data, 0, size); }*/ // now all these return a virtual list final List asList(){ return toList(); } final List asVirtualList(){ return toList(); } List toList() { return new RandomAccessAbstractList() { public int size() { return size; } public Short get(int i) { return ShortBuffer.this.get(i); } public Short set(int i, Short val) { Short a = get(i); data[i] = val; return a; } }; } void reset() { size = 0; } void clear() { reset(); } int size() { return size; } boolean isEmpty() { return size == 0; } short get(int idx) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); return data[idx]; } void set(int idx, short value) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); data[idx] = value; } short popLast() { if (size == 0) throw fail("empty buffer"); return data[--size]; } short last() { return data[size-1]; } short nextToLast() { return data[size-2]; } public String toString() { return squareBracket(joinWithSpace(toList())); } public Iterator iterator() { return new IterableIterator() { int i = 0; public boolean hasNext() { return i < size; } public Short next() { //if (!hasNext()) fail("Index out of bounds: " + i); return data[i++]; } }; } public ShortIterator shortIterator() { return new ShortIterator() { int i = 0; public boolean hasNext() { return i < size; } public short next() { //if (!hasNext()) fail("Index out of bounds: " + i); return data[i++]; } public String toString() { return "Iterator@" + i + " over " + ShortBuffer.this; } }; } void trimToSize() { data = resizeShortArray(data, size); } } interface HasBounds extends WidthAndHeight { Rect bounds(); default int getWidth() { return bounds().h; } default int getHeight() { return bounds().w; } } interface IVarWithNotify extends IVar, IF0WithChangeListeners { default IVarWithNotify onChange(IVF1 r) { if (r == null) return this; onChange(() -> r.get(get())); return this; } default IVarWithNotify onChangeAndNow(IVF1 r) { if (r == null) return this; onChangeAndNow(() -> r.get(get())); return this; } } interface IImageRegions { public List> regions(); // for each pixel: region index (starting at 1) int[] regionMatrix(); Img image(); int nRegions(); IImageRegion getRegion(int i); } static class NotifyingCollection extends AbstractCollection implements Collection { Collection c; // Backing Collection NotifyingCollection() {} // for persistence NotifyingCollection(Collection 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[] toArray(T[] a) { synchronized (this) {return c.toArray(a);} } public Iterator 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 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 consumer) { synchronized (this) {c.forEach(consumer);} }*/ /*public boolean removeIf(Predicate filter) { synchronized (this) {return c.removeIf(filter);} }*/ @Override public Spliterator spliterator() { return c.spliterator(); // Must be manually synched by user! } /*public Stream stream() { return c.stream(); // Must be manually synched by user! }*/ /*public Stream parallelStream() { return c.parallelStream(); // Must be manually synched by user! }*/ void beforeChange() {} void change() {} Collection unwrap() { return c; } } static abstract class VF2 { abstract void get(A a, B b); } static class Timestamp implements Comparable , IFieldsToList{ long date; Timestamp(long date) { this.date = date;} public boolean equals(Object o) { if (!(o instanceof Timestamp)) return false; Timestamp __1 = (Timestamp) o; return date == __1.date; } public int hashCode() { int h = 2059094262; h = boostHashCombine(h, _hashCode(date)); return h; } public Object[] _fieldsToList() { return new Object[] {date}; } Timestamp() { date = now(); } Timestamp(Date date) { if (date != null) this.date = date.getTime(); } final long toLong(){ return unixDate(); } long unixDate() { return date; } long unixSeconds() { return unixDate()/1000; } 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); } Duration minusAsDuration(Timestamp ts) { return Duration.ofMillis(minus(ts)); } } 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(); } final boolean scaffolded(){ return scaffoldingEnabled(); } boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); } boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); } // Implementing setMetaToString String toString_base() { return super.toString(); } public String toString() { Object o = metaGet("toString", this); if (o instanceof String) return ((String) o); if (o instanceof IF1) return str(((IF1) o).get(this)); return toString_base(); } 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 InterpolatedDoubleArray implements IntSize { InterpolatedDoubleArray() {} int[] indices; double[] values; InterpolatedDoubleArray(int[] indices, double[] values) { this.values = values; this.indices = indices;} final public int length(){ return size(); } public int size() { return empty(indices) ? 0 : last(indices)+1; } int nPillars() { return l(indices); } final double[] get(){ return toDoubleArray(); } double[] toDoubleArray() { int n = length(); double[] array = new double[n]; for (int i = 0; i < indices.length; i++) { int iEnd = indices[i]; double value = values[i]; array[iEnd] = value; if (i > 0) { int iStart = indices[i-1]; if (iStart+1 < iEnd) { double startValue = values[i-1]; double step = (value-startValue)/(iEnd-iStart), val = startValue; for (int j = iStart+1; j < iEnd; j++) { val += step; array[j] = val; } } } } return array; } int[] rounded() { return iroundDoubleArray(get()); } double[] indicesAndValues() { int n = nPillars(); double[] array = new double[n*2]; for (int i = 0; i < n; i++) { array[i*2] = indices[i]; array[i*2+1] = values[i]; } return array; } double[] indicesAndValues_withoutFirstAndLastIndex() { int n = nPillars(); double[] array = new double[max(1, n*2-2)]; int j = 0; for (int i = 0; i < n; i++) { if (i != 0 && i != n-1) array[j++] = indices[i]; array[j++] = values[i]; } return array; } int nInts_withoutFirstAndLastIndex() { return max(1, nPillars()*2-2); } int nInts() { return nPillars()*2; } InterpolatedDoubleArray topPart(int nPillars) { return new InterpolatedDoubleArray( takeFirst(nPillars, indices), takeFirst(nPillars, values)); } } // AppendableChain has one "smart" head element (with size counter // and pointer to the chain's last element), all the other nodes are // maximally simple (MinimalChain). // This allows O(1) front insertion, front removal and back insertion // (not removal at the back though) which is fine for what I need this // for (event queues). // // Stefan Reich, Oct 21 static class AppendableChain extends MinimalChain implements Iterable, IntSize { MinimalChain last; // pointer to last element in chain (which may be us) final public int getSize(){ return size(); } public int size() { return size; } int size; // total length of chain AppendableChain() {} // only used internally AppendableChain(A element) { this.element = element; size = 1; last = this; } // intermediate constructor called by itemPlusChain() AppendableChain(A element, AppendableChain next) { this.next = next; this.element = element; if (next == null) return; MinimalChain b = new MinimalChain(); b.element = next.element; b.next = next.next; this.next = b; last = next.last; size = next.size+1; } public String toString() { return str(toList()); } // append at the end boolean add(A a) { MinimalChain newLast = new MinimalChain(a); last.next = newLast; last = newLast; ++size; return true; } // drop first element AppendableChain popFirst() { if (next == null) return null; element = next.element; if (last == next) last = this; next = next.next; --size; return this; } ArrayList toList() { ArrayList l = emptyList(size); MinimalChain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } //public Iterator iterator() { ret toList().iterator(); } class ACIt extends IterableIterator < A > { MinimalChain c = AppendableChain.this; public boolean hasNext() { return c != null; } public A next() { var a = c.element; c = c.next; return a; } } public IterableIterator iterator() { return new ACIt(); } } static class MultiSleeper extends RestartableCountdown implements ISleeper_v2 { MultiSetMap entries = treeMultiSetMap(); void check() { var time = nextWakeUpTime(); var action = firstValue(entries); setTargetTime(time == null ? 0 : time.sysTime(), new Runnable() { public void run() { try { Set toCall; synchronized(MultiSleeper.this) { toCall = entries.get(time); entries.remove(time); check(); } pcallFAll(toCall); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Set toCall;\r\n synchronized(MultiSleeper.this) {\r\n toCal..."; }}); } synchronized void removeEntry(Timestamp targetTime, Runnable action) { entries.remove(targetTime, action); } // API synchronized Timestamp nextWakeUpTime() { return firstKey(entries); } public synchronized Sleeping doLater(Timestamp targetTime, Runnable r) { if (r == null || targetTime == null) return null; targetTime = max(targetTime, tsNow()); entries.put(targetTime, r); check(); return new Sleeping(targetTime, r) { public void close() { try { removeEntry(targetTime, r); } catch (Exception __e) { throw rethrow(__e); } } }; } } 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; } } interface RunnableWithExceptions { public void run() throws Exception; } static interface IRGBImage extends MakesBufferedImage { int getIntPixel(int x, int y); } static class JustCountingOutputStream extends OutputStream { long counter; @Override public void write(int b) throws IOException { ++counter; } @Override public void write(byte[] b) throws IOException { counter += b.length; } @Override public void write(byte[] b, int off, int len) throws IOException { counter += len; } final long get(){ return getFilePointer(); } long getFilePointer() { return counter; } } static class Average { final public double getSum(){ return sum(); } public double sum() { return sum; } double sum; final public double getN(){ return n(); } public double n() { return n; } double n; final void addSample(double d){ add(d); } void add(double d) { ++n; sum += d; } void add(double d, double weight) { n += weight; sum += d*weight; } void remove(double d) { add(d, -1); } final double avg(){ return get(); } final double average(){ return get(); } final double getAverage(){ return get(); } double get() { return doubleRatio(sum, n); } boolean isEmpty() { return n == 0; } public String toString() { return get() + " (n=" + n + ")"; } void clear() { n = 0; sum = 0; } final double count(){ return size(); } double size() { return n; } } interface IBest { boolean put(A a, double score); } abstract static class RandomAccessAbstractList extends AbstractList implements RandomAccess { } 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); } boolean waitUntilTrue (double timeoutSeconds) { return waitForValue(timeoutSeconds, true); } boolean waitUntilFalse(double timeoutSeconds) { return waitForValue(timeoutSeconds, false); } synchronized boolean waitForValue(boolean wantedValue) { try { while (a != wantedValue) wait(); return a; } catch (Exception __e) { throw rethrow(__e); } } synchronized boolean waitForValue(double timeoutSeconds, boolean wantedValue) { try { if (timeoutSeconds == infinity()) return waitForValue(wantedValue); long destTime = sysNowPlusSeconds_noPing(timeoutSeconds), remainingTime; while (a != wantedValue && (remainingTime = destTime-sysNow_noPing()) > 0) wait(remainingTime); return a; } catch (Exception __e) { throw rethrow(__e); } } public synchronized boolean getAndSet(boolean b) { var value = a; set(b); return value; } public synchronized boolean waitUntilTrueAndClear() { if (!waitUntilTrue()) return false; set(false); return true; } public synchronized boolean waitUntilTrueAndClear(double timeoutSeconds) { if (!waitUntilTrue(timeoutSeconds)) return false; set(false); return true; } Object mutex() { return this; } } interface ChangeTriggerable { public void change(); } static class CompressToInterpolatedDoubleArray { // raw data to compress (integers) int[] data; // output InterpolatedDoubleArray result; // options final public CompressToInterpolatedDoubleArray setSlopeTolerance(double slopeTolerance){ return slopeTolerance(slopeTolerance); } public CompressToInterpolatedDoubleArray slopeTolerance(double slopeTolerance) { this.slopeTolerance = slopeTolerance; return this; } final public double getSlopeTolerance(){ return slopeTolerance(); } public double slopeTolerance() { return slopeTolerance; } double slopeTolerance = 0.01; // internal IntBuffer indices = new IntBuffer(); DoubleBuffer values = new DoubleBuffer(); CompressToInterpolatedDoubleArray(int[] data) { this.data = data;} InterpolatedDoubleArray get() { int iData = 0; while (iData < data.length) { int startValue = data[iData]; // possible range of actual floating point starting value DoubleRange startValueRange = doubleRange(startValue-0.5, startValue+0.5); // see how far the straight line goes from iData DoubleRange totalSlopeRange = null; int jData; // our straight line ends at jData-1 for (jData = iData+1; jData < data.length; jData++) { double x = data[jData], h = jData-iData; // possible range of actual floating point value DoubleRange xRange = doubleRange(x-0.5, x+0.5); // most generous range for possible slope depending // on possible floating point values at start and end DoubleRange slopeRange = doubleRange( (xRange.start-startValueRange.end)/h, (xRange.end-startValueRange.start)/h); // grow range a little to avoid size 0 ranges slopeRange = growRange(slopeTolerance, slopeRange); //printVars(+iData, +jData, +slopeRange, +totalSlopeRange); if (totalSlopeRange == null) totalSlopeRange = slopeRange; else { slopeRange = intersectRanges(slopeRange, totalSlopeRange); if (empty(slopeRange)) break; else totalSlopeRange = slopeRange; } } indices.add(iData); values.add(data[iData]); iData = max(iData+1, jData-1); } return result = new InterpolatedDoubleArray(indices.toArrayNonNull(), values.toArrayNonNull()); } } abstract static class Sleeping implements AutoCloseable , IFieldsToList{ Timestamp targetTime; Runnable action; Sleeping() {} Sleeping(Timestamp targetTime, Runnable action) { this.action = action; this.targetTime = targetTime;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + targetTime + ", " + action + ")"; }public Object[] _fieldsToList() { return new Object[] {targetTime, action}; } long remainingMS() { return targetTime.minus(tsNow()); } } abstract static class ShortIterator { abstract boolean hasNext(); abstract short next(); } static class IntBuffer implements Iterable { int[] data; int size; IntBuffer() {} IntBuffer(int size) { if (size != 0) data = new int[size]; } IntBuffer(Iterable l) { if (l instanceof Collection) allocate(((Collection) l).size()); addAll(l); } void add(int i) { if (size >= lIntArray(data)) { data = resizeIntArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lIntArray(data)*2L)))); if (size >= data.length) throw fail("IntBuffer too large: " + size); } data[size++] = i; } void allocate(int n) { data = resizeIntArray(data, max(n, size())); } void setSize(int n) { data = resizeIntArray(data, n); size = min(l(data), size); } void addAll(Iterable l) { if (l != null) for (int i : l) add(i); } void addAll(int... l) { if (l != null) for (int i : l) add(i); } // Note: may return the internal array final int[] toIntArray(){ return toArray(); } int[] toArray() { return size == 0 ? null : resizeIntArray(data, size); } int[] subArray(int from, int to) { return subIntArray(data, from, min(to, size)); } int[] toArrayNonNull() { return unnull(toArray()); } // abandoned version /*L toList() { ret intArrayToList(data, 0, size); }*/ // now all these return a virtual list final List asList(){ return toList(); } final List asVirtualList(){ return toList(); } List toList() { return new RandomAccessAbstractList() { public int size() { return size; } public Integer get(int i) { return IntBuffer.this.get(i); } public Integer set(int i, Integer val) { Integer a = get(i); data[i] = val; return a; } }; } void reset() { size = 0; } void clear() { reset(); } int size() { return size; } boolean isEmpty() { return size == 0; } int get(int idx) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); return data[idx]; } void set(int idx, int value) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); data[idx] = value; } int popLast() { if (size == 0) throw fail("empty buffer"); return data[--size]; } int last() { return data[size-1]; } int nextToLast() { return data[size-2]; } public String toString() { return squareBracket(joinWithSpace(toList())); } public Iterator iterator() { return new IterableIterator() { int i = 0; public boolean hasNext() { return i < size; } public Integer next() { //if (!hasNext()) fail("Index out of bounds: " + i); return data[i++]; } }; } public IntegerIterator integerIterator() { return new IntegerIterator() { int i = 0; public boolean hasNext() { return i < size; } public int next() { //if (!hasNext()) fail("Index out of bounds: " + i); return data[i++]; } public String toString() { return "Iterator@" + i + " over " + IntBuffer.this; } }; } void trimToSize() { data = resizeIntArray(data, size); } } static interface IVF3 { void get(A a, B b, C c); } static class Scored extends Var { float score; Scored() {} Scored(A a, float score) { super(a); this.score = score; } Scored(A a, double score) { super(a); this.score = (float) score; } float score() { return score; } public String toString() { return toIntPercent(score) + "%: " + str(get()); } } static class OnePath { ByteBuffer steps = new ByteBuffer(); OnePath() {} OnePath(String path) { int n = l(path); steps.allocate(n); for (int i = 0; i < n; i++) steps.add(parseDigit(path, i)); } OnePath(OnePath path) { steps = new ByteBuffer(path.steps); } // uses first point as origin, so there will be l(points)-1 steps. // Unless you set close to true, then it adds the first point at the end again. OnePath(Iterable points, boolean close) { fromPoints(points, close); } void fromPoints(Iterable points, boolean close) { var it = iterator(points); if (empty(it)) return; Pt firstPoint = it.next(), p = firstPoint; while (it.hasNext()) { Pt p2 = it.next(); steps.add(ptToDigit(ptMinus(p2, p))); p = p2; } if (close) steps.add(ptToDigit(ptMinus(firstPoint, p))); } final int length(){ return size(); } final int nSteps(){ return size(); } int size() { return steps.size(); } public String toString() { return pathString(); } String pathString() { return singleDigitBytesToString(steps); } // includes first point, so returns length()+1 points in total IterableIterator pointIterator() { return pointIterator(origin()); } IterableIterator pointIterator(Pt startPt) { return new IterableIterator() { int i = 0, n = length(); Pt p = startPt; public boolean hasNext() { return i <= n; } public Pt next() { var p = this.p; if (i < n) this.p = translatePt(this.p, getStepAsPt(i)); ++i; return p; } }; } final List pointsList(){ return pointList(); } List pointList() { return ptBuffer(pointIterator()); } int getStep(int i) { return steps.get(i); } Pt getStepAsPt(int i) { return onePathDirections()[steps.get(i)]; } static int ptToDigit(Pt p) { return p.y < 0 ? p.x < 0 ? 1 : p.x == 0 ? 2 : 3 : p.y == 0 ? p.x < 0 ? 8 : p.x == 0 ? 0 : 4 : p.x < 0 ? 7 : p.x == 0 ? 6 : 5; } Pt origin() { return main.origin(); } Pt endPoint() { return last(pointIterator()); } Pt drift() { return ptMinus(endPoint(), origin()); } void addStep(int p_x, int p_y) { addStep(pt(p_x, p_y)); } void addStep(Pt p) { int step = onePathLookupDirection(p); if (step < 0) throw fail("Invalid one path step: " + p); addStep(step); } void addStep(int step) { assertBetween(0, 8, step); steps.add(step); } void insertStep(int idx, int p_x, int p_y) { insertStep(idx, pt(p_x, p_y)); } void insertStep(int idx, Pt p) { int step = onePathLookupDirection(p); if (step < 0) throw fail("Invalid one path step: " + p); insertStep(idx, step); } void insertStep(int idx, int step) { assertBetween(0, 8, step); steps.add(idx, step); } Rect bounds() { return boundsOfPts(pointIterator()); } } static class DoubleBuffer implements Iterable, IntSize { double[] data; int size; DoubleBuffer() {} DoubleBuffer(int size) { if (size != 0) data = new double[size]; } DoubleBuffer(Iterable l) { addAll(l); } DoubleBuffer(Collection l) { this(l(l)); addAll(l); } DoubleBuffer(double... data) { this.data = data; size = l(data); } void add(double i) { if (size >= lDoubleArray(data)) { data = resizeDoubleArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lDoubleArray(data)*2L)))); if (size >= data.length) throw fail("DoubleBuffer too large: " + size); } data[size++] = i; } void addAll(Iterable l) { if (l != null) for (double i : l) add(i); } double[] toArray() { return size == 0 ? null : resizeDoubleArray(data, size); } double[] toArrayNonNull() { return unnull(toArray()); } List toList() { return doubleArrayToList(data, 0, size); } List asVirtualList() { return new RandomAccessAbstractList() { public int size() { return size; } public Double get(int i) { return DoubleBuffer.this.get(i); } public Double set(int i, Double val) { Double a = get(i); data[i] = val; return a; } }; } void reset() { size = 0; } void clear() { reset(); } public int size() { return size; } double get(int idx) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); return data[idx]; } void set(int idx, double value) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); data[idx] = value; } double popLast() { if (size == 0) throw fail("empty buffer"); return data[--size]; } double last() { return data[size-1]; } double nextToLast() { return data[size-2]; } public String toString() { return squareBracket(joinWithSpace(toList())); } public Iterator iterator() { return new IterableIterator() { int i = 0; public boolean hasNext() { return i < size; } public Double next() { //if (!hasNext()) fail("Index out of bounds: " + i); return data[i++]; } }; } /*public DoubleIterator doubleIterator() { ret new DoubleIterator { int i = 0; public bool hasNext() { ret i < size; } public int next() { //if (!hasNext()) fail("Index out of bounds: " + i); ret data[i++]; } toString { ret "Iterator@" + i + " over " + DoubleBuffer.this; } }; }*/ void trimToSize() { data = resizeDoubleArray(data, size); } int indexOf(double b) { for (int i = 0; i < size; i++) if (data[i] == b) return i; return -1; } double[] subArray(int start, int end) { return subDoubleArray(data, start, min(end, size)); } } abstract static class AbstractMatrix implements Matrix { AbstractMatrix() {} int w, h; AbstractMatrix(int w, int h) { this.h = h; this.w = w;} public int getWidth() { return w; } public int getHeight() { return h; } public String toString() { return flexLines( roundBracket(w+"x"+h), countIteratorToList(y -> str(getLine(y)), getHeight())); } public void set(int x, int y, A a) { throw unimplemented(); } } final static class DoubleRange implements Comparable { final public double getStart(){ return start(); } public double start() { return start; } double start; final public double getEnd(){ return end(); } public double end() { return end; } double end; DoubleRange() {} DoubleRange(double start, double end) { this.end = end; this.start = start;} public boolean equals(Object o) { return stdEq2(this, o); } public int hashCode() { return stdHash2(this); } double length() { return end-start; } boolean isEmpty() { return start >= end; } double center() { return (start+end)/2; } static String _fieldOrder = "start end"; public String toString() { return "[" + start + ";" + end + "]"; } @Override public int compareTo(DoubleRange r) { int c = cmp(start, r.start); if (c != 0) return c; return cmp(end, r.end); } boolean contains(double d) { return d >= start && d <= end; } } interface Matrix extends WidthAndHeight { public A get(int x, int y); public void set(int x, int y, A a); default Pt size() { return pt(getWidth(), getHeight()); } default int nCells() { return getWidth()*getHeight(); } default A get(Pt p) { return get(p.x, p.y); } default void put(Pt p, A a){ set(p, a); } default void set(Pt p, A a) { set(p.x, p.y, a); } // one row of the matrix as a virtual list (mutable) default List getLine(int y) { return new RandomAccessAbstractList() { public int size() { return getWidth(); } public A get(int x) { return Matrix.this.get(x, y); } public A set(int x, A val) { A old = Matrix.this.get(x, y); Matrix.this.set(x, y, val); return old; } }; } } // uses hash sets as inner sets unless subclassed // uses a hash map as the outer map by default static class MultiSetMapimplements IMultiMap { Map> data = new HashMap>(); int size; // number of values MultiSetMap() {} MultiSetMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); } MultiSetMap(MultiSetMap map) { putAll(map); } MultiSetMap(Map> data) { this.data = data;} boolean put(A key, B value) { synchronized(data) { Set set = data.get(key); if (set == null) data.put(key, set = _makeEmptySet()); if (!set.add(value)) return false; { ++size; return true; } }} boolean add(A key, B value) { return put(key, value); } void addAll(A key, Collection values) { synchronized(data) { putAll(key, values); }} void addAllIfNotThere(A key, Collection values) { synchronized(data) { for (B value : values) setPut(key, value); }} void setPut(A key, B value) { synchronized(data) { if (!containsPair(key, value)) put(key, value); }} final boolean contains(A key, B value){ return containsPair(key, value); } boolean containsPair(A key, B value) { synchronized(data) { return get(key).contains(value); }} void putAll(A key, Collection values) { synchronized(data) { for (B value : values) put(key, value); }} void removeAll(A key, Collection values) { synchronized(data) { for (B value : values) remove(key, value); }} public Set get(A key) { synchronized(data) { Set set = data.get(key); return set == null ? Collections. emptySet() : set; }} List getAndClear(A key) { synchronized(data) { List l = cloneList(data.get(key)); remove(key); return l; }} // return null if empty Set getOpt(A key) { synchronized(data) { return data.get(key); }} // returns actual mutable live set // creates the set if not there Set getActual(A key) { synchronized(data) { Set set = data.get(key); if (set == null) data.put(key, set = _makeEmptySet()); return set; }} // TODO: this looks unnecessary void clean(A key) { synchronized(data) { Set list = data.get(key); if (list != null && list.isEmpty()) data.remove(key); }} final public Set keys(){ return keySet(); } public Set keySet() { synchronized(data) { return data.keySet(); }} void remove(A key) { synchronized(data) { size -= l(data.get(key)); data.remove(key); }} void remove(A key, B value) { synchronized(data) { Set set = data.get(key); if (set != null) { if (set.remove(value)) { --size; if (set.isEmpty()) data.remove(key); } } }} void clear() { synchronized(data) { data.clear(); size = 0; }} boolean containsKey(A key) { synchronized(data) { return data.containsKey(key); }} B getFirst(A key) { synchronized(data) { return first(get(key)); }} void addAll(MultiSetMap map) { putAll(map); } void putAll(MultiSetMap map) { synchronized(data) { for (A key : map.keySet()) putAll(key, map.get(key)); }} void putAll(Map map) { synchronized(data) { if (map != null) for (Map.Entry e : map.entrySet()) put(e.getKey(), e.getValue()); }} final public int keyCount(){ return keysSize(); } public int keysSize() { synchronized(data) { return l(data); }} // full size public int size() { synchronized(data) { return size; }} // count values for key int getSize(A key) { return l(data.get(key)); } int count(A key) { return getSize(key); } // expensive operation Set reverseGet(B b) { synchronized(data) { Set l = new HashSet(); for (A key : data.keySet()) if (data.get(key).contains(b)) l.add(key); return l; }} // expensive operation A keyForValue(B b) { synchronized(data) { for (A key : data.keySet()) if (data.get(key).contains(b)) return key; return null; }} Map> asMap() { synchronized(data) { return cloneMap(data); }} boolean isEmpty() { synchronized(data) { return data.isEmpty(); }} // override in subclasses Set _makeEmptySet() { return new HashSet(); } Collection> allLists() { synchronized(data) { return new HashSet(data.values()); } } List allValues() { return concatLists(values(data)); } List> allEntries() { synchronized(data) { List> l = emptyList(size); for (Map.Entry> __0 : _entrySet( data)) { A a = __0.getKey(); Set set = __0.getValue(); for (B b : set) l.add(pair(a, b)); } return l; }} Object mutex() { return data; } public String toString() { return "mm" + str(data); } Pair firstEntry() { synchronized(data) { if (empty(data)) return null; Map.Entry> entry = data.entrySet().iterator().next(); return pair(entry.getKey(), first(entry.getValue())); }} A firstKey() { synchronized(data) { return main.firstKey(data); }} A lastKey() { synchronized(data) { return (A) ((NavigableMap) data).lastKey(); }} A higherKey(Object a) { synchronized(data) { return (A) ((NavigableMap) data).higherKey(a); }} } static class MinimalChain implements Iterable { A element; MinimalChain next; MinimalChain() {} MinimalChain(A element) { this.element = element;} MinimalChain(A element, MinimalChain next) { this.next = next; this.element = element;} public String toString() { return str(toList()); } ArrayList toList() { ArrayList l = new ArrayList(); MinimalChain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } void setElement(A a) { element = a; } void setNext(MinimalChain next) { this.next = next; } // TODO: optimize public Iterator iterator() { return toList().iterator(); } A get() { return element; } } interface IF0WithChangeListeners extends IF0, IHasChangeListeners {} 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 Seconds implements Comparable , 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()); } } abstract static class IntegerIterator { abstract boolean hasNext(); abstract int next(); } static class ByteBuffer implements Iterable { byte[] data; int size; ByteBuffer() {} ByteBuffer(int size) { if (size != 0) data = new byte[size]; } ByteBuffer(Iterable l) { if (l instanceof Collection) allocate(((Collection) l).size()); addAll(l); } ByteBuffer(byte[] data) { this.data = data; size = l(data); } // TODO *(ByteBuffer buf) { ... } void add(int idx, int i) { add(0); arraycopy(data, idx, data, idx+1, size-(idx+1)); data[idx] = (byte) i; } void add(int i) { add((byte) i); } void add(byte i) { if (size >= lByteArray(data)) { allocate(Math.max(1, toInt(Math.min(maximumSafeArraySize(), lByteArray(data)*2L)))); if (size >= data.length) throw fail("ByteBuffer too large: " + size); } data[size++] = i; } void allocate(int n) { data = resizeByteArray(data, max(n, size())); } void addAll(Iterable l) { if (l != null) for (byte i : l) add(i); } final byte[] toByteArray(){ return toArray(); } byte[] toArray() { return size == 0 ? null : resizeByteArray(data, size); } List toList() { return byteArrayToList(data, 0, size); } List asVirtualList() { return new RandomAccessAbstractList() { public int size() { return size; } public Byte get(int i) { return ByteBuffer.this.get(i); } public Byte set(int i, Byte val) { Byte a = get(i); data[i] = val; return a; } }; } void reset() { size = 0; } void clear() { reset(); } int size() { return size; } boolean isEmpty() { return size == 0; } byte get(int idx) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); return data[idx]; } void set(int idx, byte value) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); data[idx] = value; } byte popLast() { if (size == 0) throw fail("empty buffer"); return data[--size]; } // unefficient byte popFirst() { if (size == 0) throw fail("empty buffer"); byte b = data[0]; arraycopy(data, 1, 0, --size); return b; } byte last() { return data[size-1]; } byte nextToLast() { return data[size-2]; } public String toString() { return squareBracket(joinWithSpace(toList())); } public Iterator iterator() { return new IterableIterator() { int i = 0; public boolean hasNext() { return i < size; } public Byte next() { //if (!hasNext()) fail("Index out of bounds: " + i); return data[i++]; } }; } /*public ByteIterator byteIterator() { ret new ByteIterator { int i = 0; public bool hasNext() { ret i < size; } public int next() { //if (!hasNext()) fail("Index out of bounds: " + i); ret data[i++]; } toString { ret "Iterator@" + i + " over " + ByteBuffer.this; } }; }*/ void trimToSize() { data = resizeByteArray(data, size); } int indexOf(byte b) { for (int i = 0; i < size; i++) if (data[i] == b) return i; return -1; } byte[] subArray(int start, int end) { return subByteArray(data, start, min(end, size)); } } /*sinterface ISleeper extends ISleeper_v2, AutoCloseable { void doLater(long targetSysTime, Runnable r); // call only once }*/ static short rgbIntToHi15(int rgb) { int r = (rgb >> 16) & 0xFF; int g = (rgb >> 8) & 0xFF; int b = rgb & 0xFF; return rgbToHi15(r, g, b); } static int hi15ToRGBInt(short hi15) { return hi15ToRGBInt_clean(hi15); } static void assertPowerOfTwo(int i) { if (!isPowerOfTwo(i)) throw fail(i); } static int dualLog(int i) { return 32-Integer.numberOfLeadingZeros(max(0, i-1)); } static int assertDivisibleBy(int divisor, int a) { if ((a % divisor) != 0) throw fail("Not divisible by " + divisor + ":" + a); return a; } 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 void add(BitSet bs, int i) { bs.set(i); } static boolean add(Collection c, A a) { return c != null && c.add(a); } static void add(Container c, Component x) { addToContainer(c, x); } static long add(AtomicLong l, long b) { return l.addAndGet(b); } static A _print(String s, A a) { return print(s, a); } static A _print(A a) { return print(a); } static void _print() { print(); } static boolean _eq(Object a, Object b) { return eq(a, b); } static String toString(Object o) { return strOrNull(o); } 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 formatDouble(DoubleRange r, int digits) { return r == null ? "null" : "[" + formatDouble(r.start, digits) + ";" + formatDouble(r.end, digits) + "]"; } static double ratioToPercent(double x, double y) { return x*100/y; } 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 Class run(String progID, String... args) { Class main = hotwire(progID); callMain(main, args); return main; } static A last(List l) { return empty(l) ? null : l.get(l.size()-1); } static char last(String s) { return empty(s) ? '#' : s.charAt(l(s)-1); } static 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 long last(long[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static double last(double[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static A last(A[] a) { return l(a) != 0 ? a[l(a)-1] : null; } static A last(Iterator it) { A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static A last(Collection l) { if (l == null) return null; if (l instanceof List) return (A) last((List) l); if (l instanceof SortedSet) return (A) last((SortedSet) l); Iterator it = iterator(l); A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static A last(SortedSet l) { return l == null ? null : l.last(); } static A last(ReverseChain l) { return l == null ? null : l.element; } static int last(IntBuffer buf) { return buf.get(buf.size()-1); } static byte last(ByteBuffer buf) { return buf.get(buf.size()-1); } static double last(DoubleBuffer l) { return l.last(); } static long last(LongBuffer l) { return l.last(); } static A last(CompactLinkedHashSet set) { return set == null ? null : set.last(); } static IterableIterator pixelIterator(Rect r) { return new IterableIterator() { int x2 = r.x2(), y2 = r.y2(); int x = r.x, y = r.y; public boolean hasNext() { return y < y2 && x < x2; } public Pt next() { Pt p = pt(x, y); if (++x >= x2) { x = r.x; ++y; } return p; } }; } static java.awt.Color color(String hex) { return awtColor(hex); } static OnePathWithOrigin g22_regionOutline(IImageRegion region) { // TODO: optimize List outlines = g22_allBorderTraces_autoDiagonals(region); return new OnePathWithOrigin(first(outlines), false); } static void fillArrayUnlessZero(byte[] a, byte value) { if (value != 0) Arrays.fill(a, value); } static void fillArrayUnlessZero(int[] a, int value) { if (value != 0) Arrays.fill(a, value); } static void fillArrayUnlessZero(float[] a, float value) { if (value != 0) Arrays.fill(a, value); } 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 ubyteToInt(byte b) { return b & 0x0FF; } static int ubyteToInt(char c) { return c & 0x0FF; } static int getInt(List c, int i) { return toInt(get(c, i)); } static int getInt(Map map, Object key) { return toInt(mapGet(map, key)); } static int getInt(Object o, String field) { return toInt(getOpt(o, (String) field)); } static int getInt(String field, Object o) { return getInt(o, field); } 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 BufferedImage bufferedImage(int[] pixels, int w, int h) { return intArrayToBufferedImage(pixels, w, h); } static BufferedImage bufferedImage(int[] pixels, WidthAndHeight size) { return bufferedImage(pixels, size.getWidth(), size.getHeight()); } static BufferedImage bufferedImage(int w, int h, int[] pixels) { return intArrayToBufferedImage(pixels, w, h); } // undefined color, seems to be all black in practice static BufferedImage bufferedImage(int w, int h) { return newBufferedImage(w, h); } static BufferedImage bufferedImage(int w, int h, RGB rgb) { return newBufferedImage(w, h, rgb); } static BufferedImage bufferedImage(int w, Color color) { return bufferedImage(w, w, color); } static BufferedImage bufferedImage(int w, int h, Color color) { return newBufferedImage(w, h, color); } static BufferedImage bufferedImage(Pt p, Color color) { return newBufferedImage(p, color); } static BufferedImage bufferedImage(WidthAndHeight size, Color color) { return newBufferedImage(size.w(), size.h(), color); } static BufferedImage bufferedImage(Color color, WidthAndHeight size) { return bufferedImage(size, color); } 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 int ifloor(double d) { return (int) Math.floor(d); } static IntRange ifloor(DoubleRange r) { return r == null ? null : intRange(ifloor(r.start), ifloor(r.end)); } static int clampUByteInt(long l) { return (int) clamp(l, 0, 255); } static int clampUByteInt(int i) { return clamp(i, 0, 255); } 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 shallowClone(A o) { return (A) shallowClone_impl(o); } static A shallowClone(A o, A emptyClone) { return copyFields(o, emptyClone); } static Object shallowClone_impl(Object o) { if (o == null) return o; if (o instanceof List) return cloneList((List) o); if (o instanceof Map) return cloneMap((Map) o); if (o instanceof String || o instanceof Number || o instanceof Boolean) return o; if (o instanceof Object[]) { Object[] l = (Object[]) o; return l.clone(); } // clone an arbitrary custom object Object clone; if (o instanceof IMakeEmptyClone) clone = ((IMakeEmptyClone) o).makeEmptyClone(); else clone = nuEmptyObject(o.getClass()); //print("Cloning custom: " + o); copyFields(o, clone); return clone; } static String stdToString(Object o) { return standard_toString(o); } static boolean stdEq_nonTransient(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 : nonTransientNonStaticFields(a)) if (neq(getOpt(a, field), getOpt(b, field))) return false; return true; } static int stdHash_nonTransient(Object a) { if (a == null) return 0; return stdHash(a, toStringArray(nonTransientNonStaticFields(a))); } static RGB hi15ToRGB(short hi15) { return new RGB(hi15ToRGBInt_clean(hi15)); } 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 packWindow(final A c) { { swing(() -> { Window w = getWindow(c); if (w != null) w.pack(); }); } return c; } static VF1 toVF1(IVF1 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 shortClassName_dropNumberPrefix(Object o) { return dropNumberPrefix(shortClassName(o)); } static int y2(Rectangle r) { return r.y+r.height; } static boolean isEmpty(Collection c) { return c == null || c.isEmpty(); } static boolean isEmpty(File f) { return f == null || f.length() == 0; } 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(DoubleRange r) { return r == null || r.isEmpty(); } static boolean isEmpty(AppendableChain c) { return c == null; } static boolean isEmpty(IntSize l) { return l == null || l.size() == 0; } static IntRange intRange(int start, int end) { return new IntRange(start, end); } static boolean intRangesOverlapNempty(IntRange a, IntRange b) { return nempty(intersectIntRanges(a, b)); } static boolean intRangesOverlapNempty(int a1, int a2, int b1, int b2) { return intRangesOverlapNempty(intRange(a1, a2), intRange(b1, b2)); } static short toShort_enforce(long l) { if (l != (short) l) throw fail("Too large for short: " + l); return (short) l; } static boolean scaffoldingEnabled(Object o) { return metaGet(o, "scaffolding") != null; } // Use like this: printVars(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(, +x); static void printVars(Object... params) { printVars_str(params); } static List reallyLazyMap(IF1 f, List l) { return lazyMap_noSave(f, l); } static List reallyLazyMap(List l, IF1 f) { return lazyMap_noSave(f, l); } static List shiftIntRanges(int shift, Iterable l) { return map(l, r -> shiftIntRange(shift, r)); } static List genericStreaks(IIntPred pred, int n) { List out = new ArrayList(); int i = 0; while (i < n) { int j = i; while (j < n && pred.get(j)) ++j; if (j > i) out.add(intRange(i, j)); i = j+1; } return out; } static List genericStreaks(int n, IIntPred pred) { return genericStreaks(pred, n); } static List assertProperStreaks(List l) { int n = l(l); for (int i = 0; i+1 < n; i++) if (l.get(i+1).start <= l.get(i).end) throw fail("assertProperStreaks", str(l)); return l; } static List cloneAndClear(Collection l) { return cloneAndClearList(l); } static GrabbableRGBBytePixels grabbableRGBBytePixels(BufferedImage img) { Raster raster = img.getRaster(); SampleModel _sampleModel = raster.getSampleModel(); if (!(_sampleModel instanceof PixelInterleavedSampleModel)) return null; // Check if we need a conversion to RGB (e.g. after unpacking a JPEG) var imageModel = img.getColorModel(); if (imageModel != ColorModel.getRGBdefault()) return null; PixelInterleavedSampleModel sampleModel = (PixelInterleavedSampleModel) _sampleModel; DataBufferByte dataBuffer = (DataBufferByte) (raster.getDataBuffer()); assertEquals(1, dataBuffer.getNumBanks()); assertEquals(DataBuffer.TYPE_BYTE, 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 pixelStride = sampleModel.getPixelStride(); int scanlineStride = sampleModel.getScanlineStride(); byte[] pixels = dataBuffer.getData(); int offset = dataBuffer.getOffset(); int translateX = raster.getSampleModelTranslateX(); int translateY = raster.getSampleModelTranslateY(); offset += -translateX-translateY*scanlineStride; return new GrabbableRGBBytePixels(pixels, w, h, offset, scanlineStride, pixelStride); } static GrabbableIntPixels grabbableIntPixels_fastOrSlow(BufferedImage image) { try { try { GrabbableIntPixels gp = grabbableIntPixels(image); if (gp != null) return gp; } catch (Throwable __e) { pcallFail(__e); } // TODO: use GrabbableRGBBytePixels and convert manually // - should be way faster than PixelGrabber 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 CachedPosterizer cachePosterizer(IPosterizer posterizer) { if (posterizer == null) return null; if (posterizer instanceof CachedPosterizer) return ((CachedPosterizer) posterizer); return new CachedPosterizer(posterizer); } static Value value(A a) { return new Value(a); } static A set(A o, String field, Object value) { if (o == null) return null; if (o instanceof Class) set((Class) o, field, value); else try { Field f = set_findField(o.getClass(), field); makeAccessible(f); smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } return o; } static void set(Class c, String field, Object value) { if (c == null) return; try { Field f = set_findStaticField(c, field); makeAccessible(f); smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field set_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field set_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static void set(BitSet bs, int idx) { { if (bs != null) bs.set(idx); } } static A onChange(A spinner, Object r) { return onChange(spinner, toRunnable(r)); } static A onChange(A spinner, Runnable r) { if (r != null) { swing(() -> { spinner.addChangeListener(changeListener(r)); }); } return spinner; } static A onChange(A b, Runnable r) { { swing(() -> { b.addItemListener(itemListener(r)); }); } return b; } static void onChange(JTextComponent tc, Runnable r) { onUpdate(tc, r); } static A onChange(A slider, Runnable r) { { swing(() -> { slider.addChangeListener(changeListener(r)); }); } return slider; } static JComboBox onChange(JComboBox cb, ChangeTriggerable r) { return onChange(cb, new ChangeTrigger(r)); } static JComboBox onChange(JComboBox cb, IVF1 f) { if (f != null) addActionListener(cb, () -> f.get(getSelectedItem_typed(cb))); return cb; } // Note: JComboBox also supports addActionListener. // Not sure if that would help us any. static JComboBox onChange(JComboBox cb, Runnable r) { if (isEditableComboBox(cb)) onChange(textFieldFromComboBox(cb), r); else onSelectedItem(cb, runnableToIVF1(r)); return cb; } static A onChange(A tabs, Runnable r) { { swing(() -> { tabs.addChangeListener(changeListener(r)); }); } return tabs; } static A onChange(Runnable r, A cc) { if (cc != null && r != null) { swing(() -> { cc.getSelectionModel().addChangeListener(changeListener(r)); }); } return cc; } static A onChange(A a, ChangeTriggerable b) { if (a != null && b != null) a.onChange(new ChangeTrigger(b)); return a; } static A onChange(A a, Updateable b) { if (a != null && b != null) a.onChange(new UpdateTrigger(b)); return a; } static A onChange(A a, Runnable r) { if (a != null && r != null) a.onChange(r); return a; } static int _hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static String find(String pattern, String text) { Matcher matcher = Pattern.compile(pattern).matcher(text); if (matcher.find()) return matcher.group(1); return null; } static A find(Collection c, Object... data) { for (A x : c) if (checkFields(x, data)) return x; return null; } static String joinNemptiesWithColon(String... strings) { return joinNempties(": ", strings); } static String joinNemptiesWithColon(Collection strings) { return joinNempties(": ", strings); } static int boostHashCombine(int a, int b) { return a ^ (b + 0x9e3779b9 + (a << 6) + (a >>> 2)); // OLD (changed) 2022/3/10: ret a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2)); } static Pt ptMinus(Pt a, Pt b) { if (b == null) return a; return new Pt(a.x-b.x, a.y-b.y); } static AbstractSSI vectorizeSSIIfBeneficial(SSI ssi) { var vectorSSI = new VectorSSI(ssi); return vectorSSI.sizeInInts() < ssi.sizeInInts() ? vectorSSI : ssi; } static UnsupportedOperationException unsupportedOperation() { throw new UnsupportedOperationException(); } static int rgbRed(int rgb) { return (rgb >> 16) & 0xFF; } static int rgbGreen(int rgb) { return (rgb >> 8) & 0xFF; } static int rgbBlue(int rgb) { return rgb & 0xFF; } 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 float clampZeroToOne(float x) { return x < 0 ? 0 : x > 1 ? 1 : x; } static double clampZeroToOne(double x) { return x < 0 ? 0 : x > 1 ? 1 : x; } 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 rgbInt(byte r, byte g, byte b) { return (ubyteToInt(r) << 16) | (ubyteToInt(g) << 8) | ubyteToInt(b); } static int asInt(Object o) { return toInt(o); } static String spaceCombine(Object... l) { return joinNemptiesWithSpace(flattenCollections(ll(l))); } 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_getPlural(String singular) { //ret singular + "s"; return plural(singular); } static String n2(double l, String singular) { return empty(singular) ? str(l) : n2(l, singular, n2_getPlural(singular)); } 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, n2_getPlural(singular)); } 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(IMultiMap mm, String singular) { return n2(mm, singular, n2_getPlural(singular)); } static String n2(IMultiMap mm, String singular, String plural) { return n_fancy2(l(mm), singular, plural); } static BufferedImage render(WidthAndHeight size, G2Drawable drawable) { return drawableToImage(size, drawable); } static BufferedImage renderAll(int w, int h, Iterable drawables) { return renderAll(drawables, whiteImage(w, h)); } static BufferedImage renderAll(Iterable drawables, BufferedImage canvas) { for (var d : unnullForIteration(drawables)) { if (d != null) d.drawOn(canvas); } return canvas; } static BufferedImage renderAll(Iterable drawables, WidthAndHeight size) { return renderAll(size.w(), size.h(), drawables); } static List instancesOf(Iterable i, Class c) { return collectInstances(i, c); } static List instancesOf(Class c, Iterable i) { return collectInstances(c, i); } static Class getClass(String name) { return _getClass(name); } static Class getClass(Object o) { return _getClass(o); } static Class getClass(Object realm, String name) { return _getClass(realm, name); } static 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 boolean isAbstract(Class c) { return (c.getModifiers() & Modifier.ABSTRACT) != 0; } static boolean isAbstract(Method m) { return (m.getModifiers() & Modifier.ABSTRACT) != 0; } static boolean reflection_isForbiddenMethod(Method m) { return m.getDeclaringClass() == Object.class && eqOneOf(m.getName(), "finalize", "clone", "registerNatives"); } static Set allInterfacesImplementedBy(Object o) { return allInterfacesImplementedBy(_getClass(o)); } static Set allInterfacesImplementedBy(Class c) { if (c == null) return null; HashSet set = new HashSet(); allInterfacesImplementedBy_find(c, set); return set; } static void allInterfacesImplementedBy_find(Class c, Set set) { if (c.isInterface() && !set.add(c)) return; do { for (Class intf : c.getInterfaces()) allInterfacesImplementedBy_find(intf, set); } while ((c = c.getSuperclass()) != null); } static Method findStaticMethod(Class c, String method, Object... args) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (!m.getName().equals(method)) continue; if ((m.getModifiers() & Modifier.STATIC) == 0 || !findStaticMethod_checkArgs(m, args)) continue; return m; } c = c.getSuperclass(); } return null; } static boolean findStaticMethod_checkArgs(Method m, Object[] args) { Class[] types = m.getParameterTypes(); if (types.length != args.length) return false; for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) return false; return true; } static List quoteAll(String[] l) { return quoteAll(asList(l)); } static List quoteAll(Collection l) { List x = new ArrayList(); for (String s : l) x.add(quote(s)); return x; } static ArrayList toList(A[] a) { return asList(a); } static ArrayList toList(int[] a) { return asList(a); } static ArrayList toList(short[] a) { return asList(a); } static ArrayList toList(long[] a) { return asList(a); } static ArrayList toList(Set s) { return asList(s); } static ArrayList toList(Iterable s) { return asList(s); } 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 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 setImage(A is, BufferedImage img) { { if (is != null) is.setImage(img); } return is; } static A bindToComponent(A component, Runnable onShow, 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 bindToComponent(A component, Runnable onShow) { return bindToComponent(component, onShow, null); } // passing a value from onShow to onUnShow static A bindToComponent(A component, IF0 onShow, IVF1 onUnShow) { Var b = new Var(); return bindToComponent(component, () -> b.set(onShow.get()), () -> { try { onUnShow.get(b.get()); } finally { b.set(null); } }); } static void setComponent(SingleComponentPanel scp, Component c) { setSCPComponent(scp, c); } static void setComponent(SingleComponentPanel scp, IF0 c) { if (scp != null) setComponent(scp, callF(c)); } static A jMinSize(int w, int h, A c) { return jMinWidth(w, jMinHeight(h, c)); } static A jMinSize(A c, int w, int h) { return jMinSize(w, h, c); } static A jMinSize(A c, Dimension d) { return jMinSize(d.width, d.height, c); } 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 BufferedImage shootScreen2() { return shootScreen2(screenRectangle()); } static BufferedImage shootScreen2(Rectangle area) { try { ping(); return new Robot().createScreenCapture(area); } catch (Exception __e) { throw rethrow(__e); } } static BufferedImage shootScreen2(Rect area) { return shootScreen2(area.getRectangle()); } static BufferedImage shootScreen2(int x, int y, int w, int h) { return shootScreen2(new Rectangle(x, y, w, h)); } // internal static boolean infoMessage_alwaysOnTop = true; static double infoMessage_defaultTime = 5.0; // automatically switches to AWT thread for you static JWindow infoMessage(String text) { return infoMessage(text, infoMessage_defaultTime); } static JWindow infoMessage(final String text, final double seconds) { printHidingCredentials(text); return infoMessage_noprint(text, seconds); } static JWindow infoMessage_noprint(String text) { return infoMessage_noprint(text, infoMessage_defaultTime); } static JWindow infoMessage_noprint(final String _text, final double seconds) { final String text = hideCredentials(_text); if (empty(text)) return null; logQuotedWithDate(infoBoxesLogFile(), text); if (isHeadless()) return null; return (JWindow) swingAndWait(new F0() { public Object get() { try { 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)); } static BufferedImage loadImage2(String snippetIDOrURL) { return loadBufferedImage(snippetIDOrURL); } static BufferedImage loadImage2(File file) { return loadBufferedImage(file); } 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 createEmptyMap) { if (o == null) return null; // The following shortcut depends on the assumption that a meta field never reverts // to null when it was a map Object meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta); // non-shortcut path (create meta) var mutex = tempMetaMutex(o); try { var actualMutex = mutex.get(); synchronized(actualMutex) { meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta); Map map = createEmptyMap.get(); if (meta != null) map.put("previousMeta" , meta); o._setMeta(map); return map; } } finally { _close(mutex); }} static void syncMapPutOrRemove(Map map, A key, B value) { syncMapPut2(map, key, value); } static Map putAll(Map a, Map b) { if (a != null && b != null) a.putAll(b); return a; } static MultiMap putAll(MultiMap a, Map b) { if (a != null) a.putAll((Map) b); return a; } static Map putAll(Map a, Object... b) { if (a != null) litmap_impl(a, b); return a; } static A getAndClear(IVar v) { A a = v.get(); v.set(null); return a; } static Set keySet(Map map) { return map == null ? new HashSet() : map.keySet(); } static Set keySet(Object map) { return keys((Map) map); } static Set keySet(MultiMap mm) { return mm.keySet(); } static int keysSize(MultiMap mm) { return lKeys(mm); } static A reverseGet(List l, int idx) { if (l == null || idx < 0) return null; int n = l(l); return idx < n ? l.get(n-1-idx) : null; } static Map cloneMap(Map map) { if (map == null) return new HashMap(); // assume mutex is equal to map synchronized(map) { return map instanceof TreeMap ? new TreeMap((TreeMap) map) // copies comparator : map instanceof LinkedHashMap ? new LinkedHashMap(map) : new HashMap(map); } } static List cloneMap(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : cloneList(l)) x.add(f.get(o)); return x; } static Collection values(Map map) { return map == null ? emptyList() : map.values(); } // convenience shortcut for values_gen static Collection values(Object map) { return values((Map) map); } static Collection values(MultiMap mm) { return mm == null ? emptyList() : concatLists(values(mm.data)); } static > List allValues(Map map) { List out = new ArrayList(); for (var l : values(map)) addAll(out, l); return out; } static File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } // 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 boolean hasNext(Iterator it) { return it != null && it.hasNext(); } static Method hashMap_findKey_method; static A hashMap_findKey(HashMap map, Object key) { try { if (hashMap_findKey_method == null) hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode"); Map.Entry entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key); // java.util.Map.Entry entry = (java.util.Map.Entry) call(hash, 'getNode, hashMap_internalHash(key), wkey); return entry == null ? null : entry.getKey(); } catch (Exception __e) { throw rethrow(__e); } } static String a(String noun) { if (eq(noun, "")) return "?"; return ("aeiou".indexOf(noun.charAt(0)) >= 0 ? "an " : "a ") + noun; } static String a(String contents, Object... params) { return hfulltag("a", contents, params); } static String b(Object contents, Object... params) { return fulltag("b", contents, params); } static int hashCodeFor(Object a) { return a == null ? 0 : a.hashCode(); } // returns number of steps static long stepAll(Steppable s) { long steps = 0; if (s != null) { var pingSource = pingSource(); while (true) { ping(pingSource); if (s.step()) ++steps; else break; } } return steps; } static boolean step(Steppable steppable) { return steppable == null ? null : steppable.step(); } static List cloneTakeFirst(Collection l, int n) { n = min(n, l(l)); List out = emptyList(n); if (n != 0) for (A a : l) { out.add(a); if (l(out) >= n) break; } return out; } static List cloneTakeFirst(int n, Collection l) { return cloneTakeFirst(l, n); } static List cloneTakeFirst(List l, int n) { return l(l) <= n ? l : cloneSubList(l, 0, n); } static List cloneTakeFirst(int n, List l) { return cloneTakeFirst(l, n); } static List cloneDropFirst(List l) { return cloneDropFirst(1, l); } static List cloneDropFirst(int n, List l) { return cloneSubList(l, n); } static List cloneDropFirst(List l, int n) { return cloneDropFirst(n, l); } static List filter(Iterable c, Object pred) { if (pred instanceof F1) return filter(c, (F1) pred); List x = new ArrayList(); if (c != null) for (Object o : c) if (isTrue(callF(pred, o))) x.add(o); return x; } static List filter(Object pred, Iterable c) { return filter(c, pred); } static List filter(Iterable c, F1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static List filter(F1 pred, Iterable c) { return filter(c, pred); } //ifclass IF1 static List filter(Iterable c, IF1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static List filter(B[] c, IF1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (pred.get(o)) x.add(o); return x; } static List filter(IF1 pred, Iterable c) { return filter(c, pred); } //endif static void setRGB(BufferedImage img, Pt p, RGB rgb) { { if (img != null) img.setRGB(p.x, p.y, rgb.asIntWithAlpha()); } } static void setRGB(BufferedImage img, int x, int y, RGB rgb) { { if (img != null) img.setRGB(x, y, rgb.asIntWithAlpha()); } } static void setRGB(BufferedImage img, int x, int y, Color color) { { if (img != null) img.setRGB(x, y, colorToIntOpaque(color)); } } static short[] newShortArrayOrNull(int n) { return n <= 0 ? null : new short[n]; } 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 arrayCopy(Object[] a, Object[] b) { arraycopy(a, b); } static void arrayCopy(Object src, Object dest, int destPos, int n) { arrayCopy(src, 0, dest, destPos, n); } static void arrayCopy(Object src, int srcPos, Object dest, int destPos, int n) { arraycopy(src, srcPos, dest, destPos, n); } static boolean intRangesOverlap(IntRange a, IntRange b) { return intersectIntRanges(a, b) != null; } static boolean intRangesOverlap(int a1, int a2, int b1, int b2) { return intRangesOverlap(intRange(a1, a2), intRange(b1, b2)); } static IterableIterator splitAtSpaceIterator(String s) { return iff_null(new IF0() { int i = 0, l = l(s); public String get() { // skip space(s) while (i < l) { if (isSpaceEtc(s.charAt(i))) ++i; else break; } if (i >= l) return null; int j = i; // scan for non-whitespace while (j < l && !isSpaceEtc(s.charAt(j))) ++j; String part = substring(s, i, j); i = j; return part; } }); } static A assertEquals(Object x, A y) { return assertEquals("", x, y); } static A assertEquals(String msg, Object x, A y) { if (assertVerbose()) return assertEqualsVerbose(msg, x, y); if (!(x == null ? y == null : x.equals(y))) throw fail((msg != null ? msg + ": " : "") + y + " != " + x); return y; } static void assertEquals(Scorer scorer, Object x, Object y) { assertEquals(scorer, "", x, y); } static void assertEquals(Scorer scorer, String msg, Object x, Object y) { if (scorer == null) { assertEquals(msg, x, y); return; } scorer.add(eq(x, y), nullIfEmpty(msg)); } static A nextFromIterator(Iterator it) { return it == null || !it.hasNext() ? null : it.next(); } static A setToolTipText(final A c, final Object toolTip) { if (c == null) return null; { swing(() -> { String s = str_nullIfEmpty(toolTip); if (neq(s, c.getToolTipText())) c.setToolTipText(s); }); } return c; } static A setToolTipText(Object toolTip, A c) { return setToolTipText(c, toolTip); } static String nullIfEmpty(String s) { return isEmpty(s) ? null : s; } static Map nullIfEmpty(Map map) { return isEmpty(map) ? null : map; } static List nullIfEmpty(List l) { return isEmpty(l) ? null : l; } static 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 LinkedHashMap cloneLinkedHashMap(Map map) { return map == null ? new LinkedHashMap() : new LinkedHashMap(map); } 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 long longMul(long a, long b) { return a*b; } static Image2B toImage2B(BufferedImage img) { return img == null ? null : new Image2B(img); } static Image2B toImage2B(MakesBufferedImage img) { if (img == null) return null; if (img instanceof Image2B) return ((Image2B) img); if (img instanceof BWImage) return new Image2B((BWImage) img); if (img instanceof IBinaryImage) return iBinaryImageToImage2B((IBinaryImage) img); return new Image2B(toBufferedImage(img)); } static Image2B iBinaryImageToImage2B(IBinaryImage img) { if (img == null) return null; if (img instanceof Image2B) return ((Image2B) img); int w = img.getWidth(), h = img.getHeight(); byte[] pixels = new byte[(w*h+7)/8]; int i = 0; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { if (img.getBoolPixel(x, y)) pixels[i/8] |= 1 << (i & 7); i++; } return new Image2B(w, h, pixels); } static int[] iroundAll(double[] a, int start, int end) { return iroundDoubleArray(a, start, end); } static int[] iroundAll(double[] a) { return iroundDoubleArray(a); } static volatile int numberOfCores_value; static int numberOfCores() { if (numberOfCores_value == 0) numberOfCores_value = Runtime.getRuntime().availableProcessors(); return numberOfCores_value; } static A popFirst(List l) { if (empty(l)) return null; A a = first(l); l.remove(0); return a; } static A popFirst(Collection l) { if (empty(l)) return null; A a = first(l); l.remove(a); return a; } static Pair popFirst(Map 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 List popFirst(int n, List l) { List part = cloneSubList(l, 0, n); removeSubList(l, 0, n); return part; } static AppendableChain popFirst(AppendableChain a) { return a == null ? null : a.popFirst(); } // Yes the nomenclature is a bit illogical static Chain chainPlus(Chain chain, A a) { return new Chain(a, chain); } static Chain chainPlus(Chain chain, A... l) { for (A a : unnullForIteration(l)) chain = chainPlus(chain, a); return chain; } static ReverseChain chainPlus(ReverseChain chain, A a) { return new ReverseChain(chain, a); } static ReverseChain chainPlus(ReverseChain chain, A... l) { for (A a : unnullForIteration(l)) chain = chainPlus(chain, a); return chain; } static AppendableChain chainPlus(AppendableChain chain, A a) { if (chain == null) return new AppendableChain(a); chain.add(a); return chain; } static AppendableChain chainPlus(AppendableChain chain, A... l) { for (A a : unnullForIteration(l)) chain = chainPlus(chain, a); return chain; } static void syncNotifyAll(Object o) { if (o != null) synchronized(o) { o.notifyAll(); } } static int syncL(Collection l) { if (l == null) return 0; synchronized(collectionMutex(l)) { return l.size(); } } static int syncL(Map map) { if (map == null) return 0; synchronized(collectionMutex(map)) { return map.size(); } } static List syncL() { return syncList(); } static BufferedImage whiteImage(int w, int h) { return newBufferedImage(w, h, Color.white); } static BufferedImage whiteImage(int size) { return whiteImage(size, size); } static BufferedImage whiteImage(WidthAndHeight size) { return whiteImage(size.getWidth(), size.getHeight()); } static 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 onResize(Runnable r, A c) { return onResize(c, r); } // also works when using centering of the component, // e.g. through jscroll_center_borderless static void onEnclosingScrollPaneResize(JComponent c, Runnable r) { new OnEnclosingScrollPaneResize(c, r).install(); } static class OnEnclosingScrollPaneResize implements IFieldsToList{ JComponent c; Runnable r; OnEnclosingScrollPaneResize() {} OnEnclosingScrollPaneResize(JComponent c, Runnable r) { this.r = r; this.c = c;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + r + ")"; }public Object[] _fieldsToList() { return new Object[] {c, r}; } final public OnEnclosingScrollPaneResize setDebug(boolean debug){ return debug(debug); } public OnEnclosingScrollPaneResize debug(boolean debug) { this.debug = debug; return this; } final public boolean getDebug(){ return debug(); } public boolean debug() { return debug; } boolean debug = false; ComponentAdapter listener; JViewport viewport; void install() { if (r == null) return; bindToComponent(c, () -> { viewport = optCast(JViewport.class, getParent(getParent(c))); if (viewport != null) { listener = new ComponentAdapter() { public void componentResized(ComponentEvent e) { if (debug) print("Viewport resized"); pcallF(r); } }; viewport.addComponentListener(listener); if (debug) print("OnEnclosingScrollPaneResize installed"); } }, () -> { if (listener != null && viewport != null) { viewport.removeComponentListener(listener); if (debug) print("OnEnclosingScrollPaneResize removed"); } }); } } static void componentPopupMenu2(A component, final VF2 menuMaker) { final WeakReference < A > ref = new WeakReference<>(component); componentPopupMenu(component, new VF1() { 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 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) tr.getTransferData(flavor)); if (file != null && !isFalse(callF(onDrop, file))) e.dropComplete(true); return; } } } catch (Throwable __e) { pcallFail(__e); } e.rejectDrop(); } }); return c; } static A jHandleFileDrop(IVF1 onDrop, A c) { return jHandleFileDrop((Object) onDrop, c); } static 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 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) { try { callF(onHover, e == null ? (Pt) null : is.pointFromEvent(e)); } catch (Throwable __e) { pcallFail(__e); }} }; is.addMouseMotionListener(ma); is.addMouseListener(ma); }); } } static void imageSurfaceOnHover(ImageSurface is, IVF1 onHover) { imageSurfaceOnHover(is, toVF1(onHover)); } static JCheckBoxMenuItem jMenuItemStayCheckedOnClick(String text, IF0 checked, Runnable action) { var mi = swing(() -> new JCheckBoxMenuItem(text, isTrue(checked.get()))); 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 setChecked(mi, b);"; }}); addActionListener(mi, () -> { if (!isChecked(mi)) setChecked(mi, true); callF(action); }); return mi; } static JFrame showFullScreen(JComponent c) { return showFullScreen(defaultFrameTitle(), c); } static JFrame showFullScreen(final String title, final JComponent c) { return (JFrame) swingAndWait(new F0() { 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 action) { selectFile(msg, userDir(), action); } static void selectFile(final String msg, final File defaultFile, VF1 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 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 onSelect) { return selectSnippetID_v1(onSelect); } static JComponent selectImageSnippet(String defaultID, VF1 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 List cloneClipBufferedImage(BufferedImage src, Iterable rects) { return map(rects, r -> cloneClipBufferedImage(src, r)); } static BufferedImage getImageFromClipboard() { try { Transferable t = rawClipboardContents(); if (t == null) return null; List l = (List) (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 Color getBackground(final Component c) { return c == null ? null : swing(new F0() { public Color get() { try { return c.getBackground(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getBackground();"; }}); } static boolean hasTransparency(BufferedImage img) { return img.getColorModel().hasAlpha(); } static Graphics2D cloneGraphics(Graphics2D g) { return (Graphics2D) g.create(); } static 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 printIfScaffoldingEnabled(Object o, A a) { return printIfScaffoldingEnabled(o, "", a); } static 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 boolean imagesHaveSameSize(BufferedImage a, BufferedImage b) { return a != null && b != null && a.getWidth() == b.getWidth() && a.getHeight() == b.getHeight(); } static Dimension getPreferredSize(final Component c) { return c == null ? null : swing(new F0() { public Dimension get() { try { return c.getPreferredSize(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getPreferredSize();"; }}); } static Container getParent(final Component c) { return c == null ? null : swing(new F0() { public Container get() { try { return c.getParent(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "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; } // 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 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 close(AutoCloseable c) { _close(c); } static void closeAllAndClear(Collection l) { if (l == null) return; for (AutoCloseable c : cloneList(l)) { try { close(c); } catch (Throwable __e) { pcallFail(__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 Color hi15color(short hi15) { return hi15ToColor_clean(hi15); } static short colorToHi15(RGB rgb) { return rgbToHi15(rgb); } static short colorToHi15(Color color) { return rgbToHi15(color); } static short colorToHi15(int rgb) { return rgbToHi15(rgb); } static String lower(String s) { return s == null ? null : s.toLowerCase(); } static char lower(char c) { return Character.toLowerCase(c); } static String colorToHex(Color c) { return c == null ? null : new RGB(c).getHexString(); } static StringWriter stringWriter() { return new StringWriter(); } static PrintWriter bufferedFileWriter(File f) { return filePrintWriter(f); } 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 StringReader stringReader(String s) { return new StringReader(s); } static RuntimeException todo() { throw new RuntimeException("TODO"); } static RuntimeException todo(Object msg) { throw new RuntimeException("TODO: " + msg); } static Pt origin() { return pt(0, 0); } static Pt pt(int x, int y) { return new Pt(x, y); } static Pt pt(int x) { return new Pt(x, x); } static List reversedList(Iterable l) { List x = cloneList(l); Collections.reverse(x); return x; } static List repF(int n, Object f, Object... args) { List l = emptyList(n); for (int i = 0; i < n; i++) l.add(callF(f, args)); return l; } static List repF(Object f, int n) { return repF(n, f); } static List repF(int n, IF0 f) { List l = emptyList(n); for (int i = 0; i < n; i++) l.add(f.get()); return l; } static List repF(IF0 f, int n) { return repF(n, f); } static boolean removeAll(Collection a, Collection b) { return a != null && b != null && a.removeAll(b); } static void removeAll(Map a, Collection b) { if (a != null && b != null) for (A x : b) a.remove(x); } static boolean removeAll(Collection c, B... b) { return c != null && b != null && c.removeAll(Arrays.asList(b)); } static void removeAll(Map a, A... b) { if (a != null && b != null) for (A x : b) a.remove(x); } static A _revalidate(A c) { return revalidate(c); } static void _revalidate(JFrame f) { revalidate(f); } static void _revalidate(JInternalFrame f) { revalidate(f); } static Container _getParent(Component c) { return getParent(c); } static boolean isNaN(double d) { return Double.isNaN(d); } static boolean isNaN(float f) { return Float.isNaN(f); } static double infinity() { return positiveInfinity(); } static double minusInfinity() { return negativeInfinity(); } static Scored scored(A a, float score) { return new Scored(a, score); } static Scored scored(A a, double score) { return new Scored(a, (float) score); } static Scored scored(A a, Scored scored) { return new Scored(a, getScore(scored)); } static Scored scored(double score, A a) { return scored(a, score); } static float score(Scored s) { return s == null ? 0 : s.score(); } static String formatDouble_significant2(double d, int digits) { try { digits -= Math.floor(max(-10, Math.log10(abs(d))+1)); return formatDouble(d, digits); } catch (Throwable _e) { print("Had number: " + d + ", digits: " + digits); throw rethrow(_e); } } static String callStringifier(Object stringifier, Object o) { return stringifier != null ? str(callF(stringifier, o)) : str(o); } static List syncMap(Object f, Map map) { return syncMap(map, f); } // map: func(key, value) -> list element static List syncMap(Map map, Object f) { return map(cloneLinkedHashMap(map), f); // TODO: use a temporary list instead } static Map syncMap() { return synchroHashMap(); } static Map syncMap(Map map) { return synchronizedMap(map); } static ThreadLocal saveTiming_last = new ThreadLocal(); static void saveTiming(long ms) { print(ms + " ms"); saveTiming_noPrint(ms); } static void saveTiming_noPrint(long ms) { saveTiming_last.set(ms); } static ThreadLocal saveTiming_tl() { return saveTiming_last; } static Object time(Object f) { long time = sysNow(); Object o = callF(f); done2_always(str(f), time); return o; } static A time(F0 f) { return (A) time((Object) f); } static A time(IF0 f) { return (A) time((Object) f); } static A time(String msg, IF0 f) { long time = sysNow(); A o = f.get(); done2_always(msg, time); return o; } static void time(Runnable f) { time(str(f), f); } static void time(String msg, Runnable f) { time(msg, runnableToIF0(f)); } static DynamicObject dO(String className, Object... x) { return dynamicObject(className, x); } // Irgendwie gab's mal nen Unterschied zwischen den mapGet und get // Methoden. null keys oder so. static B syncMapGetOrCreate(Map map, A key, Class c) { return syncGetOrCreate(map, key, c); } // f : func -> B static B syncMapGetOrCreate(Map map, A key, Object f) { return syncGetOrCreate(map, key, f); } static B syncMapGetOrCreate(Class c, Map map, A key) { return syncGetOrCreate(c, map, key); } static B syncMapGetOrCreate(Map map, A key, IF0 f) { return syncGetOrCreate(map, key, f); } static List ciSorted(Collection c) { return sortedIgnoreCase(c); } static double nsToMicroseconds(double ns) { return nanosToMicroseconds(ns); } static String microSymbol() { return "\u00B5"; } // brightness: 0 to 255 static int rgbIntFromGrayscale(int brightness) { brightness = clampToUByte(brightness); return brightness | (brightness << 8) | (brightness << 16); } // brightness: 0 to 1 static int rgbIntFromGrayscale(double brightness) { return rgbIntFromGrayscale(zeroToOneToZeroTo255(brightness)); } static BufferedImage grayImageFromIBWImage(IBWImage img) { int w = img.getWidth(), h = img.getHeight(); byte[] pixels = new byte[w*h]; int i = 0; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int pixel = iround(img.getFloatPixel(x, y)*255); // tODO: is it round or floor? pixels[i++] = clampToUByte(pixel); } return newGrayBufferedImage(w, h, pixels); } static BWImage iBWImageToBWImage(IBWImage img) { int w = img.getWidth(), h = img.getHeight(); BWImage out = new BWImage(w, h); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) out.setPixel(x, y, img.getFloatPixel(x, y)); return out; } static void clearPopupMenu(final JPopupMenu menu) { if (menu != null) { swing(() -> { menu.removeAll(); }); } } static String formatDoubleX(double d, int digits) { return formatDoubleFull(d, digits); } static byte[] cloneByteArray(byte[] a) { if (a == null) return null; byte[] b = new byte[a.length]; System.arraycopy(a, 0, b, 0, a.length); return b; } static BWImage toBW(RGBImage img) { return img == null ? null : new BWImage(img); } static String str_px(int w, int h) { return n2(w) + "*" + n2(h) + "px"; } static String str_px(WidthAndHeight image) { return image == null ? null : str_px(image.getWidth(), image.getHeight()); } static int bitCount(BitSet bs) { return bs == null ? 0 : bs.cardinality(); } static int bitCount(int i) { return Integer.bitCount(i); } static int bitCount(byte[] array) { if (array == null) return 0; int sum = 0; for (var b : array) sum += bitCount(ubyteToInt(b)); return sum; } static A listGetOrCreate(List l, int i, Class c) { return listGetOrCreate(l, i, () -> nuInstance(c)); } static A listGetOrCreate(List l, int i, IF0 f) { if (l == null) return null; A a = get(l, i); if (a == null) listSet(l, i, a = f.get()); return a; } static A listGetOrCreate(Class c, List l, int i) { return listGetOrCreate(l, i, c); } static IBWImage iBWImageFromFunction(IF2_IntInt_Double f, int w, int h) { return new IBWImage() { public int getWidth() { return w; } public int getHeight() { return h; } public float getFloatPixel(int x, int y) { return (float) f.get(x, y); } }; } static List virtualCountList(int n) { return new RandomAccessAbstractList() { public int size() { return n; } public Integer get(int i) { return i; } }; } // to is exclusive static List virtualCountList(int from, int to) { int n = max(to-from, 0); return new RandomAccessAbstractList() { public int size() { return n; } public Integer get(int i) { return from+i; } }; } static String nRegions(long n) { return n2(n, "region"); } static String nRegions(Collection l) { return nRegions(l(l)); } static String nRegions(Map map) { return nRegions(l(map)); } // TODO: clean up these type signatures? static > IterableIterator nestedIterator(Iterable c, final F1 makeInnerIterator) { return nestedIterator(iterator(c), makeInnerIterator); } static > IterableIterator nestedIterator(Iterable c, IF1 makeInnerIterator) { return nestedIterator(iterator(c), makeInnerIterator); } static > IterableIterator nestedIterator(IterableIterator c, IF1 makeInnerIterator) { return nestedIterator((Iterator) c, makeInnerIterator); } static > IterableIterator nestedIterator(Iterator it1, IF1 makeInnerIterator) { if (it1 == null || !it1.hasNext()) return emptyItIt(); return iff(new F0() { A a; Iterator 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 > IterableIterator nestedIterator(final Iterator it1, F1 makeInnerIterator) { if (it1 == null || !it1.hasNext()) return emptyItIt(); return iff(new F0() { A a; Iterator 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 > IterableIterator nestedIterator(IF1 makeInnerIterator, Iterator it1) { return nestedIterator(it1, makeInnerIterator); } static > IterableIterator nestedIterator(IF1 makeInnerIterator, Collection l) { return nestedIterator(l, makeInnerIterator); } static IterableIterator countIterator_inclusive_backwards(int a, int b) { return new IterableIterator() { int i = a; public boolean hasNext() { return i >= b; } public Integer next() { return i--; } }; } static IterableIterator countIterator_inclusive_backwards(int a, int b, IF1 f) { return mapI_if1(f, countIterator_inclusive_backwards(a, b)); } static class ListFromFunction extends RandomAccessAbstractList implements IFieldsToList{ int n; IF1 f; ListFromFunction() {} ListFromFunction(int n, IF1 f) { this.f = f; this.n = n;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + n + ", " + f + ")"; }public Object[] _fieldsToList() { return new Object[] {n, f}; } public int size() { return n; } public A get(int i) { return f.get(i); } } static List listFromFunction(int n, IF1 f) { return new ListFromFunction(n, f); } static List listFromFunction(IF1 f, int n) { return new ListFromFunction(n, f); } static WeakReference creator_class; static Object creator() { return creator_class == null ? null : creator_class.get(); } // f: func -> A (stream ends when f returns null) static IterableIterator iteratorFromFunction(final Object f) { class IFF extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = (A) callF(f); done = a == null; } }; return new IFF(); } // optimized version for F0 argument static IterableIterator iteratorFromFunction(F0 f) { return iteratorFromFunction_f0(f); } static IterableIterator iteratorFromFunction(IF0 f) { return iteratorFromFunction_if0(f); } static Color toColor(RGB rgb) { return rgb == null ? null : rgb.getColor(); } static Color toColor(int rgb) { return new Color(rgb); } static Color toColor(String hex) { return awtColor(hex); } static Color toColor(int[] rgb) { return new Color(rgb[0], rgb[1], rgb[2]); } // Use like this: renderRecordVars("MyRecord", +x, +y) static String renderRecordVars(String recordName, Object... params) { List 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 byte[] toASCII(String s) { return s.getBytes(java.nio.charset.StandardCharsets.US_ASCII); } static byte toASCII(char c) { return toASCII(str(new char[] {c}))[0]; } static String fromUtf8(byte[] bytes) { try { return bytes == null ? null : new String(bytes, utf8charset()); } catch (Exception __e) { throw rethrow(__e); } } static byte toUByte(int i) { if (!isUByte(i)) throw fail("Not a u-byte: " + i); return (byte) i; } static ByteArrayOutputStream byteArrayOutputStream() { return new ByteArrayOutputStream(); } static String toHexString(byte[] a) { return bytesToHex(a); } static String toHexString(List a) { return bytesToHex(byteListToArray(a)); } static byte[] toByteArray(ByteArrayOutputStream baos) { return baos == null ? null : baos.toByteArray(); } static byte[] toByteArray(Iterator it) { ByteBuffer buf = new ByteBuffer(); while (it.hasNext()) buf.add((byte) it.next().intValue()); return buf.toByteArray(); } static byte[] toByteArray(Collection it) { int n = l(it), i = 0; byte[] a = new byte[n]; for (var x : it) a[i++] = (byte) x.intValue(); return a; } static byte[] toByteArray(Object o) { if (o == null) return null; if (o instanceof byte[]) return ((byte[]) o); if (o instanceof Iterator) return toByteArray((Iterator) o); if (o instanceof Collection) return toByteArray((Collection) ((Collection) o)); // not sure what else to put here throw fail("toByteArray", o); } static BufferedOutputStream bufferedFileOutputStream(File f) { try { return bufferedOutputStream(newFileOutputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } 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 IterableIterator filterI(Iterator it, F1 f) { return filterIterator(it, f); } static IterableIterator filterI(Iterator it, IF1 f) { return filterIterator(it, f); } static IterableIterator filterI(F1 f, Iterator it) { return filterIterator(f, it); } static IterableIterator filterI(IF1 f, Iterator it) { return filterIterator(f, it); } static IterableIterator filterI(IF1 f, Iterable it) { return filterIterator(f, iterator(it)); } 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 Timestamp sysTimeToTimestamp(long now) { return now == 0 ? null : new Timestamp(now - clockToSysTimeDiff()); } static Timestamp tsNow() { return new Timestamp(); } static String readLineFromReader(BufferedReader r) { try { return r.readLine(); } catch (Exception __e) { throw rethrow(__e); } } 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 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 : keysList((Map) c)) cleanUp(o); for (Object o : valuesList((Map) c)) cleanUp(o); syncClear((Map) c); 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 classes = (List) (getOpt(c, "hotwire_classes")); if (classes != null) for (WeakReference cc : classes) { try { cleanUp(cc.get()); } catch (Throwable __e) { pcallFail(__e); }} // interrupt all threads (experimental, they might be doing cleanup?) if (cleanUp_interruptThreads) { List threads = registeredThreads(c); if (nempty(threads)) { print("cleanUp: Interrupting " + n2(threads, "thread") + ": " + joinWithComma(allToString(threads))); interruptThreads(threads); } } } catch (Throwable __e) { pcallFail(__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 void setPixel(BufferedImage img, int p_x, int p_y, Color color) { setPixel(img, pt(p_x, p_y), color); } static void setPixel(BufferedImage img, Pt p, Color color) { { if (img != null) img.setRGB(p.x, p.y, colorToIntOpaque(color)); } } static void setPixel(Graphics2D g, int p_x, int p_y, Color color) { setPixel(g, pt(p_x, p_y), color); } static void setPixel(Graphics2D g, Pt p, Color color) { g.setColor(color); g.drawRect(p.x, p.y, 1, 1); } static int lShortArray(short[] a) { return a == null ? 0 : a.length; } static short[] resizeShortArray(short[] a, int n) { if (n == lShortArray(a)) return a; short[] b = new short[n]; arraycopy(a, 0, b, 0, Math.min(lShortArray(a), n)); return b; } static short toShort(Object o) { if (o == null) return (short) 0; if (o instanceof Number) return ((Number) o).shortValue(); if (o instanceof String) return toShort(parseLong((String) o)); throw fail("not convertible to short: " + getClassName(o)); } static short toShort(long l) { if (l != (short) l) throw fail("Too large for short: " + l); return (short) l; } static int maximumSafeArraySize() { return Integer.MAX_VALUE-8; } static Object[] toArray(Collection c) { return toObjectArray(c); } static A[] toArray(Class type, Iterable c) { return toArray(c, type); } static A[] toArray(Class type, Collection c) { return toArray(c, type); } static A[] toArray(Collection c, Class type) { A[] a = arrayOfType(l(c), type); if (a.length == 0) return a; asList(c).toArray(a); return a; } static A[] toArray(Iterable c, Class type) { var c2 = asList(c); A[] a = arrayOfType(l(c2), type); if (a.length == 0) return a; c2.toArray(a); return a; } // array must have correct length and will be filled static A[] toArray(A[] array, Collection c) { if (array == null || c == null) return null; asList(c).toArray(array); return array; } static List asVirtualList(A[] a) { return wrapArrayAsList(a); } static String squareBracket(String s) { return "[" + s + "]"; } // action = runnable or method name static void onChangeAndNow(JComponent c, Object r) { onUpdateAndNow(c, r); } static void onChangeAndNow(List l, Object r) { onUpdateAndNow(l, r); } static void onChangeAndNow(JTextComponent c, IVF1 r) { onUpdateAndNow(c, r); } static JComboBox onChangeAndNow(JComboBox cb, IVF1 f) { onChange(cb, f); { if (f != null) f.get(getSelectedItem_typed(cb)); } return cb; } static A onChangeAndNow(A tabs, Runnable r) { if (r != null) { onChange(tabs, r); r.run(); } return tabs; } static JSlider onChangeAndNow(JSlider s, Runnable f) { if (s != null && f != null) { onChange(s, f); f.run(); } return s; } static JComponent unwrap(JComponent c) { return c instanceof JScrollPane ? unwrapScrollPane((JScrollPane) c) : c; } static String formatLocalDateWithSeconds(long time) { return localDateWithSeconds(time); } static String formatLocalDateWithSeconds() { return localDateWithSeconds(); } 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 toMS(double seconds) { return (long) (seconds*1000); } static long toMS(Duration d) { return d == null ? 0 : d.toMillis(); } static long clockTimeToSystemTime(long now) { return now == 0 ? 0 : now + clockToSysTimeDiff(); } static List minus(Collection 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 int minus(int a) { return -a; } static double minus(double a, double b) { return a-b; } static double minus(double a) { return -a; } static long minus(long a, long b) { return a-b; } static long minus(long a) { return -a; } static A setDoubleBuffered(A c) { return setDoubleBuffered(c, true); } static A setDoubleBuffered(A c, boolean b) { { swing(() -> { { if (c != null) c.setDoubleBuffered(b); } }); } return c; } static double[] toDoubleArray(Collection l) { double[] a = new double[l(l)]; int i = 0; if (a.length != 0) for (double x : l) a[i++] = x; return a; } static double[] toDoubleArray(IntBuffer b) { int n = l(b); double[] a = new double[n]; for (int i = 0; i < n; i++) a[i] = b.get(i); return a; } static double[] toDoubleArray(float... l) { double[] a = new double[l(l)]; for (int i = 0; i < a.length; i++) a[i] = l[i]; return a; } static double[] toDoubleArray(int... l) { double[] a = new double[l(l)]; for (int i = 0; i < a.length; i++) a[i] = l[i]; return a; } static int length(Object[] array) { return array == null ? 0 : array.length; } static int length(List list) { return list == null ? 0 : list.size(); } static int length(String s) { return s == null ? 0 : s.length(); } static int[] iroundDoubleArray(double[] a, int start, int end) { int[] b = new int[end-start]; for (int i = start; i < end; i++) b[i-start] = iround(a[i]); return b; } static int[] iroundDoubleArray(double[] a) { return a == null ? null : iroundDoubleArray(a, 0, a.length); } static MultiSetMap treeMultiSetMap() { return new MultiSetMap(true); } static MultiSetMap treeMultiSetMap(Comparator comparator) { return new MultiSetMap(new TreeMap>(comparator)); } static B firstValue(Map map) { return first(values(map)); } static B firstValue(MultiSetMap map) { return map == null ? null : first(firstValue(map.data)); } static B firstValue(MultiMap map) { return map == null ? null : first(firstValue(map.data)); } static A firstKey(Map map) { return first(keys(map)); } static A firstKey(IMultiMap map) { return map == null ? null : first(map.keySet()); } static boolean containsInstance(Iterable i, Class c) { if (i != null) for (Object o : i) if (isInstanceX(c, o)) return true; return false; } 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 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 long sysNowPlusSeconds_noPing(double seconds) { return sysNow_noPing()+secondsToMS(seconds); } static long sysNow_noPing() { return System.nanoTime()/1000000; } static DoubleRange doubleRange(double start, double end) { return new DoubleRange(start, end); } static DoubleRange growRange(DoubleRange r, double amount) { return r == null ? null : doubleRange(r.start-amount, r.end+amount); } static DoubleRange growRange(double amount, DoubleRange r) { return growRange(r, amount); } static Range intersectRanges(Range a, Range b) { float min = max(a.min, b.min); float max = min(a.max, b.max); return min <= max ? new Range(min, max) : null; } static DoubleRange intersectRanges(DoubleRange a, DoubleRange b) { return intersectDoubleRanges(a, b); } static int lIntArray(int[] a) { return a == null ? 0 : a.length; } static int[] resizeIntArray(int[] a, int n) { if (n == lIntArray(a)) return a; int[] b = new int[n]; arraycopy(a, 0, b, 0, Math.min(lIntArray(a), n)); return b; } 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 parseDigit(char c) { return c >= '0' && c <= '9' ? c-'0' : 0; } static int parseDigit(String s, int i) { return parseDigit(s.charAt(i)); } static String singleDigitBytesToString(ByteBuffer l) { if (l == null) return null; int n = l.size(); char[] data = new char[n]; for (int i = 0; i < n; i++) data[i] = (char) ('0' + ubyteToInt(l.get(i)) % 10); return str(data); } static Pt translatePt(Pt a, Pt b) { return addPts(a, b); } static Pt translatePt(int x, int y, Pt a) { return addPts(a, pt(x, y)); } static Pt translatePt(Pt a, int x, int y) { return translatePt(x, y, a); } static PtBuffer ptBuffer(Iterable l) { return new PtBuffer(l); } static final Pt[] onePathDirections_directions = { pt(0, 0), pt(-1, -1), pt(0, -1), pt(1, -1), pt(1, 0), pt(1, 1), pt(0, 1), pt(-1, 1), pt(-1, 0) }; static Pt[] onePathDirections() { return onePathDirections_directions; } static int onePathLookupDirection(Pt p) { return indexOf(onePathDirections(), p); } static void assertBetween(long a, long b, long x) { assertBetween("", a, b, x); } static void assertBetween(String msg, long a, long b, long x) { { if (x >= a && x <= b) return; } throw fail(colonCombine(msg, x + " is not between " + a + " and " + b)); } static void assertBetween(int a, int b, int x) { assertBetween("", a, b, x); } static void assertBetween(String msg, int a, int b, int x) { { if (x >= a && x <= b) return; } throw fail(colonCombine(msg, x + " is not between " + a + " and " + b)); } static void assertBetween(double a, double b, double x) { assertBetween("", a, b, x); } static void assertBetween(String msg, double a, double b, double x) { { if (x >= a && x <= b) return; } throw fail(colonCombine(msg, x + " is not between " + a + " and " + b)); } static Rect boundsOfPts(Iterable l) { BoundsFinder bf = new BoundsFinder(); for (var p : unnullForIteration(l)) bf.add(p); return bf.get(); } static int lDoubleArray(double[] a) { return a == null ? 0 : a.length; } static double[] resizeDoubleArray(double[] a, int n) { if (n == l(a)) return a; double[] b = new double[n]; arraycopy(a, 0, b, 0, min(l(a), n)); return b; } static ArrayList doubleArrayToList(double[] a) { if (a == null) return null; return doubleArrayToList(a, 0, a.length); } // no range checking on from/to in the interest of speed static ArrayList doubleArrayToList(double[] a, int from, int to) { if (a == null) return null; ArrayList < Double > l = new ArrayList<>(to-from); for (int i = from; i < to; i++) l.add(a[i]); return l; } static String flexLines(Object... l) { return lines(flattenIterablesAndArrays(ll(l))); } static List countIteratorToList(int b) { return countIteratorToList(0, b); } static List countIteratorToList(int a, int b) { return asList(countIterator(a, b)); } static List countIteratorToList(int b, IF1 f) { return countIteratorToList(0, b, f); } static List countIteratorToList(int a, int b, IF1 f) { return asList(countIterator(a, b, f)); } static List countIteratorToList(int a, int b, int step) { return asList(countIterator(a, b, step)); } static List countIteratorToList(double a, double b, double step, IF1 f) { return asList(countIterator(a, b, step, f)); } static List countIteratorToList(double a, double b, double step) { return asList(countIterator(a, b, step)); } static List countIteratorToList(IF1 f, double a, double b, double step) { return asList(countIterator(f, a, b, step)); } static List countIteratorToList(IF1 f, int b) { return countIteratorToList(f, 0, b); } static List countIteratorToList(IF1 f, int a, int b) { return asList(countIterator(f, a, b)); } // lineNr starts at 1 static String getLine(String s, int lineNr) { return safeGet(toLines(s), lineNr-1); } static Set emptySet() { return new HashSet(); } static Map.Entry firstEntry(Map map) { return empty(map) ? null : first(map.entrySet()); } static long sysNow() { ping(); return System.nanoTime()/1000000; } 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) { pcallFail(__e); }} } static int seconds() { return seconds(java.util.Calendar.getInstance()); } static int seconds(java.util.Calendar c) { return c.get(java.util.Calendar.SECOND); } static int lByteArray(byte[] a) { return a == null ? 0 : a.length; } static byte[] resizeByteArray(byte[] a, int n) { if (n == l(a)) return a; byte[] b = new byte[n]; arraycopy(a, 0, b, 0, min(l(a), n)); return b; } static ArrayList byteArrayToList(byte[] a) { if (a == null) return null; return byteArrayToList(a, 0, a.length); } // no range checking on from/to in the interest of speed static ArrayList byteArrayToList(byte[] a, int from, int to) { if (a == null) return null; ArrayList < Byte > l = new ArrayList<>(to-from); for (int i = from; i < to; i++) l.add(a[i]); return l; } static A nextToLast(List l) { return get(l, l(l)-2); } static short rgbToHi15(RGB rgb) { return rgbIntToHi15(rgb.asInt()); } static short rgbToHi15(Color color) { return rgbIntToHi15(colorToInt(color)); } static short rgbToHi15(int rgb) { return rgbIntToHi15(rgb); } static short rgbToHi15(int r, int g, int b) { r >>= 3; g >>= 3; b >>= 3; return (short) ((r << 10) | (g << 5) | b); } static int hi15ToRGBInt_clean(short hi15) { int r = (hi15 >> 10) & 0x1F; int g = (hi15 >> 5) & 0x1F; int b = hi15 & 0x1F; double factor = 255.0/31; r = iround(r*factor); g = iround(g*factor); b = iround(b*factor); return (r << 16) | (g << 8) | b; } static boolean isPowerOfTwo(int x) { return (x != 0) && ((x & (x - 1)) == 0); } static String rep(int n, char c) { return repeat(c, n); } static String rep(char c, int n) { return repeat(c, n); } static List rep(A a, int n) { return repeat(a, n); } static List rep(int n, A a) { return repeat(n, a); } static String 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 java.awt.Color awtColor(String hex) { byte[] b = bytesFromHex(dropPrefix("#", hex)); return new Color(ubyteToInt(b[0]), ubyteToInt(b[1]), ubyteToInt(b[2])); } static List g22_allBorderTraces_autoDiagonals(IImageRegion region) { return region.createdWithDiagonals() ? g22_allBorderTraces_withDiagonals(region) : g22_allBorderTraces(region); } static Map> allFields_cache = weakHashMap(); static Set allFields(Object o) { if (o == null) return emptySet(); Class _c = _getClass(o); Set fields = allFields_cache.get(_c); if (fields == null) allFields_cache.put(_c, fields = asTreeSet(keys(getOpt_getFieldMap(o)))); return fields; } static 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 A copyFields(Object x, A y, String... fields) { if (empty(fields)) { // assume we should copy all fields Map map = objectToMap(x); for (String field : map.keySet()) setOpt(y, field, map.get(field)); } else for (String field : fields) { Object o = getOpt(x, field); if (o != null) setOpt(y, field, o); } return y; } static A copyFields(Object x, A y, Collection fields) { return copyFields(x, y, asStringArray(fields)); } static Map nuEmptyObject_cache = newDangerousWeakHashMap(); static A nuEmptyObject(Class c) { try { Constructor ctr; synchronized(nuEmptyObject_cache) { ctr = nuEmptyObject_cache.get(c); if (ctr == null) { nuEmptyObject_cache.put(c, ctr = nuEmptyObject_findConstructor(c)); makeAccessible(ctr); } } try { return (A) ctr.newInstance(); } catch (InstantiationException e) { if (empty(e.getMessage())) if ((c.getModifiers() & Modifier.ABSTRACT) != 0) throw fail("Can't instantiate abstract class " + className(c), e); else throw fail("Can't instantiate " + className(c), e); else throw rethrow(e); } } catch (Exception __e) { throw rethrow(__e); } } static Constructor nuEmptyObject_findConstructor(Class c) { for (Constructor m : getDeclaredConstructors_cached(c)) if (m.getParameterTypes().length == 0) return m; throw fail("No default constructor declared in " + c.getName()); } static String standard_toString(Object o) { if (o == null) return "null"; String name = shortClassName(o); Set fields = fieldNames(o); if (empty(fields)) return name; return name + "(" + joinWithComma(mapNonNulls(fields, field -> { Object value = get(o, field); if (value == null) return null; return field + "=" + str(value); })) + ")"; } static Set nonTransientNonStaticFields(Object o) { return allNonStaticNonTransientFields(o); } static VF1 ivf1ToVF1(IVF1 f) { return f == null ? null : new VF1() { public void get(A a) { try { f.get(a) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "f.get(a)"; }}; } static A parentOfType(Component _c, Class theClass) { return swing(() -> { Component c = _c; while (c != null) { if (isInstance(theClass, c)) return (A) c; c = c.getParent(); } return null; }); } static String dropNumberPrefix(String s) { return dropFirst(s, indexOfNonDigit(s)); } static IntRange intersectIntRanges(IntRange a, IntRange b) { int start = max(a.start, b.start); int end = min(a.end, b.end); return start <= end ? new IntRange(start, end) : null; } static List intersectIntRanges(Iterable l, IntRange b) { return map(l, r -> intersectIntRanges(r, b)); } // 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 List lazyMap_noSave(IF1 f, List l) { return new RandomAccessAbstractList() { final int size = l(l); public int size() { return size; } public B get(int i) { return f.get(l.get(i)); } }; } static List lazyMap_noSave(List l, IF1 f) { return lazyMap_noSave(f, l); } static IntRange shiftIntRange(int shift, IntRange r) { return r == null ? null : shift == 0 ? r : intRange(r.start+shift, r.end+shift); } static IntRange shiftIntRange(IntRange r, int shift) { return shiftIntRange(shift, r); } static List cloneAndClearList(Collection l) { List l2 = cloneList(l); l.clear(); return l2; } 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 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 void onUpdate(JComponent c, Runnable r) { onUpdate(c, (Object) r); } static void onUpdate(JTextComponent c, IVF1 r) { if (c == null || r == null) return; c.getDocument().addDocumentListener(runnableToDocumentListener(() -> r.get(c.getText()))); } // legacy signature static void onUpdate(JComponent c, Object r) { if (c instanceof JTextComponent) ((JTextComponent) c).getDocument().addDocumentListener(runnableToDocumentListener(toRunnable(r))); else if (c instanceof ItemSelectable) // JCheckBox and others ((ItemSelectable) c).addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { pcallF(r); } }); else if (c instanceof JSpinner) onChange(((JSpinner) c), r); else print("Warning: onUpdate doesn't know " + getClassName(c)); } static void onUpdate(List l, Object r) { for (JComponent c : l) onUpdate(c, r); } static A getSelectedItem_typed(JList l) { return swing(() -> l.getSelectedValue()); } static A getSelectedItem_typed(JComboBox cb) { return swing(() -> (A) cb.getSelectedItem()); } static boolean isEditableComboBox(final JComboBox cb) { return cb != null && swing(new F0() { public Boolean get() { try { return cb.isEditable(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return cb.isEditable();"; }}); } static JTextField textFieldFromComboBox(JComboBox cb) { return (JTextField) cb.getEditor().getEditorComponent(); } static JComboBox onSelectedItem(final JComboBox cb, final VF1 f) { addActionListener(cb, new Runnable() { public void run() { try { pcallF(f, selectedItem(cb)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(f, selectedItem(cb))"; }}); return cb; } static JComboBox onSelectedItem(final JComboBox cb, IVF1 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 IVF1 runnableToIVF1(Runnable r) { return r == null ? null : a -> r.run(); } 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 Color colorFromRGBA(int rgba) { return new Color(rgba, true); } static String joinNemptiesWithSpace(String... strings) { return joinNempties(" ", strings); } static String joinNemptiesWithSpace(Collection strings) { return joinNempties(" ", strings); } static List flattenCollections(Iterable a) { List l = new ArrayList(); for (Object x : a) if (x instanceof Collection) l.addAll(flattenCollections((Collection) x)); else l.add(x); return l; } static String formatWithThousands(long l) { return formatWithThousandsSeparator(l); } static String plural(String s) { return getPlural(s); } 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 BufferedImage drawableToImage(WidthAndHeight size, G2Drawable drawable) { var img = whiteImage(size); { if (drawable != null) drawable.drawOn(img); } return img; } static List collectInstances(Iterable i, Class c) { List l = new ArrayList(); if (i == null) return l; c = primitiveToBoxedTypeOpt(c); for (Object o : i) if (isInstance(c, o)) l.add(o); return l; } static List collectInstances(Class c, Iterable i) { return collectInstances(i, c); } static void setSCPComponent(SingleComponentPanel scp, Component c) { if (scp != null) scp.setComponent(c); } static A jMinWidth(final int w, final A c) { if (c == null) return null; return swing(new F0() { public A get() { try { Dimension size = c.getMinimumSize(); c.setMinimumSize(new Dimension(/*max(w, size.width) ??? */w, size.height)); return jPreferWidth(w, c); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "Dimension size = c.getMinimumSize();\r\n c.setMinimumSize(new Dimension(/*ma..."; }}); } static Component jMinHeight(Swingable c, int h) { return jMinHeight(wrap(c), h); } static Component jMinHeight(int h, Swingable c) { return jMinHeight(wrap(c), h); } static A jMinHeight(A c, int h) { return jMinHeight(h, c); } static 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 MenuItem menuItem(String text, final Object r) { MenuItem mi = new MenuItem(text); mi.addActionListener(actionListener(r)); return mi; } static MenuItem menuItem(String text, Runnable 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 Rectangle screenRectangle() { return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); } static Rectangle screenRectangle(GraphicsDevice device) { if (device == null) return null; DisplayMode mode = device.getDisplayMode(); return new Rectangle(0, 0, mode.getWidth(), mode.getHeight()); } static 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() { 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 moveToTopRightCorner(A a) { return moveToTopRightCorner(moveToTopRightCorner_inset, moveToTopRightCorner_inset, a); } static 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 disposeWindowAfter(int delay, final A w) { if (w != null) swingLater(delay, new Runnable() { public void run() { try { w.dispose(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "w.dispose();"; }}); return w; } static A disposeWindowAfter(A w, double seconds) { return disposeWindowAfter(toMS_int(seconds), w); } static A disposeWindowAfter(double seconds, A w) { return disposeWindowAfter(w, seconds); } static IMeta toIMeta(Object o) { return initIMeta(o); } static Map makeObjectMetaMap() { //ret synchroLinkedHashMap(); return new CompactHashMap(); } static IAutoCloseableF0 tempMetaMutex(IMeta o) { return o == null ? null : o._tempMetaMutex(); } static void syncMapPut2(Map map, A key, B value) { if (map != null && key != null) synchronized(collectionMutex(map)) { if (value != null) map.put(key, value); else map.remove(key); } } static int lKeys(MultiMap mm) { return mm == null ? 0 : mm.keysSize(); } static int latestInstalledJavaX() { File[] files = new File(userHome(), ".javax").listFiles(); int v = 0; if (files != null) for (File f : files) { Matcher m = regexpMatcher("x(\\d\\d\\d?)\\.jar", f.getName()); if (m.matches()) v = Math.max(v, Integer.parseInt(m.group(1))); } return v; } static String x30JarServerURL() { return "https://botcompany.de:9898/x30.jar"; } // 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; } static int hashMap_internalHash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } static String hfulltag(String tag) { return hfulltag(tag, ""); } static String hfulltag(String tag, Object contents, Object... params) { return hopeningTag(tag, params) + str(contents) + ""; } static String fulltag(String tag) { return hfulltag(tag); } static String fulltag(String tag, Object contents, Object... params) { return hfulltag(tag, contents, params); } static int colorToIntOpaque(Color c) { return c.getRGB() | 0xFF000000; } static Rect pointsRect(int x1, int y1, int x2, int y2) { return new Rect(x1, y1, x2-x1, y2-y1); } // f: func -> A (stream ends when f returns null) static IterableIterator iff_null(final Object f) { return iteratorFromFunction(f); } static IterableIterator iff_null(F0 f) { return iteratorFromFunction(f); } static IterableIterator iff_null(IF0 f) { return iteratorFromFunction(f); } static boolean isSpaceEtc(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } static ThreadLocal assertVerbose_value = new ThreadLocal(); static void assertVerbose(boolean b) { assertVerbose_value.set(b); } static boolean assertVerbose() { return isTrue(assertVerbose_value.get()); } static A assertEqualsVerbose(Object x, A y) { assertEqualsVerbose((String) null, x, y); return y; } // x = expected, y = actual static A assertEqualsVerbose(String msg, Object x, A y) { if (!eq(x, y)) { throw fail((nempty(msg) ? msg + ": " : "") + "expected: "+ x + ", got: " + y); } else print("OK" + (empty(msg) ? "" : " " + msg) + ": " + /*sfu*/(x)); return y; } static void assertEqualsVerbose(Scorer scorer, Object x, Object y) { assertEqualsVerbose(scorer, "", x, y); } static void assertEqualsVerbose(Scorer scorer, String msg, Object x, Object y) { if (scorer == null) { assertEqualsVerbose(x, y); return; } if (!eq(x, y)) { print(appendColonIfNempty(msg) + y + " != " + x); scorer.add(false); } else { print("OK: " + appendColonIfNempty(msg) + x); scorer.add(true); } } static String str_nullIfEmpty(Object o) { return nullIfEmpty(strOrNull(o)); } static Object swingCall(final Object o, final String method, final Object... args) { return swing(new F0() { public Object get() { try { return call(o, method, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return call(o, method, args);"; }}); } static List syncList() { return synchroList(); } static List syncList(List l) { return synchroList(l); } 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 onSelect) { return selectSnippetID_v1("#", onSelect); } static JComponent selectSnippetID_v1(String defaultID, final VF1 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 Transferable rawClipboardContents() { try { return Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); } catch (Exception __e) { throw rethrow(__e); } } 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() { return (String) getTransferData(rawClipboardContents(), DataFlavor.stringFlavor); } 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 printIf(boolean b, A a) { if (b) print(a); return a; } static A printIf(boolean b, String s, A a) { if (b) print(s, a); return a; } // first delay = delay static Timer installTimer(JComponent component, Object r, long delay) { return installTimer(component, r, delay, delay); } // first delay = delay static Timer installTimer(RootPaneContainer frame, long delay, Object r) { return installTimer(frame.getRootPane(), r, delay, delay); } // first delay = delay static Timer installTimer(JComponent component, long delay, Object r) { return installTimer(component, r, delay, delay); } static Timer installTimer(JComponent component, long delay, long firstDelay, Object r) { return installTimer(component, r, delay, firstDelay); } static Timer installTimer(final JComponent component, final Object r, final long delay, final long firstDelay) { return installTimer(component, r, delay, firstDelay, true); } static Timer installTimer(final JComponent component, final Object r, final long delay, final long firstDelay, final boolean repeats) { if (component == null) return null; return (Timer) swingAndWait(new F0() { public Object get() { try { final Var timer = new Var(); timer.set(new Timer(toInt(delay), new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { AutoCloseable __1 = tempActivity(r); try { try { if (!allPaused()) if (isFalse(callF(r))) cancelTimer(timer.get()); } catch (Throwable __e) { pcallFail(__e); } } finally { _close(__1); }} catch (Throwable __e) { messageBox(__e); }}})); timer.get().setInitialDelay(toInt(firstDelay)); timer.get().setRepeats(repeats); bindTimerToComponent(timer.get(), component); return timer.get(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "final new Var timer;\r\n timer.set(new Timer(toInt(delay), actionList..."; }}); } static Timer installTimer(RootPaneContainer frame, long delay, long firstDelay, Object r) { return installTimer(frame.getRootPane(), delay, firstDelay, r); } static 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 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 JScrollPane jscroll_centered_borderless(Component c) { return borderlessScrollPane(jscroll_centered(c)); } static Color hi15ToColor_clean(short hi15) { return intToColorOpaque(hi15ToRGBInt_clean(hi15)); } static PrintWriter filePrintWriter(File f) { return printWriter(bufferedFileOutputStream(f)); } static void readLocally(String progID, String varNames) { readLocally2(mc(), progID, varNames); } static void readLocally(String varNames) { readLocally2(mc(), programID(), varNames); } static void readLocally2(Object obj, String varNames) { readLocally2(obj, programID(), varNames); } static int readLocally_stringLength; static ThreadLocal readLocally2_allDynamic = new ThreadLocal(); static ThreadLocal readLocally2_classFinder = new ThreadLocal(); // read a string variable from standard storage // does not overwrite variable contents if there is no file static void readLocally2(Object obj, String progID, String varNames) { try { boolean allDynamic = isTrue(getAndClearThreadLocal(readLocally2_allDynamic)); for (String variableName : javaTokC(varNames)) { File textFile = new File(programDir(progID), variableName + ".text"); String value = loadTextFile(textFile); if (value != null) set(main.class, variableName, value); else { File structureFile = new File(programDir(progID), variableName + ".structure"); value = loadTextFile(structureFile); if (value == null) { File structureGZFile = new File(programDir(progID), variableName + ".structure.gz"); if (!structureGZFile.isFile()) return; //value = loadGZTextFile(structureGZFile); InputStream fis = new FileInputStream(structureGZFile); try { GZIPInputStream gis = newGZIPInputStream(fis); InputStreamReader reader = new InputStreamReader(gis, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(reader); //O o = unstructure_reader(bufferedReader); Object o = unstructure_tok(javaTokC_noMLS_onReader(bufferedReader), allDynamic, readLocally2_classFinder.get()); readLocally_set(obj, variableName, o); return; } finally { _close(fis); }} readLocally_stringLength = l(value); if (nempty(value)) readLocally_set(obj, variableName, unstructure(value, allDynamic, readLocally2_classFinder.get())); } } } catch (Exception __e) { throw rethrow(__e); } } static void readLocally_set(Object c, String varName, Object value) { Object oldValue = get(c, varName); if (oldValue instanceof List && !(oldValue instanceof ArrayList) && value != null) { // Assume it's a synchroList. value = synchroList((List) value); } set(c, varName, value); } static double positiveInfinity() { return Double.POSITIVE_INFINITY; } static double negativeInfinity() { return Double.NEGATIVE_INFINITY; } static float getScore(Scored s) { return s == null ? 0 : s.score; } static int abs(int i) { return Math.abs(i); } static long abs(long i) { return Math.abs(i); } static float abs(float i) { return Math.abs(i); } static double abs(double i) { return Math.abs(i); } static double abs(Complex c) { return c.abs(); } static long done2_always(long startTime, String desc) { long time = sysNow()-startTime; // BREAKING CHANGE: Now stores result for lastTiming() saveTiming_noPrint(time); print(desc + " [" + time + " ms]"); return time; } static long done2_always(String desc, long startTime) { return done2_always(startTime, desc); } static long done2_always(long startTime) { return done2_always(startTime, ""); } static IF0 runnableToIF0(Runnable r) { return r == null ? null : () -> { r.run(); return null; }; } static DynamicObject dynamicObject(String className, Object... x) { DynamicObject d = new DynamicObject(className); for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) setDyn(d, (String) x[i], x[i+1]); return d; } static B syncGetOrCreate(Map map, A key, Class c) { try { synchronized(map) { return getOrCreate(map, key, c); } } catch (Exception __e) { throw rethrow(__e); } } // f : func -> B static B syncGetOrCreate(Map map, A key, Object f) { synchronized(map) { return getOrCreate(map, key, f); } } static B syncGetOrCreate(Class c, Map map, A key) { return syncGetOrCreate(map, key, c); } static B syncGetOrCreate(Map map, A key, IF0 f) { synchronized(map) { return getOrCreate(map, key, f); } } static List sortedIgnoreCase(Collection c) { List l = cloneList(c); Collections.sort(l, caseInsensitiveComparator()); return l; } static double nanosToMicroseconds(double ns) { return ns/1000; } static byte clampToUByte(long l) { return (byte) clamp(l, 0, 255); } static int zeroToOneToZeroTo255(double brightness) { return iround(brightness*255.0); } // undefined color, seems to be all black in practice static BufferedImage newGrayBufferedImage(int w, int h) { return new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY); } static BufferedImage newGrayBufferedImage(int w, int h, byte[] pixels) { return byteArrayToGrayBufferedImage(pixels, w, h); } static String formatDoubleFull(double d, int digits) { String format = digits <= 0 ? "0" : "0." + rep(digits, '0'); return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)).format(d); } static A nuInstance(Class c) { return nuEmptyObject(c); } static void listSet(List l, int i, A a, A emptyElement) { if (i < 0) return; while (i >= l(l)) l.add(emptyElement); l.set(i, a); } static void listSet(List l, int i, A a) { listSet(l, i, a, null); } static IterableIterator emptyItIt() { return emptyIterableIterator(); } // f: func -> A | endMarker static IterableIterator iff(Object f) { return iteratorFromFunction_withEndMarker(f); } // can't use type parameter because end marker static IterableIterator iff(F0 f) { return iteratorFromFunction_withEndMarker(f); } static IterableIterator iff(IF0 f) { return iteratorFromFunction_withEndMarker(f); } static Object endMarker() { return iteratorFromFunction_endMarker; } static class mapI_if1_It extends IterableIterator { IF1 f; Iterator i; mapI_if1_It() {} mapI_if1_It(IF1 f, Iterator 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 IterableIterator mapI_if1(IF1 f, Iterable i) { return new mapI_if1_It(f, i.iterator()); } static IterableIterator mapI_if1(Iterable i, IF1 f) { return mapI_if1(f, i); } static IterableIterator iteratorFromFunction_f0(final F0 f) { class IFF2 extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } }; return new IFF2(); } static IterableIterator iteratorFromFunction_if0(final IF0 f) { class IFF2 extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } }; return new IFF2(); } // 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 boolean isUByte(int i) { return (i & 0xFF) == i; } static byte[] byteListToArray(List l) { if (l == null) return null; int n = l(l), i = 0; byte[] a = new byte[n]; for (var x : l) a[i++] = x; return a; } static BufferedOutputStream bufferedOutputStream(OutputStream out) { if (out == null) return null; if (out instanceof BufferedOutputStream) return ((BufferedOutputStream) out); return new BufferedOutputStream(out, defaultBufferedOutputStreamSize()); } static BufferedOutputStream bufferedOutputStream(File f) { return bufferedOutputStream(fileOutputStream(f)); } static IterableIterator filterIterator(final Iterator it, final F1 f) { if (it == null) return null; return iff(new F0() { public Object get() { try { while (it.hasNext()) { A a = it.next(); if (callF(f, a)) return a; } return endMarker(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "while (it.hasNext()) {\r\n A a = it.next();\r\n if (callF(f, a))\r\n ..."; }}); } static IterableIterator filterIterator(final Iterator it, final IF1 f) { if (it == null) return null; return iff(new F0() { public Object get() { try { while (it.hasNext()) { A a = it.next(); if (callF(f, a)) return a; } return endMarker(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "while (it.hasNext()) {\r\n A a = it.next();\r\n if (callF(f, a))\r\n ..."; }}); } static IterableIterator filterIterator(final F1 f, final Iterator it) { return filterIterator(it, f); } static IterableIterator filterIterator(Collection l, IF1 f) { if (l == null) return null; Iterator it = iterator(l); return iff(new F0() { public Object get() { try { while (it.hasNext()) { A a = it.next(); if (f.get(a)) return a; } return endMarker(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "while (it.hasNext()) {\r\n A a = it.next();\r\n if (f.get(a))\r\n ..."; }}); } static IterableIterator filterIterator(IF1 f, Iterator it) { return filterIterator(it, f); } static TimerTask timerTask(final Object r, final java.util.Timer timer) { return new TimerTask() { public void run() { if (!licensed()) timer.cancel(); else pcallF(r); } }; } static A vmBus_timerStarted(A timer) { vmBus_send("timerStarted", timer, costCenter()); return timer; } static long clockToSysTimeDiff() { return sysNow()-now(); } 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 void close_pcall(AutoCloseable c) { if (c != null) { try { c.close(); } catch (Throwable __e) { pcallFail(__e); }} } static List valuesList(Map map) { return cloneListSynchronizingOn(values(map), map); } static List valuesList(MultiMap mm) { return mm == null ? emptyList() : concatLists(values(mm.data)); } static void syncClear(Collection c) { if (c != null) synchronized(collectionMutex(c)) { c.clear(); } } static void syncClear(Map map) { if (map != null) synchronized(collectionMutex(map)) { map.clear(); } } static 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); } 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) { pcallFail(__e); } return null; } static List registeredThreads(Object o) { Map map = (Map) (getOpt(o, "_registerThread_threads")); if (map == null) return ll(); map.size(); // force clean-up synchronized(map) { return asList(keys(map)); } } static List registeredThreads() { _registerThread_threads.size(); // force clean-up return asList(keys(_registerThread_threads)); } static void interruptThreads(Collection threads) { for (Thread t : unnull(threads)) interruptThread(t); } static void interruptThreads(Class mainClass) { interruptThreads(registeredThreads(mainClass)); } static void retireClassLoader(ClassLoader cl) { try { if (isJavaXClassLoader(cl)) setOptAll(cl, "retired" , true, "retiredMarker" , new DefunctClassLoader()); } catch (Throwable __e) { pcallFail(__e); } } static A[] arrayOfType(Class type, int n) { return makeArray(type, n); } static A[] arrayOfType(int n, Class type) { return arrayOfType(type, n); } static List wrapArrayAsList(A[] a) { return a == null ? null : Arrays.asList(a); } // action = runnable or method name (old) static void onUpdateAndNow(JComponent c, final Object r) { onUpdate(c, r); callF(r); } static void onUpdateAndNow(JTextComponent c, IVF1 r) { swing(() -> { if (c == null) return; onUpdate(c, r); callF(r, c.getText()); }); } static void onUpdateAndNow(List l, Object r) { for (JComponent c : l) onUpdate(c, r); callF(r); } static JComponent unwrapScrollPane(JScrollPane sp) { return sp == null ? null : (JComponent) sp.getViewport().getView(); } 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 BigInteger bigint(String s) { return new BigInteger(s); } static BigInteger bigint(long l) { return BigInteger.valueOf(l); } static Set asSet(Object[] array) { HashSet set = new HashSet(); for (Object o : array) if (o != null) set.add(o); return set; } static Set asSet(String[] array) { TreeSet set = new TreeSet(); for (String o : array) if (o != null) set.add(o); return set; } static Set asSet(Iterable l) { if (l instanceof Set) return (Set) l; HashSet set = new HashSet(); for (A o : unnull(l)) if (o != null) set.add(o); return set; } 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 Map singular_specials = litmap( "children", "child", "images", "image", "chess", "chess"); static Set singular_specials2 = litciset("time", "machine", "line", "rule"); static String singular(String s) { if (s == null) return null; { String __1 = singular_specials.get(s); if (!empty(__1)) return __1; } //try answer hippoSingulars().get(lower(s)); if (singular_specials2.contains(dropSuffix("s", afterLastSpace(s)))) return dropSuffix("s", s); if (s.endsWith("ness")) return s; if (s.endsWith("ges")) return dropSuffix("s", s); if (endsWith(s, "bases")) return dropLast(s); s = dropSuffix("es", s); s = dropSuffix("s", s); return s; } static Set 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 long secondsToMS(double seconds) { return toMS(seconds); } static DoubleRange intersectDoubleRanges(DoubleRange a, DoubleRange b) { double start = max(a.start, b.start); double end = min(a.end, b.end); return start <= end ? new DoubleRange(start, end) : null; } static int roundToInt(double d) { return (int) Math.round(d); } static Pt addPts(Pt a, Pt b) { return a == null ? b : b == null ? a : new Pt(a.x+b.x, a.y+b.y); } static String colonCombine(Object... l) { return joinNemptiesWithColon(flattenCollectionsAndArrays(ll(l))); } static List flattenIterablesAndArrays(Iterable a) { List l = new ArrayList(); for (Object x : a) if (x instanceof Iterable) l.addAll(flattenIterablesAndArrays((Iterable) x)); else if (x instanceof Iterator) l.addAll(flattenIterablesAndArrays(asList((Iterator) x))); else if (x instanceof Object[]) l.addAll(flattenIterablesAndArrays(asList((Object[]) x))); else l.add(x); return l; } static IterableIterator countIterator(int b) { return countIterator(0, b); } static IterableIterator countIterator(int a, int b) { return countIterator_exclusive(a, b); } static IterableIterator countIterator(int b, IF1 f) { return countIterator(0, b, f); } static IterableIterator countIterator(int a, int b, IF1 f) { return countIterator_exclusive(a, b, f); } static IterableIterator countIterator(int a, int b, int step) { return countIterator_exclusive_step(a, b, step); } static IterableIterator countIterator(double a, double b, double step, IF1 f) { return countIterator_exclusive_step(a, b, step, f); } static IterableIterator countIterator(double a, double b, double step) { return countIterator_exclusive_step(a, b, step); } static IterableIterator countIterator(IF1 f, double a, double b, double step) { return countIterator(a, b, step, f); } static IterableIterator countIterator(IF1 f, int b) { return countIterator(f, 0, b); } static IterableIterator countIterator(IF1 f, int a, int b) { return countIterator_exclusive(a, b, f); } static IterableIterator countIterator(List l) { return countIterator(l(l)); } static A safeGet(List l, int i) { return i >= 0 && i < l(l) ? l.get(i) : null; } // requires ugly casting when used (O -> A) static Object iteratorFromFunction_endMarker = new Object(); // f: func -> A | endMarker static IterableIterator iteratorFromFunction_withEndMarker(final Object f) { class IFF extends IterableIterator { A a; boolean have, done; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; have = false; return _a; } void getNext() { if (done || have) return; Object o = callF(f); if (o == iteratorFromFunction_endMarker) { done = true; return; } a = (A) o; have = true; } }; return new IFF(); } // optimized version for F0 argument; TODO: do same for IF0 static IterableIterator iteratorFromFunction_withEndMarker(final F0 f) { return iteratorFromFunction_withEndMarker_f0(f); } static int colorToInt(Color c) { return c.getRGB() & 0xFFFFFF; } static String repeat(char c, int n) { n = Math.max(n, 0); char[] chars = new char[n]; for (int i = 0; i < n; i++) chars[i] = c; return new String(chars); } static List repeat(A a, int n) { n = Math.max(n, 0); List l = new ArrayList(n); for (int i = 0; i < n; i++) l.add(a); return l; } static List repeat(int n, A a) { return repeat(a, n); } static List g22_allBorderTraces_withDiagonals(IImageRegion region) { RegionBorder_innerPoints_withDiagonals walker = new RegionBorder_innerPoints_withDiagonals(region); List out = new ArrayList(); walker.onNewTrace(hole -> out.add(new PtBuffer())); walker.onFoundPoint(p -> last(out).add(p)); walker.run(); return out; } static List g22_allBorderTraces(IImageRegion region) { RegionBorder_innerPoints_v2 walker = new RegionBorder_innerPoints_v2(region); List out = new ArrayList(); walker.onNewTrace(hole -> out.add(new PtBuffer())); walker.onFoundPoint(p -> last(out).add(p)); walker.run(); return out; } static TreeSet asTreeSet(Collection set) { return set == null ? null : set instanceof TreeSet ? (TreeSet) set : new TreeSet(set); } static int hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static int hashCode(long l) { return Long.hashCode(l); } static int hashCode(double d) { return Double.hashCode(d); } // o is either a map already (string->object) or an arbitrary object, // in which case its fields are converted into a map. static Map objectToMap(Object o) { try { if (o instanceof Map) return (Map) o; if (o == null) return null; TreeMap map = new TreeMap(); Class c = o.getClass(); while (c != Object.class) { Field[] fields = c.getDeclaredFields(); for (final Field field : fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) continue; field.setAccessible(true); final Object value = field.get(o); if (value != null) map.put(field.getName(), value); } c = c.getSuperclass(); } // XXX NEW - hopefully this doesn't break anything if (o instanceof DynamicObject) putAll(map, ((DynamicObject) o).fieldValues); return map; } catch (Exception __e) { throw rethrow(__e); } } // same for a collection (convert each element) static List> objectToMap(Iterable l) { if (l == null) return null; List x = new ArrayList(); for (Object o : l) x.add(objectToMap(o)); return x; } static String[] asStringArray(Collection c) { return toStringArray(c); } static String[] asStringArray(Object o) { return toStringArray(o); } static Set fieldNames(Object o) { return allFields(o); } static Set allNonStaticNonTransientFields(Object o) { TreeSet fields = new TreeSet(); Class _c = _getClass(o); do { for (Field f : _c.getDeclaredFields()) if ((f.getModifiers() & (Modifier.STATIC|Modifier.TRANSIENT)) == 0) fields.add(f.getName()); _c = _c.getSuperclass(); } while (_c != null); return fields; } static String[] dropFirst(int n, String[] a) { return drop(n, a); } static String[] dropFirst(String[] a) { return drop(1, a); } static Object[] dropFirst(Object[] a) { return drop(1, a); } static List dropFirst(List l) { return dropFirst(1, l); } static List dropFirst(int n, Iterable i) { return dropFirst(n, toList(i)); } static List dropFirst(Iterable i) { return dropFirst(toList(i)); } static List dropFirst(int n, List l) { return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size())); } static List dropFirst(List l, int n) { return dropFirst(n, l); } static String dropFirst(int n, String s) { return substring(s, n); } static String dropFirst(String s, int n) { return substring(s, n); } static String dropFirst(String s) { return substring(s, 1); } static Chain dropFirst(Chain c) { return c == null ? null : c.next; } 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; } // Use like this: renderVars(+x, +y) // Or like this: renderVars("bla", +x, +y) static String renderVars_str(Object... params) { List l = new ArrayList(); int i = 0; if (odd(l(params))) { l.add(strOrNull(first(params))); ++i; } for (; i+1 < l(params); i += 2) l.add(params[i] + "=" + params[i+1]); return trim(joinWithComma(l)); } static DocumentListener runnableToDocumentListener(Runnable r) { return new DocumentListener() { public void insertUpdate(DocumentEvent e) { pcallF(r); } public void removeUpdate(DocumentEvent e) { pcallF(r); } public void changedUpdate(DocumentEvent e) { pcallF(r); } }; } static String selectedItem(JList l) { return getSelectedItem(l); } static String selectedItem(JComboBox cb) { return getSelectedItem(cb); } static String formatWithThousandsSeparator(long l) { return NumberFormat.getInstance(new Locale("en_US")).format(l); } static Class primitiveToBoxedTypeOpt(Class type) { return or(primitiveToBoxedType(type), type); } static A jPreferHeight(int h, A c) { Dimension size = c.getPreferredSize(); c.setPreferredSize(new Dimension(size.width, max(h, size.height))); return c; } static JMenuItem directJMenuItem(Action a) { return new JMenuItem(a) { public Dimension getMaximumSize() { return new Dimension(super.getPreferredSize().width, super.getMaximumSize().height); } }; } static JMenuItem directJMenuItem(String text, Object action) { return directJMenuItem(abstractAction(text, action)); } static JMenuBar addMenuBar(final Component c) { return swing(new F0() { public JMenuBar get() { try { RootPaneContainer f = getPossiblyInternalFrame(c); if (f == null) return null; JMenuBar bar = (JMenuBar) (call(f, "getJMenuBar")); if (bar == null) { setMenuBar(f, bar = new JMenuBar()); revalidate((Component) f); } return bar; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "RootPaneContainer f = getPossiblyInternalFrame(c);\r\n if (f == null) null;\r..."; }}); } static void logQuotedWithTime(String s) { logQuotedWithTime(standardLogFile(), s); } static void logQuotedWithTime(File logFile, String s) { logQuoted(logFile, logQuotedWithTime_format(s)); } static void logQuotedWithTime(String logFile, String s) { logQuoted(logFile, logQuotedWithTime_format(s)); } static String logQuotedWithTime_format(String s) { return /*formatGMTWithDate_24*/(now()) + " " + s; } static JTextArea wrappedTextArea(final JTextArea ta) { enableWordWrapForTextArea(ta); return ta; } static JTextArea wrappedTextArea() { return wrappedTextArea(jtextarea()); } static JTextArea wrappedTextArea(String text) { JTextArea ta = wrappedTextArea(); setText(ta, text); return ta; } static A onClick(A c, IVF1 runnable) { return onClick(c, (Object) runnable); } static A onClick(A c, 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, Object runnable) { onEnter(btn, runnable); } static Font typeWriterFont() { return typeWriterFont(iround(14*getSwingFontScale())); } static Font typeWriterFont(int size) { return new Font(localCourierFontName(), Font.PLAIN, size); } static A typeWriterFont(A c) { return withTypeWriterFont(c); } 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 String hopeningTag(String tag, Map params) { return hopeningTag(tag, mapToParams(params)); } static String hopeningTag(String tag, Object... params) { StringBuilder buf = new StringBuilder(); buf.append("<" + tag); params = unrollParams(params); for (int i = 0; i < l(params); i += 2) { String name = (String) get(params, i); Object val = get(params, i+1); if (nempty(name) && val != null) { if (eqOneOf(val, html_valueLessParam(), true)) buf.append(" " + name); else { String s = str(val); if (!empty(s)) buf.append(" " + name + "=" + htmlQuote(s)); } } } buf.append(">"); return str(buf); } static String appendColonIfNempty(String s) { return empty(s) ? "" : s + ": "; } static String autoFrameTitle_value; static String autoFrameTitle() { return autoFrameTitle_value != null ? autoFrameTitle_value : getProgramTitle(); } static void autoFrameTitle(Component c) { setFrameTitle(getFrame(c), autoFrameTitle()); } 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 JTextField jtextfield() { return jTextField(); } static JTextField jtextfield(String text) { return jTextField(text); } static JTextField jtextfield(Object o) { return jTextField(o); } static String programName() { return getProgramName(); } static JPanel centerAndEast(final Component c, final Component e) { return swing(new F0() { public JPanel get() { try { JPanel panel = new JPanel(new BorderLayout()); panel.add(BorderLayout.CENTER, wrap(c)); panel.add(BorderLayout.EAST, wrap(e)); return panel; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JPanel panel = new JPanel(new BorderLayout);\r\n panel.add(BorderLayout.CENT..."; }}); } static 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 A renameSubmitButton(A form, String newName) { renameButton(form, showFormSubmitButtonName(), newName); return form; } static A renameSubmitButton(String newName, A form) { return renameSubmitButton(form, newName); } static boolean ewicOneOf(String s, String... l) { if (s != null) for (String x : l) if (ewic(s, x)) return true; return false; } static String fileName(File f) { return f == null ? null : f.getName(); } static BufferedImage reconstructBufferedImage(BufferedImage img) { if (img == null) return null; RGBImage rgb = new RGBImage(img); rgb.uncacheBufferedImage(); return rgb.getBufferedImage(); } static List 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) { pcallFail(__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 moveCaretToEnd(A ta) { setCaretPosition(ta, textAreaTextLength(ta)); return ta; } static JComponent showTitledForm(String title, Object... _parts) { return showFormTitled(title, _parts); } 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 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 AutoCloseable tempActivity(Object r) { return null; } 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 JPanel jFullCenter(final Component c) { return swing(new F0() { 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 JScrollPane borderlessScrollPane(JScrollPane sp) { return setBorder(null, withoutViewportBorder(sp)); } static Color intToColorOpaque(int rgb) { return new Color(rgb); } static PrintWriter printWriter(OutputStream out) { return newPrintWriter(out); } static PrintWriter printWriter(Writer out) { return newPrintWriter(out); } static List javaTokC(String s) { if (s == null) return null; int l = s.length(); ArrayList tok = new ArrayList(); int i = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener || s.charAt(j) == '\n') { // end at \n to not propagate unclosed string literal errors ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0)); // for stuff like "don't" else if (Character.isDigit(c)) { do ++j; while (j < l && Character.isDigit(s.charAt(j))); if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L } else if (c == '[' && d == '[') { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]")); j = Math.min(j+3, l); } else ++j; tok.add(javaTok_substringC(s, i, j)); i = j; } return tok; } // TODO: cyclic structures involving certain lists & sets static Object unstructure(String text) { return unstructure(text, false); } static Object unstructure(String text, boolean allDynamic) { return unstructure(text, allDynamic, null); } static Object unstructure(String text, IF1 classFinder) { return unstructure(text, false, classFinder); } static int structure_internStringsLongerThan = 50; static int unstructure_unquoteBufSize = 100; static int unstructure_tokrefs; // stats abstract static class unstructure_Receiver { abstract void set(Object o); } // classFinder: func(name) -> class (optional) static Object unstructure(String text, boolean allDynamic, Object classFinder) { if (text == null) return null; return unstructure_tok(javaTokC_noMLS_iterator(text), allDynamic, classFinder); } static Object unstructure_reader(BufferedReader reader) { return unstructure_tok(javaTokC_noMLS_onReader(reader), false, null); } interface unstructure_Handler { void parse(int refID, int tokIndex, unstructure_Receiver out); } static class Unstructurer { final public Unstructurer setTok(Producer tok){ return tok(tok); } public Unstructurer tok(Producer tok) { this.tok = tok; return this; } final public Producer getTok(){ return tok(); } public Producer tok() { return tok; } Producer tok; final public Unstructurer setAllDynamic(boolean allDynamic){ return allDynamic(allDynamic); } public Unstructurer allDynamic(boolean allDynamic) { this.allDynamic = allDynamic; return this; } final public boolean getAllDynamic(){ return allDynamic(); } public boolean allDynamic() { return allDynamic; } boolean allDynamic = false; int i = -1; Object classFinder; String mcDollar = actualMCDollar(); Unstructurer classFinder(Object _classFinder) { classFinder = _classFinder != null ? _classFinder : _defaultClassFinder(); return this; } // use Eclipse primitive collection if possible (smaller & hopefully faster?) HashMap refs = new HashMap(); HashMap tokrefs = new HashMap(); HashSet concepts = new HashSet(); List stack = new ArrayList(); Map baseClassMap = new HashMap(); HashMap innerClassConstructors = new HashMap(); String curT; char[] unquoteBuf = new char[unstructure_unquoteBufSize]; // value is a class or a Handler final HashMap handlers = new HashMap(); Unstructurer() { try { Class mc = (Class) (callF(classFinder, "
")); if (mc != null) mcDollar = mc.getName() + "$"; } catch (Throwable __e) { pcallFail(__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); handlers.put("shortarray", (unstructure_Handler) (refID, tokIndex, out) -> { consume(); String hex = trivialUnquote(tpp()); out.set(shortArrayFromBytes(hexToBytes(hex))); }); handlers.put("longarray", (unstructure_Handler) (refID, tokIndex, out) -> { consume(); String hex = trivialUnquote(tpp()); out.set(longArrayFromBytes(hexToBytes(hex))); }); } // 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 = trivialUnquote(tpp()); out.set(hexToBytes(hex)); return; } if (t.equals("boolarray")) { consume(); int n = parseInt(tpp()); String hex = trivialUnquote(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(), ".")) { className = t; do { consume(); className += "." + assertIdentifier(tpp()); } while (eq(t(), ".")); fullClassName = className; } 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 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; try { 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; } } catch (Throwable __e) { pcallFail(__e); } // end of pcall // Creating instance failed? Use DynamicObject if (o == null && dO == null) dO = new DynamicObject(); // Save in references list early because contents of object // might link back to main object if (refID >= 0) refs.put(refID, o != null ? o : dO); tokrefs.put(tokIndex, o != null ? o : dO); // NOW parse the fields! HashMap fields = new HashMap(); // no longer preserving order (why did we do this?) Object _o = o; DynamicObject _dO = dO; if (hasBracket) { stack.add(new Runnable() { public void run() { try { if (eq(t(), ",")) consume(); if (eq(t(), ")")) { consume(")"); objRead(_o, _dO, fields, hasOuter); out.set(_o != null ? _o : _dO); } else { final String key = unquote(tpp()); String t = tpp(); if (!eq(t, "=")) throw fail("= expected, got " + t + " after " + quote(key) + " in object " + thingName /*+ " " + sfu(fields)*/); stack.add(this); parse(new unstructure_Receiver() { void set(Object value) { fields.put(key, value); /*ifdef unstructure_debug print("Got field value " + value + ", next token: " + t()); endifdef*/ //if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ifdef unstructure_debug\r\n print(\"in object values, token: \" + t());\r..."; }}); } else { objRead(o, dO, fields, hasOuter); out.set(o != null ? o : dO); } } void objRead(Object o, DynamicObject dO, Map fields, boolean hasOuter) { // translate between diferent compilers (this$0 vs this$1) Object outer = fields.get("this$0"); if (outer != null) fields.put("this$1", outer); else { outer = fields.get("this$1"); if (outer != null) fields.put("this$0", outer); } if (o != null) { if (dO != null) { setOptAllDyn_pcall(dO, fields); } else { setOptAll_pcall(o, fields); } if (hasOuter) fixOuterRefs(o); } else for (Map.Entry e : fields.entrySet()) setDynObjectValue(dO, intern(e.getKey()), e.getValue()); if (o != null) pcallOpt_noArgs(o, "_doneLoading"); } void parseSet(final Set set, final unstructure_Receiver out) { this.parseList(new ArrayList(), new unstructure_Receiver() { void set(Object o) { set.addAll((List) o); out.set(set); } }); } void parseLisp(final unstructure_Receiver out) { throw fail("class Lisp not included"); } void parseBitSet(final unstructure_Receiver out) { consume("bitset"); consume("{"); final BitSet bs = new BitSet(); stack.add(new Runnable() { public void run() { try { if (eq(t(), "}")) { consume("}"); out.set(bs); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { bs.set((Integer) o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"}\")) {\r\n consume(\"}\");\r\n out.set(bs);\r\n } els..."; }}); } 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\n ..."; }}); } 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); dynamicObject_setRawFieldValue(dO, "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(); } } static Object unstructure_tok(Producer tok, boolean allDynamic, Object classFinder) { boolean debug = unstructure_debug; AutoCloseable __1 = tempSetTL(dynamicObjectIsLoading_threadLocal(), true); try { Var v = new Var(); var unstructurer = new Unstructurer() .tok(tok) .allDynamic(allDynamic) .classFinder(classFinder); unstructurer.parse_initial(new unstructure_Receiver() { void set(Object o) { v.set(o); } }); unstructure_tokrefs = unstructurer.tokrefs.size(); return v.get(); } finally { _close(__1); }} static boolean unstructure_debug = false; static Producer javaTokC_noMLS_onReader(final BufferedReader r) { final class X implements Producer { StringBuilder buf = new StringBuilder(); // stores from "i" char c, d, e = 'x'; // just not '\0' X() { // fill c, d and e nc(); nc(); nc(); } // get next character(s) into c, d and e void nc() { try { c = d; d = e; if (e == '\0') return; int i = r.read(); e = i < 0 ? '\0' : i == '\0' ? '_' // shouldn't happen anymore : (char) i; } catch (Exception __e) { throw rethrow(__e); } } void ncSave() { if (c != '\0') { buf.append(c); nc(); } } public String next() { // scan for whitespace while (c != '\0') { if (c == ' ' || c == '\t' || c == '\r' || c == '\n') nc(); else if (c == '/' && d == '*') { do nc(); while (c != '\0' && !(c == '*' && d == '/')); nc(); nc(); } else if (c == '/' && d == '/') { do nc(); while (c != '\0' && "\r\n".indexOf(c) < 0); } else break; } if (c == '\0') return null; // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ncSave(); while (c != '\0') { if (c == opener || c == '\n') { // end at \n to not propagate unclosed string literal errors ncSave(); break; } else if (c == '\\') { ncSave(); ncSave(); } else ncSave(); } } else if (Character.isJavaIdentifierStart(c)) do ncSave(); while (Character.isJavaIdentifierPart(c) || c == '\''); // for stuff like "don't" else if (Character.isDigit(c)) { do ncSave(); while (Character.isDigit(c)); if (c == 'L') ncSave(); // Long constants like 1L } else ncSave(); String t = buf.toString(); buf.setLength(0); return t; } } return new X(); } // allows null keys but not null values static B getOrCreate(Map map, A key, Class c) { try { B b = map.get(key); if (b == null) map.put(key, b = c.newInstance()); return b; } catch (Exception __e) { throw rethrow(__e); } } // f : func -> B static B getOrCreate(Map map, A key, Object f) { try { B b = map.get(key); if (b == null) map.put(key, b = (B) callF(f)); return b; } catch (Exception __e) { throw rethrow(__e); } } static B getOrCreate(IF0 f, Map map, A key) { return getOrCreate(map, key, f); } static B getOrCreate(Map map, A key, IF0 f) { B b = map.get(key); if (b == null) map.put(key, b = f.get()); return b; } static B getOrCreate(Class c, Map map, A key) { return getOrCreate(map, key, c); } static Comparator caseInsensitiveComparator() { return betterCIComparator(); } static BufferedImage byteArrayToGrayBufferedImage(byte[] pixels, int w, int h) { PixelInterleavedSampleModel sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, w, h, 1, // pixelStride w, // scanlineStride, new int[] {0} // bandOffsets ); DataBufferByte db = new DataBufferByte(pixels, pixels.length); WritableRaster wr = Raster.createWritableRaster(sm, db, new Point()); return new BufferedImage(grayColorModel(), wr, false, null); } static IterableIterator emptyIterableIterator_instance = new IterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static IterableIterator emptyIterableIterator() { return emptyIterableIterator_instance; } // This seems like a sweet spot. Small values like 1024 slow things down // noticeably, big values like 1 MB do too (probably due to GC). static int defaultBufferedOutputStreamSize() { return 65536; } static FileOutputStream fileOutputStream(File path) { try { return newFileOutputStream(path); } catch (Exception __e) { throw rethrow(__e); } } static FileOutputStream fileOutputStream(String path) { try { return newFileOutputStream(path); } catch (Exception __e) { throw rethrow(__e); } } static FileOutputStream fileOutputStream(File path, boolean append) { try { return newFileOutputStream(path, append); } catch (Exception __e) { throw rethrow(__e); } } static FileOutputStream fileOutputStream(String path, boolean append) { try { return newFileOutputStream(path, append); } catch (Exception __e) { throw rethrow(__e); } } static Object costCenter() { return mc(); } static void consoleClearInput() { consoleSetInput(""); } static List sorted(Collection c, Object comparator) { List l = cloneList(c); sort(l, makeComparator(comparator)); return l; } static List sorted(Collection c) { List l = cloneList(c); sort(l); return l; } static List sorted(Comparator comparator, Collection c) { List l = cloneList(c); sort(l, comparator); return l; } static List methodsStartingWith(Object o, final String prefix) { return filter(allMethodNames(o), new F1() { public Object get(String s) { try { return startsWith(s, prefix); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "startsWith(s, prefix)"; }}); } static volatile PersistableThrowable _handleException_lastException; static List _handleException_onException = synchroList(ll((IVF1) (__1 -> printStackTrace2(__1)))); static boolean _handleException_showThreadCancellations = false; static void _handleException(Throwable e) { _handleException_lastException = persistableThrowable(e); Throwable e2 = innerException(e); if (e2.getClass() == RuntimeException.class && eq(e2.getMessage(), "Thread cancelled.") || e2 instanceof InterruptedException) { if (_handleException_showThreadCancellations) System.out.println(getStackTrace_noRecord(e2)); return; } for (Object f : cloneList(_handleException_onException)) try { callF(f, e); } catch (Throwable e3) { try { printStackTrace2(e3); // not using pcall here - it could lead to endless loops } catch (Throwable e4) { System.out.println(getStackTrace(e3)); System.out.println(getStackTrace(e4)); } } } static 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) { pcallFail(__e); }} } static boolean isJavaXClassLoader(ClassLoader cl) { return startsWithOneOf(className(cl), "main$JavaXClassLoader", "x30$JavaXClassLoader"); } static void setOptAll(Object o, Map fields) { if (fields == null) return; for (String field : keys(fields)) setOpt/*_flex*/(o, field, fields.get(field)); } static void setOptAll(Object o, Object... values) { //values = expandParams(c.getClass(), values); warnIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; setOpt(o, field, value); } } static A[] makeArray(Class type, int n) { return (A[]) Array.newInstance(type, n); } static SimpleDateFormat simpleDateFormat_local(String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(localTimeZone()); return sdf; } static TreeSet litciset(String... items) { TreeSet set = caseInsensitiveSet(); for (String a : items) set.add(a); return set; } static TreeSet litciset(Symbol... items) { TreeSet set = treeSet(); // HashSet would also do, but we might have the return type fixed somewhere, and they might want a NavigableMap. for (Symbol a : items) set.add(a); return set; } static String afterLastSpace(String s) { return s == null ? null : substring(s, s.lastIndexOf(' ')+1); } static String dropSuffixIgnoreCase(String suffix, String s) { return ewic(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static IterableIterator countIterator_exclusive(int b) { return countIterator_exclusive(0, b); } static IterableIterator countIterator_exclusive(int a, int b) { return new IterableIterator() { int i = a; public boolean hasNext() { return i < b; } public Integer next() { return i++; } }; } static IterableIterator countIterator_exclusive(int b, IF1 f) { return countIterator_exclusive(0, b, f); } static IterableIterator countIterator_exclusive(int a, int b, IF1 f) { return mapI_if1(f, countIterator_exclusive(a, b)); } static IterableIterator countIterator_exclusive_step(final int a, final int b, final int step) { assertTrue("step > 0", step > 0); return new IterableIterator() { int i = a; public boolean hasNext() { return i < b; } public Integer next() { var j = i; i += step; return j; } }; } static IterableIterator countIterator_exclusive_step(double a, double b, double step) { assertTrue("step > 0", step > 0); return new IterableIterator() { double i = a; public boolean hasNext() { return i < b; } public Double next() { var j = i; i += step; return j; } }; } static IterableIterator countIterator_exclusive_step(double a, double b, double step, IF1 f) { return mapI_if1(f, countIterator_exclusive_step(a, b, step)); } static IterableIterator iteratorFromFunction_withEndMarker_f0(final F0 f) { class IFF2 extends IterableIterator { A a; boolean have, done; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; have = false; return _a; } void getNext() { if (done || have) return; Object o = f.get(); if (o == iteratorFromFunction_endMarker) { done = true; return; } a = (A) o; have = true; } }; return new IFF2(); } static String[] 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 List drop(Iterable c, Object pred) { return antiFilter(c, pred); } static List drop(Object pred, Iterable c) { return antiFilter(pred, c); } static List drop(Object pred, Object[] c) { return antiFilter(pred, c); } static List drop(Iterable c, F1 pred) { return antiFilter(c, pred); } static List drop(F1 pred, Iterable c) { return antiFilter(pred, c); } static List drop(Iterable c, IF1 pred) { return antiFilter(c, pred); } static List drop(IF1 pred, Iterable c) { return antiFilter(pred, c); } static boolean isDigit(char c) { return Character.isDigit(c); } 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 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 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 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 boolean setText_opt = true; // optimize by calling getText first static A setText(A c, Object text) { setText((JComponent) c, text); return c; } static A setText(final A c, Object text) { // only for editable combo boxes at this point final String s = strUnnull(text); { swing(() -> { c.getEditor().setItem(s); }); } return c; } static void setText(JLabel c, Object text) { setText((JComponent) c, text); } static JButton setText(JButton c, Object text) { setText((JComponent) c, jlabel_textAsHTML_center_ifNeeded(strUnnull(text))); return c; } static A setText(final A c, Object text) { if (c == null) return null; final String s = strUnnull(text); { swing(() -> { if (!setText_opt || neq(callOpt(c, "getText"), s)) call(c, "setText", s); }); } return c; } static float getSwingFontScale() { return or((Float) vm_generalMap_get("swingFontScale_value"), 1f); } static String localCourierFontName() { // determined this by trial & error return isLinux() ? "Courier" : "Courier New"; } static A withTypeWriterFont(A c) { return setFont(c, typeWriterFont()); } 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 Object[] mapToParams(Map map) { return mapToObjectArray(map); } static Object[] unrollParams(Object[] params) { if (l(params) == 1 && params[0] instanceof Map) return mapToParams((Map) params[0]); return params; } static Object html_valueLessParam_cache; static Object html_valueLessParam() { if (html_valueLessParam_cache == null) html_valueLessParam_cache = html_valueLessParam_load(); return html_valueLessParam_cache;} static Object html_valueLessParam_load() { return new Object(); } static String htmlQuote(String s) { return "\"" + htmlencode_forParams(s) + "\""; } static String getProgramTitle() { return getProgramName(); } static JTextField jTextField() { return jTextField(""); } static JTextField jTextField(final String text) { return swing(new F0() { public JTextField get() { try { JTextField tf = new JTextField(unnull(text)); standardTextFieldPopupMenu(tf); jenableUndoRedo(tf); tf.selectAll(); return tf; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "JTextField tf = new JTextField(unnull(text));\r\n standardTextFieldPopupMenu..."; }}); } static JTextField jTextField(Object o) { return jTextField(strOrEmpty(o)); } static String getText(final AbstractButton c) { return c == null ? "" : (String) swingAndWait(new F0() { public Object get() { try { return c.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getText();"; }}); } static String getText(final JTextComponent c) { return c == null ? "" : (String) swingAndWait(new F0() { public Object get() { try { return c.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return c.getText();"; }}); } static String getText(final JLabel l) { return l == null ? "" : (String) swingAndWait(new F0() { public Object get() { try { return l.getText(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "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 strOrNull(cb.getSelectedItem()); } 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 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 List collect(Iterable c, String field) { return collectField(c, field); } static List collect(String field, Iterable c) { return collectField(c, field); } /*ifclass Concept static L collect(Class c, S field) { ret collect(list(c), field); } endif TODO: make translator ignore stuff in ifclass until resolved */ static void sleepInCleanUp(long ms) { try { if (ms < 0) return; Thread.sleep(ms); } catch (Exception __e) { throw rethrow(__e); } } static 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) { pcallFail(__e); } }); } } static int textAreaTextLength(JTextComponent ta) { return l(getText(ta)); } static byte[] base64decode(String s) { byte[] alphaToInt = base64decode_base64toint; int sLen = s.length(); int numGroups = sLen/4; if (4*numGroups != sLen) throw new IllegalArgumentException( "String length must be a multiple of four."); int missingBytesInLastGroup = 0; int numFullGroups = numGroups; if (sLen != 0) { if (s.charAt(sLen-1) == '=') { missingBytesInLastGroup++; numFullGroups--; } if (s.charAt(sLen-2) == '=') missingBytesInLastGroup++; } byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; // Translate all full groups from base64 to byte array elements int inCursor = 0, outCursor = 0; for (int i=0; i> 4)); result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); result[outCursor++] = (byte) ((ch2 << 6) | ch3); } // Translate partial group, if present if (missingBytesInLastGroup != 0) { int ch0 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); int ch1 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); if (missingBytesInLastGroup == 1) { int ch2 = base64decode_base64toint(s.charAt(inCursor++), alphaToInt); result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); } } // assert inCursor == s.length()-missingBytesInLastGroup; // assert outCursor == result.length; return result; } static int base64decode_base64toint(char c, byte[] alphaToInt) { int result = alphaToInt[c]; if (result < 0) throw new IllegalArgumentException("Illegal character " + c); return result; } static final byte base64decode_base64toint[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; static A setBorder(Border border, A c) { if (c != null) { swing(() -> { c.setBorder(border); }); } return c; } static A setBorder(A c, Border border) { return setBorder(border, c); } static JScrollPane withoutViewportBorder(JScrollPane sp) { if (sp != null) { swing(() -> { sp.setViewportBorder(null); }); } return sp; } static PrintWriter newPrintWriter(OutputStream out) { return new PrintWriter(outputStreamToWriter(out)); } static PrintWriter newPrintWriter(Writer out) { return new PrintWriter(out); } static String javaTok_substringC(String s, int i, int j) { return s.substring(i, j); } static Producer javaTokC_noMLS_iterator(final String s) { return javaTokC_noMLS_iterator(s, 0); } static Producer javaTokC_noMLS_iterator(final String s, final int startIndex) { return new Producer() { final int l = s.length(); int i = startIndex; public String next() { if (i >= l) return null; int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } i = j; if (i >= l) return null; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener || s.charAt(j) == '\n') { // end at \n to not propagate unclosed string literal errors ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))); else if (Character.isDigit(c)) { do ++j; while (j < l && Character.isDigit(s.charAt(j))); if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L } else ++j; String t = quickSubstring(s, i, j); i = j; return t; } }; } static String actualMCDollar() { return actualMC().getName() + "$"; } static Object _defaultClassFinder_value = defaultDefaultClassFinder(); static Object _defaultClassFinder() { return _defaultClassFinder_value; } static BigInteger parseBigInt(String s) { return new BigInteger(s); } static float parseFloat(String s) { return Float.parseFloat(s); } static String trivialUnquote(String s) { return dropPrefixSuffix("\"", s); } static short[] shortArrayFromBytes(byte[] a) { return shortArrayFromBytes(a, 0, l(a)); } static short[] shortArrayFromBytes(byte[] a, int from, int to) { short[] b = new short[(to-from)/2]; for (int i = 0; i < b.length; i++) b[i] = shortFromBytes(a, from+i*2); return b; } static long[] longArrayFromBytes(byte[] a) { return longArrayFromBytes(a, 0, l(a)); } static long[] longArrayFromBytes(byte[] a, int from, int to) { long[] b = new long[(to-from)/8]; for (int i = 0; i < b.length; i++) b[i] = longFromBytes_bigEndian(a, from+i*8); return b; } static HashMap findClass_fullName_cache = new HashMap(); // returns null on not found // this is the simple version that is not case-tolerant static Class findClass_fullName(String name) { synchronized(findClass_fullName_cache) { if (findClass_fullName_cache.containsKey(name)) return findClass_fullName_cache.get(name); Class c; try { c = Class.forName(name); } catch (ClassNotFoundException e) { c = null; } findClass_fullName_cache.put(name, c); return c; } } static String unquoteUsingCharArray(String s, char[] buf) { if (s == null) return null; if (startsWith(s, '[')) { int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; if (i < s.length() && s.charAt(i) == '[') { String m = s.substring(1, i); if (s.endsWith("]" + m + "]")) return s.substring(i+1, s.length()-i-1); } } if (s.length() > 1) { char c = s.charAt(0); if (c == '\"' || c == '\'') { int l = endsWith(s, c) ? s.length()-1 : s.length(); if (l > buf.length) return unquote(s); // fallback int n = 0; for (int i = 1; i < l; i++) { char ch = s.charAt(i); if (ch == '\\') { char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; } } buf[n++] = (char) Integer.parseInt(code, 8); continue; } switch (nextChar) { case '\"': ch = '\"'; break; case '\\': ch = '\\'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case '\'': ch = '\''; break; // Hex Unicode: u???? case 'u': if (i >= l - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.charAt(i + 5), 16); char[] x = Character.toChars(code); int lx = x.length; for (int j = 0; j < lx; j++) buf[n++] = x[j]; i += 5; continue; default: ch = nextChar; // added by Stefan } i++; } buf[n++] = ch; } return new String(buf, 0, n); } } return s; // not quoted - return original } static boolean structure_isMarker(String s, int i, int j) { if (i >= j) return false; if (s.charAt(i) != 'm') return false; ++i; while (i < j) { char c = s.charAt(i); if (c < '0' || c > '9') return false; ++i; } return true; } static String internIfLongerThan(String s, int l) { return s == null ? null : l(s) >= l ? intern(s) : s; } static char unquoteCharacter(String s) { assertTrue(s.startsWith("'") && s.length() > 1); return unquote("\"" + s.substring(1, s.endsWith("'") ? s.length()-1 : s.length()) + "\"").charAt(0); } static 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 boolean warn_on = true; static ThreadLocal> warn_warnings = new ThreadLocal(); static void warn(String s) { if (warn_on) print("Warning: " + s); } static void warn(String s, List warnings) { warn(s); if (warnings != null) warnings.add(s); addToCollection(warn_warnings.get(), s); } static TreeMap ciMap() { return caseInsensitiveMap(); } static List parseList(String s) { return (List) safeUnstructure(s); } static List synchroLinkedList() { return synchroList(new LinkedList()); } static NavigableMap synchroNavigableMap(NavigableMap map) { return Collections.synchronizedNavigableMap(map); } static SortedMap synchroSortedMap(SortedMap map) { return Collections.synchronizedSortedMap(map); } 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 String assertIdentifier(String s) { return assertIsIdentifier(s); } static String assertIdentifier(String msg, String s) { return assertIsIdentifier(msg, s); } static Constructor nuStubInnerObject_findConstructor(Class c) { return nuStubInnerObject_findConstructor(c, null); } static Constructor nuStubInnerObject_findConstructor(Class c, Object classFinder) { try { Class outerType = getOuterClass(c, classFinder); Constructor m = c.getDeclaredConstructor(outerType); makeAccessible(m); return m; } catch (Exception __e) { throw rethrow(__e); } } static void setOptAllDyn_pcall(DynamicObject o, Map fields) { if (fields == null || o == null) return; HashMap fieldMap = instanceFieldsMap(o); for (Map.Entry e : fields.entrySet()) { try { String field = e.getKey(); Object val = e.getValue(); Field f = fieldMap.get(field); if (f != null) smartSet(f, o, val); else { dynamicObject_setRawFieldValue(o, intern(field), val); } } catch (Throwable __e) { pcallFail(__e); }} } static void setOptAll_pcall(Object o, Map fields) { if (fields == null) return; for (String field : keys(fields)) try { setOpt(o, field, fields.get(field)); } catch (Throwable __e) { print(exceptionToStringShort(__e)); } } static void setOptAll_pcall(Object o, Object... values) { //values = expandParams(c.getClass(), values); warnIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; try { setOpt(o, field, value); } catch (Throwable __e) { print(exceptionToStringShort(__e)); } } } static void fixOuterRefs(Object o) { try { if (o == null) return; Field[] l = thisDollarOneFields(o.getClass()); if (l.length <= 1) return; Object father = null; for (Field f : l) { father = f.get(o); if (father != null) break; } if (father == null) return; for (Field f : l) f.set(o, father); } catch (Exception __e) { throw rethrow(__e); } } static String intern(String s) { return fastIntern(s); } static void pcallOpt_noArgs(Object o, String method) { try { callOpt_noArgs(o, method); } catch (Throwable __e) { pcallFail(__e); } } static Object newMultiDimensionalOuterArray(Class elementType, int dimensions, int length) { int[] dims = new int[dimensions]; dims[0] = length; return Array.newInstance(elementType, dims); } static int[] toIntArray(Collection l) { int[] a = new int[l(l)]; int i = 0; if (a.length != 0) for (int x : l) a[i++] = x; return a; } static TreeSet ciSet() { return caseInsensitiveSet(); } // DIFFERENCES to jfind: always ignores case, doesn't recognize etc // You probably want jmatch2 static boolean jmatch(String pat, String s) { return jmatch(pat, s, null); } static boolean jmatch(String pat, String s, Matches matches) { if (s == null) return false; return jmatch(pat, javaTok(s), matches); } static boolean jmatch(String pat, List toks) { return jmatch(pat, toks, null); } static boolean jmatch(String pat, List toks, Matches matches) { List tokpat = javaTok(pat); String[] m = match2(tokpat, toks); //print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m)); if (m == null) return false; else { if (matches != null) matches.m = m; return true; } } static ThreadLocal DynamicObject_loading = or((ThreadLocal) get(getClass("x30_pkg.x30_util"), "DynamicObject_loading"), new ThreadLocal()); static ThreadLocal dynamicObjectIsLoading_threadLocal() { return DynamicObject_loading; } static betterCIComparator_C betterCIComparator_instance; static betterCIComparator_C betterCIComparator() { if (betterCIComparator_instance == null) betterCIComparator_instance = new betterCIComparator_C(); return betterCIComparator_instance; } final static class betterCIComparator_C implements Comparator { public int compare(String s1, String s2) { if (s1 == null) return s2 == null ? 0 : -1; if (s2 == null) return 1; int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; } } static ColorModel grayColorModel_cache; static ColorModel grayColorModel() { if (grayColorModel_cache == null) grayColorModel_cache = grayColorModel_load(); return grayColorModel_cache;} static ColorModel grayColorModel_load() { return newGrayBufferedImage(1, 1).getColorModel(); } static void consoleSetInput(final String text) { if (headless()) return; setTextAndSelectAll(consoleInputField(), text); focusConsole(); } static Comparator makeComparator(final Object f) { if (f instanceof Comparator) return (Comparator) f; return new Comparator() { public int compare(Object a, Object b) { return (Integer) callF(f, a, b); } }; } static List allMethodNames(Object o) { Class c = _getClass(o); TreeSet names = new TreeSet(); while (c != null) { for (Method m : c.getDeclaredMethods()) names.add(m.getName()); c = c.getSuperclass(); } return asList(names); } static 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 void warnIfOddCount(Object... list) { if (odd(l(list))) printStackTrace("Odd list size: " + list); } static TimeZone localTimeZone() { return getTimeZone(standardTimeZone()); // TimeZone.getDefault()? } static TreeSet caseInsensitiveSet() { return caseInsensitiveSet_treeSet(); } static TreeSet caseInsensitiveSet(Collection c) { return caseInsensitiveSet_treeSet(c); } static TreeSet treeSet() { return new TreeSet(); } static List antiFilter(Iterable c, Object pred) { if (pred instanceof F1) return antiFilter(c, (F1) pred); List x = new ArrayList(); if (c != null) for (Object o : c) if (!isTrue(callF(pred, o))) x.add(o); return x; } static List antiFilter(Object pred, Iterable c) { return antiFilter(c, pred); } static List antiFilter(Object pred, Object[] c) { return antiFilter(pred, wrapArrayAsList(c)); } static List antiFilter(Iterable c, F1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (!pred.get(o).booleanValue()) x.add(o); return x; } static List antiFilter(F1 pred, Iterable c) { return antiFilter(c, pred); } static List antiFilter(Iterable c, IF1 pred) { List x = new ArrayList(); if (c != null) for (B o : c) if (!pred.get(o).booleanValue()) x.add(o); return x; } static List antiFilter(IF1 pred, Iterable c) { return antiFilter(c, pred); } static Lock appendToFile_lock = lock(); static boolean appendToFile_keepOpen = false; static HashMap appendToFile_writers = new HashMap(); static void appendToFile(String path, String s) { try { Lock __0 = appendToFile_lock; lock(__0); try { // Let's just generally synchronize this to be safe. mkdirsForFile(new File(path)); path = getCanonicalPath(path); Writer writer = appendToFile_writers.get(path); if (writer == null) { //print("[Logging to " + path + "]"); writer = new BufferedWriter(new OutputStreamWriter( newFileOutputStream(path, true), "UTF-8")); if (appendToFile_keepOpen) appendToFile_writers.put(path, writer); } writer.write(s); if (!appendToFile_keepOpen) writer.close(); } finally { unlock(__0); } } catch (Exception __e) { throw rethrow(__e); } } static void appendToFile(File path, String s) { if (path != null) appendToFile(path.getPath(), s); } static void cleanMeUp_appendToFile() { AutoCloseable __3 = tempCleaningUp(); try { Lock __1 = appendToFile_lock; lock(__1); try { closeAllWriters(values(appendToFile_writers)); appendToFile_writers.clear(); } finally { unlock(__1); } } finally { _close(__3); }} static JTextArea jTextArea() { return jTextArea(""); } static JTextArea jTextArea(final String text) { return jTextAreaWithUndo(text); } static String strUnnull(Object o) { return o == null ? "" : str(o); } static String jlabel_textAsHTML_center_ifNeeded(String text) { if (swic(text, "") && ewic(text, "")) return text; if (!containsNewLines(text)) return text; return jlabel_textAsHTML_center(text); } static Cache isLinux_cache = new Cache<>(() -> isLinux_load()); static boolean isLinux() { return isLinux_cache.get(); } static Boolean isLinux_load() { return !isWindows() && !isMac() && !isAndroid(); } static A setFont(final Font font, final A a) { if (a != null) { swing(() -> { a.setFont(font); }); } return a; } static A setFont(A a, Font font) { return setFont(font, a); } static A setFont(final String fontID, float fontSize, final A a) { return setFont(loadFont_cached(fontID, fontSize), a); } static Object[] mapToObjectArray(Map map) { List l = new ArrayList(); for (Object o : keys(map)) { l.add(o); l.add(map.get(o)); } return toObjectArray(l); } static Object[] mapToObjectArray(Object f, Collection l) { int n = l(l); Object[] array = new Object[n]; if (n != 0) { Iterator it = iterator(l); for (int i = 0; i < n; i++) array[i] = callF(f, it.next()); } return array; } static Object[] mapToObjectArray(Object f, Object[] l) { int n = l(l); Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = callF(f, l[i]); return array; } static Object[] mapToObjectArray(Collection l, IF1 f) { return mapToObjectArray(f, l); } static Object[] mapToObjectArray(A[] l, IF1 f) { return mapToObjectArray(f, l); } static Object[] mapToObjectArray(IF1 f, A[] l) { int n = l(l); Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = f.get(l[i]); return array; } static Object[] mapToObjectArray(IF1 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; } // this should be on by default now I think, but it may break // legacy code... static ThreadLocal htmlencode_forParams_useV2 = new ThreadLocal(); static String htmlencode_forParams(String s) { if (s == null) return ""; if (isTrue(htmlencode_forParams_useV2.get())) return htmlencode_forParams_v2(s); StringBuilder out = new StringBuilder(Math.max(16, s.length())); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127 || c == '"' || c == '<' || c == '>') { out.append("&#"); out.append((int) c); out.append(';'); } else out.append(c); } return out.toString(); } static JTextField standardTextFieldPopupMenu(final JTextField tf) { final WeakReference ref = weakRef(tf); componentPopupMenuItem(tf, "Copy text to clipboard", new Runnable() { public void run() { try { copyTextToClipboard(ref.get().getText()) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyTextToClipboard(ref.get().getText())"; }}); componentPopupMenuItem(tf, "Paste", new Runnable() { public void run() { try { ref.get().paste() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "ref.get().paste()"; }}); return tf; } static A jenableUndoRedo(A textcomp) { { swing(() -> { UndoManager undo = new UndoManager(); vm_generalWeakSet("Undo Managers").add(undo); setMeta(textcomp, "UndoManager", 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 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 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 Writer outputStreamToWriter(OutputStream out) { try { return new OutputStreamWriter(out, "UTF-8"); } catch (Exception __e) { throw rethrow(__e); } } 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 defaultDefaultClassFinder() { return new F1() { 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, "
")) 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 dropPrefixSuffix(String prefix, String s) { return dropPrefixSuffix(prefix, prefix, s); } static String dropPrefixSuffix(String prefix, String suffix, String s) { return dropPrefix(prefix, dropSuffix(suffix, s)); } static short shortFromBytes(byte[] a, int i) { return (short) (ubyteToInt(a[i]) << 8 | ubyteToInt(a[i+1])); } static long longFromBytes_bigEndian(byte[] a, int i) { return twoIntsToLong_bigEndian( intFromBytes_bigEndian(a, i), intFromBytes_bigEndian(a, i+4)); } static boolean addToCollection(Collection c, A a) { return c != null && c.add(a); } static TreeMap caseInsensitiveMap() { return new TreeMap(caseInsensitiveComparator()); } static Object safeUnstructure(String s) { return unstructure(s, true); } static Object safeUnstructure(File f) { return safeUnstructureGZFile(f); } 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 Class getOuterClass(Class c) { return getOuterClass(c, null); } static Class getOuterClass(Class c, Object classFinder) { try { String s = c.getName(); int i = s.lastIndexOf('$'); String name = substring(s, 0, i); return classForName(name, classFinder); } catch (Exception __e) { throw rethrow(__e); } } static Class getOuterClass(Object o) { return getOuterClass(o, null); } static Class getOuterClass(Object o, Object classFinder) { return getOuterClass(_getClass(o), classFinder); } static HashMap instanceFieldsMap(Object o) { return (HashMap) getOpt_getFieldMap(o); } static Map thisDollarOneFields_cache = newDangerousWeakHashMap(); static Field[] thisDollarOneFields(Class c) { synchronized(thisDollarOneFields_cache) { Field[] l = thisDollarOneFields_cache.get(c); if (l == null) thisDollarOneFields_cache.put(c, l = thisDollarOneFields_uncached(c)); return l; } } static Field[] thisDollarOneFields_uncached(Class c) { List fields = new ArrayList(); do { for (Field f : c.getDeclaredFields()) if (f.getName().startsWith("this$")) fields.add(makeAccessible(f)); c = c.getSuperclass(); } while (c != null); return toArray(new Field[l(fields)], fields); } static 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 Map> callOpt_noArgs_cache = newDangerousWeakHashMap(); static Object callOpt_noArgs(Object o, String method) { try { if (o == null) return null; if (o instanceof Class) return callOpt(o, method); // not optimized Class c = o.getClass(); HashMap map; synchronized(callOpt_noArgs_cache) { map = callOpt_noArgs_cache.get(c); if (map == null) map = callOpt_noArgs_makeCache(c); } Method m = map.get(method); return m != null ? m.invoke(o) : null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap callOpt_noArgs_makeCache(Class c) { HashMap map = new HashMap(); Class _c = c; do { for (Method m : c.getDeclaredMethods()) if (m.getParameterTypes().length == 0 && !reflection_isForbiddenMethod(m)) { makeAccessible(m); String name = m.getName(); if (!map.containsKey(name)) map.put(name, m); } _c = _c.getSuperclass(); } while (_c != null); callOpt_noArgs_cache.put(c, map); return map; } // TODO: extended multi-line strings static int javaTok_n, javaTok_elements; static boolean javaTok_opt = false; static List javaTok(String s) { ++javaTok_n; ArrayList tok = new ArrayList(); int l = s == null ? 0 : s.length(); int i = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !regionMatches(s, j, "*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(javaTok_substringN(s, i, j)); i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace // Special JavaX syntax: 'identifier if (c == '\'' && Character.isJavaIdentifierStart(d) && i+2 < l && "'\\".indexOf(s.charAt(i+2)) < 0) { j += 2; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j; } else if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { int c2 = s.charAt(j); if (c2 == opener || c2 == '\n' && opener == '\'') { // allow multi-line strings, but not for ' ++j; break; } else if (c2 == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for stuff like "don't" else if (Character.isDigit(c)) { do ++j; while (j < l && Character.isDigit(s.charAt(j))); if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L } else if (c == '[' && d == '[') { do ++j; while (j < l && !regionMatches(s, j, "]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !regionMatches(s, j, "]=]")); j = Math.min(j+3, l); } else ++j; tok.add(javaTok_substringC(s, i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); javaTok_elements += tok.size(); return tok; } static List javaTok(List tok) { return javaTokWithExisting(join(tok), tok); } // match2 matches multiple "*" (matches a single token) wildcards and zero or one "..." wildcards (matches multiple tokens) static String[] match2(List pat, List tok) { // standard case (no ...) int i = pat.indexOf("..."); if (i < 0) return match2_match(pat, tok); pat = new ArrayList(pat); // We're modifying it, so copy first pat.set(i, "*"); while (pat.size() < tok.size()) { pat.add(i, "*"); pat.add(i+1, ""); // doesn't matter } return match2_match(pat, tok); } static String[] match2_match(List pat, List tok) { List result = new ArrayList(); if (pat.size() != tok.size()) { return null; } for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(i); if (eq(p, "*")) result.add(t); else if (!equalsIgnoreCase(unquote(p), unquote(t))) // bold change - match quoted and unquoted now. TODO: should remove return null; } return result.toArray(new String[result.size()]); } static boolean headless() { return isHeadless(); } static JTextField setTextAndSelectAll(final JTextField tf, final String text) { if (tf != null) { swing(() -> { tf.setText(text); tf.selectAll(); }); } return tf; } static JTextField consoleInputField() { Object console = get(getJavaX(), "console"); return (JTextField) getOpt(console, "tfInput"); } static void focusConsole(String s) { setConsoleInput(s); focusConsole(); } static void focusConsole() { JComponent tf = consoleInputFieldOrComboBox(); if (tf != null) { //print("Focusing console"); tf.requestFocus(); } } static String getStackTrace2(Throwable e) { return hideCredentials(getStackTrace(unwrapTrivialExceptionWraps(e)) + replacePrefix("java.lang.RuntimeException: ", "FAIL: ", hideCredentials(str(innerException2(e)))) + "\n"); } static TimeZone getTimeZone(String name) { return TimeZone.getTimeZone(name); } static String standardTimeZone_name = "Europe/Berlin"; static String standardTimeZone() { return standardTimeZone_name; } static TreeSet caseInsensitiveSet_treeSet() { return new TreeSet(caseInsensitiveComparator()); } static TreeSet caseInsensitiveSet_treeSet(Collection c) { return toCaseInsensitiveSet_treeSet(c); } 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 l) { for (Writer w : unnull(l)) { try { w.close(); } catch (Throwable __e) { pcallFail(__e); }} } static JTextArea jTextAreaWithUndo() { return jTextAreaWithUndo(""); } static JTextArea jTextAreaWithUndo(final String text) { return jenableUndoRedo(swingNu(JTextArea.class, text)); } static boolean containsNewLines(String s) { return containsNewLine(s); } static String jlabel_textAsHTML_center(String text) { return "
" + replace(htmlencode2(text), "\n", "
") + "
"; } public static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } static boolean isMac() { return System.getProperty("os.name").toLowerCase().contains("mac"); } static Map loadFont_cached_cache = new HashMap(); static synchronized Font loadFont_cached(String snippetID) { try { snippetID = formatSnippetID(snippetID); Font f = loadFont_cached_cache.get(snippetID); if (f == null) loadFont_cached_cache.put(snippetID, f = loadFont(snippetID, 12f)); return f; } catch (Exception __e) { throw rethrow(__e); } } static synchronized Font loadFont_cached(String snippetID, float size) { try { return loadFont_cached(snippetID).deriveFont(size); } catch (Exception __e) { throw rethrow(__e); } } static 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
A componentPopupMenuItem(A c, String name, Runnable action) { componentPopupMenu(c, new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, name, action); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, name, action);"; }}); return c; } static void componentPopupMenuItem(JComponent c, JMenuItem menuItem) { componentPopupMenu(c, new VF1() { public void get(JPopupMenu menu) { try { addMenuItem(menu, menuItem); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "addMenuItem(menu, menuItem);"; }}); } static 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 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 A childOfType(Component c, Class theClass) { return first(childrenOfType(c, theClass)); } static A childOfType(Class theClass, Component c) { return childOfType(c, theClass); } static Object realMC() { return getThreadLocal(realMC_tl()); } static Object get2(Object o, String field1, String field2) { return get(get(o, field1), field2); } static boolean startsWithAny(String a, Collection b) { for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix)) return true; return false; } static boolean startsWithAny(String a, String... b) { if (b != null) for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix)) return true; return false; } static boolean startsWithAny(String a, Collection b, Matches m) { for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix, m)) return true; return false; } static String mcDollar() { return mcName() + "$"; } static String afterDollar(String s) { return substring(s, smartIndexOf(s, '$')+1); } static long twoIntsToLong_bigEndian(int a, int b) { return twoIntsToLong(a, b); } static int intFromBytes_bigEndian(byte[] a, int i) { return intFromBytes(a, i); } 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 void upgradeJavaXAndRestart() { run("#1001639"); restart(); sleep(); } 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 List javaTokWithExisting(String s, List existing) { ++javaTok_n; int nExisting = javaTok_opt && existing != null ? existing.size() : 0; ArrayList tok = existing != null ? new ArrayList(nExisting) : new ArrayList(); int l = s.length(); int i = 0, n = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } if (n < nExisting && javaTokWithExisting_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(javaTok_substringN(s, i, j)); ++n; i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace // Special JavaX syntax: 'identifier if (c == '\'' && Character.isJavaIdentifierStart(d) && i+2 < l && "'\\".indexOf(s.charAt(i+2)) < 0) { j += 2; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j; } else if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener /*|| s.charAt(j) == '\n'*/) { // allow multi-line strings ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0)); // for stuff like "don't" else if (Character.isDigit(c)) { do ++j; while (j < l && Character.isDigit(s.charAt(j))); if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L } else if (c == '[' && d == '[') { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]")); j = Math.min(j+3, l); } else ++j; if (n < nExisting && javaTokWithExisting_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(javaTok_substringC(s, i, j)); ++n; i = j; } if ((tok.size() % 2) == 0) tok.add(""); javaTok_elements += tok.size(); return tok; } static boolean javaTokWithExisting_isCopyable(String t, String s, int i, int j) { return t.length() == j-i && s.regionMatches(i, t, 0, j-i); // << could be left out, but that's brave } static boolean equalsIgnoreCase(String a, String b) { return eqic(a, b); } static boolean equalsIgnoreCase(char a, char b) { return eqic(a, b); } 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 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 TreeSet toCaseInsensitiveSet_treeSet(Iterable c) { if (isCISet(c)) return (TreeSet) c; TreeSet set = caseInsensitiveSet_treeSet(); addAll(set, c); return set; } static TreeSet toCaseInsensitiveSet_treeSet(String... x) { TreeSet set = caseInsensitiveSet_treeSet(); addAll(set, x); return set; } static boolean containsNewLine(String s) { return contains(s, '\n'); // screw \r, nobody needs it } static List replace(List l, A a, A b) { for (int i = 0; i < l(l); i++) if (eq(l.get(i), a)) l.set(i, b); return l; } static List replace(A a, A b, List l) { return replace(l, a, b); } // replace all occurrences of a in s with b static String replace(String s, String a, String b) { return s == null ? null : a == null || b == null ? s : s.replace(a, b); } static String replace(String s, char a, char b) { return s == null ? null : s.replace(a, b); } static String htmlencode2(String s) { return htmlencode_noQuotes(s); } 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 Set newWeakHashSet() { return synchroWeakHashSet(); } static ThreadLocal realMC_tl_tl = new ThreadLocal(); static ThreadLocal realMC_tl() { return realMC_tl_tl; } static String mcName() { return mc().getName(); } // returns l(s) if not found static int smartIndexOf(String s, String sub, int i) { if (s == null) return 0; i = s.indexOf(sub, min(i, l(s))); return i >= 0 ? i : l(s); } static int smartIndexOf(String s, int i, char c) { return smartIndexOf(s, c, i); } static int smartIndexOf(String s, char c, int i) { if (s == null) return 0; i = s.indexOf(c, min(i, l(s))); return i >= 0 ? i : l(s); } static int smartIndexOf(String s, String sub) { return smartIndexOf(s, sub, 0); } static int smartIndexOf(String s, char c) { return smartIndexOf(s, c, 0); } static int smartIndexOf(List l, A sub) { return smartIndexOf(l, sub, 0); } static int smartIndexOf(List l, int start, A sub) { return smartIndexOf(l, sub, start); } static int smartIndexOf(List l, A sub, int start) { int i = indexOf(l, sub, start); return i < 0 ? l(l) : i; } static long twoIntsToLong(int a, int b) { return (((long) a) << 32) | (((long) b) & 0xFFFFFFFFL); } static int intFromBytes(byte[] a, int i) { return ubyteToInt(a[i]) << 24 | ubyteToInt(a[i+1]) << 16 | ubyteToInt(a[i+2]) << 8 | ubyteToInt(a[i+3]); } static BufferedReader utf8BufferedReader(InputStream in) { return utf8bufferedReader(in); } static BufferedReader utf8BufferedReader(File f) { return utf8bufferedReader(f); } static boolean isCISet(Iterable l) { return l instanceof TreeSet && ((TreeSet) l).comparator() == caseInsensitiveComparator(); } 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 Set synchroWeakHashSet() { return Collections.newSetFromMap((Map) newWeakHashMap()); } static String intToHex_flexLength(int i) { return Integer.toHexString(i); } static class Chain implements Iterable { A element; Chain next; int size; Chain() {} Chain(A element) { this.element = element; size = 1; } Chain(A element, Chain next) { this.next = next; this.element = element; size = next != null ? next.size+1 : 1; } public String toString() { return str(toList()); } ArrayList toList() { ArrayList l = emptyList(size); Chain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } // TODO: optimize public Iterator iterator() { return toList().iterator(); } } // both the outer outline and the outline of a hole are called a "trace" // should return outer outline first and then outline of holes // seems to return first point again at the end sometimes static class RegionBorder_innerPoints_v2 extends AbstractBorderTracer implements IFieldsToList{ IImageRegion region; RegionBorder_innerPoints_v2() {} RegionBorder_innerPoints_v2(IImageRegion region) { this.region = region;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; } Rect bounds; // region bounds int x, y, dir; Iterator it; byte[] reachedInDirections; // bits 0 to 3 boolean tracing = false; int nTrace; // 1 for outline, 2+ for hole // directions (0 = right, 1 = down, 2 = left, 3 = up) static Pt[] directions = { pt(1, 0), pt(0, 1), pt(-1, 0), pt(0, -1) // r, d, l, u }; void init() { bounds = region.bounds(); reachedInDirections = new byte[area(bounds)]; it = region.pixelIterator(); } public boolean step() { if (reachedInDirections == null) init(); if (tracing) return walkOnePixel(); else return findFirstPixel(); } boolean walkOnePixel() { int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if ((reachedInDirections[posInBounds] & (1 << dir)) != 0) { traceDone(); tracing = false; return includeHoles; } reachedInDirections[posInBounds] |= (byte) 1 << dir; foundPoint(x, y); for (int turn = 3; turn <= 6; turn++) { // try left, straight, right, back int newDir = (dir+turn) & 3; Pt d = directions[newDir]; int x2 = x+d.x, y2 = y+d.y; boolean b = region.contains(x2, y2); if (b) { x = x2; y = y2; dir = newDir; return true; } } return true; // no black pixels found in any direction - region must be a single pixel } boolean findFirstPixel() { // search for first border pixel if (!it.hasNext()) return false; // done Pt p = it.next(); x = p.x; y = p.y; int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if (reachedInDirections[posInBounds] != 0) return true; // seen pixel before // if pixel above is empty, walk to the right if (!region.contains(x, y-1)) { startTrace(0); return true; } // if pixel on the left is empty, walk upwards if (!region.contains(x-1, y)) { startTrace(3); return true; } // if pixel on the right is empty, walk downwards if (!region.contains(x+1, y)) { startTrace(1); return true; } // if pixel below is empty, walk left if (!region.contains(x, y+1)) { startTrace(2); return true; } // not a border pixel, continue search return true; } void startTrace(int dir) { this.dir = dir; // mark point reached from all directions in next step int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; reachedInDirections[posInBounds] = (byte) (15 & ~(1 << dir)); tracing = true; newTrace(++nTrace > 1); } void foundPoint(int x, int y) { foundPoint(pt(x, y)); } // get all points as list List allPoints_cache; List allPoints() { if (allPoints_cache == null) allPoints_cache = allPoints_load(); return allPoints_cache;} List allPoints_load() { PtBuffer l = new PtBuffer(); onFoundPoint(p -> l.add(p)); run(); return l; } // get outline as OnePath OnePath onePath_cache; OnePath onePath() { if (onePath_cache == null) onePath_cache = onePath_load(); return onePath_cache;} OnePath onePath_load() { includeHoles(false); return new OnePath(allPoints(), true); } // or as OnePathWithOrigin OnePathWithOrigin onePathWithOrigin_cache; OnePathWithOrigin onePathWithOrigin() { if (onePathWithOrigin_cache == null) onePathWithOrigin_cache = onePathWithOrigin_load(); return onePathWithOrigin_cache;} OnePathWithOrigin onePathWithOrigin_load() { includeHoles(false); return new OnePathWithOrigin(allPoints(), true); } // for debugging void runAndPrint() { onNewTrace(hole -> print(!hole ? "new outline" : "new hole")); onTraceDone(() -> print("traceDone")); onFoundPoint(p -> print("foundPoint " + p)); stepMaxWithStats(this, 10000); } boolean tracingHole() { return nTrace > 1; } } static class BoundsFinder { int x1 = Integer.MAX_VALUE, y1 = Integer.MAX_VALUE, x2 = 0, y2 = 0; BoundsFinder() {} BoundsFinder(int w, int h) { x1 = w; y1 = h; } final void add(Pt p){ addPt(p); } void addPt(Pt p) { if (p != null) addPt(p.x, p.y); } final void add(int x, int y){ addPt(x, y); } void addPt(int x, int y) { if (x < x1) x1 = x; if (x > x2) x2 = x; if (y < y1) y1 = y; if (y > y2) y2 = y; } Rect get() { return rectFromPointsOrNull(x1, y1, x2+1, y2+1); } } // 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 extends CompactAbstractMap { final static int INITIAL_SIZE = 3; final static double LOAD_FACTOR = 0.6; // This object is used to represent a null KEY (null value are kept as is) 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 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 true 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 true if this map contains the specified key. */ public synchronized boolean containsKey(Object k) { return key(findKeyIndex(k)) != null; } /** * Returns true 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> entrySet() { return new EntrySet(); } /** * 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 values() { return new ValueCollection(); } /** * Returns a virtual read-only set of all the keys in the map. */ public synchronized Set 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 extends AbstractSet { public int size() { synchronized(CompactHashMap.this) { return elements; }} public boolean contains(Object k) { synchronized(CompactHashMap.this) { return containsKey(k); }} public Iterator iterator() { synchronized(CompactHashMap.this) { return new KeyIterator(); }} } class KeyIterator implements Iterator { private int ix; private KeyIterator() { synchronized(CompactHashMap.this) { // 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 boolean hasNext() { synchronized(CompactHashMap.this) { return ix < tableSize(); }} public void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public K next() { synchronized(CompactHashMap.this) { 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; }} } // --- Entry set class EntrySet extends AbstractSet> { public int size() { synchronized(CompactHashMap.this) { return elements; }} public boolean contains(Object o) { synchronized(CompactHashMap.this) { if (o instanceof Map.Entry) { Object key = ((Map.Entry) o).getKey(); if (!containsKey((Map.Entry) o)) return false; return eq(((Map.Entry) o).getValue(), get(key)); } return false; }} public Iterator> iterator() { return new EntryIterator(); } } class EntryIterator implements Iterator> { private int ix; private EntryIterator() { synchronized(CompactHashMap.this) { // 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 boolean hasNext() { synchronized(CompactHashMap.this) { return ix < tableSize(); }} public void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public Map.Entry next() { synchronized(CompactHashMap.this) { if (ix >= tableSize()) throw new NoSuchElementException(); K key = key(ix); V val = value(ix); ++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 simpleMapEntry(key, val); }} } // --- Value collection class ValueCollection extends AbstractCollection { public int size() { synchronized(CompactHashMap.this) { return elements; }} public Iterator iterator() { return new ValueIterator(); } public boolean contains(Object v) { return containsValue(v); } } class ValueIterator implements Iterator { private int ix; private ValueIterator() { synchronized(CompactHashMap.this) { // 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 boolean hasNext() { synchronized(CompactHashMap.this) { return ix < tableSize(); }} public void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public V next() { synchronized(CompactHashMap.this) { 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; } } // has equals semantics so passing it to onChange() etc is idempotent static class UpdateTrigger implements Runnable , IFieldsToList{ Updateable target; UpdateTrigger() {} UpdateTrigger(Updateable target) { this.target = target;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + target + ")"; } public boolean equals(Object o) { if (!(o instanceof UpdateTrigger)) return false; UpdateTrigger __1 = (UpdateTrigger) o; return eq(target, __1.target); } public int hashCode() { int h = -1756880753; h = boostHashCombine(h, _hashCode(target)); return h; } public Object[] _fieldsToList() { return new Object[] {target}; } public void run() { try { { if (target != null) target.update(); } } catch (Exception __e) { throw rethrow(__e); } } } static class DefunctClassLoader {} static class CachedPosterizer implements IPosterizer { IPosterizer posterizer; int[] cache; CachedPosterizer(IPosterizer posterizer) { this.posterizer = posterizer; makeCache(); } // 0..255 to 0..255 public int get(int brightness) { return cache[brightness]; } void makeCache() { cache = new int[256]; for (int brightness = 0; brightness < 256; brightness++) cache[brightness] = posterizer.get(brightness); } } static class Range { float min = 1f, max = 0f; // indicates no range Range() {} Range(float x) { min = max = x; } Range(float min, float max) { this.max = max; this.min = min;} boolean empty() { return min > max; } float length() { return max(0f, max-min); } } interface IMakeEmptyClone { Object makeEmptyClone(); } 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 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; } } static interface Producer { public A next(); // null when end } static class PtBuffer extends RandomAccessAbstractList < Pt > { LongBuffer buf = new LongBuffer(); PtBuffer() {} PtBuffer(int size) { buf.allocate(size); } PtBuffer(Iterable l) { if (l != null) for (Pt p : l) add(p); } public int size() { return buf.size(); } public Pt get(int i) { return longToPt(buf.get(i)); } public long getLong(int i) { return buf.get(i); } public Pt set(int i, Pt val) { Pt old = get(i); buf.set(i, ptToLong(val)); return old; } public boolean add(Pt p) { buf.add(ptToLong(p)); return true; } public void add(int x, int y) { buf.add(ptToLong(x, y)); } public Pt remove(int i) { Pt p = get(i); buf.remove(i); return p; } public void clear() { buf.clear(); } } static class ReverseChain implements Iterable { A element; ReverseChain prev; int size; ReverseChain() {} ReverseChain(ReverseChain prev, A element) { this.element = element; this.prev = prev; if (prev == null) size = 1; else { prev.check(); size = prev.size+1; } } void check() { if (size < 1) throw fail("You called the ReverseChain default constructor. Don't do that"); } public String toString() { return str(toList()); } ArrayList toList() { check(); ArrayList l = emptyList(size); for (int i = 0; i < size; i++) l.add(null); int i = size; ReverseChain c = this; while (c != null) { l.set(--i, c.element); c = c.prev; } return l; } public Iterator iterator() { return toList().iterator(); } } interface Updateable { void update(); } static class Value implements IF0 , 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 interface IF2_IntInt_Double { double get(int a, int b); } // both the outer outline and the outline of a hole are called a "trace" // should return outer outline first and then outline of holes // seems to return first point again at the end sometimes static class RegionBorder_innerPoints_withDiagonals extends AbstractBorderTracer implements IFieldsToList{ IImageRegion region; RegionBorder_innerPoints_withDiagonals() {} RegionBorder_innerPoints_withDiagonals(IImageRegion region) { this.region = region;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; } Rect bounds; // region bounds int x, y; int dir; // current direction (1 to 8) Iterator it; byte[] reachedInDirections; // bits 0 to 7 boolean tracing = false; int nTrace; // 1 for outline, 2+ for hole void init() { bounds = region.bounds(); reachedInDirections = new byte[area(bounds)]; it = region.pixelIterator(); } public boolean step() { if (reachedInDirections == null) init(); if (tracing) return walkOnePixel(); else return findFirstPixel(); } boolean walkOnePixel() { int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if ((reachedInDirections[posInBounds] & (1 << (dir-1))) != 0) { traceDone(); tracing = false; return includeHoles; } reachedInDirections[posInBounds] |= (byte) 1 << (dir-1); foundPoint(x, y); // try left, half left, straight, half right, right, back for (int turn = -2; turn <= 4; turn++) { int newDir = modRange_incl(dir+turn, 1, 8); Pt d = onePathDirection(newDir); int x2 = x+d.x, y2 = y+d.y; boolean b = region.contains(x2, y2); if (b) { x = x2; y = y2; dir = newDir; return true; } } return true; // no black pixels found in any direction - region must be a single pixel } boolean findFirstPixel() { // search for first border pixel if (!it.hasNext()) return false; // done Pt p = it.next(); x = p.x; y = p.y; int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if (reachedInDirections[posInBounds] != 0) return true; // seen pixel before // if pixel above is empty, walk to the right if (!region.contains(x, y-1)) { startTrace(4); return true; } // if pixel on the left is empty, walk upwards if (!region.contains(x-1, y)) { startTrace(2); return true; } // if pixel on the right is empty, walk downwards if (!region.contains(x+1, y)) { startTrace(6); return true; } // if pixel below is empty, walk left if (!region.contains(x, y+1)) { startTrace(8); return true; } // not a border pixel, continue search return true; } void startTrace(int dir) { this.dir = dir; // mark point reached from all directions in next step int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; reachedInDirections[posInBounds] = (byte) ~(1 << (dir-1)); tracing = true; newTrace(++nTrace > 1); } void foundPoint(int x, int y) { foundPoint(pt(x, y)); } // get all points as list List allPoints_cache; List allPoints() { if (allPoints_cache == null) allPoints_cache = allPoints_load(); return allPoints_cache;} List allPoints_load() { PtBuffer l = new PtBuffer(); onFoundPoint(p -> l.add(p)); run(); return l; } // get outline as OnePath OnePath onePath_cache; OnePath onePath() { if (onePath_cache == null) onePath_cache = onePath_load(); return onePath_cache;} OnePath onePath_load() { includeHoles(false); return new OnePath(allPoints(), true); } // or as OnePathWithOrigin OnePathWithOrigin onePathWithOrigin_cache; OnePathWithOrigin onePathWithOrigin() { if (onePathWithOrigin_cache == null) onePathWithOrigin_cache = onePathWithOrigin_load(); return onePathWithOrigin_cache;} OnePathWithOrigin onePathWithOrigin_load() { includeHoles(false); return new OnePathWithOrigin(allPoints(), true); } // for debugging void runAndPrint() { onNewTrace(hole -> print(!hole ? "new outline" : "new hole")); onTraceDone(() -> print("traceDone")); onFoundPoint(p -> print("foundPoint " + p)); stepMaxWithStats(this, 10000); } boolean tracingHole() { return nTrace > 1; } } static interface IIntPred { boolean get(int a); } // 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 Cache { Object maker; // func -> A A value; long loaded; static boolean debug = false; long changeCount; Lock lock = lock(); Cache() {} Cache(Object maker) { this.maker = maker;} Cache(IF0 maker) { this.maker = maker;} A get() { if (hasLock(lock)) return value; // Must be called from within maker Lock __0 = lock; lock(__0); try { if (loaded == 0) { value = make(); changeCount++; loaded = sysNow(); } return value; } finally { unlock(__0); } } void clear() { Lock __1 = lock; lock(__1); try { if (debug && loaded != 0) print("Clearing cache"); value = null; changeCount++; loaded = 0; } finally { unlock(__1); } } // clear if older than x seconds // 0 does not do anything void clear(double seconds) { Lock __2 = lock; lock(__2); try { if (seconds != 0 && loaded != 0 && sysNow() >= loaded+seconds*1000) clear(); } finally { unlock(__2); } } // override void set(A a) { Lock __3 = lock; lock(__3); try { value = a; ++changeCount; loaded = sysNow(); } finally { unlock(__3); } } A make() { return (A) callF(maker); } } static class LongBuffer implements Iterable, ILongQueue, IntSize { long[] data; int size; LongBuffer() {} LongBuffer(int size) { if (size != 0) data = new long[size]; } LongBuffer(Iterable l) { if (l instanceof Collection) allocate(((Collection) l).size()); addAll(l); } public void add(long i) { if (size >= lLongArray(data)) { data = resizeLongArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lLongArray(data)*2L)))); if (size >= data.length) throw fail("LongBuffer too large: " + size); } data[size++] = i; } void allocate(int n) { data = resizeLongArray(data, max(n, size())); } void addAll(Iterable l) { if (l != null) for (long i : l) add(i); } long[] toArray() { return size == 0 ? null : resizeLongArray(data, size); } List toList() { return longArrayToList(data, 0, size); } List asVirtualList() { return listFromFunction(__65 -> get(__65), size); } void reset() { size = 0; } void clear() { reset(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } long get(int idx) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); return data[idx]; } void set(int idx, long value) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); data[idx] = value; } long popLast() { if (size == 0) throw fail("empty buffer"); return data[--size]; } long last() { return data[size-1]; } long nextToLast() { return data[size-2]; } public String toString() { return squareBracket(joinWithSpace(toList())); } public Iterator iterator() { return new IterableIterator() { int i = 0; public boolean hasNext() { return i < size; } public Long next() { if (!hasNext()) throw fail("Index out of bounds: " + i); return data[i++]; } }; } void trimToSize() { data = resizeLongArray(data, size); } void remove(int idx) { arraycopy(data, idx+1, data, idx, size-1-idx); --size; } // don't rely on return value if buffer is empty public long poll() { return size == 0 ? -1 : data[--size]; } } abstract static class AbstractBorderTracer extends AbstractSteppable { transient Set> onNewTrace; public AbstractBorderTracer onNewTrace(IVF1 f) { onNewTrace = createOrAddToSyncLinkedHashSet(onNewTrace, f); return this; } public AbstractBorderTracer removeNewTraceListener(IVF1 f) { main.remove(onNewTrace, f); return this; } public void newTrace(boolean isHole) { if (onNewTrace != null) for (var listener : onNewTrace) pcallF_typed(listener, isHole); } transient Set> onFoundPoint; public AbstractBorderTracer onFoundPoint(IVF1 f) { onFoundPoint = createOrAddToSyncLinkedHashSet(onFoundPoint, f); return this; } public AbstractBorderTracer removeFoundPointListener(IVF1 f) { main.remove(onFoundPoint, f); return this; } public void foundPoint(Pt p) { if (onFoundPoint != null) for (var listener : onFoundPoint) pcallF_typed(listener, p); } transient Set onTraceDone; public AbstractBorderTracer onTraceDone(Runnable r) { onTraceDone = createOrAddToSyncLinkedHashSet(onTraceDone, r); return this; } public AbstractBorderTracer removeTraceDoneListener(Runnable r) { main.remove(onTraceDone, r); return this; } public void traceDone() { if (onTraceDone != null) for (var listener : onTraceDone) pcallF_typed(listener); } final public AbstractBorderTracer setIncludeHoles(boolean includeHoles){ return includeHoles(includeHoles); } public AbstractBorderTracer includeHoles(boolean includeHoles) { this.includeHoles = includeHoles; return this; } final public boolean getIncludeHoles(){ return includeHoles(); } public boolean includeHoles() { return includeHoles; } boolean includeHoles = true; } abstract static class CompactAbstractMap implements Map { public int size() { return entrySet().size(); } public boolean isEmpty() { return size() == 0; } public boolean containsValue(Object value) { Iterator> i = entrySet().iterator(); if (value == null) { while (i.hasNext()) { Entry e = i.next(); if (e.getValue() == null) return true; } } else { while (i.hasNext()) { Entry e = i.next(); if (value.equals(e.getValue())) return true; } } return false; } public boolean containsKey(Object key) { Iterator> i = entrySet().iterator(); if (key == null) { while (i.hasNext()) { Entry e = i.next(); if (e.getKey() == null) return true; } } else { while (i.hasNext()) { Entry e = i.next(); if (key.equals(e.getKey())) return true; } } return false; } public V get(Object key) { Iterator> i = entrySet().iterator(); if (key == null) { while (i.hasNext()) { Entry e = i.next(); if (e.getKey() == null) return e.getValue(); } } else { while (i.hasNext()) { Entry 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> i = entrySet().iterator(); Entry correctEntry = null; if (key == null) { while (correctEntry == null && i.hasNext()) { Entry e = i.next(); if (e.getKey() == null) correctEntry = e; } } else { while (correctEntry == null && i.hasNext()) { Entry 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 m) { for (Entry e : m.entrySet()) put(e.getKey(), e.getValue()); } public void clear() { entrySet().clear(); } public Set keySet() { return new AbstractSet() { public Iterator iterator() { return new Iterator() { private Iterator> 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 values() { return new AbstractCollection() { public Iterator iterator() { return new Iterator() { private Iterator> 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> 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 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 entry : entrySet()) h += entry.hashCode(); return h; } public String toString() { Iterator> i = entrySet().iterator(); if (!i.hasNext()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); for (; ; ) { Entry 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 implements Entry, 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 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 implements Entry, 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 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; } } } abstract static class AbstractSteppable implements Steppable { public void run() { try { stepAll(this); } catch (Exception __e) { throw rethrow(__e); } } } static interface ILongQueue { public boolean isEmpty(); public void add(long element); // return value is undefined if queue is empty public long poll(); } static ListIterator listIterator(List l) { return l == null ? emptyListIterator() : l.listIterator(); } static void stepMaxWithStats(Steppable s, long maxSteps) { stepAllWithStats(s, maxSteps); } static void stepMaxWithStats(long maxSteps, Steppable s) { stepMaxWithStats(s, maxSteps); } static Rect rectFromPointsOrNull(int x1, int y1, int x2, int y2) { return nullIfEmptyRect(pointsRect(x1, y1, x2, y2)); } static boolean containsKey(Map map, A key) { return map != null && map.containsKey(key); } static Map.Entry simpleMapEntry(A key, B value) { return new Map.Entry() { public A getKey() { return key; } public B getValue() { return value; } public B setValue(B newValue) { throw unimplemented(); } }; } 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 Pt longToPt(long l) { return new Pt(firstIntFromLong(l), secondIntFromLong(l)); } static long ptToLong(Pt p) { return p == null ? -1: intPairToLong(p.x, p.y); } static long ptToLong(int x, int y) { return intPairToLong(x, y); } static int modRange_incl(int i, int start, int end) { return start+mod(i-start, end-start+1); } static Pt onePathDirection(int index) { return onePathDirections()[index]; } static Pt onePathDirection(Pt p, int index) { return ptPlus(p, onePathDirections()[index]); } static boolean hasLock(Lock lock) { return ((ReentrantLock) lock).isHeldByCurrentThread(); } static int lLongArray(long[] a) { return a == null ? 0 : a.length; } static long[] resizeLongArray(long[] a, int n) { if (n == lLongArray(a)) return a; long[] b = new long[n]; arraycopy(a, 0, b, 0, Math.min(lLongArray(a), n)); return b; } static ArrayList longArrayToList(long[] a) { if (a == null) return null; return longArrayToList(a, 0, a.length); } // no range checking on from/to in the interest of S P E E E E E EEED static ArrayList longArrayToList(long[] a, int from, int to) { if (a == null) return null; ArrayList < Long > l = new ArrayList<>(to-from); for (int i = from; i < to; i++) l.add(a[i]); return l; } static ListIterator emptyListIterator() { return Collections.emptyListIterator(); } static void stepAllWithStats(Steppable s) { stepAllWithStats(s, null); } static void stepAllWithStats(Steppable s, Long maxSteps) { if (s == null) return; long n = 0; long time = sysNow(); if (maxSteps == null) while (s.step()) { ping(); ++n; } else while (n < maxSteps && s.step()) { ping(); ++n; } time = sysNow()-time; print(n2(n, "step") + " in " + n2(time) + " ms"); } static Rect nullIfEmptyRect(Rect r) { return rectEmpty(r) ? null : r; } static double frac_nonNeg(double d) { return mod(d, 1); } static int firstIntFromLong(long l) { return (int) (l >> 32); } static int secondIntFromLong(long l) { return (int) l; } static long intPairToLong(IntPair p) { return p == null ? 0 : (((long) p.a) << 32) | (((long) p.b) & 0xFFFFFFFF); } static long intPairToLong(int a, int b) { return (((long) a) << 32) | (((long) b) & 0xFFFFFFFF); } // 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 Pt ptPlus(Pt a, Pt b) { return addPts(a, b); } static final class IntPair implements Comparable , IFieldsToList{ int a; int b; IntPair() {} IntPair(int a, int b) { this.b = b; this.a = a;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + a + ", " + b + ")"; }public Object[] _fieldsToList() { return new Object[] {a, b}; } public boolean equals(Object o) { if (!(o instanceof IntPair)) return false; IntPair x = (IntPair) o; return a == x.a && b == x.b; } public int hashCode() { int h = -672893111; h = boostHashCombine(h, _hashCode(a)); h = boostHashCombine(h, _hashCode(b)); return h; } public int compareTo(IntPair p) { if (p == null) return 1; int pa = p.a; if (a < pa) return -1; if (a > pa) return 1; return Integer.compare(b, p.b); } } }