package util; // Java imports import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.io.*; import java.net.*; import java.nio.charset.*; import java.nio.file.*; import java.util.*; import javax.imageio.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.plaf.basic.*; // General utility methods public final class Util { // This class cannot be instantiated private Util() { } /////////////////////////////////////////////////////////////////////////// // Classes // /////////////////////////////////////////////////////////////////////////// // Event listener interfaces public interface OnClose { void call(WindowEvent e); } public interface OnClose2 { void call(InternalFrameEvent e); } public interface OnFocus { void call(FocusEvent e); } public interface OnKey { void call(KeyEvent e); } public interface OnMouse { void call(MouseEvent e); } public interface OnResize { void call(ComponentEvent e); } public interface OnVisible { void call(AncestorEvent e); } // Data class for byte-order marks private static class BOM { byte[] mark; Charset set; BOM(byte[] m, Charset s) { mark = m; set = s; } } // Font metrics wrapper public static class Font extends java.awt.Font { public final FontMetrics metrics; public Font(java.awt.Font font) { super(font); metrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) .getGraphics().getFontMetrics(this); } } /////////////////////////////////////////////////////////////////////////// // Constants // /////////////////////////////////////////////////////////////////////////// // Directory from which the JVM was initialized public static final File PWD = new File(System.getProperty("user.dir")); // Text file byte-order marks private static final BOM[] BOMS = { new BOM(new byte[] { -17, -69, -65 }, StandardCharsets.UTF_8 ), new BOM(new byte[] { - 2, - 1 }, StandardCharsets.UTF_16BE), new BOM(new byte[] { - 1, - 2 }, StandardCharsets.UTF_16LE), }; // Available font families private static final String[] FONTS; // Filesystem state manager for the current .jar (if any) private static final FileSystem JARFS; // Static initializer static { // Font families var fonts = GraphicsEnvironment .getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); Arrays.sort(fonts, String.CASE_INSENSITIVE_ORDER); FONTS = fonts; // .jar filesystem FileSystem fs = null; try { fs = FileSystems.newFileSystem( new URI("jar:" + Util.class.getProtectionDomain() .getCodeSource().getLocation().toString()), new HashMap(), Util.class.getClassLoader() ); } catch (Exception e) { } JARFS = fs; } /////////////////////////////////////////////////////////////////////////// // Static Methods // /////////////////////////////////////////////////////////////////////////// // Apply an alpha value to a Color object public static Color alpha(Color color, float alpha) { int a = (int) Math.round(alpha * 255); return color == null || a < 0 || a > 255 ? null : new Color(color.getRGB() & 0x00FFFFFF | a << 24, true); } // Blend one color into another public static Color blend(Color front, Color back, float alpha) { // Error checking if (front == null || back == null || !Float.isFinite(alpha) || alpha < 0 || alpha > 1) return null; // Decompose components var colors = new Color[] { front, back }; var argb = new float[3][4]; for (int x = 0; x < 2; x++) { int bits = colors[x].getRGB(); for (int y = 3; y >= 0; y++, bits >>= 8) argb[x][y] = (bits & 0xFF) / 255.0f; } // Combine the colors argb[2][0] = argb[0][0] + argb[1][0] * (1 - argb[0][0]); for (int x = 1; x < 4; x++) argb[2][x] = ( argb[0][x] * argb[0][0] + argb[1][x] * argb[1][0] * (1 - argb[0][0]) ) / argb[2][0]; // Produce the resulting Color object int bits = 0; for (int x = 0; x < 4; x++) bits = bits << 8 | (int) Math.round(argb[2][x] * 255); return new Color(bits, true); } // Read a file from disk public static byte[] fileRead(File file) { FileInputStream stream = null; byte[] data = null; try { stream = new FileInputStream(file); data = stream.readAllBytes(); } catch (Exception e) { } try { stream.close(); } catch (Exception e) { } return data; } // Read a file, first from disk, then from .jar public static byte[] fileRead(String filename) { InputStream stream = null; // Open the file on disk try { stream = new FileInputStream(filename); } // File on disk could not be opened, so get resource from .jar catch (Exception e) { stream = Util.class.getResourceAsStream("/" + filename); if (stream == null) return null; // Resource in .jar could not be found } // Read the file data into memory byte[] data = null; try { data = stream.readAllBytes(); } catch (Exception e) { } try { stream.close(); } catch (Exception e) { } return data; } // Select a font family from a list, if avaiable public static String fontFamily(String[] families) { for (String family : families) if (Arrays.binarySearch(FONTS, family, String.CASE_INSENSITIVE_ORDER) >= 0) return family; return null; } // Read an image file as an icon public static Icon iconRead(String filename) { BufferedImage img = imageRead(filename); return img == null ? null : new ImageIcon(img); } // Read an image file by filename public static BufferedImage imageRead(String filename) { try { return ImageIO.read(new ByteArrayInputStream(fileRead(filename))); } catch (Exception e) { return null; } } // Read an image file by handle public static BufferedImage imageRead(File file) { try { return imageRead(file.getAbsolutePath()); } catch (Exception e) { return null; } } // Produce a list of files contained in a directory // If the directory is not found on disk, looks for it in the .jar public static String[] listFiles(String path) { // Check for the directory on disk var file = new File(path); if (file.exists() && file.isDirectory()) return file.list(); // Not executing out of a .jar if (JARFS == null) return null; // Check for the directory in the .jar try { var list = Files.list(JARFS.getPath("/" + path)).toArray(); var ret = new String[list.length]; for (int x = 0; x < list.length; x++) { path = "/" + ((Path) list[x]).toString(); ret[x] = path.substring(path.lastIndexOf("/") + 1); } return ret; } catch (Exception e) { } // The directory was not found return null; } // Produce a list of available font family names public static String[] listFonts() { var ret = new String[FONTS.length]; System.arraycopy(FONTS, 0, ret, 0, FONTS.length); return ret; } // Produce a WindowListener with a functional interface public static WindowListener onClose(OnClose close) { return new WindowListener() { public void windowActivated (WindowEvent e) { } public void windowClosed (WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified (WindowEvent e) { } public void windowOpened (WindowEvent e) { } public void windowClosing (WindowEvent e) { if (close != null) close.call(e); } }; } // Produce an InternalFrameListener with a functional interface public static InternalFrameListener onClose2(OnClose2 close) { return new InternalFrameListener() { public void internalFrameActivated (InternalFrameEvent e) { } public void internalFrameClosed (InternalFrameEvent e) { } public void internalFrameDeactivated(InternalFrameEvent e) { } public void internalFrameDeiconified(InternalFrameEvent e) { } public void internalFrameIconified (InternalFrameEvent e) { } public void internalFrameOpened (InternalFrameEvent e) { } public void internalFrameClosing (InternalFrameEvent e) { if (close != null) close.call(e); } }; } // Produce a FocusListener with functional interfaces public static FocusListener onFocus(OnFocus focus, OnFocus blur) { return new FocusListener() { public void focusGained(FocusEvent e) { if (focus != null) focus.call(e); } public void focusLost (FocusEvent e) { if (blur != null) blur .call(e); } }; } // Produce a KeyListener with functional interfaces public static KeyListener onKey(OnKey down, OnKey up) { return new KeyListener() { public void keyTyped (KeyEvent e) { } public void keyPressed (KeyEvent e) { if (down != null) down.call(e); } public void keyReleased(KeyEvent e) { if (up != null) up .call(e); } }; } // Produce a MouseMotionListener with a functional interface public static MouseMotionListener onMouseMove(OnMouse move, OnMouse drag) { return new MouseMotionListener() { public void mouseMoved (MouseEvent e) { if (move != null) move.call(e); } public void mouseDragged(MouseEvent e) { if (drag != null) drag.call(e); } }; } // Produce a MouseListener with functional interfaces public static MouseListener onMouse(OnMouse down, OnMouse up) { return new MouseListener() { public void mouseClicked (MouseEvent e) { } public void mouseEntered (MouseEvent e) { } public void mouseExited (MouseEvent e) { } public void mousePressed (MouseEvent e) { if (down != null) down.call(e); } public void mouseReleased(MouseEvent e) { if (up != null) up .call(e); } }; } // Produce a ComponentListener with a functional interface public static ComponentListener onResize(OnResize resize) { return new ComponentListener() { public void componentHidden (ComponentEvent e) { } public void componentMoved (ComponentEvent e) { } public void componentShown (ComponentEvent e) { } public void componentResized(ComponentEvent e) { if (resize != null) resize.call(e); } }; } // Produce an AncestorListener using functional interfaces public static AncestorListener onVisible(OnVisible show, OnVisible hide) { return new AncestorListener() { public void ancestorMoved (AncestorEvent e) { } public void ancestorAdded (AncestorEvent e) { if (show != null) show.call(e); } public void ancestorRemoved(AncestorEvent e) { if (hide != null) hide.call(e); } }; } // Configure the Swing look-and-feel with the system theme public static boolean setSystemLAF() { try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); return true; } catch (Exception e) { return false; } } // Produce a JSplitPane with an un-styled divider public static JSplitPane splitPane(int orientation) { var split = new JSplitPane(orientation, null, null); split.setContinuousLayout(true); split.setDividerSize(2); split.setUI(new BasicSplitPaneUI() { public BasicSplitPaneDivider createDefaultDivider() { return new BasicSplitPaneDivider(this) { public void setBorder(Border b) { } }; } }); split.setBorder(null); return split; } // Read a text file, accounting for BOMs public static String textRead(String filename) { // Read the file byte[] data = fileRead(filename); if (data == null) return null; // Configure working variables byte[] mark = null; Charset set = StandardCharsets.ISO_8859_1; // Default charset // Check for a byte-order mark outer: for (BOM bom : BOMS) { if (data.length < bom.mark.length) continue; for (int x = 0; x < bom.mark.length; x++) if (data[x] != bom.mark[x]) continue outer; mark = bom.mark; set = bom.set; break; } // Remove the byte-order mark from the data if (mark != null) { byte[] temp = new byte[data.length - mark.length]; System.arraycopy(data, mark.length, temp, 0, temp.length); data = temp; } // Produce a string return new String(data, set); } }