diff --git a/pom.xml b/pom.xml
index fafddc47..49b0276e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -127,6 +127,7 @@
1.8
+ 0.9.10
@@ -210,6 +211,11 @@
jhotdraw
7.6.0
+
+ org.reflections
+ reflections
+ ${reflections.version}
+
diff --git a/src/main/java/net/imagej/ui/swing/script/TextEditor.java b/src/main/java/net/imagej/ui/swing/script/TextEditor.java
index db3a4d62..430a8b11 100644
--- a/src/main/java/net/imagej/ui/swing/script/TextEditor.java
+++ b/src/main/java/net/imagej/ui/swing/script/TextEditor.java
@@ -59,6 +59,7 @@
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
@@ -107,6 +108,10 @@
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
+import org.reflections.Reflections;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
import org.scijava.Context;
import org.scijava.command.CommandService;
import org.scijava.event.ContextDisposingEvent;
@@ -168,6 +173,7 @@ public class TextEditor extends JFrame implements ActionListener,
}
private static AbstractTokenMakerFactory tokenMakerFactory = null;
+ private static Reflections reflections = null;
private JTabbedPane tabbed;
private JMenuItem newFile, open, save, saveas, compileAndRun, compile,
@@ -2064,9 +2070,41 @@ public String getSelectedTextOrAsk(final String label) {
public String getSelectedClassNameOrAsk() {
String className = getSelectedTextOrAsk("Class name");
if (className != null) className = className.trim();
+
return className;
}
+ /**
+ * Returns the static Reflections instance, constructing it
+ * if it doesn't already exist. This is to limit the number of
+ * classpath scans.
+ *
+ * @return static {@link Reflections} instance
+ */
+ private static Reflections getReflections() {
+ //TODO consider moving this to a service with plugins to determine how the
+ //packages are filtered. For example having scijava-common on the classpath
+ //adds org.scijava. Adding imagej-common would add net.imagej to the filter
+ //list.. etc...
+ if (reflections == null) {
+ synchronized(TextEditor.class) {
+ if (reflections == null) {
+ final Collection packages = new HashSet<>();
+ packages.addAll(ClasspathHelper.forPackage("net.imagej"));
+ packages.addAll(ClasspathHelper.forPackage("org.scijava"));
+ packages.addAll(ClasspathHelper.forPackage("net.imglib2"));
+ packages.addAll(ClasspathHelper.forPackage("io.scif"));
+ packages.addAll(ClasspathHelper.forPackage("sc.fiji"));
+ packages.addAll(ClasspathHelper.forPackage("ij"));
+ reflections = new Reflections(new ConfigurationBuilder().setUrls(
+ packages).setScanners(new SubTypesScanner(false)));
+ }
+ }
+ }
+
+ return reflections;
+ }
+
private static void append(final JTextArea textArea, final String text) {
final int length = textArea.getDocument().getLength();
textArea.insert(text, length);
@@ -2209,7 +2247,38 @@ private void updateGitDirectory() {
public void addImport(final String className) {
if (className != null) {
- new TokenFunctions(getTextArea()).addImport(className.trim());
+
+ boolean addRaw = true;
+
+ // If there are NO package separators then this is a raw class name. Try
+ // and find matching packages
+
+ // NB: decided to only look for complete class names without packages.
+ // Matching "endsWith(className) can produce a plethora of false positives
+ // which we want to limit, because the current implementation imports
+ // all matches.
+ // Some alternatives to consider (including combinations):
+ // - match *className
+ // - match *className*
+ // - match .className*
+ // - if >1 match show list to the user to choose a "winner" to import
+ if (!className.contains(".")) {
+ final String packagedClass = "." + className;
+ final Reflections refl = TextEditor.getReflections();
+ final StringBuilder sb = new StringBuilder();
+
+ for (final String type : refl.getAllTypes()) {
+ // look for "blah.className"
+ if (type.endsWith(packagedClass)) {
+ addRaw = false;
+ new TokenFunctions(getTextArea()).addImport(type.trim());
+ }
+ }
+ }
+
+ // If there was a package separator or no matching packages, import the raw
+ // class.
+ if (addRaw) new TokenFunctions(getTextArea()).addImport(className.trim());
}
}