/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.utils; import com.jcwhatever.nucleus.managed.scripting.IScript; import com.jcwhatever.nucleus.managed.scripting.IScriptFactory; import com.jcwhatever.nucleus.utils.file.FileUtils; import com.jcwhatever.nucleus.utils.file.FileUtils.DirectoryTraversal; import com.jcwhatever.nucleus.utils.text.TextUtils; import org.bukkit.plugin.Plugin; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; /** * Script utilities. * * @see com.jcwhatever.nucleus.Nucleus#getScriptManager */ public final class ScriptUtils { private ScriptUtils() {} private static final Pattern PATTERN_LEADING_DOT = Pattern.compile("^\\."); /** * Load scripts from a script folder. * * @param plugin The scripts owning plugin. * @param engineManager The engine manager used to determine if a file type is a script. * @param scriptFolder The folder to find scripts in. * @param traversal The type of directory traversal used to find script files. * @param scriptFactory A script constructor to create new script instances. */ public static List<IScript> loadScripts(Plugin plugin, ScriptEngineManager engineManager, File scriptFolder, DirectoryTraversal traversal, IScriptFactory scriptFactory) { return loadScripts(plugin, engineManager, scriptFolder, null, traversal, scriptFactory); } /** * Load scripts from a script folder. * * @param plugin The scripts owning plugin. * @param engineManager The engine manager used to determine if a file type is a script. * @param scriptFolder The folder to find scripts in. * @param exclude Optional file or folder to exclude. * @param traversal The type of directory traversal used to find script files. * @param scriptFactory A script constructor to create new script instances. * */ public static List<IScript> loadScripts(Plugin plugin, ScriptEngineManager engineManager, File scriptFolder, @Nullable File exclude, DirectoryTraversal traversal, IScriptFactory scriptFactory) { PreCon.notNull(plugin); PreCon.notNull(scriptFolder); PreCon.isValid(scriptFolder.isDirectory()); if (!scriptFolder.exists()) return new ArrayList<>(0); List<File> scriptFiles = FileUtils.getFiles(scriptFolder, traversal); List<IScript> result = new ArrayList<>(scriptFiles.size()); for (File file : scriptFiles) { if (exclude != null && file.getAbsolutePath().startsWith(exclude.getAbsolutePath())) { continue; } String type = getScriptType(file); if (type == null || type.isEmpty()) continue; if (engineManager.getEngineByExtension(type) == null) continue; String name = getScriptName(scriptFolder, file); try { BufferedReader reader = new BufferedReader(new FileReader(file)); StringBuilder buffer = new StringBuilder(2000); String line; while ((line = reader.readLine()) != null) { buffer.append(line); buffer.append('\n'); } reader.close(); IScript script = scriptFactory.create(name, file, type, buffer.toString()); if (script != null) result.add(script); } catch (IOException e) { e.printStackTrace(); } } return result; } /** * Load a single script into a new script instance. * * @param plugin The scripts owning plugin. * @param scriptFolder The folder to find scripts in. * @param scriptFile The script file. * @param scriptFactory A script constructor used to create new script instances. * * @return Null if the file is not found or there is an error opening or reading from it. */ @Nullable public static IScript loadScript(Plugin plugin, File scriptFolder, File scriptFile, IScriptFactory scriptFactory) { PreCon.notNull(plugin); PreCon.notNull(scriptFolder); PreCon.notNull(scriptFile); if (!scriptFile.exists()) return null; try { BufferedReader reader = new BufferedReader(new FileReader(scriptFile)); StringBuilder buffer = new StringBuilder(2000); String line; while ((line = reader.readLine()) != null) { buffer.append(line); buffer.append('\n'); } reader.close(); String scriptName = getScriptName(scriptFolder, scriptFile); String scriptType = getScriptType(scriptFile); return scriptFactory.create(scriptName, scriptFile, scriptType, buffer.toString()); } catch (IOException e) { e.printStackTrace(); return null; } } /** * Get a name for a script based on the scriptFolder and file name. * * @param scriptFolder The base folder where scripts are kept. * @param file The script file. */ public static String getScriptName(File scriptFolder, File file) { PreCon.notNull(scriptFolder); PreCon.isValid(scriptFolder.isDirectory()); PreCon.notNull(file); PreCon.isValid(!file.isDirectory()); String baseCacheName = FileUtils.getRelative(scriptFolder, file.getParentFile()); Matcher pathMatcher = TextUtils.PATTERN_FILEPATH_SLASH.matcher(baseCacheName); baseCacheName = pathMatcher.replaceAll("."); String result = baseCacheName + '.' + FileUtils.getNameWithoutExtension(file); Matcher leadingDotMatcher = PATTERN_LEADING_DOT.matcher(result); return leadingDotMatcher.replaceAll(""); } /** * Get the script type from a file. * * <p>Returns the file extension.</p> * * @param file The script file. */ public static String getScriptType(File file) { PreCon.notNull(file); PreCon.isValid(!file.isDirectory()); return FileUtils.getFileExtension(file); } /** * Evaluate a script into a script engine. * * @param engine The script engine. * @param context The script context. * @param script The script to evaluate. * * @return The results returned from the script, if any. */ public static Result<Object> eval(ScriptEngine engine, ScriptContext context, IScript script) { File file = script.getFile(); String filename = file != null ? file.getName() : "<unknown>"; String engineName = engine.getFactory().getEngineName(); boolean isNashorn = engineName.contains("Nashorn"); engine.put(ScriptEngine.FILENAME, filename); context.setAttribute(ScriptEngine.FILENAME, filename, ScriptContext.ENGINE_SCOPE); Object result; try { if (isNashorn) { context.setAttribute("script", script.getScript(), ScriptContext.ENGINE_SCOPE); context.setAttribute("scriptName", filename, ScriptContext.ENGINE_SCOPE); // evaluate script into Nashorn result = engine.eval("load({ script: script, name: scriptName})", context); } else { // evaluate script result = engine.eval(script.getScript(), context); } return new Result<Object>(true, result); } catch (Throwable e) { e.printStackTrace(); return new Result<Object>(false); } } }