Skip to content

Commit

Permalink
jrt classes: manage tab-completions & minor fixes and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Sep 3, 2020
1 parent 19fedee commit 6ee5032
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 60 deletions.
88 changes: 61 additions & 27 deletions groovy/src/main/java/org/jline/script/GroovyEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,16 @@ public enum Format {JSON, GROOVY, NONE}
, Pattern.DOTALL);
private static final Pattern PATTERN_CLASS_DEF = Pattern.compile("^class\\s+(" + REGEX_VAR + ") .*?\\{.*?}(|\n)$"
, Pattern.DOTALL);
private static final Pattern PATTERN_CLASS_NAME = Pattern.compile("(.*?)\\.([A-Z].*)");
private static final List<String> defaultImports = Arrays.asList("java.lang.*", "java.util.*", "java.io.*"
, "java.net.*", "groovy.lang.*", "groovy.util.*"
, "java.math.BigInteger", "java.math.BigDecimal");
private final Map<String,Class<?>> defaultNameClass = new HashMap<>();
private final GroovyShell shell;
protected Binding sharedData;
private final Map<String,String> imports = new HashMap<>();
private final Map<String,String> methods = new HashMap<>();
private final Map<String,Class<?>> nameClass = new HashMap<>();
private final Map<String,Class<?>> nameClass;
private Cloner objectCloner = new ObjectCloner();

