-
Notifications
You must be signed in to change notification settings - Fork 270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jline3 and other console improvements #935
base: dev
Are you sure you want to change the base?
Changes from 34 commits
bbe00c8
caee56b
50807ae
8564117
af7dc17
5e4f2bb
513aa89
d64f760
b4ba93e
4cfb5a1
a252158
2bdb7bf
04d9904
ebd0ab2
1df265f
7a72573
5c3d4ab
bfcf555
1a4a474
e3e2943
373e4a3
1459cd9
7e55e31
033afc1
f374568
b80a454
dda61a5
0a8f9a3
5e1c389
fa1ad33
e64a545
7610133
e848efd
401938e
6ccd6ee
09c7160
b51a978
796be6d
ca95b46
894a459
c659625
b4cc09f
17187c8
62ff057
125358e
dfffe70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package net.glowstone.i18n; | ||
|
||
import java.util.logging.LogRecord; | ||
|
||
public interface LogRecordableLocalizedString extends LoggableLocalizedString { | ||
|
||
LogRecord record(); | ||
|
||
LogRecord record(Object... args); | ||
|
||
LogRecord record(Throwable ex); | ||
|
||
LogRecord record(Throwable ex, Object... args); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package net.glowstone.util.compiler; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.net.URI; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.logging.Level; | ||
import javax.tools.FileObject; | ||
import javax.tools.ForwardingJavaFileManager; | ||
import javax.tools.JavaCompiler; | ||
This comment was marked as outdated.
Sorry, something went wrong. |
||
import javax.tools.JavaFileObject; | ||
import javax.tools.SimpleJavaFileObject; | ||
import javax.tools.StandardJavaFileManager; | ||
import javax.tools.ToolProvider; | ||
import net.glowstone.GlowServer; | ||
import net.glowstone.ServerProvider; | ||
import net.glowstone.util.ReflectionProcessor; | ||
import org.bukkit.ChatColor; | ||
|
||
/** | ||
* A task for which a source string must be compiled and ran to complete. | ||
*/ | ||
public class EvalTask implements Runnable { | ||
This comment was marked as outdated.
Sorry, something went wrong. |
||
|
||
private final boolean output; | ||
private final String source; | ||
private static Object lastOutput = null; | ||
final Map<String, byte[]> classData = new HashMap<>(); | ||
private MapClassLoader classLoader; | ||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); | ||
|
||
/** | ||
* Create a new EvalTask. | ||
* | ||
* @param command The statement to evaluate. | ||
* @param output Whether the statement returns an object. | ||
*/ | ||
public EvalTask(String command, boolean output) { | ||
this.output = output; | ||
String evaluatedCommand; | ||
if (output) { | ||
evaluatedCommand = command.substring(1); | ||
} else { | ||
evaluatedCommand = command; | ||
} | ||
if (compiler == null) { | ||
GlowServer.logger.info("Glowstone is not running with a JDK and REPL is falling" | ||
+ "back to basic parsing using reflection."); | ||
source = evaluatedCommand; | ||
} else { | ||
source = "import org.bukkit.*;\n" | ||
+ "import net.glowstone.*;\n" | ||
+ "public class REPLShell {\n" | ||
+ (output ? "public static Object run(Object last) {\n" | ||
: "public static void run(Object last) {\n") + evaluatedCommand + "\n}\n}\n"; | ||
} | ||
} | ||
|
||
/** | ||
* Compiles and runs the source associated with this evaluation task. | ||
*/ | ||
@Override | ||
public void run() { | ||
if (compiler == null) { | ||
ReflectionProcessor processor = new ReflectionProcessor(source, | ||
ServerProvider.getServer()); | ||
Object result = processor.process(); | ||
GlowServer.logger.info( | ||
ChatColor.GOLD + "Eval returned: " + (result == null ? ChatColor.RED | ||
+ "<no value>" | ||
: ChatColor.AQUA + result.toString())); | ||
return; | ||
} | ||
classLoader = new MapClassLoader(); | ||
ClassDataFileManager classDataFileManager = new ClassDataFileManager(compiler | ||
.getStandardFileManager(null, null, null)); | ||
|
||
List<JavaFileObject> compilationUnit = new ArrayList<>(); | ||
compilationUnit.add(new JavaSource("REPLShell", source)); | ||
|
||
JavaCompiler.CompilationTask task = compiler.getTask(null, classDataFileManager, | ||
null, null, null, compilationUnit); | ||
if (task.call()) { | ||
try { | ||
Object returned = MethodInvocationUtils | ||
.invokeStaticMethod(getCompiledClass("REPLShell"), "run", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move the methods of MethodInvocationUtils to this class, unless and until another class uses them. |
||
lastOutput); | ||
if (output) { | ||
lastOutput = returned; | ||
GlowServer.logger.info(" -> " + returned); | ||
} else { | ||
GlowServer.logger.info(" -> <no value>"); | ||
} | ||
} catch (Exception e) { | ||
GlowServer.logger.log(Level.SEVERE, "Error in running REPL shell! ", e); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Find a class compiled in this tasks' class loader. | ||
* | ||
* @param name The name of the class to find. | ||
* @return The class. | ||
*/ | ||
public Class<?> getCompiledClass(String name) { | ||
return classLoader.findClass(name); | ||
} | ||
|
||
private class JavaSource extends SimpleJavaFileObject { | ||
|
||
private final String source; | ||
|
||
JavaSource(String name, String source) { | ||
super(URI.create("string:///" + name + ".java"), Kind.SOURCE); | ||
this.source = source; | ||
} | ||
|
||
@Override | ||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { | ||
return source; | ||
} | ||
} | ||
|
||
private class MapClassLoader extends ClassLoader { | ||
|
||
@Override | ||
protected Class<?> findClass(String name) { | ||
return defineClass(name, classData.get(name), 0, classData.get(name).length); | ||
} | ||
} | ||
|
||
public class JavaClass extends SimpleJavaFileObject { | ||
|
||
private final String name; | ||
|
||
public JavaClass(String name) { | ||
super(URI.create("string:///" + name + ".java"), Kind.CLASS); | ||
this.name = name; | ||
} | ||
|
||
@Override | ||
public OutputStream openOutputStream() throws IOException { | ||
return new ClassDataOutputStream(name); | ||
} | ||
} | ||
|
||
public class ClassDataFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> { | ||
|
||
public ClassDataFileManager(StandardJavaFileManager fileManager) { | ||
super(fileManager); | ||
} | ||
|
||
@Override | ||
public JavaFileObject getJavaFileForOutput(Location location, String className, | ||
JavaFileObject.Kind kind, FileObject sibling) throws IOException { | ||
return new JavaClass(className); | ||
} | ||
} | ||
|
||
public class ClassDataOutputStream extends OutputStream { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this do differently than the underlying ByteArrayOutputStream? If this can't simply be replaced by a BAOS, document why not, and can we use a FilterOutputStream instead? |
||
|
||
private final String name; | ||
private final ByteArrayOutputStream bytes; | ||
|
||
public ClassDataOutputStream(String name) { | ||
this.name = name; | ||
bytes = new ByteArrayOutputStream(); | ||
} | ||
|
||
@Override | ||
public void write(int b) throws IOException { | ||
bytes.write(b); | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
classData.put(name, bytes.toByteArray()); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package net.glowstone.util.compiler; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Modifier; | ||
|
||
public class MethodInvocationUtils { | ||
|
||
/** | ||
* Invokes a specified static method belonging to a class. | ||
* | ||
* @param c The class. | ||
* @param methodName The method to find in the class. | ||
* @param args The arguments of the method. | ||
* @return The result of invoking the method. | ||
*/ | ||
public static Object invokeStaticMethod(Class<?> c, String methodName, Object... args) { | ||
Method m = findFirstMatchingStaticMethod(c, methodName, args); | ||
if (m == null) { | ||
throw new RuntimeException("Could not find method " + methodName); | ||
} | ||
try { | ||
return m.invoke(null, args); | ||
} catch (IllegalAccessException | IllegalArgumentException | SecurityException | ||
| InvocationTargetException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private static Method findFirstMatchingStaticMethod(Class<?> c, String methodName, | ||
Object... args) { | ||
Method[] methods = c.getDeclaredMethods(); | ||
for (Method m : methods) { | ||
if (m.getName().equals(methodName) && Modifier.isStatic(m.getModifiers())) { | ||
Class<?>[] parameterTypes = m.getParameterTypes(); | ||
if (areAssignable(parameterTypes, args)) { | ||
return m; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private static boolean areAssignable(Class<?>[] types, Object... args) { | ||
if (types.length != args.length) { | ||
return false; | ||
} | ||
for (int i = 0; i < types.length; i++) { | ||
Object arg = args[i]; | ||
Class<?> type = types[i]; | ||
if (arg != null && !type.isAssignableFrom(arg.getClass())) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<configuration> | ||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="INFO"> | ||
<appender-ref ref="STDOUT"/> | ||
</root> | ||
|
||
<logger name="io.netty" level="WARN"/> | ||
</configuration> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just add these methods to LoggableLocalizedString instead?