2017-12-21 23:21:29 +01:00
|
|
|
package org.warp.picalculator.math.rules;
|
|
|
|
|
|
|
|
import java.io.File;
|
2018-06-11 22:41:11 +02:00
|
|
|
import java.io.FileInputStream;
|
2017-12-21 23:21:29 +01:00
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.IOException;
|
2017-12-23 01:14:38 +01:00
|
|
|
import java.io.InputStream;
|
2018-03-22 20:40:41 +01:00
|
|
|
import java.io.PrintWriter;
|
2017-12-21 23:21:29 +01:00
|
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.net.URL;
|
2018-05-09 22:54:00 +02:00
|
|
|
import java.util.ArrayList;
|
2017-12-21 23:21:29 +01:00
|
|
|
import java.util.List;
|
2018-05-09 22:54:00 +02:00
|
|
|
|
2018-06-09 19:37:34 +02:00
|
|
|
import org.warp.picalculator.ConsoleUtils;
|
2017-12-23 15:20:42 +01:00
|
|
|
import org.warp.picalculator.Error;
|
2018-06-11 22:41:11 +02:00
|
|
|
import org.warp.picalculator.PlatformUtils;
|
2018-08-28 02:38:27 +02:00
|
|
|
import org.warp.picalculator.StaticVars;
|
2018-06-09 19:37:34 +02:00
|
|
|
import org.warp.picalculator.ZipUtils;
|
|
|
|
import org.warp.picalculator.deps.DJDTCompiler;
|
2018-06-11 22:41:11 +02:00
|
|
|
import org.warp.picalculator.deps.DStandardOpenOption;
|
2018-06-09 19:37:34 +02:00
|
|
|
import org.warp.picalculator.deps.DSystem;
|
2018-06-11 22:41:11 +02:00
|
|
|
import org.warp.picalculator.deps.DURLClassLoader;
|
2018-09-02 12:45:51 +02:00
|
|
|
import org.warp.picalculator.deps.StorageUtils;
|
2017-12-23 15:20:42 +01:00
|
|
|
import org.warp.picalculator.math.Function;
|
2017-12-21 23:21:29 +01:00
|
|
|
import org.warp.picalculator.math.MathContext;
|
2017-12-23 15:20:42 +01:00
|
|
|
import org.warp.picalculator.math.functions.Expression;
|
2017-12-21 23:21:29 +01:00
|
|
|
import org.warp.picalculator.math.functions.Variable;
|
|
|
|
import org.warp.picalculator.math.functions.Variable.V_TYPE;
|
2018-03-01 09:54:14 +01:00
|
|
|
import org.warp.picalculator.math.solver.MathSolver;
|
2017-12-21 23:21:29 +01:00
|
|
|
|
|
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
|
|
|
|
|
|
public class RulesManager {
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2017-12-21 23:21:29 +01:00
|
|
|
public static ObjectArrayList<Rule>[] rules;
|
2018-05-12 21:18:29 +02:00
|
|
|
|
|
|
|
private RulesManager() {}
|
2017-12-21 23:21:29 +01:00
|
|
|
|
2018-09-02 12:45:51 +02:00
|
|
|
@SuppressWarnings({ "unchecked" })
|
2017-12-21 23:21:29 +01:00
|
|
|
public static void initialize() {
|
2018-06-09 19:37:34 +02:00
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Loading the rules");
|
2017-12-21 23:21:29 +01:00
|
|
|
rules = new ObjectArrayList[RuleType.values().length];
|
2018-05-12 21:18:29 +02:00
|
|
|
for (final RuleType val : RuleType.values()) {
|
|
|
|
rules[val.ordinal()] = new ObjectArrayList<>();
|
2017-12-21 23:21:29 +01:00
|
|
|
}
|
|
|
|
try {
|
2018-03-26 09:01:26 +02:00
|
|
|
boolean compiledSomething = false;
|
2018-06-11 22:41:11 +02:00
|
|
|
InputStream defaultRulesList;
|
|
|
|
try {
|
|
|
|
defaultRulesList = StorageUtils.getResourceStream("/default-rules.lst");
|
|
|
|
} catch (IOException ex) {
|
2018-05-09 22:54:00 +02:00
|
|
|
throw new FileNotFoundException("default-rules.lst not found!");
|
2017-12-21 23:21:29 +01:00
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
final List<String> ruleLines = new ArrayList<>();
|
2018-06-11 22:41:11 +02:00
|
|
|
final File rulesPath = StorageUtils.get("rules/");
|
|
|
|
if (rulesPath.exists()) {
|
|
|
|
for (File f : StorageUtils.walk(rulesPath)) {
|
|
|
|
if (f.toString().endsWith(".java")) {
|
|
|
|
String path = StorageUtils.relativize(rulesPath, f).toString();
|
|
|
|
path = path.substring(0, path.length() - ".java".length());
|
|
|
|
ruleLines.add(path);
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Found external rule: " + f.getAbsolutePath());
|
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
}
|
2018-05-09 22:54:00 +02:00
|
|
|
}
|
2018-06-11 22:41:11 +02:00
|
|
|
ruleLines.addAll(StorageUtils.readAllLines(defaultRulesList));
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2018-06-11 22:41:11 +02:00
|
|
|
final File tDir = StorageUtils.resolve(StorageUtils.get(System.getProperty("java.io.tmpdir"), "WarpPi-Calculator"), "rules-rt");
|
|
|
|
// try {
|
|
|
|
// final Path defaultResource = Utils.getResource("/math-rules-cache.zip");
|
|
|
|
// }
|
|
|
|
InputStream cacheFileStream = null;
|
|
|
|
File cacheFilePath = null;
|
|
|
|
cacheFilePath = new File("math-rules-cache.zip");
|
|
|
|
boolean cacheFileExists = false;
|
|
|
|
if (PlatformUtils.isJavascript) {
|
|
|
|
PlatformUtils.loadPlatformRules();
|
|
|
|
} else {
|
|
|
|
if (cacheFilePath.exists()) {
|
|
|
|
cacheFileExists = true;
|
|
|
|
cacheFileStream = new FileInputStream(cacheFilePath);
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
cacheFileStream = StorageUtils.getResourceStream("/math-rules-cache.zip");//Paths.get(Utils.getJarDirectory().toString()).resolve("math-rules-cache.zip").toAbsolutePath(
|
|
|
|
org.apache.commons.io.FileUtils.copyInputStreamToFile(cacheFileStream, cacheFilePath);
|
|
|
|
cacheFileExists = true;
|
|
|
|
} catch (IOException ex) { //File does not exists.
|
2018-03-22 20:40:41 +01:00
|
|
|
}
|
|
|
|
}
|
2018-06-11 22:41:11 +02:00
|
|
|
boolean useCache = false;
|
|
|
|
if (cacheFileExists) {
|
|
|
|
try {
|
|
|
|
if (tDir.exists()) {
|
|
|
|
tDir.delete();
|
|
|
|
}
|
|
|
|
ZipUtils.unzip(cacheFilePath.toString(), tDir.getParent().toString(), "");
|
2018-08-28 02:38:27 +02:00
|
|
|
useCache = !StaticVars.startupArguments.isUncached();
|
2018-06-11 22:41:11 +02:00
|
|
|
} catch (final Exception ex) {
|
|
|
|
ex.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2018-08-28 02:39:41 +02:00
|
|
|
|
2018-06-11 22:41:11 +02:00
|
|
|
for (final String rulesLine : ruleLines) {
|
|
|
|
if (rulesLine.length() > 0) {
|
|
|
|
final String[] ruleDetails = rulesLine.split(",", 1);
|
|
|
|
final String ruleName = ruleDetails[0];
|
|
|
|
final String ruleNameEscaped = ruleName.replace(".", "_");
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Evaluating /rules/" + ruleNameEscaped + ".java");
|
|
|
|
final String pathWithoutExtension = "/rules/" + ruleNameEscaped;
|
|
|
|
final String scriptFile = pathWithoutExtension + ".java";
|
|
|
|
final InputStream resourcePath = StorageUtils.getResourceStream(scriptFile);
|
|
|
|
if (resourcePath == null) {
|
|
|
|
System.err.println(new FileNotFoundException("/rules/" + ruleName + ".java not found!"));
|
|
|
|
} else {
|
|
|
|
Rule r = null;
|
|
|
|
if (useCache) {
|
|
|
|
try {
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", ruleName, "Trying to load cached rule");
|
|
|
|
r = loadClassRuleFromSourceFile(scriptFile, tDir);
|
|
|
|
if (r != null) {
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", ruleName, "Loaded cached rule");
|
|
|
|
}
|
|
|
|
} catch (final Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", ruleName, "Can't load the rule!");
|
2018-03-22 10:06:31 +01:00
|
|
|
}
|
|
|
|
}
|
2018-06-11 22:41:11 +02:00
|
|
|
if (r == null || !useCache) {
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", ruleName, "This rule is not cached. Compiling");
|
|
|
|
try {
|
|
|
|
r = compileJavaRule(scriptFile, tDir);
|
|
|
|
compiledSomething = true;
|
|
|
|
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2018-08-28 02:39:41 +02:00
|
|
|
|
2018-06-11 22:41:11 +02:00
|
|
|
}
|
|
|
|
if (r != null) {
|
|
|
|
RulesManager.addRule(r);
|
2018-03-22 10:06:31 +01:00
|
|
|
}
|
2018-03-24 23:33:04 +01:00
|
|
|
}
|
2017-12-24 11:59:09 +01:00
|
|
|
}
|
2017-12-21 23:21:29 +01:00
|
|
|
}
|
|
|
|
}
|
2018-06-09 19:37:34 +02:00
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Loaded all the rules successfully");
|
2018-06-11 22:41:11 +02:00
|
|
|
if (!PlatformUtils.isJavascript && compiledSomething) {
|
|
|
|
if (cacheFileExists || cacheFilePath.exists()) {
|
|
|
|
cacheFilePath.delete();
|
2018-05-09 22:30:15 +02:00
|
|
|
}
|
2018-06-09 19:37:34 +02:00
|
|
|
ZipUtils.zip(tDir.toString(), cacheFilePath.toString(), "");
|
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Cached the compiled rules");
|
2018-03-26 09:01:26 +02:00
|
|
|
}
|
2018-06-11 22:41:11 +02:00
|
|
|
if (cacheFileStream != null) {
|
|
|
|
cacheFileStream.close();
|
|
|
|
}
|
2017-12-21 23:21:29 +01:00
|
|
|
} catch (URISyntaxException | IOException e) {
|
|
|
|
e.printStackTrace();
|
2018-06-09 19:37:34 +02:00
|
|
|
DSystem.exit(1);
|
2017-12-21 23:21:29 +01:00
|
|
|
}
|
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2018-06-11 22:41:11 +02:00
|
|
|
public static Rule compileJavaRule(String scriptFile, File tDir) throws IOException, URISyntaxException,
|
2018-05-12 21:18:29 +02:00
|
|
|
InstantiationException, IllegalAccessException, ClassNotFoundException {
|
2018-06-11 22:41:11 +02:00
|
|
|
final InputStream resource = StorageUtils.getResourceStream(scriptFile);
|
|
|
|
final String text = StorageUtils.read(resource);
|
|
|
|
final String[] textArray = text.split("\\n", 6);
|
|
|
|
if (textArray[3].contains("PATH=")) {
|
|
|
|
final String javaClassDeclaration = textArray[3].substring(6);
|
|
|
|
int extIndex = javaClassDeclaration.lastIndexOf('.');
|
|
|
|
final String javaClassNameOnly = javaClassDeclaration.substring(extIndex + 1, javaClassDeclaration.length());
|
|
|
|
final String javaClassNameAndPath = new StringBuilder("org.warp.picalculator.math.rules.").append(javaClassDeclaration).toString();
|
|
|
|
extIndex = javaClassNameAndPath.lastIndexOf('.');
|
|
|
|
final String javaCode = new StringBuilder("package ").append(javaClassNameAndPath.substring(0, extIndex >= 0 ? extIndex : javaClassNameAndPath.length())).append(";\n").append(textArray[5]).toString();
|
|
|
|
final File tDirPath = StorageUtils.getParent(StorageUtils.resolve(tDir, javaClassNameAndPath.replace('.', File.separatorChar)));
|
|
|
|
final File tFileJava = StorageUtils.resolve(tDirPath, javaClassNameOnly + ".java");
|
|
|
|
final File tFileClass = StorageUtils.resolve(tDirPath, javaClassNameOnly + ".class");
|
|
|
|
if (!tDirPath.exists()) {
|
|
|
|
StorageUtils.createDirectories(tDirPath);
|
|
|
|
}
|
|
|
|
if (tFileJava.exists()) {
|
|
|
|
tFileJava.delete();
|
|
|
|
}
|
|
|
|
StorageUtils.write(tFileJava, javaCode.getBytes("UTF-8"), DStandardOpenOption.WRITE, DStandardOpenOption.CREATE);
|
|
|
|
final boolean compiled = DJDTCompiler.compile(new String[] { "-nowarn", "-1.8", tFileJava.toString() }, new PrintWriter(System.out), new PrintWriter(System.err));
|
2018-08-28 02:38:27 +02:00
|
|
|
if (StaticVars.startupArguments.isUncached()) {
|
2018-06-11 22:41:11 +02:00
|
|
|
tFileJava.deleteOnExit();
|
|
|
|
} else {
|
|
|
|
tFileJava.delete();
|
|
|
|
}
|
|
|
|
if (compiled) {
|
|
|
|
tFileClass.deleteOnExit();
|
|
|
|
return loadClassRuleDirectly(javaClassNameAndPath, tDir);
|
|
|
|
} else {
|
|
|
|
throw new IOException("Can't build script file '" + scriptFile + "'");
|
|
|
|
}
|
2018-03-22 20:40:41 +01:00
|
|
|
} else {
|
2018-06-11 22:41:11 +02:00
|
|
|
throw new IOException("Can't build script file '" + scriptFile + "', the header is missing or wrong.");
|
2018-03-22 20:40:41 +01:00
|
|
|
}
|
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2018-06-11 22:41:11 +02:00
|
|
|
public static Rule loadClassRuleFromSourceFile(String scriptFile, File tDir) throws IOException, URISyntaxException,
|
2018-05-12 21:18:29 +02:00
|
|
|
InstantiationException, IllegalAccessException, ClassNotFoundException {
|
2018-06-11 22:41:11 +02:00
|
|
|
final InputStream resource = StorageUtils.getResourceStream(scriptFile);
|
|
|
|
final String text = StorageUtils.read(resource);
|
|
|
|
final String[] textArray = text.split("\\n", 6);
|
|
|
|
if (textArray[3].contains("PATH=")) {
|
|
|
|
final String javaClassName = textArray[3].substring(6);
|
|
|
|
System.err.println(javaClassName);
|
|
|
|
final String javaClassNameAndPath = new StringBuilder("org.warp.picalculator.math.rules.").append(javaClassName).toString();
|
|
|
|
try {
|
|
|
|
return loadClassRuleDirectly(javaClassNameAndPath, tDir);
|
|
|
|
} catch (final Exception ex) {
|
|
|
|
ex.printStackTrace();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new IOException("Can't load script file '" + scriptFile + "', the header is missing or wrong.");
|
2018-03-22 20:40:41 +01:00
|
|
|
}
|
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2018-06-11 22:41:11 +02:00
|
|
|
public static Rule loadClassRuleDirectly(String javaClassNameAndPath, File tDir) throws IOException,
|
2018-05-12 21:18:29 +02:00
|
|
|
URISyntaxException, InstantiationException, IllegalAccessException, ClassNotFoundException {
|
2018-06-11 22:41:11 +02:00
|
|
|
final DURLClassLoader cl = new DURLClassLoader(new URL[] { tDir.toURI().toURL() });
|
2018-05-12 21:18:29 +02:00
|
|
|
final Class<?> aClass = cl.loadClass(javaClassNameAndPath);
|
2018-03-22 20:40:41 +01:00
|
|
|
cl.close();
|
|
|
|
return (Rule) aClass.newInstance();
|
2018-03-22 10:06:31 +01:00
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2018-03-25 12:36:22 +02:00
|
|
|
public static void warmUp() throws Error, InterruptedException {
|
2017-12-23 15:20:42 +01:00
|
|
|
ObjectArrayList<Function> uselessResult = null;
|
2018-02-05 00:05:31 +01:00
|
|
|
boolean uselessVariable = false;
|
2018-05-12 21:18:29 +02:00
|
|
|
for (final RuleType val : RuleType.values()) {
|
2017-12-23 15:20:42 +01:00
|
|
|
final ObjectArrayList<Rule> ruleList = rules[val.ordinal()];
|
|
|
|
for (final Rule rule : ruleList) {
|
2018-02-05 00:05:31 +01:00
|
|
|
String ruleName = "<null>";
|
|
|
|
try {
|
|
|
|
ruleName = rule.getRuleName();
|
2018-05-12 21:18:29 +02:00
|
|
|
final ObjectArrayList<Function> uselessResult2 = rule.execute(generateUselessExpression());
|
2018-02-05 00:05:31 +01:00
|
|
|
uselessVariable = (uselessResult == null ? new ObjectArrayList<>() : uselessResult).equals(uselessResult2);
|
|
|
|
uselessResult = uselessResult2;
|
2018-05-12 21:18:29 +02:00
|
|
|
} catch (final Exception e) {
|
2018-02-05 00:05:31 +01:00
|
|
|
if (uselessVariable || true) {
|
|
|
|
System.err.println("Exception thrown by rule '" + ruleName + "'!");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2017-12-23 15:20:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
new MathSolver(generateUselessExpression()).solveAllSteps();
|
|
|
|
} catch (InterruptedException | Error e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2017-12-24 11:59:09 +01:00
|
|
|
private static Function generateUselessExpression() {
|
2018-05-12 21:18:29 +02:00
|
|
|
final MathContext mc = new MathContext();
|
2017-12-24 11:59:09 +01:00
|
|
|
Function expr = new Expression(mc);
|
|
|
|
expr = expr.setParameter(0, new Variable(mc, 'x', V_TYPE.VARIABLE));
|
2017-12-23 15:20:42 +01:00
|
|
|
return expr;
|
|
|
|
}
|
2018-05-12 21:18:29 +02:00
|
|
|
|
2017-12-21 23:21:29 +01:00
|
|
|
public static void addRule(Rule rule) {
|
2017-12-22 22:39:58 +01:00
|
|
|
rules[rule.getRuleType().ordinal()].add(rule);
|
2018-06-09 19:37:34 +02:00
|
|
|
ConsoleUtils.out.println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", rule.getRuleName(), "Loaded as " + rule.getRuleType() + " rule");
|
2017-12-21 23:21:29 +01:00
|
|
|
}
|
|
|
|
}
|