package net.aufdemrand.denizen.objects;
import java.util.List;
import net.aufdemrand.denizen.objects.properties.Property;
import net.aufdemrand.denizen.objects.properties.PropertyParser;
import net.aufdemrand.denizen.scripts.ScriptRegistry;
import net.aufdemrand.denizen.scripts.containers.ScriptContainer;
import net.aufdemrand.denizen.tags.Attribute;
import net.aufdemrand.denizen.tags.TagManager;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizencore.utilities.YamlConfiguration;
import org.json.JSONObject;
public class dScript implements dObject {
// <--[language]
// @name Script
// @description
// A somewhat vague term used to describe a collection of script entries and other script parts.
//
// For example, 'Hey, check out this script I just wrote!', probably refers to a collection of script entries
// that make up some kind of script container. Perhaps it is a NPC Assignment Script Container that provides
// waypoint functionality, or a world script that implements and keeps track of a new player stat. 'Script' can
// refer to a single container, as well as a collection of containers that share a common theme.
//
// Scripts that contain a collection of containers are typically kept to a single file. Multiple containers are
// permitted inside a single file, but it should be noted that container names are stored on a global level. That
// is, naming scripts should be done with care to avoid duplicate script names.
//
// -->
// <--[language]
// @name dScript
// @group Object System
// @description
// 1) A dObject that represents a script container. dScripts contain all information inside the script, and can be
// used in a variety of commands that require script arguments. For example, run and inject will 'execute'
// script entries inside of a script container when given a matching dScript object.
//
// dScripts also provide a way to access attributes accessed by the replaceable tag system by using the object
// fetcher or any other entry point to a dScript object. dScript objects have the object identifier of 's@'.
// For example: s@script_name
//
// 2) The overall 'scripting language' that Denizen implements is referred to as 'dScripting', or 'dScript'.
// dScripts use YAML + Denizen's Scripting API to parse scripts that are stored as .yml or .dscript files. Scripts
// go in the .../plugins/Denizen/scripts folder.
//
// -->
///////////////
// Object Fetcher
/////////////
// <--[language]
// @name s@
// @group Object Fetcher System
// @description
// s@ refers to the 'object identifier' of a dScript. The 's@' is notation for Denizen's Object
// Fetcher. The only valid constructor for a dScript is the name of the script container that it should be
// associated with. For example, if my script container is called 'cool_script', the dScript object for that script
// would be able to be referenced (fetched) with s@cool_script.
// -->
/**
* Gets a dContainer Object from a dScript argument.
*
* @param string the dScript argument String
* @return a Script, or null if incorrectly formatted
*/
@Fetchable("s")
public static dScript valueOf(String string) {
if (string.startsWith("s@"))
string = string.substring(2);
dScript script = new dScript(string);
// Make sure it's valid.
if (script.isValid())
return script;
else
return null;
}
public static boolean matches(String string) {
if (string.toLowerCase().startsWith("s@")) return true;
dScript script = new dScript(string);
// Make sure it's valid.
return script.isValid();
}
//////////////////
// Constructor
////////////////
/**
* Creates a script object from a script name. If the script is valid, {@link #isValid()} will return true.
*
* @param scriptName the name of the script
*/
public dScript(String scriptName) {
if (ScriptRegistry.getScriptContainer(scriptName) != null) {
container = ScriptRegistry.getScriptContainer(scriptName);
name = scriptName.toUpperCase();
valid = true;
}
}
public dScript(ScriptContainer container) {
this.container = container;
name = container.getName().toUpperCase();
valid = true;
}
///////////////////////
// Instance fields and methods
/////////////////////
// Keep track of the corresponding ScriptContainer
private ScriptContainer container;
// Make the default prefix "Container"
private String prefix = "Container";
private boolean valid = false;
/**
* Confirms that the script references a valid name and type in current loaded ScriptsContainers.
*
* @return true if the script is valid, false if the script was not found, or the type is missing
*/
public boolean isValid() {
return valid;
}
/**
* Gets the type of the ScriptContainer, as defined by the TYPE: key.
*
* @return the type of the Script Container
*/
public String getType() {
return (container != null ? container.getContainerType() : "invalid");
}
private String name = null;
/**
* Gets the name of the ScriptContainer.
*
* @return script name
*/
public String getName() {
return name;
}
/**
* Gets the contents of the scriptContainer.
*
* @return ConfigurationSection of the script contents
*/
public ScriptContainer getContainer() {
return container;
}
///////////////
// dObject Methods
////////////
@Override
public String getObjectType() {
return "Container";
}
@Override
public String identify() {
return "s@" + name;
}
@Override
public String identifySimple() {
return identify();
}
@Override
public String toString() {
return identify();
}
@Override
public String getPrefix() {
return prefix;
}
@Override
public dObject setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
@Override
public String debug() {
return String.format("<G>%s='<A>%s<Y>(%s)<G>' ", prefix, name, getType());
}
@Override
public boolean isUnique() {
return true;
}
@Override
public String getAttribute(Attribute attribute) {
if (attribute == null) return "null";
// <--[tag]
// @attribute <s@script.container_type>
// @returns Element
// @description
// Returns the type of script container that is associated with this dScript object. For example: 'task', or
// 'world'.
// -->
if (attribute.startsWith("container_type"))
return new Element(container.getContainerType())
.getAttribute(attribute.fulfill(1));
// <--[tag]
// @attribute <s@script.name>
// @returns Element
// @description
// Returns the name of the script container.
// -->
if (attribute.startsWith("name")) {
return new Element(name)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.relative_filename>
// @returns Element
// @description
// Returns the filename that contains the script, relative to the denizen/ folder.
// -->
if (attribute.startsWith("relative_filename")) {
return new Element(container.getFileName().replace(DenizenAPI.getCurrentInstance().getDataFolder().getAbsolutePath(), "").replace("\\", "/"))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.filename>
// @returns Element
// @description
// Returns the absolute filename that contains the script.
// -->
if (attribute.startsWith("filename")) {
return new Element(container.getFileName().replace("\\", "/"))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.original_name>
// @returns Element
// @description
// Returns the originally cased script name.
// -->
if (attribute.startsWith("original_name")) {
return new Element(container.getOriginalName())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.constant[<constant_name>]>
// @returns Element or dList
// @description
// Returns the value of the constant as either an Element or dList.
// -->
if (attribute.startsWith("cons")) {
if (!attribute.hasContext(1)) return null;
YamlConfiguration section = getContainer().getConfigurationSection("constants");
if (section == null) return null;
Object obj = section.get(attribute.getContext(1).toUpperCase());
if (obj == null) return null;
if (obj instanceof List) {
dList list = new dList();
for (Object each : (List<Object>) obj)
list.add(TagManager.tag(attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getPlayer(),
attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getNPC(), each.toString(), false, attribute.getScriptEntry()));
return list.getAttribute(attribute.fulfill(1));
}
else return new Element(TagManager.tag(attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getPlayer(),
attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getNPC(), obj.toString(), false, attribute.getScriptEntry()))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.yaml_key[<constant_name>]>
// @returns Element or dList
// @description
// Returns the value of the script's YAML as either an Element or dList.
// -->
if (attribute.startsWith("yaml_key")
&& attribute.hasContext(1)) {
ScriptContainer container = getContainer();
if (container == null) {
dB.echoError("Missing script container?!");
return new Element(identify()).getAttribute(attribute);
}
YamlConfiguration section = container.getConfigurationSection("");
if (section == null) {
dB.echoError("Missing YAML section?!");
return new Element(identify()).getAttribute(attribute);
}
Object obj = section.get(attribute.getContext(1).toUpperCase());
if (obj == null) return null;
if (obj instanceof List) {
dList list = new dList();
for (Object each : (List<Object>) obj)
list.add(TagManager.tag(attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getPlayer(),
attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getNPC(), each.toString(), false, attribute.getScriptEntry()));
return list.getAttribute(attribute.fulfill(1));
}
else return new Element(TagManager.tag(attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getPlayer(),
attribute.getScriptEntry() == null ? null: attribute.getScriptEntry().getNPC(), obj.toString(), false, attribute.getScriptEntry()))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.list_keys[<constant_name>]>
// @returns dList
// @description
// Returns a list of all keys within a script.
// -->
if (attribute.startsWith("list_keys")) {
return new dList(getContainer().getConfigurationSection(attribute.hasContext(1) ? attribute.getContext(1): "").getKeys(false))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.list_deep_keys[<constant_name>]>
// @returns dList
// @description
// Returns a list of all keys within a script, searching recursively.
// -->
if (attribute.startsWith("list_deep_keys")) {
return new dList(getContainer().getConfigurationSection(attribute.hasContext(1) ? attribute.getContext(1): "").getKeys(true))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.to_json>
// @returns Element
// @description
// Converts the YAML Script Container to a JSON array.
// Best used with 'yaml data' type scripts.
// -->
if (attribute.startsWith("to_json")) {
JSONObject jsobj = new JSONObject(container.getConfigurationSection("").getMap());
jsobj.remove("TYPE");
return new Element(jsobj.toString()).getAttribute(attribute.fulfill(1));
}
/////////////////
// dObject attributes
///////////////
// <--[tag]
// @attribute <s@script.debug>
// @returns Element
// @description
// Returns the debug entry for this object. This contains the prefix, the name of the dScript object, and the
// type of ScriptContainer is held within. All objects fetchable by the Object Fetcher will return a valid
// debug entry for the object that is fulfilling this attribute.
// -->
if (attribute.startsWith("debug")) {
return new Element(debug()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.prefix>
// @returns Element
// @description
// Returns the prefix for this object. By default this will return 'Script', however certain situations will
// return a finer scope. All objects fetchable by the Object Fetcher will return a valid prefix for the object
// that is fulfilling this attribute.
// -->
if (attribute.startsWith("prefix")) {
return new Element(prefix).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <s@script.type>
// @returns Element
// @description
// Always returns 'Script' for dScript objects. All objects fetchable by the Object Fetcher will return the
// type of object that is fulfilling this attribute.
// -->
if (attribute.startsWith("type")) {
return new Element("Script").getAttribute(attribute.fulfill(1));
}
// Iterate through this object's properties' attributes
for (Property property : PropertyParser.getProperties(this)) {
String returned = property.getAttribute(attribute);
if (returned != null) return returned;
}
return new Element(identify()).getAttribute(attribute);
}
}