public interface Cloner {
Expand All @@ -86,6 +91,10 @@ public interface Cloner {
public GroovyEngine() {
this.sharedData = new Binding();
shell = new GroovyShell(sharedData);
for (String s : defaultImports) {
addToNameClass(s, defaultNameClass);
}
nameClass = new HashMap<>(defaultNameClass);
}

@Override
Expand Down Expand Up @@ -195,21 +204,42 @@ public Object execute(File script, Object[] args) throws Exception {
return s.run();
}

void addToNameClass(String classname) {
private static Set<Class<?>> classesForPackage(String pckgname) throws ClassNotFoundException {
String name = pckgname;
Matcher matcher = PATTERN_CLASS_NAME.matcher(name);
if (matcher.matches()) {
name = matcher.group(1) + ".**";
}
Set<Class<?>> out = new HashSet<>(PackageHelper.getClassesForPackage(name));
if (out.isEmpty()) {
out.addAll(JrtJavaBasePackages.getClassesForPackage(name));
}
return out;
}

private void addToNameClass(String name) {
addToNameClass(name, nameClass);
}

private void addToNameClass(String name, Map<String,Class<?>> nameClass) {
try {
if (classname.endsWith(".*")) {
List<Class<?>> classes = PackageHelper.getClassesForPackage(classname);
for (Class<?> c : classes) {
if (name.endsWith(".*")) {
for (Class<?> c : classesForPackage(name)) {
nameClass.put(c.getSimpleName(), c);
}
} else {
int idx = classname.lastIndexOf(".");
String name = classname.substring(idx + 1);
try {
nameClass.put(name, Class.forName(classname));
} catch (ClassNotFoundException ex) {
String innerclass = classname.substring(0, idx) + "$" + name;
nameClass.put(name, Class.forName(innerclass));
Matcher matcher = PATTERN_CLASS_NAME.matcher(name);
if (matcher.matches()) {
String classname = matcher.group(2).replaceAll("\\.", "\\$");
int idx = classname.lastIndexOf("$");
String simpleName = classname.substring(idx + 1);
try {
nameClass.put(simpleName, Class.forName(matcher.group(1) + "." + classname));
} catch (ClassNotFoundException ex) {
if (Log.isDebugEnabled()) {
ex.printStackTrace();
}
}
}
}
} catch (Exception e) {
Expand Down Expand Up @@ -300,6 +330,7 @@ private boolean classDef(String statement) {

private void refreshNameClass() {
nameClass.clear();
nameClass.putAll(defaultNameClass);
for (String name : imports.keySet()) {
addToNameClass(name);
}
Expand Down Expand Up @@ -471,23 +502,26 @@ public static Set<String> nextDomain(String domain, CandidateType type) {
out = names(domain);
} else {
try {
List<Class<?>> classes = PackageHelper.getClassesForPackage(domain);
for (Class<?> c : classes) {
if (!Modifier.isPublic(c.getModifiers()) || c.getName().contains("$")) {
continue;
}
if ((type == CandidateType.CONSTRUCTOR && (c.getConstructors().length == 0 || Modifier.isAbstract(c.getModifiers())))
|| (type == CandidateType.STATIC_METHOD && getStaticMethods(c).size() == 0
&& getStaticFields(c).size() == 0)){
continue;
}
for (Class<?> c : classesForPackage(domain)) {
try {
if (!Modifier.isPublic(c.getModifiers()) || c.getCanonicalName() == null) {
continue;
}
if ((type == CandidateType.CONSTRUCTOR && (c.getConstructors().length == 0
|| Modifier.isAbstract(c.getModifiers())))
|| (type == CandidateType.STATIC_METHOD && getStaticMethods(c).size() == 0
&& getStaticFields(c).size() == 0)){
continue;
}
String name = c.getCanonicalName();
int idx = name.indexOf('.', domain.length());
if (idx < 0) {
idx = name.length();
Log.debug(name);
if (name.startsWith(domain)) {
int idx = name.indexOf('.', domain.length());
if (idx < 0) {
idx = name.length();
}
out.add(name.substring(domain.length(), idx));
}
out.add(name.substring(domain.length(), idx));
} catch (NoClassDefFoundError e) {
if (Log.isDebugEnabled()) {
e.printStackTrace();
Expand Down Expand Up @@ -1086,7 +1120,7 @@ private CmdDesc checkSyntax(CmdLine line) {
String objEquation = line.getHead().substring(eqsep + 1, end);
equationLines = objEquation.split("\\r?\\n");
cuttedSize = eqsep + 1;
if (objEquation == null || objEquation.matches("\\(\\s*\\w+\\s*[,\\s*\\w+\\s*]*\\)")) {
if (objEquation.matches("\\(\\s*\\w+\\s*[,\\s*\\w+\\s*]*\\)")) {
// do nothing
} else {
try {
Expand Down
88 changes: 88 additions & 0 deletions groovy/src/main/java/org/jline/script/JrtJavaBasePackages.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
*
* https://opensource.org/licenses/BSD-3-Clause
*/
package org.jline.script;

import org.jline.utils.Log;

import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import static java.nio.file.FileVisitResult.CONTINUE;

/**
*
* @author <a href="mailto:matti.rintanikkola@gmail.com">Matti Rinta-Nikkola</a>
*/
public class JrtJavaBasePackages {
public static List<Class<?>> getClassesForPackage(String pckgname) {
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
List<String> dirs = new ArrayList<>();
dirs.add("java.base");
boolean nestedClasses = true;
boolean onlyCurrent = false;
if (pckgname.endsWith(".*")) {
onlyCurrent = true;
nestedClasses = false;
pckgname = pckgname.substring(0, pckgname.length() - 2);
} else if (pckgname.endsWith(".**")) {
onlyCurrent = true;
pckgname = pckgname.substring(0, pckgname.length() - 3);
}
dirs.addAll(Arrays.asList(pckgname.split("\\.")));
Path path = fs.getPath("modules", dirs.toArray(new String[0]));
FileVisitor fv = new FileVisitor(nestedClasses);
try {
if (onlyCurrent) {
Files.walkFileTree(path, new HashSet<>(),1, fv);
} else {
Files.walkFileTree(path, fv);
}
} catch (IOException e) {
if (Log.isDebugEnabled()) {
e.printStackTrace();
}
}
return fv.getClasses();
}

private static class FileVisitor extends SimpleFileVisitor<Path> {
private final List<Class<?>> classes = new ArrayList<>();
private final boolean nestedClasses;

public FileVisitor(boolean nestedClasses) {
super();
this.nestedClasses = nestedClasses;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attr) {
try {
String name = file.toString().substring(18);
if (name.endsWith(".class") && (nestedClasses || !name.contains("$"))) {
classes.add(Class.forName(name.substring(0, name.length() - 6).replaceAll("/", ".")));
}
} catch (ClassNotFoundException|NoClassDefFoundError e) {
if (Log.isDebugEnabled()) {
e.printStackTrace();
}
}
return CONTINUE;
}

private List<Class<?>> getClasses() {
return classes;
}
}
}
66 changes: 33 additions & 33 deletions groovy/src/main/java/org/jline/script/PackageHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
*/
public class PackageHelper {
private enum ClassesToScann {ALL, PACKAGE_ALL, PACKAGE_CLASS};
/**
* Private helper method
*
Expand All @@ -36,30 +37,22 @@ public class PackageHelper {
* Class object.
* @param classes
* if a file isn't loaded but still is in the directory
* @param classesOnly
* if true returns only classes of the pckgname
* @throws ClassNotFoundException
* if something went wrong
* @param scann
* determinate which classes will be added
*/
private static void checkDirectory(File directory, String pckgname,
List<Class<?>> classes, boolean classesOnly) throws ClassNotFoundException {
List<Class<?>> classes, ClassesToScann scann) {
File tmpDirectory;

if (directory.exists() && directory.isDirectory()) {
final String[] files = directory.list();

for (final String file : files != null ? files : new String[0]) {
if (file.endsWith(".class")) {
try {
classes.add(Class.forName(pckgname + '.'
+ file.substring(0, file.length() - 6)));
} catch (final NoClassDefFoundError e) {
// e.printStackTrace();
// do nothing. this class hasn't been found by the
// loader, and we don't care.
}
} else if (!classesOnly && (tmpDirectory = new File(directory, file)).isDirectory()) {
checkDirectory(tmpDirectory, pckgname + "." + file, classes, false);
addClass(pckgname + '.'
+ file.substring(0, file.length() - 6), classes);
} else if (scann == ClassesToScann.ALL && (tmpDirectory = new File(directory, file)).isDirectory()) {
checkDirectory(tmpDirectory, pckgname + "." + file, classes, ClassesToScann.ALL);
}
}
}
Expand All @@ -75,16 +68,14 @@ private static void checkDirectory(File directory, String pckgname,
* @param classes
* the current ArrayList of all classes. This method will simply
* add new classes.
* @param classesOnly
* if true returns only classes of the pckgname
* @throws ClassNotFoundException
* if a file isn't loaded but still is in the jar file
* @param scann
* determinate which classes will be added
* @throws IOException
* if it can't correctly read from the jar file.
*/
private static void checkJarFile(JarURLConnection connection,
String pckgname, List<Class<?>> classes, boolean classesOnly)
throws IOException, ClassNotFoundException {
String pckgname, List<Class<?>> classes, ClassesToScann scann)
throws IOException {
final JarFile jarFile = connection.getJarFile();
final Enumeration<JarEntry> entries = jarFile.entries();
String name;
Expand All @@ -93,18 +84,27 @@ private static void checkJarFile(JarURLConnection connection,
name = jarEntry.getName();
if (name.contains(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
if (classesOnly) {
if (scann != ClassesToScann.ALL) {
String namepckg = name.substring(0, name.lastIndexOf("."));
if (pckgname.equals(namepckg) && !name.contains("$")) {
classes.add(Class.forName(name));
if (pckgname.equals(namepckg) && ((scann == ClassesToScann.PACKAGE_CLASS && !name.contains("$"))
|| scann == ClassesToScann.PACKAGE_ALL)) {
addClass(name, classes);
}
} else if (name.contains(pckgname)) {
classes.add(Class.forName(name));
addClass(name, classes);
}
}
}
}

private static void addClass(String className, List<Class<?>> classes) {
try {
classes.add(Class.forName(className));
} catch (ClassNotFoundException|NoClassDefFoundError e) {
// ignore
}
}

/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
Expand All @@ -117,9 +117,13 @@ private static void checkJarFile(JarURLConnection connection,
*/
public static List<Class<?>> getClassesForPackage(String pckgname) throws ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
boolean classesOnly = pckgname.endsWith(".*");
if (classesOnly) {
ClassesToScann scann = ClassesToScann.ALL;
if (pckgname.endsWith(".*")) {
scann = ClassesToScann.PACKAGE_CLASS;
pckgname = pckgname.substring(0, pckgname.length() - 2);
} else if (pckgname.endsWith(".**")) {
pckgname = pckgname.substring(0, pckgname.length() - 3);
scann = ClassesToScann.PACKAGE_ALL;
}

try {
Expand All @@ -136,11 +140,11 @@ public static List<Class<?>> getClassesForPackage(String pckgname) throws ClassN
connection = url.openConnection();

if (connection instanceof JarURLConnection) {
checkJarFile((JarURLConnection) connection, pckgname, classes, classesOnly);
checkJarFile((JarURLConnection) connection, pckgname, classes, scann);
} else if (connection.getClass().getCanonicalName().equals("sun.net.www.protocol.file.FileURLConnection")) {
try {
checkDirectory(
new File(URLDecoder.decode(url.getPath(), "UTF-8")), pckgname, classes, classesOnly);
new File(URLDecoder.decode(url.getPath(), "UTF-8")), pckgname, classes, scann);
} catch (final UnsupportedEncodingException ex) {
throw new ClassNotFoundException(
pckgname
Expand All @@ -167,10 +171,6 @@ public static List<Class<?>> getClassesForPackage(String pckgname) throws ClassN
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
} catch (final NoClassDefFoundError e) {
throw new ClassNotFoundException(
"NoClassDefFoundError was thrown when trying to get all resources for "
+ pckgname, e);
}
return classes;
}
Expand Down

0 comments on commit 6ee5032

Please sign in to comment.