import javax.imageio.*; import java.awt.image.*; import java.awt.event.*; import java.awt.*; import java.security.NoSuchAlgorithmException; import java.security.MessageDigest; import java.lang.management.*; import java.lang.ref.*; import java.lang.reflect.*; import java.net.*; import java.io.*; import javax.swing.table.*; import javax.swing.text.*; import javax.swing.event.*; import javax.swing.*; import java.util.concurrent.atomic.*; import java.util.concurrent.*; import java.util.regex.*; import java.util.List; import java.util.zip.*; import java.util.*; class RGB { public final float r, g, b; 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 brightness) { this.r = this.g = this.b = (float) brightness; } public RGB(Color color) { this.r = color.getRed()/255f; this.g = color.getGreen()/255f; this.b = color.getBlue()/255f; } public RGB(String hex) { r = Integer.parseInt(hex.substring(0, 2), 16)/255f; g = Integer.parseInt(hex.substring(2, 4), 16)/255f; b = Integer.parseInt(hex.substring(4, 6), 16)/255f; } public float getComponent(int i) { return i == 0 ? r : i == 1 ? g : b; } public Color getColor() { return new Color(r, g, b); } public static RGB newSafe(float r, float g, float b) { return new RGB(Math.max(0, Math.min(1, r)), Math.max(0, Math.min(1, g)), Math.max(0, Math.min(1, b))); } public int asInt() { return getColor().getRGB() & 0xFFFFFF; } public float getBrightness() { return (r+g+b)/3.0f; } public String getHexString() { return Integer.toHexString(asInt() | 0xFF000000).substring(2).toUpperCase(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof RGB)) return false; RGB rgb = (RGB) o; if (Float.compare(rgb.b, b) != 0) return false; if (Float.compare(rgb.g, g) != 0) return false; if (Float.compare(rgb.r, r) != 0) return false; return true; } @Override public int hashCode() { int result = (r != +0.0f ? Float.floatToIntBits(r) : 0); result = 31 * result + (g != +0.0f ? Float.floatToIntBits(g) : 0); result = 31 * result + (b != +0.0f ? Float.floatToIntBits(b) : 0); return result; } public boolean isBlack() { return r == 0f && g == 0f && b == 0f; } public boolean isWhite() { return r == 1f && g == 1f && b == 1f; } public String toString() { return getHexString(); } } class RGBImage { private BufferedImage bufferedImage; private File file; private int width, height; private int[] pixels; // color returned when getPixel is called with out-of-bounds position private int background = 0xFFFFFF; public RGBImage(BufferedImage image) { this(image, null); } public RGBImage(BufferedImage image, File file) { this.file = file; bufferedImage = image; width = image.getWidth(); height = image.getHeight(); pixels = new int[width*height]; PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width); try { if (!pixelGrabber.grabPixels()) throw new RuntimeException("Could not grab pixels"); cleanPixels(); // set upper byte to 0 } catch (InterruptedException e) { throw new RuntimeException(e); } } /** We assume it's a file name to load from */ public RGBImage(String file) throws IOException { this(new File(file)); } public RGBImage(Dimension size, Color color) { this(size.width, size.height, color); } public RGBImage(Dimension size, RGB color) { this(size.width, size.height, color); } private void cleanPixels() { for (int i = 0; i < pixels.length; i++) pixels[i] &= 0xFFFFFF; } public RGBImage(int width, int height, int[] pixels) { this.width = width; this.height = height; this.pixels = pixels; } public 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); } public 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; } public RGBImage(RGBImage image) { this(image.width, image.height, copyPixels(image.pixels)); } public RGBImage(int width, int height, Color color) { this(width, height, new RGB(color)); } public RGBImage(File file) throws IOException { this(javax.imageio.ImageIO.read(file)); } 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 background; } 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(background); } /** alias of getRGB - I kept typing getPixel instead of getRGB all the time, so I finally created it */ public RGB getPixel(int x, int y) { return getRGB(x, y); } public int getWidth() { return width; } public int getHeight() { 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; } public RGBImage clip(Rectangle r) { r = fixClipRect(r); 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 File getFile() { return file; } /** can now also do GIF (not just JPEG) */ public static RGBImage load(String fileName) { return load(new File(fileName)); } /** can now also do GIF (not just JPEG) */ public static RGBImage load(File file) { try { BufferedImage bufferedImage = javax.imageio.ImageIO.read(file); return new RGBImage(bufferedImage); } catch (IOException e) { throw new RuntimeException(e); } } public int getInt(int x, int y) { return pixels[y * width + x]; } public void save(File file) throws IOException { String name = file.getName().toLowerCase(); String type; if (name.endsWith(".png")) type = "png"; else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) type = "jpeg"; else throw new IOException("Unknown image extension: " + name); javax.imageio.ImageIO.write(getBufferedImage(), type, file); } public static RGBImage dummyImage() { return new RGBImage(1, 1, new int[] {0xFFFFFF}); } public int[] getPixels() { return pixels; } public void setPixel(int x, int y, RGB rgb) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = rgb.asInt(); } public void setPixel(int x, int y, Color color) { setPixel(x, y, new RGB(color)); } public void setPixel(int x, int y, int rgb) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = rgb; } public RGBImage copy() { return new RGBImage(this); } public boolean inRange(int x, int y) { return x >= 0 && y >= 0 && x < width && y < height; } public int getBackground() { return background; } public void setBackground(int background) { this.background = background; } 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; } } abstract class Surface extends JPanel { public Object AntiAlias = RenderingHints.VALUE_ANTIALIAS_ON; public Object Rendering = RenderingHints.VALUE_RENDER_SPEED; public AlphaComposite composite; public Paint texture; public BufferedImage bimg; public int imageType; public String name; public boolean clearSurface = true; // Demos using animated gif's that implement ImageObserver set dontThread. public boolean dontThread; protected long sleepAmount = 50; // max20 fps private long orig, start, frame; private Toolkit toolkit; private boolean perfMonitor, outputPerf; private int biw, bih; private boolean clearOnce; private boolean toBeInitialized = true; public Surface() { setDoubleBuffered(false); toolkit = getToolkit(); name = this.getClass().getName(); name = name.substring(name.indexOf(".", 7)+1); setImageType(0); // To launch an individual demo with the performance str output : // java -Djava2demo.perf= -cp Java2Demo.jar demos.Clipping.ClipAnim try { if (System.getProperty("java2demo.perf") != null) { perfMonitor = outputPerf = true; } } catch (Exception ex) { } } /*protected Image getImage(String name) { return DemoImages.getImage(name, this); } protected Font getFont(String name) { return DemoFonts.getFont(name); }*/ public int getImageType() { return imageType; } public void setImageType(int imgType) { if (imgType == 0) { imageType = 1; } else { imageType = imgType; } bimg = null; } public void setAntiAlias(boolean aa) { AntiAlias = aa ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF; } public void setRendering(boolean rd) { Rendering = rd ? RenderingHints.VALUE_RENDER_QUALITY : RenderingHints.VALUE_RENDER_SPEED; } public void setTexture(Object obj) { if (obj instanceof GradientPaint) { texture = new GradientPaint(0, 0, Color.white, getSize().width*2, 0, Color.green); } else { texture = (Paint) obj; } } public void setComposite(boolean cp) { composite = cp ? AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f) : null; } public void setMonitor(boolean pm) { perfMonitor = pm; } public void setSleepAmount(long amount) { sleepAmount = amount; } public long getSleepAmount() { return sleepAmount; } public BufferedImage createBufferedImage(Graphics2D g2, int w, int h, int imgType) { BufferedImage bi = null; if (imgType == 0) { bi = (BufferedImage) g2.getDeviceConfiguration(). createCompatibleImage(w, h); } else if (imgType > 0 && imgType < 14) { bi = new BufferedImage(w, h, imgType); } else if (imgType == 14) { bi = createBinaryImage(w, h, 2); } else if (imgType == 15) { bi = createBinaryImage(w, h, 4); } else if (imgType == 16) { bi = createSGISurface(w, h, 32); } else if (imgType == 17) { bi = createSGISurface(w, h, 16); } biw = w; bih = h; return bi; } // Lookup tables for BYTE_BINARY 1, 2 and 4 bits. static byte[] lut1Arr = new byte[] {0, (byte)255 }; static byte[] lut2Arr = new byte[] {0, (byte)85, (byte)170, (byte)255}; static byte[] lut4Arr = new byte[] {0, (byte)17, (byte)34, (byte)51, (byte)68, (byte)85,(byte) 102, (byte)119, (byte)136, (byte)153, (byte)170, (byte)187, (byte)204, (byte)221, (byte)238, (byte)255}; private BufferedImage createBinaryImage(int w, int h, int pixelBits) { int bytesPerRow = w * pixelBits / 8; if (w * pixelBits % 8 != 0) { bytesPerRow++; } byte[] imageData = new byte[h * bytesPerRow]; IndexColorModel cm = null; switch (pixelBits) { case 1: cm = new IndexColorModel(pixelBits, lut1Arr.length, lut1Arr, lut1Arr, lut1Arr); break; case 2: cm = new IndexColorModel(pixelBits, lut2Arr.length, lut2Arr, lut2Arr, lut2Arr); break; case 4: cm = new IndexColorModel(pixelBits, lut4Arr.length, lut4Arr, lut4Arr, lut4Arr); break; default: {new Exception("Invalid # of bit per pixel").printStackTrace();} } DataBuffer db = new DataBufferByte(imageData, imageData.length); WritableRaster r = Raster.createPackedRaster(db, w, h, pixelBits, null); return new BufferedImage(cm, r, false, null); } private BufferedImage createSGISurface(int w, int h, int pixelBits) { int rMask32 = 0xFF000000; int rMask16 = 0xF800; int gMask32 = 0x00FF0000; int gMask16 = 0x07C0; int bMask32 = 0x0000FF00; int bMask16 = 0x003E; DirectColorModel dcm = null; DataBuffer db = null; WritableRaster wr = null; switch (pixelBits) { case 16: short[] imageDataUShort = new short[w * h]; dcm = new DirectColorModel(16, rMask16, gMask16, bMask16); db = new DataBufferUShort(imageDataUShort, imageDataUShort.length); wr = Raster.createPackedRaster(db, w, h, w, new int[] {rMask16, gMask16, bMask16}, null); break; case 32: int[] imageDataInt = new int[w * h]; dcm = new DirectColorModel(32, rMask32, gMask32, bMask32); db = new DataBufferInt(imageDataInt, imageDataInt.length); wr = Raster.createPackedRaster(db, w, h, w, new int[] {rMask32, gMask32, bMask32}, null); break; default: {new Exception("Invalid # of bit per pixel").printStackTrace();} } return new BufferedImage(dcm, wr, false, null); } public Graphics2D createGraphics2D(int width, int height, BufferedImage bi, Graphics g) { Graphics2D g2 = null; if (bi != null) { g2 = bi.createGraphics(); } else { g2 = (Graphics2D) g; } g2.setBackground(getBackground()); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, AntiAlias); g2.setRenderingHint(RenderingHints.KEY_RENDERING, Rendering); if (clearSurface || clearOnce) { g2.clearRect(0, 0, width, height); clearOnce = false; } if (texture != null) { // set composite to opaque for texture fills g2.setComposite(AlphaComposite.SrcOver); g2.setPaint(texture); g2.fillRect(0, 0, width, height); } if (composite != null) { g2.setComposite(composite); } return g2; } public abstract void render(int w, int h, Graphics2D g); /** * It's possible to turn off double-buffering for just the repaint * calls invoked directly on the non double buffered component. * This can be done by overriding paintImmediately() (which is called * as a result of repaint) and getting the current RepaintManager and * turning off double buffering in the RepaintManager before calling * super.paintImmediately(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(); if (imageType == 1) bimg = null; else if (bimg == null || biw != d.width || bih != d.height) { bimg = createBufferedImage((Graphics2D)g, d.width, d.height, imageType-2); clearOnce = true; toBeInitialized = true; } if (toBeInitialized) { toBeInitialized = false; startClock(); } Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g); render(d.width, d.height, g2); g2.dispose(); if (bimg != null) { g.drawImage(bimg, 0, 0, null); toolkit.sync(); } } public void startClock() { orig = System.currentTimeMillis(); start = orig; frame = 0; } private static final int REPORTFRAMES = 30; public static void setAlpha(Graphics2D g, float alpha) { g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); } } class ImageSurface extends Surface { private BufferedImage image; private double zoomX = 1, zoomY = 1; private Rectangle selection; public ImageSurface() { this(new RGBImage(1, 1, new int[] { 0xFFFFFF })); } public ImageSurface(RGBImage image) { this(image.getBufferedImage()); } public ImageSurface(BufferedImage image) { clearSurface = false; this.image = image; /*addMouseMotionListener(new MouseAdapter() { public void mouseMoved(MouseEvent e) { getMousePosition() } });*/ } public ImageSurface(RGBImage image, double zoom) { this(image); setZoom(zoom); } public void render(int w, int h, Graphics2D g) { g.setColor(Color.white); g.fillRect(0, 0, w, h); if (image != null) g.drawImage(image, 0, 0, getZoomedWidth(), getZoomedHeight(), null); if (selection != null) { // 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) { 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 void setZoom(double zoom) { setZoom(zoom, zoom); } public void setZoom(double zoomX, double zoomY) { this.zoomX = zoomX; this.zoomY = zoomY; revalidate(); repaint(); } public Dimension getMinimumSize() { int w = getZoomedWidth(); int h = getZoomedHeight(); Dimension min = super.getMinimumSize(); return new Dimension(Math.max(w, min.width), Math.max(h, min.height)); } private int getZoomedHeight() { return (int) (image.getHeight() * zoomY); } private int getZoomedWidth() { return (int) (image.getWidth() * zoomX); } public void setImage(RGBImage image) { setImage(image.getBufferedImage()); } public void setImage(BufferedImage image) { this.image = image; revalidate(); repaint(); } public BufferedImage getImage() { return image; } public double getZoomX() { return zoomX; } public double getZoomY() { return zoomY; } public Dimension getPreferredSize() { return 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 zoomToDisplaySize() { if (image == null) return; Dimension display = getDisplaySize(); double xRatio = display.width/(double) image.getWidth(); double yRatio = display.height/(double) image.getHeight(); setZoom(Math.min(xRatio, yRatio)); revalidate(); } /** tricky magic to get parent scroll pane */ private Dimension getDisplaySize() { Container c = getParent(); while (c != null) { if (c instanceof JScrollPane) return c.getSize(); c = c.getParent(); } return getSize(); } public void setSelection(Rectangle r) { selection = r; repaint(); } public Rectangle getSelection() { return selection; } public RGBImage getRGBImage() { return new RGBImage(getImage()); } } public class main { static class ShowBigText { String fontID = "#1000993"; // Brother Deluxe Font Color background = Color.white; Color foreground = Color.black; String text = "test"; ImageSurface is; Font font; String title = "JavaX"; // thread-safe void showText(final String text) { SwingUtilities.invokeLater(new Runnable() { public void run() { try { ShowBigText.this.text = text; if (is == null) { font = loadFont(fontID); BufferedImage img = makeImage(); is = showImage(img, title); moveToTopRightCorner(getFrame(is)); } is.setImage(makeImage()); } catch (Exception _e) { throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } } }); } BufferedImage makeImage() { // TODO: dispose old image? BufferedImage img = new BufferedImage(200, 60, BufferedImage.TYPE_INT_RGB); Graphics g = img.getGraphics(); int w = img.getWidth(), h = img.getHeight(); g.setColor(background); g.fillRect(0, 0, w, h); g.setColor(foreground); g.setFont(font.deriveFont(30f)); FontMetrics fm = g.getFontMetrics(); int y = fm.getAscent() + (h-fm.getHeight())/2; int x = (w-fm.stringWidth(text))/2; g.drawString(text, x, y); g.dispose(); return img; } } // ShowBigText static String os; static boolean wtf; public static void main(String[] args) throws Exception { readLocally("os"); ShowBigText sbt = new ShowBigText(); sbt.title = "OS Sensor"; if (os != null) sbt.showText(os + "!"); while (true) { String theOS = getOS(); if (os != null && !eq(os, theOS)) { for (int i = 0; i < 3; i++) { wtf = true; sbt.showText("WTF!"); sleepSeconds(1); sbt.showText(os + "!"); sleepSeconds(1); sbt.showText(theOS + "!"); sleepSeconds(1); } wtf = false; } if (!eq(os, theOS)) { os = theOS; saveLocally("os"); sbt.showText(os + "!"); } sleepSeconds(5); } } static String getOS() { if (isAndroid()) return "Android"; if (isMac()) return "Mac OS"; if (isWindows()) return "Windows"; return "Linux"; // Everything else is Linux... Yeah it's generous :) } static boolean isMac() { return System.getProperty("os.name").toLowerCase().contains("mac"); } static void moveToTopRightCorner(JFrame frame) { if (frame == null) return; int x = getScreenSize().width - frame.getWidth() - 10; int y = 10; frame.setLocation(x, y); } static JFrame getFrame(Component c) { while (c != null) { if (c instanceof JFrame) return (JFrame) c; c = c.getParent(); } return null; } static ImageSurface showImage(BufferedImage img, String title) { ImageSurface is = showImage(img); getFrame(is).setTitle(title); return is; } static ImageSurface showImage(BufferedImage img) { JFrame frame = new JFrame("JavaX"); ImageSurface is = new ImageSurface(img); frame.getContentPane().add(new JScrollPane(is)); frame.pack(); centerFrame(frame); frame.setVisible(true); return is; } static ImageSurface showImage(RGBImage img) { return showImage(img.getBufferedImage()); } static ImageSurface showImage(String imageID) { return showImage(loadImage(imageID)); } static void sleepSeconds(long s) { if (s > 0) sleep(s*1000); } static void showText(String title, String text) { JTextArea textArea = new JTextArea(text); makeFrame(title, new JScrollPane(textArea)); } static String programID; static synchronized void saveLocally(String variableName) { File textFile = new File(programDir(programID), variableName + ".text"); File structureFile = new File(programDir(programID), variableName + ".structure"); Object x = get(main.class, variableName); if (x == null) { textFile.delete(); structureFile.delete(); } else if (x instanceof String) { structureFile.delete(); saveTextFile(textFile, (String) x); } else { textFile.delete(); saveTextFile(structureFile, structure(x)); } } static synchronized void readLocally(String varNames) { for (String variableName : codeTokensOnly(javaTok(varNames))) { File textFile = new File(programDir(programID), variableName + ".text"); File structureFile = new File(programDir(programID), variableName + ".structure"); String value = loadTextFile(textFile); if (value != null) set(main.class, variableName, value); else { value = loadTextFile(structureFile); if (value != null) set(main.class, variableName, unstructure(value)); } } } static boolean eq(Object a, Object b) { return a == null ? b == null : a.equals(b); } static Font loadFont(String snippetID) { try { return loadFont(snippetID, 12f); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Font loadFont(String snippetID, float fontSize) { try { return Font.createFont(Font.TRUETYPE_FONT, loadLibrary(snippetID)).deriveFont(fontSize); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static boolean isAndroid() { return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0; } static boolean equals(Object a, Object b) { return a == null ? b == null : a.equals(b); } public static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } static Class getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; } } static Dimension getScreenSize() { return Toolkit.getDefaultToolkit().getScreenSize(); } static String programID() { return getProgramID(); } static void centerFrame(JFrame frame) { frame.setLocationRelativeTo(null); // magic trick } static List codeTokensOnly(List tok) { List l = new ArrayList(); for (int i = 1; i < tok.size(); i += 2) l.add(tok.get(i)); return l; } static class DynamicObject { String className; Map fieldValues = new TreeMap(); } // actually it's now almost the same as jsonDecode :) static Object unstructure(String text) { final List tok = javaTok(text); class X { int i = 1; Object parse() { String t = tok.get(i); if (t.startsWith("\"")) { String s = unquote(tok.get(i)); i += 2; return s; } if (t.equals("{")) return parseMap(); if (t.equals("[")) return parseList(); if (t.equals("null")) { i += 2; return null; } if (t.equals("false")) { i += 2; return false; } if (t.equals("true")) { i += 2; return true; } if (isInteger(t)) { i += 2; return Long.parseLong(t); } if (isJavaIdentifier(t)) { Class c = findClass(t); DynamicObject dO = null; Object o = null; if (c != null) o = nuObject(c); else { dO = new DynamicObject(); dO.className = t; } i += 2; if (i < tok.size() && tok.get(i).equals("(")) { consume("("); while (!tok.get(i).equals(")")) { // It's like parsing a map. //Object key = parse(); //if (tok.get(i).equals(")")) // key = onlyField(); String key = unquote(tok.get(i)); i += 2; consume("="); Object value = parse(); if (o != null) set(o, key, value); else dO.fieldValues.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume(")"); } return o != null ? o : dO; } throw new RuntimeException("Unknown token " + (i+1) + ": " + t); } Object parseList() { consume("["); List list = new ArrayList(); while (!tok.get(i).equals("]")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume("]"); return list; } Object parseMap() { consume("{"); Map map = new TreeMap(); while (!tok.get(i).equals("}")) { String key = unquote(tok.get(i)); i += 2; consume("="); Object value = parse(); map.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume("}"); return map; } void consume(String s) { if (!tok.get(i).equals(s)) { String prevToken = i-2 >= 0 ? tok.get(i-2) : ""; String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i += 2; } } return new X().parse(); } static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { return new File(userHome(), "JavaX-Data/" + formatSnippetID(snippetID)); } static File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } static void makeFrame(Component content) { // TODO: get program ID makeFrame("A JavaX Frame", content); } static void makeFrame(String title, Component content) { JFrame frame = new JFrame(title); frame.getContentPane().add(content); frame.setBounds(300, 100, 500, 400); frame.setVisible(true); exitOnFrameClose(frame); } static void sleep(long ms) { try { Thread.sleep(ms); } catch (Exception e) { throw new RuntimeException(e); } } static void sleep() { try { synchronized(main.class) { main.class.wait(); } } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} // replacement for class JavaTok // maybe incomplete, might want to add floating point numbers // todo also: extended multi-line strings static List javaTok(String s) { List tok = new ArrayList(); int l = s.length(); int i = 0; while (i < l) { int j = i; char c; String cc; // scan for whitespace while (j < l) { c = s.charAt(j); cc = s.substring(j, Math.min(j+2, l)); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (cc.equals("/*")) { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (cc.equals("//")) { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(s.substring(i, j)); i = j; if (i >= l) break; c = s.charAt(i); // cc is not needed in rest of loop body cc = s.substring(i, Math.min(i+2, l)); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener) { ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.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))); else if (cc.equals("[[")) { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else ++j; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } /** writes safely (to temp file, then rename) */ public static void saveTextFile(String fileName, String contents) throws IOException { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; if (contents != null) { FileOutputStream fileOutputStream = new FileOutputStream(tempFileName); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); printWriter.print(contents); printWriter.close(); } if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (contents != null) if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); } public static void saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(String fileName) { try { return loadTextFile(fileName, null); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(String fileName, String defaultContents) throws IOException { if (!new File(fileName).exists()) return defaultContents; FileInputStream fileInputStream = new FileInputStream(fileName); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); return loadTextFile(inputStreamReader); } public static String loadTextFile(File fileName) { try { return loadTextFile(fileName, null); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(File fileName, String defaultContents) throws IOException { try { return loadTextFile(fileName.getPath(), defaultContents); } catch (IOException e) { throw new RuntimeException(e); } } public 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 builder.toString(); } static void set(Object o, String field, Object value) { if (o instanceof Class) set((Class) o, field, value); else try { Field f = set_findField(o.getClass(), field); f.setAccessible(true); f.set(o, value); } catch (Exception e) { throw new RuntimeException(e); } } static void set(Class c, String field, Object value) { try { Field f = set_findStaticField(c, field); f.setAccessible(true); f.set(null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field set_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static Field set_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static boolean loadImage_useImageCache = true; static RGBImage loadImage(String snippetID) { try { // TODO: androidify File dir = new File(System.getProperty("user.home"), ".tinybrain/image-cache"); if (loadImage_useImageCache) { dir.mkdirs(); File file = new File(dir, snippetID + ".png"); if (file.exists() && file.length() != 0) try { return new RGBImage(ImageIO.read(file)); } catch (Throwable e) { e.printStackTrace(); // fall back to loading from sourceforge } } String imageURL = loadImage_getImageURL(parseSnippetID(snippetID)); System.err.println("Loading image: " + imageURL); BufferedImage image = ImageIO.read(new URL(imageURL)); if (loadImage_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 new RGBImage(image); } catch (IOException e) { throw new RuntimeException(e); } } static String loadImage_getImageURL(long snippetID) throws IOException { String url; if (snippetID == 1000010 || snippetID == 1000012) url = "http://tinybrain.de:8080/tb/show-blobimage.php?id=" + snippetID; else url = "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + snippetID + "&contentType=image/png"; return url; } static Object get(Object o, String field) { if (o instanceof Class) return get((Class) o, field); try { Field f = get_findField(o.getClass(), field); f.setAccessible(true); return f.get(o); } catch (Exception e) { throw new RuntimeException(e); } } static Object get(Class c, String field) { try { Field f = get_findStaticField(c, field); f.setAccessible(true); return f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } static Field get_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field get_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static String structure(Object o) { return structure(o, 0); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static String structure(Object o, int stringSizeLimit) { if (o == null) return "null"; String name = o.getClass().getName(); StringBuilder buf = new StringBuilder(); if (o instanceof Collection) { // TODO: store the type (e.g. HashSet/TreeSet) for (Object x : (Collection) o) { if (buf.length() != 0) buf.append(", "); buf.append(structure(x, stringSizeLimit)); } return "[" + buf + "]"; } if (o instanceof Map) { for (Object e : ((Map) o).entrySet()) { if (buf.length() != 0) buf.append(", "); buf.append(structure(((Map.Entry) e).getKey(), stringSizeLimit)); buf.append("="); buf.append(structure(((Map.Entry) e).getValue(), stringSizeLimit)); } return "{" + buf + "}"; } if (o.getClass().isArray()) { int n = Array.getLength(o); for (int i = 0; i < n; i++) { if (buf.length() != 0) buf.append(", "); buf.append(structure(Array.get(o, i), stringSizeLimit)); } return "array{" + buf + "}"; } if (o instanceof String) return quote(stringSizeLimit != 0 ? shorten((String) o, stringSizeLimit) : (String) o); // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) return String.valueOf(o); String shortName = o.getClass().getName().replaceAll("^main\\$", ""); int numFields = 0; String fieldName = ""; if (shortName.equals("DynamicObject")) { shortName = (String) get(o, "className"); Map fieldValues = (Map) get(o, "fieldValues"); for (String _fieldName : fieldValues.keySet()) { fieldName = _fieldName; Object value = fieldValues.get(fieldName); if (value != null) { if (buf.length() != 0) buf.append(", "); buf.append(fieldName + "=" + structure(value, stringSizeLimit)); } ++numFields; } } else { // regular class // TODO: go to superclasses too Field[] fields = o.getClass().getDeclaredFields(); for (Field field : fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) continue; Object value; try { field.setAccessible(true); value = field.get(o); } catch (Exception e) { value = "?"; } fieldName = field.getName(); // put special cases here... if (value != null) { if (buf.length() != 0) buf.append(", "); buf.append(fieldName + "=" + structure(value, stringSizeLimit)); } ++numFields; } } String b = buf.toString(); if (numFields == 1 && structure_allowShortening) b = b.replaceAll("^" + fieldName + "=", ""); // drop field name if only one String s = shortName; if (buf.length() != 0) s += "(" + b + ")"; return s; } static String quote(String s) { if (s == null) return "null"; return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\""; } static String quote(long l) { return quote("" + l); } static boolean isJavaIdentifier(String s) { if (s.length() == 0 || !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; } // currently finds only inner classes of class "main" // returns null on not found // this is the simple version that is not case-tolerant static Class findClass(String name) { try { return Class.forName("main$" + name); } catch (ClassNotFoundException e) { return null; } } static Object nuObject(Class c, Object... args) { try { Constructor m = nuObject_findConstructor(c, args); m.setAccessible(true); return m.newInstance(args); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Constructor nuObject_findConstructor(Class c, Object... args) { for (Constructor m : c.getDeclaredConstructors()) { if (!nuObject_checkArgs(m.getParameterTypes(), args, false)) continue; return m; } throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName()); } 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 String getProgramID() { return programID; } static File loadBinarySnippet(String snippetID) { try { long id = parseSnippetID(snippetID); File f = DiskSnippetCache_getLibrary(id); if (f == null) { byte[] data = loadDataSnippetImpl(snippetID); DiskSnippetCache_putLibrary(id, data); f = DiskSnippetCache_getLibrary(id); } return f; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static void exitOnFrameClose(JFrame frame) { frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static String unquote(String s) { if (s.startsWith("[")) { 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.startsWith("\"") && s.endsWith("\"") && s.length() > 1) { String st = s.substring(1, s.length()-1); StringBuilder sb = new StringBuilder(st.length()); for (int i = 0; i < st.length(); i++) { char ch = st.charAt(i); if (ch == '\\') { char nextChar = (i == st.length() - 1) ? '\\' : st .charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) <= '7') { code += st.charAt(i + 1); i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) <= '7') { code += st.charAt(i + 1); i++; } } sb.append((char) Integer.parseInt(code, 8)); continue; } switch (nextChar) { 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; case '\'': ch = '\''; break; // Hex Unicode: u???? case 'u': if (i >= st.length() - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + st.charAt(i + 2) + st.charAt(i + 3) + st.charAt(i + 4) + st.charAt(i + 5), 16); sb.append(Character.toChars(code)); i += 5; continue; } i++; } sb.append(ch); } return sb.toString(); } else return s; // return original } public static String join(String glue, Iterable strings) { StringBuilder buf = new StringBuilder(); Iterator i = strings.iterator(); if (i.hasNext()) { buf.append(i.next()); while (i.hasNext()) buf.append(glue).append(i.next()); } return buf.toString(); } public static String join(String glue, String[] strings) { return join(glue, Arrays.asList(strings)); } public static String join(Iterable strings) { return join("", strings); } public static String join(String[] strings) { return join("", strings); } static RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); } static boolean isInteger(String s) { return Pattern.matches("\\-?\\d+", s); } static String _userHome; static String userHome() { if (_userHome == null) { if (isAndroid()) _userHome = "/storage/sdcard0/"; else _userHome = System.getProperty("user.home"); //System.out.println("userHome: " + _userHome); } return _userHome; } static String shorten(String s, int max) { return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "..."; } public static long parseSnippetID(String snippetID) { return Long.parseLong(shortenSnippetID(snippetID)); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } // extended over Class.isInstance() to handle primitive types 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; return type.isInstance(arg); } // Data files are immutable, use centralized cache public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException { File file = new File(getGlobalCache(), "data_" + snippetID + ".jar"); return file.exists() ? file : null; } public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException { saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data); } static byte[] loadDataSnippetImpl(String snippetID) throws IOException { byte[] data; try { URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + parseSnippetID(snippetID) + "&contentType=application/binary"); System.err.println("Loading library: " + url); try { data = loadBinaryPage(url.openConnection()); } catch (IOException e) { url = new URL("http://data.tinybrain.de/blobs/" + parseSnippetID(snippetID)); System.err.println("Loading library: " + url); data = loadBinaryPage(url.openConnection()); } System.err.println("Bytes loaded: " + data.length); } catch (FileNotFoundException e) { throw new IOException("Binary snippet #" + snippetID + " not found or not public"); } return data; } 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 snippetID; } static File getGlobalCache() { File file = new File(userHome(), ".tinybrain/snippet-cache"); file.mkdirs(); return file; } static byte[] loadBinaryPage(String url) throws IOException { return loadBinaryPage(new URL(url).openConnection()); } public static byte[] loadBinaryPage(URLConnection con) throws IOException { //setHeaders(con); ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) System.err.println(" " + n + " bytes loaded."); } inputStream.close(); return buf.toByteArray(); } /** writes safely (to temp file, then rename) */ public static void saveBinaryFile(String fileName, byte[] contents) throws IOException { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; FileOutputStream fileOutputStream = new FileOutputStream(tempFileName); fileOutputStream.write(contents); fileOutputStream.close(); if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); } static void saveBinaryFile(File fileName, byte[] contents) { try { saveBinaryFile(fileName.getPath(), contents); } catch (IOException e) { throw new RuntimeException(e); } } }