| Compiler |

    package ij.plugin;
    import java.awt.*;
    import java.io.*;
    import java.util.*;
    import ij.*;
    import ij.gui.*;
    import ij.io.*;
    import ij.util.*;
    import ij.plugin.frame.Editor;
    import ij.text.TextWindow;
    import java.awt.event.KeyEvent;

    /** Compiles and runs plugins using the javac compiler. */
    public class Compiler implements PlugIn, FilenameFilter {

        private static sun.tools.javac.Main javac;
        private static ByteArrayOutputStream output;
        private static String dir, name;
        private static Editor errors;
        private static boolean generateDebuggingInfo;

        public void run(String arg) {
            IJ.register(Compiler.class);
            if (arg.equals("edit"))
                edit();
            else
                compileAndRun(arg);
         }

        void edit() {
            if (open("", "Open macro or plugin")) {
                Editor ed = (Editor)IJ.runPlugIn("ij.plugin.frame.Editor", "");
                if (ed!=null) ed.open(dir, name);
            }
        }

        void compileAndRun(String path) {
            if (!isJavac())
                return;
            if (IJ.altKeyDown()) {
                IJ.setKeyUp(KeyEvent.VK_ALT);
                GenericDialog gd = new GenericDialog("Compile and Run");
                gd.addCheckbox("Generate Debugging Info (javac -g)", generateDebuggingInfo);
                gd.showDialog();
                if (gd.wasCanceled()) return;
                generateDebuggingInfo = gd.getNextBoolean();
            }
            if (!open(path, "Compile and Run Plugin..."))
                return;
            if (name.endsWith(".class"))
                runPlugin(name.substring(0, name.length()-1));
            else if (compile(dir+name))
                runPlugin(name);
        }

        boolean isJavac() {
            try {
                if (javac==null) {
                    output = new ByteArrayOutputStream(4096);
                    javac = new sun.tools.javac.Main(output, "javac");
                }
            } catch (NoClassDefFoundError e) {
                IJ.error("This JVM does not include the javac compiler.\n"
                        +"Javac is included with the Windows and Linux\n"
                        +"versions of ImageJ that are bundled with Java.");
                return false;
            }
            return true;
        }

        boolean compile(String path) {
            IJ.showStatus("compiling: "+path);
            String classpath = System.getProperty("java.class.path");
            File f = new File(path);
            if (f!=null)  // add directory containing file to classpath
                classpath += File.pathSeparator + f.getParent();
            //IJ.log("classpath: " + classpath);
            output.reset();
            String[] arguments;
            if (generateDebuggingInfo)
                arguments = new String[] {"-g", "-deprecation", "-classpath", classpath, path};
            else
                arguments = new String[] {"-deprecation", "-classpath", classpath, path};
            boolean compiled = javac.compile(arguments);
            String s = output.toString();
            boolean errors = (!compiled || areErrors(s));
            if (errors)
                showErrors(s);
            else
                IJ.showStatus("done");
            return compiled;
         }

        boolean areErrors(String s) {
            boolean errors = s!=null && s.length()>0;
            if (errors && s.startsWith("Note: sun.tools.javac") && s.indexOf("error")==-1)
                errors = false;
            return errors;
        }

        void showErrors(String s) {
            if (errors==null || !errors.isVisible()) {
                errors = (Editor)IJ.runPlugIn("ij.plugin.frame.Editor", "");
                errors.setFont(new Font("Monospaced", Font.PLAIN, 12));
            }
            if (errors!=null)
                errors.display("Errors", s);
            IJ.showStatus("done (errors)");
        }

         // open the .java source file
         boolean open(String path, String msg) {
            boolean okay;
            String fileName, directory;
            if (path.equals("")) {
                if (dir==null)
                    dir = Prefs.getHomeDir();
                OpenDialog od = new OpenDialog(msg, dir, name);
                directory = od.getDirectory();
                fileName = od.getFileName();
                okay = fileName!=null;
                String lcName = okay?fileName.toLowerCase(Locale.US):null;
                if (okay) {
                    if (msg.startsWith("Compile")) {
                        if (!(lcName.endsWith(".java")||lcName.endsWith(".class"))) {
                            IJ.error("File name must end with \".java\" or \".class\".");
                            okay = false;
                        }
                    } else if (!(lcName.endsWith(".java")||lcName.endsWith(".txt")||lcName.endsWith(".ijm"))) {
                        IJ.error("File name must end with \".java\" or \".txt\".");
                        okay = false;
                    }
                }
            } else {
                int i = path.lastIndexOf('/');
                if (i==-1)
                    i = path.lastIndexOf('\\');
                if (i>0) {
                    directory = path.substring(0, i+1);
                    fileName = path.substring(i+1);
                } else {
                    directory = "";
                    fileName = path;
                }
                okay = true;
            }
            if (okay) {
                name = fileName;
                dir = directory;
                Editor.setDefaultDirectory(dir);
            }
            return okay;
        }

        // only show files with names ending in ".java"
        // doesn't work with Windows
        public boolean accept(File dir, String name) {
            return name.endsWith(".java")||name.endsWith(".macro")||name.endsWith(".txt");
        }

        // run the plugin using a new class loader
        void runPlugin(String name) {
            name = name.substring(0,name.length()-5); // remove ".java"
            new PlugInExecuter(name);
        }

    }

    class PlugInExecuter implements Runnable {

        private String plugin;
        private Thread thread;

        /** Create a new object that runs the specified plugin
            in a separate thread. */
        PlugInExecuter(String plugin) {
            this.plugin = plugin;
            thread = new Thread(this, plugin);
            thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
            thread.start();
        }

        public void run() {
            try {
                ImageJ ij = IJ.getInstance();
                IJ.resetEscape();
                if (ij!=null) ij.runUserPlugIn(plugin, plugin, "", true);
            } catch(Throwable e) {
                IJ.showStatus("");
                IJ.showProgress(1.0);
                ImagePlus imp = WindowManager.getCurrentImage();
                if (imp!=null) imp.unlock();
                String msg = e.getMessage();
                if (e instanceof RuntimeException && msg!=null && e.getMessage().equals(Macro.MACRO_CANCELED))
                    return;
                CharArrayWriter caw = new CharArrayWriter();
                PrintWriter pw = new PrintWriter(caw);
                e.printStackTrace(pw);
                String s = caw.toString();
                if (IJ.isMacintosh())
                    s = Tools.fixNewLines(s);
                new TextWindow("Exception", s, 350, 250);
            }
        }

    }

| Compiler |