Skip to content

Commit 9018416

Browse files
committed
v2.2.0, improved ClassBuilder, new JSONWriter for debugging, fixes
1 parent aa16217 commit 9018416

File tree

9 files changed

+275
-39
lines changed

9 files changed

+275
-39
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>net.b07z.sepia.server.core</groupId>
66
<artifactId>sepia-core-tools</artifactId>
7-
<version>2.1.1</version>
7+
<version>2.2.0</version>
88
<name>SEPIA Core-Tools</name>
99
<description>The Core-Tools repository contains classes and methods that are shared by all SEPIA servers and other tools like the SDK.</description>
1010
<url>https://github.com/SEPIA-Framework/sepia-core-tools-java</url>
Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package net.b07z.sepia.server.core.tools;
22

3+
import java.io.File;
34
import java.lang.reflect.Constructor;
45
import java.util.ArrayList;
56
import java.util.List;
7+
import java.util.Locale;
68

9+
import javax.tools.Diagnostic;
710
import javax.tools.DiagnosticCollector;
811
import javax.tools.JavaCompiler;
912
import javax.tools.JavaFileObject;
13+
import javax.tools.StandardJavaFileManager;
1014
import javax.tools.ToolProvider;
1115

1216
/**
@@ -22,19 +26,18 @@ public class ClassBuilder {
2226
* NOTE! Because getClass always returns non-primitives like "Integer" instead of "int" but they are rarely used, all
2327
* classes are cast to primitives if possible. That means the constructor will not be found if it has "Integer" instead of "int".
2428
* Same is most likely true for the constructors containing the class "Object".
25-
*
26-
* @param package_name - name of the package, e.g. "java.util". Can be empty.
27-
* @param module_name - name of the class inside the package as string, e.g.: "Date"
28-
* @param arguments - arguments usually passed to the constructor
29+
* @param classLoader - loader that has access to the class. Use null or 'ClassLoader.getSystemClassLoader()' for default.
30+
* @param canonicalClassName - canonical name of the class, e.g.: "java.lang.String"
31+
* @param arguments - arguments usually passed to the constructor (optional).
2932
* @return constructed class
3033
*/
31-
public static Object construct(String package_name, String module_name, Object... arguments){
32-
if (!package_name.isEmpty()){
33-
module_name = package_name + "." + module_name; //package_name and module_name are separated in case the package_name changes
34-
}
34+
public static Object construct(ClassLoader classLoader, String canonicalClassName, Object... arguments){
3535
try{
36+
if (classLoader == null){
37+
classLoader = ClassLoader.getSystemClassLoader();
38+
}
3639
Object clazz;
37-
if (arguments.length > 0){
40+
if (arguments != null && arguments.length > 0){
3841
Class<?>[] arg_clazzes = new Class[arguments.length];
3942
for (int i=0; i<arguments.length; i++){
4043
arg_clazzes[i] = arguments[i].getClass();
@@ -56,55 +59,76 @@ public static Object construct(String package_name, String module_name, Object..
5659
arg_clazzes[i] = Short.TYPE;
5760
}
5861
}
59-
Constructor<?> conztructor = Class.forName(module_name).getConstructor(arg_clazzes);
62+
Constructor<?> conztructor = Class.forName(canonicalClassName, true, classLoader).getConstructor(arg_clazzes);
6063
clazz = conztructor.newInstance(arguments);
6164
}else{
62-
Constructor<?> conztructor = Class.forName(module_name).getConstructor();
65+
Constructor<?> conztructor = Class.forName(canonicalClassName, true, classLoader).getConstructor();
6366
clazz = conztructor.newInstance();
6467
}
6568
return clazz;
6669

6770
}catch (Exception e){
6871
e.printStackTrace();
69-
throw new RuntimeException(DateTime.getLogDate() + " ERROR - Class not found: " + module_name, e);
72+
throw new RuntimeException(DateTime.getLogDate() + " ERROR - Class not found: " + canonicalClassName, e);
7073
}
7174
}
7275

7376
/**
7477
* Constructs a new instance of a class just by using the name of the class.<br>
7578
*
76-
* @param module_name - name of the class inside the package as string, e.g.: "Date"
79+
* @param canonicalClassName - canonical name of the class, e.g.: "java.lang.String"
7780
* @return constructed class
7881
*/
79-
public static Object construct(String module_name){
80-
try{
81-
Object clazz;
82-
Constructor<?> conztructor = Class.forName(module_name).getConstructor();
83-
clazz = conztructor.newInstance();
84-
return clazz;
85-
86-
}catch (Exception e){
87-
e.printStackTrace();
88-
throw new RuntimeException(DateTime.getLogDate() + " ERROR - Class not found: " + module_name, e);
89-
}
82+
public static Object construct(String canonicalClassName){
83+
return construct(null, canonicalClassName);
9084
}
9185

9286
/**
9387
* Experimental string source-code compiler.
94-
* @param className
95-
* @param classCode
96-
* @param fileName
88+
* @param className - full class name including package, e.g. com.example.my_package.MyNewClass
89+
* @param classCode - source-code as seen in Java files
90+
* @param targetFolder - parent directory of compiled class file (without package-path) or null
9791
*/
98-
public static void compile(String className, String classCode, String fileName){
92+
public static void compile(String className, String classCode, File targetFolder){
9993
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
94+
if (compiler == null){
95+
String msg = "Cannot find Java compiler! "
96+
+ "Please upgrade from JRE to JDK or check JAVA_HOME: " + System.getProperty("java.home");
97+
//Debugger.println(msg, 1);
98+
throw new RuntimeException(msg);
99+
}
100100
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
101101

102102
List<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
103103
JavaFileObject file = new SourceCodeFromString(className, classCode);
104104
compilationUnits.add(file);
105+
106+
// This sets up the class path that the compiler will use.
107+
// I've added the .jar file that contains the DoStuff interface within in it...
108+
List<String> optionList = new ArrayList<>();
109+
String folderOrMemory = "MEMORY ONLY";
110+
if (targetFolder != null){
111+
folderOrMemory = targetFolder.getAbsolutePath().toString();
112+
optionList.add("-d");
113+
optionList.add(folderOrMemory);
114+
}
115+
/*
116+
optionList.add("-classpath");
117+
optionList.add(System.getProperty("java.class.path") + ";dist/InlineCompiler.jar"); //example for classpath
118+
*/
119+
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
105120

106-
JavaCompiler.CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);
107-
System.out.println(task.call() + diagnostics.getDiagnostics().toString()); //passes every time
121+
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, optionList, null, compilationUnits);
122+
if (task.call()){
123+
//Done
124+
Debugger.println("Compiled '" + className + "' to '" + folderOrMemory, 3);
125+
}else{
126+
//Error(s)
127+
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()){
128+
Debugger.println("ClassBuilder - compiling of '" + diagnostic.getSource().toUri() + "' failed. Error: \n" +
129+
"Line " + diagnostic.getLineNumber() + ": " + diagnostic.getMessage(Locale.ENGLISH), 1);
130+
}
131+
}
108132
}
109133

110134
}

