package it.cavallium.warppi.math.rules; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; import it.cavallium.warppi.Engine; import it.cavallium.warppi.Platform.ConsoleUtils; import it.cavallium.warppi.Platform.StorageUtils; import it.cavallium.warppi.Platform.URLClassLoader; import it.cavallium.warppi.StaticVars; import it.cavallium.warppi.math.Function; import it.cavallium.warppi.math.MathContext; import it.cavallium.warppi.math.functions.Expression; import it.cavallium.warppi.math.functions.Variable; import it.cavallium.warppi.math.functions.Variable.V_TYPE; import it.cavallium.warppi.math.solver.MathSolver; import it.cavallium.warppi.util.Error; import it.unimi.dsi.fastutil.objects.ObjectArrayList; public class RulesManager { public static ObjectArrayList[] rules; private RulesManager() {} @SuppressWarnings({ "unchecked" }) public static void initialize() { Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Loading the rules"); RulesManager.rules = new ObjectArrayList[RuleType.values().length]; for (final RuleType val : RuleType.values()) { RulesManager.rules[val.ordinal()] = new ObjectArrayList<>(); } try { boolean compiledSomething = false; InputStream defaultRulesList; try { defaultRulesList = Engine.getPlatform().getStorageUtils().getResourceStream("/default-rules.lst"); } catch (final IOException ex) { throw new FileNotFoundException("default-rules.lst not found!"); } final List ruleLines = new ArrayList<>(); final File rulesPath = Engine.getPlatform().getStorageUtils().get("rules/"); if (rulesPath.exists()) { for (final File f : Engine.getPlatform().getStorageUtils().walk(rulesPath)) { if (f.toString().endsWith(".java")) { String path = Engine.getPlatform().getStorageUtils().relativize(rulesPath, f).toString(); path = path.substring(0, path.length() - ".java".length()); ruleLines.add(path); Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Found external rule: " + f.getAbsolutePath()); } } } ruleLines.addAll(Engine.getPlatform().getStorageUtils().readAllLines(defaultRulesList)); final File tDir = Engine.getPlatform().getStorageUtils().resolve(Engine.getPlatform().getStorageUtils().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 (Engine.getPlatform().isJavascript()) { Engine.getPlatform().loadPlatformRules(); } else { if (cacheFilePath.exists()) { cacheFileExists = true; cacheFileStream = new FileInputStream(cacheFilePath); } else { try { cacheFileStream = Engine.getPlatform().getStorageUtils().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 (final IOException ex) { //File does not exists. } } boolean useCache = false; if (cacheFileExists) { try { if (tDir.exists()) { tDir.delete(); } Engine.getPlatform().unzip(cacheFilePath.toString(), tDir.getParent().toString(), ""); useCache = !StaticVars.startupArguments.isUncached(); } catch (final Exception ex) { ex.printStackTrace(); } } 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(".", "_"); Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", "Evaluating /rules/" + ruleNameEscaped + ".java"); final String pathWithoutExtension = "/rules/" + ruleNameEscaped; final String scriptFile = pathWithoutExtension + ".java"; final InputStream resourcePath = Engine.getPlatform().getStorageUtils().getResourceStream(scriptFile); if (resourcePath == null) { System.err.println(new FileNotFoundException("/rules/" + ruleName + ".java not found!")); } else { Rule r = null; if (useCache) { try { Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", ruleName, "Trying to load cached rule"); r = RulesManager.loadClassRuleFromSourceFile(scriptFile, tDir); if (r != null) { Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", ruleName, "Loaded cached rule"); } } catch (final Exception e) { e.printStackTrace(); Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", ruleName, "Can't load the rule " + ruleNameEscaped + "!"); } } if (r == null || !useCache) { Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", ruleName, "This rule is not cached. Compiling"); try { r = RulesManager.compileJavaRule(scriptFile, tDir); compiledSomething = true; } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IOException e) { e.printStackTrace(); } } if (r != null) { RulesManager.addRule(r); } } } } } Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Loaded all the rules successfully"); if (!Engine.getPlatform().isJavascript() && compiledSomething) { if (cacheFileExists || cacheFilePath.exists()) { cacheFilePath.delete(); } Engine.getPlatform().zip(tDir.toString(), cacheFilePath.toString(), ""); Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_NODEBUG, "RulesManager", "Cached the compiled rules"); } if (cacheFileStream != null) { cacheFileStream.close(); } } catch (URISyntaxException | IOException e) { e.printStackTrace(); Engine.getPlatform().exit(1); } } public static Rule compileJavaRule(final String scriptFile, final File tDir) throws IOException, URISyntaxException, InstantiationException, IllegalAccessException, ClassNotFoundException { final InputStream resource = Engine.getPlatform().getStorageUtils().getResourceStream(scriptFile); final String text = Engine.getPlatform().getStorageUtils().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("it.cavallium.warppi.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 = Engine.getPlatform().getStorageUtils().getParent(Engine.getPlatform().getStorageUtils().resolve(tDir, javaClassNameAndPath.replace('.', File.separatorChar))); final File tFileJava = Engine.getPlatform().getStorageUtils().resolve(tDirPath, javaClassNameOnly + ".java"); final File tFileClass = Engine.getPlatform().getStorageUtils().resolve(tDirPath, javaClassNameOnly + ".class"); if (!tDirPath.exists()) { Engine.getPlatform().getStorageUtils().createDirectories(tDirPath); } if (tFileJava.exists()) { tFileJava.delete(); } Engine.getPlatform().getStorageUtils(); Engine.getPlatform().getStorageUtils(); Engine.getPlatform().getStorageUtils().write(tFileJava, javaCode.getBytes("UTF-8"), StorageUtils.OpenOptionWrite, StorageUtils.OpenOptionCreate); final boolean compiled = Engine.getPlatform().compile(new String[] { "-nowarn", "-1.8", tFileJava.toString() }, new PrintWriter(System.out), new PrintWriter(System.err)); if (StaticVars.startupArguments.isUncached()) { tFileJava.deleteOnExit(); } else { tFileJava.delete(); } if (compiled) { tFileClass.deleteOnExit(); return RulesManager.loadClassRuleDirectly(javaClassNameAndPath, tDir); } else { throw new IOException("Can't build script file '" + scriptFile + "'"); } } else { throw new IOException("Can't build script file '" + scriptFile + "', the header is missing or wrong."); } } public static Rule loadClassRuleFromSourceFile(final String scriptFile, final File tDir) throws IOException, URISyntaxException, InstantiationException, IllegalAccessException, ClassNotFoundException { final InputStream resource = Engine.getPlatform().getStorageUtils().getResourceStream(scriptFile); final String text = Engine.getPlatform().getStorageUtils().read(resource); final String[] textArray = text.split("\\n", 6); if (textArray[3].contains("PATH=")) { final String javaClassName = textArray[3].substring(6); Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE, "RulesManager", "Rule java class name: " + javaClassName); final String javaClassNameAndPath = new StringBuilder("it.cavallium.warppi.math.rules.").append(javaClassName).toString(); try { return RulesManager.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."); } } public static Rule loadClassRuleDirectly(final String javaClassNameAndPath, final File tDir) throws IOException, URISyntaxException, InstantiationException, IllegalAccessException, ClassNotFoundException { final URLClassLoader cl = Engine.getPlatform().newURLClassLoader(new URL[] { tDir.toURI().toURL() }); final Class aClass = cl.loadClass(javaClassNameAndPath); cl.close(); return (Rule) aClass.newInstance(); } public static void warmUp() throws Error, InterruptedException { ObjectArrayList uselessResult = null; boolean uselessVariable = false; for (final RuleType val : RuleType.values()) { final ObjectArrayList ruleList = RulesManager.rules[val.ordinal()]; for (final Rule rule : ruleList) { String ruleName = ""; try { ruleName = rule.getRuleName(); final ObjectArrayList uselessResult2 = rule.execute(RulesManager.generateUselessExpression()); uselessVariable = (uselessResult == null ? new ObjectArrayList<>() : uselessResult).equals(uselessResult2); uselessResult = uselessResult2; } catch (final Exception e) { if (uselessVariable || true) { System.err.println("Exception thrown by rule '" + ruleName + "'!"); e.printStackTrace(); } } } } try { new MathSolver(RulesManager.generateUselessExpression()).solveAllSteps(); } catch (InterruptedException | Error e) { e.printStackTrace(); } } private static Function generateUselessExpression() { final MathContext mc = new MathContext(); Function expr = new Expression(mc); expr = expr.setParameter(0, new Variable(mc, 'x', V_TYPE.VARIABLE)); return expr; } public static void addRule(final Rule rule) { RulesManager.rules[rule.getRuleType().ordinal()].add(rule); Engine.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "RulesManager", rule.getRuleName(), "Loaded as " + rule.getRuleType() + " rule"); } }