package lineage; import ij.Prefs; import ij.gui.GenericDialog; import ini.trakem2.display.Line3D; import ini.trakem2.plugin.TPlugIn; import ini.trakem2.utils.Bureaucrat; import ini.trakem2.utils.FieldMapView; import ini.trakem2.utils.IJError; import ini.trakem2.utils.Utils; import ini.trakem2.utils.Worker; import java.io.File; import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Identify implements TPlugIn { public Identify() {} static { try { Thread.currentThread().setContextClassLoader(ij.IJ.getClassLoader()); Class<?> c = Class.forName("clojure.lang.Compiler"); Method load = c.getDeclaredMethod("load", new Class[]{Reader.class}); load.invoke(null, new Object[]{new InputStreamReader(Identify.class.getResourceAsStream("/lineage/identify.clj"))}); // As a side effect, inits clojure runtime } catch (Throwable t) { IJError.print(t); } } /** * Takes 2 arg (the Pipe or Polyline and the lib-name) * or 5 args: Line3D pipe, String lib-name, double delta, boolean direct and boolean substring */ static public Object identify(Object... args) { return invokeClojureFunction("lineage.identify", "identify", args); } /** * @param namespace * @param fnName * @param args Should not be null. Use an empty array if necessary. * @return */ static public Object invokeClojureFunction(final String namespace, final String fnName, final Object... args) { try { Class<?> RT = Class.forName("clojure.lang.RT"); Method var = RT.getDeclaredMethod("var", new Class[]{String.class, String.class}); Object fn = var.invoke(null, new Object[]{namespace, fnName}); Class<?>[] cc = new Class[args.length]; for (int i=0; i<cc.length; i++) cc[i] = Object.class; Method invoke = Class.forName("clojure.lang.Var").getDeclaredMethod("invoke", cc); return invoke.invoke(fn, args); } catch (Throwable e) { IJError.print(e); } return null; } static private class Library extends FieldMapView { @SuppressWarnings("unused") private String title, filepath, reference; private Library(String title, String filepath, String reference) { this.title = title; this.filepath = filepath; this.reference = reference; } } static private class Params { private final List<Library> libs; // = {"Drosophila-3rd-instar"}; private final long modificationTime; private double delta = 1.0; private int lib_index = 0; private boolean direct = true; private boolean substring = false; private Params() { Tuple b = init(); this.libs = b.libraries; this.modificationTime = b.lastModified; } private final class Tuple { private final long lastModified; private final List<Library> libraries; Tuple(long modificationTime, List<Library> libs) { this.lastModified = modificationTime; this.libraries = libs; } } private final File fileNITLibraries() { final String pluginsDir = Utils.fixDir(Prefs.get("plugins.dir", Utils.fixDir(System.getProperty("user.dir") + "/plugins"))); File libsList = new File(pluginsDir + "NIT-libraries.txt"); if (!libsList.exists()) { throw new RuntimeException("Could not find file " + libsList.getAbsolutePath()); } if (!libsList.canRead()) { throw new RuntimeException("Could not read file " + libsList.getAbsolutePath()); } return libsList; } private final Tuple init() { // Read in the file defining the libraries final ArrayList<Library> l = new ArrayList<Library>(); final File libsList = fileNITLibraries(); final long lastModified = libsList.lastModified(); final String[] lines = Utils.openTextFileLines(libsList.getAbsolutePath()); // for (int i=0; i<lines.length; ++i) { String line = lines[i].trim(); if (0 == line.length()) continue; if (line.startsWith("#")) continue; int comment = line.indexOf('#'); if (-1 != comment) line = line.substring(0, comment).trim(); if (0 == line.length()) continue; String[] t = line.split("\\t+"); // split at one or more tabs if (t.length < 3) { throw new RuntimeException("Errors in Library file: improper entry at line " + (i+1) + ": '" + lines[i] + "'"); } for (int k=0; k<t.length; ++k) t[k] = t[k].trim(); // Check that the file path is readable File f = new File(t[1]); if (!f.exists()) { // Try relative to NIT-libraries.txt f = new File(Utils.fixDir(libsList.getParent()) + t[1]); Utils.logAll("File f is " + f.getAbsolutePath() + " and t[1]: " + t[1]); if (!f.exists()) { Utils.logAll("Could not find file for NIT library: " + t[1]); continue; } } if (!f.canRead()) { Utils.logAll("Incorrect permissions, cannot read file for NIT library at " + t[1]); continue; } l.add(new Library(t[0], f.getAbsolutePath(), t[2])); Utils.log("Library: " + t[0] + " | " + f.getAbsolutePath() + " | " + t[2]); } Utils.logAll("T2-NIT: loaded " + l.size() + " librar" + (0 == l.size() ? "y" : "ies")); return new Tuple(lastModified, Collections.unmodifiableList(l)); } /** Will reload the libraries if necessary. */ private Params(final Params p) { final File libsList = fileNITLibraries(); if (libsList.lastModified() > p.modificationTime) { Utils.logAll("NIT-libraries.txt has been modified: libraries will be reloaded"); invokeClojureFunction("lineage.identify", "forget-libs", new Object[]{}); Tuple b = init(); this.libs = b.libraries; this.modificationTime = b.lastModified; } else { this.libs = p.libs; this.delta = p.delta; this.lib_index = p.lib_index; this.direct = p.direct; this.substring = p.substring; this.modificationTime = p.modificationTime; } } @Override synchronized public Params clone() { return new Params(this); } synchronized private boolean setup() { GenericDialog gd = new GenericDialog("Setup NIT"); gd.addNumericField("delta:", delta, 2); gd.addCheckbox("direct", direct); gd.addCheckbox("substring", substring); String[] libNames = new String[libs.size()]; for (int i=0; i<libs.size(); ++i) { libNames[i] = libs.get(i).title; } gd.addChoice("Library: ", libNames, libNames[lib_index]); gd.showDialog(); if (gd.wasCanceled()) return false; double d = gd.getNextNumber(); if (Double.isNaN(d)) { Utils.log("Invalid delta value!"); return false; } if (d < 0) d = 1.0; delta = d; lib_index = gd.getNextChoiceIndex(); direct = gd.getNextBoolean(); substring = gd.getNextBoolean(); return true; } private boolean isOutOfDate() { File f = fileNITLibraries(); return f.lastModified() > modificationTime; } } /** Store start up values. New instances take the current values. */ static private Params PARAMS = new Params(); private Params params = PARAMS.clone(); public Bureaucrat identify(final Line3D pipe) { return Bureaucrat.createAndStart(new Worker.Task("Identifying " + pipe) { public void exec() { if (null == pipe) return; Params p = params.clone(); identify(pipe, p.libs.get(p.lib_index), p.delta, p.direct, p.substring); } }, pipe.getProject()); } @Override public boolean setup(Object... args) { if (params.isOutOfDate()) params = new Params(params); return params.setup(); } @Override public Object invoke(Object... args) { if (null == args || args.length < 1 || null == args[0] || !(args[0] instanceof Line3D)) return null; Line3D pipe = (Line3D) args[0]; Params p = params.clone(); return identify(pipe, p.libs.get(p.lib_index), p.delta, p.direct, p.substring); } @Override public boolean applies(final Object ob) { return null != ob && ob instanceof Line3D; } }