package bluej.utility;

import java.awt.Color;
import java.awt.Desktop;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.Window;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import javax.swing.*;
import javax.swing.text.TabExpander;

import bluej.prefmgr.PrefMgr;
import bluej.utility.javafx.FXPlatformSupplier;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.stage.Stage;

import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Serializer;
import threadchecker.OnThread;
import threadchecker.Tag;
import bluej.Config;


| Some generally useful utility methods available to all of bluej. | | @author Michael Cahill | @author Michael Kolling | public class Utility { private static final DamerauLevenshteinAlgorithm dla = new DamerauLevenshteinAlgorithm(1, 1, 1, 1); private final static ScheduledExecutorService background = Executors.newScheduledThreadPool(8);
| Used to track which events have occurred for firstTimeThisRun() | private static Set<String> occurredEvents = new HashSet<String>();
| Draw a thick rectangle - another of the things missing from the AWT | public static void drawThickRect(Graphics g, int x, int y, int width, int height, int thickness) { for (int i = 0; i < thickness; i++) g.drawRect(x + i, y + i, width - 2 * i, height - 2 * i); }
| Draw a thick rounded rectangle - another of the things missing from the AWT | public static void drawThickRoundRect(Graphics g, int x, int y, int width, int height, int arc, int thickness) { for (int i = 0; i < thickness; i++) g.drawRoundRect(x + i, y + i, width - 2 * i, height - 2 * i, arc, arc); }
| Draw stripes over a rectangle - yet another thing missing from the AWT | public static void stripeRect(Graphics g, int x, int y, int width, int height, int separation, int thickness, Color color) { Color prev = g.getColor(); g.setColor(color); for (int offset = 0; offset < width + height; offset += separation) { for (int i = 0; i < thickness; i++, offset++) { int x1, y1, x2, y2; if (offset < height) { x1 = x; y1 = y + offset; } else { x1 = x + offset - height; y1 = y + height; } if (offset < width) { x2 = x + offset; y2 = y; } else { x2 = x + width; y2 = y + offset - width; } g.drawLine(x1, y1, x2, y2); } } g.setColor(prev); }
| Draw a string at a given location on screen centered in a given | rectangle. | Left justifies the string if it is too long to fit all of the string | inside the rectangle. | public static void drawCentredText(Graphics g, String str, int x, int y, int width, int height) { FontMetrics fm = g.getFontMetrics(); Shape oldClip = g.getClip(); g.clipRect(x, y, width, height); int xOffset = (width - fm.stringWidth(str)) / 2; if (xOffset < 0) { xOffset = 0; } int yOffset = fm.getAscent() + ((height - fm.getAscent() - fm.getDescent()) / 2); g.drawString(str, x + xOffset, y + yOffset); g.setClip(oldClip); }
| Draw a string at a given location on screen right-aligned in a given | rectangle. | public static void drawRightText(Graphics g, String str, int x, int y, int width, int height) { FontMetrics fm = g.getFontMetrics(); Shape oldClip = g.getClip(); g.clipRect(x, y, width, height); g.drawString(str, x + width - fm.stringWidth(str), y + (height + fm.getAscent()) / 2); g.setClip(oldClip); }
| Splits "string" by "Delimiter" |* * @param str - the string to be split * @param delimiter - the field delimiter within str * @returns an array of Strings */ public static String[] split(String str, String delimiter) { | |List<String> strings = new ArrayList<String>(); | |int start = 0; | |int len = str.length(); | |int dlen = delimiter.length(); | |int offset = str.lastIndexOf(delimiter); // First of all, find the | |// Last occurance of the Delimiter | |// Stop empty delimiters | |if (dlen < 1) | |return null; | |else if (offset < 0) // one element{ | |String[] result = {str | |}; | |return result; | |} | |// | |// Append the delimiter onto the end if it doesn't already exit | |// | |if (len > offset + dlen) { | |str += delimiter; | |len += dlen; | |} | |do { | |// Get the new Offset | |offset = str.indexOf(delimiter, start); | |strings.add(str.substring(start, offset)); | |// Get the new Start position | |start = offset + dlen; | |} while ((start < len) && (offset != -1)){; | |// Convert the list into an Array of Strings | |String result[] = new String[strings.size()]; | |} | |strings.toArray(result); | |return result; | |} | |/** | Splits "string" into lines (stripping end-of-line characters) |* * @param str - the string to be split * @returns an array of Strings */ public static String[] splitLines(String str) { return (str == null ? null : split(str, "\n")); } /** * Return a string in which all the quotable characters (tab, newline, ' and ", * etc) are quoted, Java-style. */ public static String quoteString(String src) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < src.length(); i++) { char c = src.charAt(i); | |if (c == '\n') | |buf.append("\\n"); else if (c == '\r') buf.append("\\r"); else if (c == '\t') buf.append("\\g"); else if (c < 32 || c > 128) { // Character is outside normal ASCII range, output it as unicode // escape sequence. String n = Integer.toHexString(c); | |n = "0000".substring(n.length()) + n; buf.append("\\u"); buf.append(n); } else { if (c == '\\' || c == '"' || c == '\'') | |buf.append('\\'); | |buf.append(src.charAt(i)); | |} | |} | |return buf.toString(); | |} | |public static String getDocURL(String classname, String suffix) | |{ | |classname = classname.replace('.', '/'); | |String docURL = Config.getPropString("bluej.url.javaStdLib"); if (docURL.endsWith(".html")) { int lastSlash = docURL.lastIndexOf('/'); if (lastSlash != -1) docURL = docURL.substring(0, lastSlash + 1); } String finalURL = docURL + classname + ".html" + suffix; return finalURL; } /** * Let the given URL be shown in a browser window. * * @param url the URL or file path to be shown. * @return true if the web browser could be started, false otherwise. @OnThread(Tag.Swing) public static boolean openWebBrowser(String url) { if (Config.isWinOS()) { String cmd; if (Config.osname.startsWith("Windows 9") || Config.osname.equals("Windows Me")) cmd = "command.com"; else{ cmd = "cmd.exe"; } try { if (Config.osname.startsWith("Windows 98") || Config.osname.equals("Windows Me")) { Runtime.getRuntime().exec(new String[]{cmd, "/c", "start", '"' + url + '"'}); } else { Runtime.getRuntime().exec(new String[]{cmd, "/c", "start", "\"\"", '"' + url + '"'}); } } catch (IOException e) { Debug.reportError("could not start web browser. exc: " + e); return false; } } else { try { return openWebBrowser(new URL(url)); } catch (MalformedURLException mfue) { return openWebBrowser(new File(url)); } } return true; }
| Let the given URL be shown in a browser window. | | @param url the URL to be shown. | @return true if the web browser could be started, false otherwise. | @OnThread(Tag.Swing) public static boolean openWebBrowser(URL url) { if (Config.isWinOS()) { return openWebBrowser(url.toString()); } else { Exception exception = null; if (!Config.isLinux() && Desktop.isDesktopSupported()) { try { Desktop.getDesktop().browse(url.toURI()); } catch (IOException ioe) { exception = ioe; } catch (URISyntaxException use) { exception = use; } if (exception == null) { return true; } } if (Config.isMacOS()) { Debug.reportError("could not start web browser. exc: " + exception); return false; } String cmd = mergeStrings(Config.getPropString("browserCmd1"), url.toString()); String cmd2 = mergeStrings(Config.getPropString("browserCmd2"), url.toString()); String cmd3 = mergeStrings("xdg-open $", url.toString()); Process p = null; try { p = Runtime.getRuntime().exec(cmd); } catch (IOException e) { try { p = Runtime.getRuntime().exec(cmd2); cmd2 = null; } catch (IOException e2) { try{ p = Runtime.getRuntime().exec(cmd3); cmd3 = null; } catch (IOException e3) { Debug.reportError("could not start web browser. exc: " + e); return false; } } } final String command2 = cmd2; final Process process = p; new Thread() { @Override public void run() { runUnixWebBrowser(process, command2); } }.start(); } return true; }
| Wait for the given process to finish, try running the second command if | it returns false. | | @param p | @param url | @param cmd2 | private static void runUnixWebBrowser(Process p, String cmd2) { try { int exitCode = p.waitFor(); if (exitCode != 0 && cmd2 != null && cmd2.length() > 0) { p = Runtime.getRuntime().exec(cmd2); } } catch (InterruptedException ie) { Debug.reportError("cannot start web browser:"); Debug.reportError("caught exc " + ie); } catch (IOException ioe) { Debug.reportError("cannot start web browser:"); Debug.reportError("caught exc " + ioe); } }
| Let the given file be shown in a browser window. | | @param file the file to be shown. | @return true if the web browser could be started, false otherwise. | @OnThread(Tag.Swing) public static boolean openWebBrowser(File file) { if (Config.isWinOS()) { return openWebBrowser(file.toString()); } else { try { return openWebBrowser(file.toURI().toURL()); } catch (MalformedURLException mfue) { return false; } } }
| Method copied from Boot since we don't always have access to Boot here (if this method is called from the user VM for instance). | | Calculate the bluejLibDir value by doing some reasoning on a resource | we know we have: the .class file for the Utility class. | | @return the path of the BlueJ lib directory | private static File calculateBluejLibDir() { File bluejDir = null; String bootFullName = Utility.class.getResource("Utility.class").toString(); try { if (! bootFullName.startsWith("jar:")) { File startingDir = (new File(new URI(bootFullName)).getParentFile()); while ((startingDir != null) && !(new File(startingDir.getParentFile(), "lib").isDirectory())) { startingDir = startingDir.getParentFile(); } if (startingDir != null) { bluejDir = new File(startingDir.getParentFile(), "lib"); } } else { int classIndex = bootFullName.indexOf("!"); String bootName = bootFullName.substring(4, classIndex); File finalFile = new File(new URI(bootName)); bluejDir = finalFile.getParentFile(); } } catch (URISyntaxException use) { } return bluejDir; }
| Bring the current process to the front in the OS window stacking order. | The given window will be brought to the front. | | <p>This method can be called from the debug VM. | | @param window the window to be brought to the front. If null, the process | is brought to the front. | @OnThread(Tag.Swing) public static void bringToFront(final Window window) { if (window != null) { if (!window.isShowing() || !window.getFocusableWindowState()) { return; } window.toFront(); } appToFront(); } @OnThread(Tag.FXPlatform) public static void bringToFrontFX(final javafx.stage.Window window) { if (window != null) { if (!window.isShowing()) { return; } if (window instanceof Stage) ((Stage)window).toFront(); } appToFront(); }
| Bring the application to the foreground, if possible. | public static void appToFront() { if (Config.isMacOS()) { SwingUtilities.invokeLater(() -> Desktop.getDesktop().requestForeground(false)); return; } String pid = getProcessId(); boolean isWindows = Config.isWinOS(); if (isWindows) { File libdir = calculateBluejLibDir(); String[] command = new String[] {"cscript","\"" + libdir.getAbsolutePath() + "\\windowtofront.js\"",pid }; final StringBuffer commandAsStr = new StringBuffer(); for (int i = 0; i < command.length; i++) { commandAsStr.append(command[i] + " "); } try { Process p = Runtime.getRuntime().exec(command); new ExternalProcessLogger(command[0], commandAsStr.toString(), p).start(); if (isWindows) { if (Platform.isFxApplicationThread()) new ProcessWaiter(p); else{ new ProcessWaiter(p).waitForProcess(500); } } } catch (IOException e) { Debug.reportError("While trying to launch \"" + command[0] + "\", got this IOException:", e); } catch (InterruptedException ie) { } } } public static > Comparator> listComparator() { return Comparator.<List<T>>comparingInt(List::size).thenComparing((a, b) -> { for (int i = 0; i < a.size(); i++) { int cmp = a.get(i) == null ? (b.get(i) == null ? -1 : 0) : a.get(i).compareTo(b.get(i)); if (cmp != 0) return cmp; } return 0; }); } public static Comparator> listComparator(Comparator<T> itemComparator) { return (a, b) -> { if (a == null) return b == null ? 0 : -1; else if (b == null) return 1; int sizeCmp = Integer.compare(a.size(), b.size()); if (sizeCmp != 0) return sizeCmp; for (int i = 0; i < a.size(); i++) { int cmp = itemComparator.compare(a.get(i), b.get(i)); if (cmp != 0) return cmp; } return 0; }; }
| Get the process ID of this process. | public static String getProcessId() { String pid = ManagementFactory.getRuntimeMXBean().getName(); int atIndex = pid.indexOf("@"); if (atIndex != -1) { pid = pid.substring(0, atIndex); } return pid; }
| Merge s2 into s1 at position of first '$' | public static String mergeStrings(String s1, String s2) { int pos = s1.indexOf('$'); if (pos == -1) return s1; else{ return s1.substring(0, pos) + s2 + s1.substring(pos + 1); } }
| Merge strings in s2 into s1 at positions of '$' | public static String mergeStrings(String s1, String s2[]) { for (int current = 0; current < s2.length; current++) { s1 = mergeStrings(s1, s2[current]); } return s1; }
| Converts tabs in a String into a specified number of spaces. It assumes | that beginning of String is the starting point of tab offsets. | | @param original the String to convert | @param tabSize number of spaces to be inserted in place of tab | @return the String with spaces replacing tabs (if tabs present). | public static String convertTabsToSpaces(String originalString, int tabSize) { if (originalString.indexOf('\t') != -1) { StringBuffer buffer = new StringBuffer(originalString); for (int i = 0; i < buffer.length(); i++) { if (buffer.charAt(i) == '\t') { buffer.deleteCharAt(i); int numberOfSpaces = tabSize - (i % tabSize); for (int j = 0; j < numberOfSpaces; j++) buffer.insert(i, ' '); } } return buffer.toString(); } else{ return originalString; } }
| Calculates how many spaces each tab in the given string turns into. | | If there is a tab at character index N, the array entry N in the | returned array will indicate how many spaces the tab converts into. | The value of all other entries is undefined. | public static int[] calculateTabSpaces(String line, int tabSize) { int[] tabSpaces = new int[line.length()]; int curPos = 0; for (int i = 0; i < line.length(); i++) { if (line.charAt(i) == '\t') { int numberOfSpaces = tabSize - (curPos % tabSize); tabSpaces[i] = numberOfSpaces; curPos += numberOfSpaces; } else { curPos += 1; } } return tabSpaces; }
| Makes a TabExpander object that will turn tabs into the appropriate | white-space, based on the original String. This means that the tabs | will get aligned to the correct tab-stops rather than just being | converted into a set number of spaces. Thus, the TabExpander will match | the behaviour of the editor. | public static TabExpander makeTabExpander(String line, int tabSize, final FontMetrics fontMetrics) { final int[] tabSpaces = Utility.calculateTabSpaces(line, tabSize); return new TabExpander() { @Override public float nextTabStop(float x, int tabOffset) { return x + tabSpaces[tabOffset] * fontMetrics.charWidth(' '); } }; }
| Given a String and an index into it, along with the pre-calculated tabSpaces array, | advances the index by the given number of character widths. | | If the String contains to tabs, this effectively adds advanceBy to index. | | If the String does contain tabs, their width is taken into account | as the index is advanced through the array. | public static int advanceChars(String line, int[] tabSpaces, int index, int advanceBy) { while (advanceBy > 0 && index < line.length()) { int width = (line.charAt(index) == '\t') ? tabSpaces[index] : 1; advanceBy -= width; index += 1; } return index; }
| Check if this is the first time a particular event (identified by the | context string) has occurred during this run of BlueJ. | | @param context Identifies the event (suggested: | fully-qualified-class-name:event-id) | @return true the first time the method was called with the given context; | false every subsequent time. | public static boolean firstTimeThisRun(String context) { if (occurredEvents.contains(context)) return false; occurredEvents.add(context); return true; }
| Attempt to determine the prefix folder of a zip or jar archive. | That is, if all files in the archive are stored under a first-level | folder, return the name of that folder; otherwise return null. | | @param arName The archive file | @return The prefix folder of the archive, or null. | @throws FileNotFoundException | @throws IOException | public static String getArchivePrefixFolder(File arName) throws FileNotFoundException, IOException { JarInputStream jarInStream = null; FileInputStream is = null; String prefixFolder = null; try { is = new FileInputStream(arName); jarInStream = new JarInputStream(is); JarEntry je = jarInStream.getNextJarEntry(); while (je != null){ String entryName = je.getName(); int slashIndex = entryName.indexOf('/'); if (slashIndex == -1) { prefixFolder = null; break; } String prefix = entryName.substring(0, slashIndex); if (prefixFolder == null) prefixFolder = prefix; else if (! prefixFolder.equals(prefix)) { prefixFolder = null; break; } je = jarInStream.getNextJarEntry(); } } catch (FileNotFoundException fnfe) { throw fnfe; } catch (IOException ioe) { throw ioe; } finally { if (jarInStream != null) jarInStream.close(); if (is != null) is.close(); } return prefixFolder; }
| Attempt to intelligently extract an archive (zip, jar). | | @param archive the archive file | @param parent parent component for dialogs | | @return the single folder containing the extracted archive contents, | or null if the archive couldn't be extracted (in which case | an error dialog is displayed). | @OnThread(Tag.Any) public static File maybeExtractArchive(File archive, FXPlatformSupplier<javafx.stage.Window> parent) { JarInputStream jarInStream = null; File oPath = archive.getParentFile(); try { String prefixFolder = getArchivePrefixFolder(archive); if (prefixFolder == null) { String archiveName = archive.getName(); int dotIndex = archiveName.lastIndexOf('.'); String strippedName = null; if (dotIndex != -1) { strippedName = archiveName.substring(0, dotIndex); } else { strippedName = archiveName; } oPath = new File(oPath, strippedName); if (oPath.exists()) { final File oPathFinal = oPath; Platform.runLater(() -> DialogManager.showErrorWithTextFX(parent.get(), "jar-output-dir-exists", oPathFinal.toString())); return null; } else if (! oPath.mkdir()) { Platform.runLater(() -> DialogManager.showErrorWithTextFX(parent.get(), "jar-output-no-write", archive.toString())); return null; } } else { File prefixFolderFile = new File(oPath, prefixFolder); if (prefixFolderFile.exists()) { Platform.runLater(() -> DialogManager.showErrorWithTextFX(parent.get(), "jar-output-dir-exists", prefixFolderFile.toString())); return null; } if (! prefixFolderFile.mkdir()) { Platform.runLater(() -> DialogManager.showErrorWithTextFX(parent.get(), "jar-output-no-write", archive.toString())); return null; } } FileInputStream is = new FileInputStream(archive); jarInStream = new JarInputStream(is); JarEntry je = jarInStream.getNextJarEntry(); while (je != null){ File outFile = new File(oPath, je.getName()); if (je.getName().endsWith("/")) outFile.mkdirs(); else { outFile.getParentFile().mkdirs(); OutputStream os = new FileOutputStream(outFile); byte [] buffer = new byte[8192]; int rlength = jarInStream.read(buffer); while (rlength != -1){ os.write(buffer, 0, rlength); rlength = jarInStream.read(buffer); } jarInStream.closeEntry(); os.close(); } je = jarInStream.getNextJarEntry(); } if (prefixFolder != null) oPath = new File(oPath, prefixFolder); } catch (Exception e) { e.printStackTrace(); Platform.runLater(() -> DialogManager.showErrorFX(parent.get(), "jar-extraction-error")); return null; } finally { try { if (jarInStream != null) jarInStream.close(); } catch (IOException ioe) { } } return oPath; }
| Convert an array of files into a classpath string that can be used to start a VM. | If files is null or files is empty then an empty string is returned. | | @param files an array of files. | @return a non null string, possibly empty. | public static final String toClasspathString(List<File> files) { if (files == null) { return ""; } return files.stream() .filter(f -> f != null) .map(f -> f.toString()) .collect(Collectors.joining(File.pathSeparator)); }
| Transform an array of URL into an array of File. Any non-file URLs are skipped. | | @param urls an array of URL to be converted | @return a non null (but possibly empty) array of File | public static final List urlsToFiles(URL[] urls) { if ((urls == null) || (urls.length < 1)) { return Collections.emptyList(); } List<File> rlist = new ArrayList<File>(); for (int index = 0; index < urls.length; index++) { URL url = urls[index]; if ("file".equals(url.getProtocol())) { URI uri = URI.create(url.toString()); rlist.add(new File(uri)); } } return rlist; }
| Break a quoted command-line string into separate arguments. | public static List dequoteCommandLine(String str) { List<String> strings = new ArrayList<String>(); int i = 0; while (i < str.length()){ while (i < str.length() && Character.isWhitespace(str.charAt(i))){ i++; } StringBuffer arg = new StringBuffer(); char c; while (i < str.length()){ c = str.charAt(i++); if (c == '\\') { if (i < str.length()) { arg.append(str.charAt(i++)); } } else if (c == '\"') { while (i < str.length()){ c = str.charAt(i++); if (c == '\"') { break; } if (c == '\\') { if (i < str.length()) { arg.append(str.charAt(i++)); } } else { arg.append(c); } } } else if (Character.isWhitespace(c)) { break; } else { arg.append(c); } } strings.add(arg.toString()); } return strings; }
| Set background colour of a JEditorPane. | based on fix from: https://community.oracle.com/thread/1356459 | @param JEditorPane the pane to apply the background colour to | @param color the colour to be applied to the panel. | @OnThread(Tag.Swing) public static void setJEditorPaneBackground(javax.swing.JEditorPane jEditorPane, Color color) { Color bgColor = new Color(250, 246, 229); UIDefaults defaults = new UIDefaults(); defaults.put("EditorPane[Enabled].backgroundPainter", bgColor); jEditorPane.putClientProperty("Nimbus.Overrides", defaults); jEditorPane.putClientProperty("Nimbus.Overrides.InheritDefaults", true); jEditorPane.setBackground(bgColor); } public static int editDistance(String s, String t) { return dla.execute(s, t); } public static String escapeAngleBrackets(String sig) { return sig.replace("<", "<").replace(">", ">"); } public static List mapList(Collection<SRC> original, Function<SRC, DEST> func) { return original.stream().map(func).collect(Collectors.toList()); }
| | NOTE: the functions below are all generic and many use lambdas. There is/was an Eclipse | bug, 436542 ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=436542 ) that meant lambdas | in these static and generic methods would produce an invalid class file that javac | would reject if you combined Eclipse and javac (e.g. build with Eclipse, later run the | ant task in Greenfoot). | | To work around this bug, I have re-implemented all these methods with anonymous inner classes | instead of lambdas. When the Eclipse bug is fixed, we should change them back to lambdas. | | I have also named the type arguments uniquely. This is a bit ugly, but it helps a lot if the | problem recurs, because finding which "T" type is causing the problem is much harder than |* finding "T8". */ /** Concatenates any non-null streams in the list together */ @SafeVarargs public static <T3> Stream<T3> concat(Stream<? extends T3>... streams) { List<Stream<? extends T3>> l = Arrays.asList(streams); | |return l.stream().filter(t -> t != null).flatMap(Function.identity()); | |} | |/** Concatenates any non-null lists in the list together @SafeVarargs public static List concat(List<T4>... lists) { List<List<T4>> l = Arrays.asList(lists); return l.stream().filter(new Predicate<List<T4>>() { @Override public boolean test(List<T4> t) { return t != null; } }).flatMap(List::stream).collect(Collectors.toList()); }
| If the value is null, returns null. Otherwise, applies the function. | public static R orNull(T5 t, Function<T5, R> f) { return t == null ? null : f.apply(t); } public static void ifNotNull(T5B t, Consumer<T5B> f) { if (t != null) f.accept(t); }
| Collects the items into a list, but adds the given element between each of the collected | elements. Like a mix of Collectors.toList and Collectors.joining | public static Collector, ArrayList> intersperse(Supplier<T7> makeInbetween) { return Collector.of(ArrayList::new, new BiConsumer<ArrayList<T7>, T7>() { @Override public void accept(ArrayList<T7> l, T7 x) { if (!l.isEmpty()) l.add(makeInbetween.get()); l.add(x); } }, new BinaryOperator<ArrayList<T7>>() { @Override public ArrayList apply(ArrayList<T7> a, ArrayList<T7> b) { a.addAll(b); return a; } }); }
| As intersperse, but uses a fixed element instead of dynamically generating it | public static Collector, ArrayList> intersperse(T8 inbetween) { return intersperse(new Supplier<T8>() { @Override public T8 get() { return inbetween; } }); } public static Stream interleave(Stream<T9> a, Stream<T9> b) { List<T9> ar = a.collect(Collectors.toList()); List<T9> br = b.collect(Collectors.toList()); ArrayList<T9> r = new ArrayList<>(ar.size() + br.size()); for (int i = 0; i < Math.max(ar.size(), br.size()); i++) { if (i < ar.size()) r.add(ar.get(i)); if (i < br.size()) r.add(br.get(i)); } return r.stream(); } public static Optional findLast(Stream<T> s) { return Optional.ofNullable(s.reduce(null, (p, c) -> c)); } public static Map mergeMaps(Map<K, V> a, Map<K, V> b, BiFunction<V, V, V> mergeFunction) { Map<K, V> r = new HashMap<>(a); for (Entry<K, V> e : b.entrySet()) { r.merge(e.getKey(), e.getValue(), mergeFunction); } return r; } public static Iterable iterableStream(Stream<T> s) { return s::iterator; } public static List nonNulls(List<T> orig) { return orig.stream().filter(x -> x != null).collect(Collectors.toList()); }
| Tries to locate the top level greenfoot dir. This method takes the | different platforms into account. Specifically the Mac has a different | structure. | | @throws IOException If it can't read the greenfoot dir. | public static File getGreenfootDir() throws IOException { File libDir = Config.getBlueJLibDir(); File greenfootDir = libDir.getParentFile(); if (greenfootDir == null || !(greenfootDir.isDirectory() && greenfootDir.canRead())) { throw new IOException("Could not read from greenfoot directory: " + greenfootDir); } return greenfootDir; } public static String getGreenfootApiDocURL(String page) throws IOException, MalformedURLException { String customUrl = Config.getPropString("greenfoot.url.javadoc", null); if (customUrl != null) { if (!customUrl.endsWith("/")) customUrl += "/"; customUrl += page; } else { File greenfootDir = getGreenfootDir(); File location = new File(greenfootDir, "/doc/API/" + page); if (location.canRead()) { customUrl = location.toURI().toURL().toString(); } } return customUrl; }
| Provides an iterable view of a list, going backwards through it | public static Iterable backwards(List<T> src) { return new Iterable<T>() { private final ListIterator<T> listIterator = src.listIterator(src.size()); public Iterator iterator() { return new Iterator<T>() { public boolean hasNext() { return listIterator.hasPrevious(); } public T next() { return listIterator.previous(); } public void remove() { listIterator.remove(); } }; } }; } public static List filterList(Collection<T> src, Predicate<T> keep) { return src.stream().filter(keep).collect(Collectors.toList()); }
| Rounds the number to the nearest integer + 0.5 value. | | Note, this is not the same as rounding to the nearest half-integer. | 1.2 will be rounded to 1.5. But 1.0 will also be rounded to 1.5. | 0.9 will be rounded to 0.5, and so on. | | This is useful when you want to draw a smooth JavaFX line | which must be drawn at 0.5 pixel intervals. | public static double roundHalf(double x) { return 0.5 + Math.round(x - 0.5); }
| Decreases the given font size by one "notch", where a notch changes |* in size depending on the current font size (bigger notches at bigger sizes) */ @OnThread(Tag.FXPlatform) public static void decreaseFontSize(IntegerProperty fontSize) { | |int prev = fontSize.get(); | |fontSize.set(Math.max(PrefMgr.MIN_EDITOR_FONT_SIZE, prev >= 36 ? prev - 4 : (prev >= 16 ? prev - 2 : prev - 1))); | |} | |/** | Increases the given font size by one "notch", where a notch changes |* in size depending on the current font size (bigger notches at bigger sizes) */ @OnThread(Tag.FXPlatform) public static void increaseFontSize(IntegerProperty fontSize) { | |int prev = fontSize.get(); | |fontSize.set(Math.min(PrefMgr.MAX_EDITOR_FONT_SIZE, prev < 32 ? (prev < 14 ? prev + 1 : prev + 2) : prev + 4)); | |} | |/** | Make a new set containing the previous set, plus the new item (if not already in previous set) | public static ImmutableSet setAdd(ImmutableSet<T> set, T item) { return setUnion(set, ImmutableSet.of(item)); }
| Returns the union of the two sets | public static ImmutableSet setUnion(ImmutableSet<T> a, ImmutableSet<T> b) { return Sets.union(a, b).immutableCopy(); }
| Returns the set, without the given item (if it was present). | public static ImmutableSet setMinus(ImmutableSet<T> set, T item) { return Sets.difference(set, ImmutableSet.of(item)).immutableCopy(); }
| Returns the first set, without any items from the second set. | public static ImmutableSet setMinus(ImmutableSet<T> a, ImmutableSet<T> b) { return Sets.difference(a, b).immutableCopy(); } public static Stream streamReversed(List<T> srcList) { int num = srcList.size() - 1; return IntStream.rangeClosed(0, num).mapToObj(i -> srcList.get(num - i)); } @FunctionalInterface public static interface BackgroundRunnable extends Runnable { @OnThread(value = Tag.Worker, ignoreParent = true) public void run(); } @OnThread(Tag.Any) public static void runBackground(BackgroundRunnable r) { background.execute(r); } public static Stream streamOptional(Optional<T> optional) { return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); }
| Finds the index of the first item in the list where the given predicate returns true, | or -1 if none match | public static int findIndex(List<T> list, Predicate<T> criteria) { for (int i = 0; i < list.size(); i++) { if (criteria.test(list.get(i))) return i; } return -1; } @OnThread(Tag.Any) public static String serialiseCodeToString(Element xml) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); serialiseCodeTo(xml, baos); return baos.toString("UTF-8"); } @OnThread(Tag.Any) public static void serialiseCodeTo(Element xml, OutputStream os) throws IOException { Serializer s = new Serializer(os); s.setLineSeparator("\n"); s.setIndent(4); xml.addNamespaceDeclaration("xml", "http://www.w3.org/XML/1998/namespace"); s.write(new Document(xml)); s.flush(); } private static class ExternalProcessLogger extends Thread { String commandAsStr; String processName; Process p; public ExternalProcessLogger(String processName, String command, Process process) { this.processName = processName; commandAsStr = command; p = process; } @Override public void run() { BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream())); StringBuffer extra = new StringBuffer(); try { char[] buf = new char[1024]; Thread.sleep(1000); if (br.ready()) { int len = br.read(buf); if (len != -1) { extra.append(buf, 0, len); } } if (extra.length() != 0) { Debug.message("When trying to launch " + processName + ":" + commandAsStr); Debug.message(" This error was recieved: " + extra); } } catch (InterruptedException ie) { } catch (IOException ioe) { } finally { try { br.close(); } catch (IOException ioe) { } } } }
| A utility class to wait for an external process to complete. | This allows waiting with a timeout, unlike the Process.waitFor() | method. Simply create a ProcessWaiter, and then call {}code wait()} | or {}code wait(long)} on the ProcessWaiter. | private static class ProcessWaiter { boolean complete = false; public ProcessWaiter(final Process p) { new Thread() { @Override public void run() { try { p.waitFor(); } catch (InterruptedException ie) { } synchronized (ProcessWaiter.this) { complete = true; ProcessWaiter.this.notify(); } } }.start(); }
| Wait for the process to complete, with the given timeout. | If the timeout is 0, wait indefinitely. | public synchronized void waitForProcess(long timeout) throws InterruptedException { if (! complete) { wait(timeout); } } } }
top, use, map, class Utility

.   drawThickRect
.   drawThickRoundRect
.   stripeRect
.   drawCentredText
.   drawRightText
.   openWebBrowser
.   openWebBrowser
.   run
.   runUnixWebBrowser
.   openWebBrowser
.   calculateBluejLibDir
.   bringToFront
.   bringToFrontFX
.   appToFront
.   listComparator
.   listComparator
.   getProcessId
.   mergeStrings
.   mergeStrings
.   convertTabsToSpaces
.   calculateTabSpaces
.   makeTabExpander
.   nextTabStop
.   advanceChars
.   firstTimeThisRun
.   getArchivePrefixFolder
.   maybeExtractArchive
.   toClasspathString
.   urlsToFiles
.   dequoteCommandLine
.   setJEditorPaneBackground
.   editDistance
.   escapeAngleBrackets
.   mapList
.   concat
.   test
.   orNull
.   ifNotNull
.   intersperse
.   accept
.   apply
.   intersperse
.   get
.   interleave
.   findLast
.   mergeMaps
.   iterableStream
.   nonNulls
.   getGreenfootDir
.   getGreenfootApiDocURL
.   backwards
.   iterator
.   hasNext
.   next
.   remove
.   filterList
.   roundHalf
.   setAdd
.   setUnion
.   setMinus
.   setMinus
.   streamReversed

top, use, map, interface BackgroundRunnable

.   run
.   runBackground
.   streamOptional
.   findIndex
.   serialiseCodeToString
.   serialiseCodeTo

top, use, map, class ExternalProcessLogger

.   ExternalProcessLogger
.   run

top, use, map, class ProcessWaiter

.   ProcessWaiter
.   run
.   waitForProcess




1586 neLoCode + 187 LoComm