src/main/java/net/b07z/sepia/server/core/tools/Debugger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ else if (type == 3 && log){
4646
*/
4747
public static void printStackTrace(Throwable ex, int numTraces){
4848
StackTraceElement[] ste = ex.getStackTrace();
49-
if (ste != null){
49+
if (ste != null && ste.length > 0){
5050
for (int i=0; i<Math.min(5, ste.length); i++){
5151
Debugger.println("TRACE: " + ex.getStackTrace()[i], 1);
5252
}

src/main/java/net/b07z/sepia/server/core/tools/JSON.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,15 @@ public static void printJSON(JSONObject input){
140140
}
141141

142142
/**
143-
* Print content of JSONObject in a kind of prettier way. Keys and values must be convertible to string!
143+
* Print content of JSONObject in a kind of prettier way to stdOut. Keys and values must be convertible to string!<br>
144+
* Used only for quick debugging, if you need more options us {@link JSONWriter}.
144145
*/
145146
public static void printJSONpretty(JSONObject input){
146147
printJSONpretty(input, "-");
147148
}
148149
/**
149-
* Print content of JSONObject in a kind of prettier way. Keys and values must be convertible to string!
150-
* The "indentSymbol" is used at the beginning of a line to make the hierarchy clear.
150+
* Print content of JSONObject in a kind of prettier way. Keys and values must be convertible to string!<br>
151+
* Used only for quick debugging, if you need more options us {@link JSONWriter}.
151152
*/
152153
@SuppressWarnings("unchecked")
153154
public static void printJSONpretty(JSONObject input, String indentSymbol){
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package net.b07z.sepia.server.core.tools;
2+
3+
import java.io.IOException;
4+
import java.io.StringWriter;
5+
import java.io.Writer;
6+
7+
import org.json.simple.JSONArray;
8+
import org.json.simple.JSONObject;
9+
10+
/**
11+
* Creates pretty strings from JSON objects.
12+
* Sample usage:
13+
* <pre>
14+
* Writer writer = new JSONWriter();
15+
* jsonobject.writeJSONString(writer);
16+
* System.out.println(writer.toString());
17+
* OR:
18+
* String s = JSONWriter.getPrettyString(jsonObject);
19+
* </pre>
20+
*
21+
* @author Elad Tabak
22+
* @author Maciej Komosinski, minor improvements, 2015
23+
* @author Florian Quirin, minor improvements, 2018
24+
* @since 28-Nov-2011
25+
* @version 0.3
26+
*/
27+
public class JSONWriter extends StringWriter
28+
{
29+
final String indentstring;
30+
final String spaceaftercolon;
31+
32+
private int indentlevel = 0;
33+
34+
/**
35+
* Default constructor with 4 space indentation and 1 space after colons.
36+
*/
37+
public JSONWriter(){
38+
this.indentstring = " "; //default indentation
39+
this.spaceaftercolon = " "; //use "" if you don't want space after colon
40+
}
41+
/**
42+
* Constructor with custom indentation and space-after-colon string.
43+
* @param indentation - default: " " (4 spaces for next indentation level)
44+
* @param colonSpace - default: " " (1 space after each colon)
45+
*/
46+
public JSONWriter(String indentation, String colonSpace){
47+
this.indentstring = indentation;
48+
this.spaceaftercolon = colonSpace;
49+
}
50+
51+
/**
52+
* Get default, pretty JSON string. Returns null on error!
53+
* @param jo - JSONObject to print pretty
54+
* @return String or null
55+
*/
56+
public static String getPrettyString(JSONObject jo){
57+
Writer sw = new JSONWriter();
58+
try {
59+
jo.writeJSONString(sw);
60+
return sw.toString()
61+
.replaceAll("\\[(\\n|\\s)+]", "[]")
62+
.replaceAll("\\{(\\n|\\s)+}", "{}");
63+
} catch (IOException e) {
64+
e.printStackTrace();
65+
return null;
66+
}
67+
}
68+
/**
69+
* Get default, pretty JSON string. Returns null on error!
70+
* @param ja - JSONArray to print pretty
71+
* @return String or null
72+
*/
73+
public static String getPrettyString(JSONArray ja){
74+
Writer sw = new JSONWriter();
75+
try {
76+
ja.writeJSONString(sw);
77+
return sw.toString()
78+
.replaceAll("\\[(\\n|\\s)+]", "[]")
79+
.replaceAll("\\{(\\n|\\s)+}", "{}");
80+
} catch (IOException e) {
81+
e.printStackTrace();
82+
return null;
83+
}
84+
}
85+
86+
@Override
87+
public void write(int c)
88+
{
89+
char ch = (char) c;
90+
if (ch == '[' || ch == '{') {
91+
super.write(c);
92+
super.write('\n');
93+
indentlevel++;
94+
writeIndentation();
95+
} else if (ch == ',') {
96+
super.write(c);
97+
super.write('\n');
98+
writeIndentation();
99+
} else if (ch == ']' || ch == '}') {
100+
super.write('\n');
101+
indentlevel--;
102+
writeIndentation();
103+
super.write(c);
104+
} else if (ch == ':') {
105+
super.write(c);
106+
super.write(spaceaftercolon);
107+
} else {
108+
super.write(c);
109+
}
110+
111+
}
112+
private void writeIndentation() {
113+
for (int i = 0; i < indentlevel; i++) {
114+
super.write(indentstring);
115+
}
116+
}
117+
}

src/main/java/net/b07z/sepia/server/core/tools/RuntimeInterface.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*/
1515
public class RuntimeInterface {
1616

17-
public static final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
17+
public static Boolean isWindows = null;
1818
public static Charset windowsShellCodepage = null;
1919

2020
/**
@@ -71,6 +71,17 @@ public String toString(CharSequence delimiter){
7171
}
7272
}
7373

74+
/**
75+
* Is the runtime a Windows OS?
76+
* @return
77+
*/
78+
public static boolean isWindows(){
79+
if (isWindows == null){
80+
isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
81+
}
82+
return isWindows;
83+
}
84+
7485
/**
7586
* We need to run the shell codepage command to get proper windows encoding (is there REALLY no BETTER WAY??).
7687
* @return
@@ -104,7 +115,7 @@ public static RuntimeResult runCommand(String[] command){
104115
}
105116
public static RuntimeResult runCommand(String[] command, long customTimeout){
106117
Charset encoding = StandardCharsets.UTF_8;
107-
if (isWindows && windowsShellCodepage == null && !command[0].equals("chcp")){
118+
if (isWindows() && windowsShellCodepage == null && !command[0].equals("chcp")){
108119
RuntimeResult rtr = getWindowsShellCodepage();
109120
if (rtr.getStatusCode() != 0){
110121
return new RuntimeResult(1, null, rtr.getException());
@@ -122,7 +133,7 @@ public static RuntimeResult runCommand(String[] command, long customTimeout){
122133
//process = Runtime.getRuntime().exec(command); //old way
123134
ProcessBuilder builder = new ProcessBuilder();
124135
String[] osPart;
125-
if (isWindows){
136+
if (isWindows()){
126137
osPart = new String[]{"cmd.exe", "/c"};
127138
}else{
128139
osPart = new String[]{"sh", "-c"};

src/main/java/net/b07z/sepia/server/core/tools/SourceCodeFromString.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
public class SourceCodeFromString extends SimpleJavaFileObject {
1313
final String code;
1414

15+
/**
16+
* Construct the new source by giving the (full) class name and code content.
17+
* @param name - full class name, e.g. com.example.my_package.MyNewClass
18+
* @param code - Java source-code as seen in .java files (including package and import sections)
19+
*/
1520
public SourceCodeFromString(String name, String code){
1621
super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);
1722
this.code = code;

0 commit comments

Comments
 (0)