/**
* BetonQuest - advanced quests for Bukkit
* Copyright (C) 2016 Jakub "Co0sh" Sapalski
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pl.betoncraft.betonquest.config;
import java.io.File;
import java.util.HashMap;
import java.util.Set;
import pl.betoncraft.betonquest.config.ConfigAccessor.AccessorType;
import pl.betoncraft.betonquest.utils.Debug;
/**
* Holds configuration files of the package
*
* @author Jakub Sapalski
*/
public class ConfigPackage {
private String name;
private File folder;
private boolean enabled;
private ConfigAccessor main;
private ConfigAccessor events;
private ConfigAccessor conditions;
private ConfigAccessor objectives;
private ConfigAccessor journal;
private ConfigAccessor items;
private ConfigAccessor custom;
private HashMap<String, ConfigAccessor> conversations = new HashMap<>();
/**
* Loads a package from specified directory. It doesn't have to be valid
* package directory.
*
* @param pack
* the directory containing this package
* @param name
* the name of this package
*/
public ConfigPackage(File pack, String name) {
if (!pack.isDirectory())
return;
folder = pack;
this.name = name;
main = new ConfigAccessor(new File(pack, "main.yml"), "main.yml", AccessorType.MAIN);
events = new ConfigAccessor(new File(pack, "events.yml"), "events.yml", AccessorType.EVENTS);
conditions = new ConfigAccessor(new File(pack, "conditions.yml"), "conditions.yml", AccessorType.CONDITIONS);
objectives = new ConfigAccessor(new File(pack, "objectives.yml"), "objectives.yml", AccessorType.OBJECTIVES);
journal = new ConfigAccessor(new File(pack, "journal.yml"), "journal.yml", AccessorType.JOURNAL);
items = new ConfigAccessor(new File(pack, "items.yml"), "items.yml", AccessorType.ITEMS);
custom = new ConfigAccessor(new File(pack, "custom.yml"), "custom.yml", AccessorType.CUSTOM);
File convFile = new File(pack, "conversations");
if (convFile.exists() && convFile.isDirectory()) {
for (File conv : convFile.listFiles()) {
String convName = conv.getName();
if (convName.endsWith(".yml")) {
ConfigAccessor convAccessor = new ConfigAccessor(conv, convName, AccessorType.CONVERSATION);
conversations.put(convName.substring(0, convName.length() - 4), convAccessor);
}
}
}
enabled = main.getConfig().getBoolean("enabled", true);
}
/**
* @return if the package is enabled (true) or disabled (false)
*/
public boolean isEnabled() {
return enabled;
}
/**
* Returns a raw string (without inserted variables)
*
* @param address
* address of the string
* @return the raw string
*/
public String getRawString(String address) {
// prepare the address
String[] parts = address.split("\\.");
if (parts.length < 2) {
return null;
}
// get the right file
String file = parts[0];
ConfigAccessor config = null;
int startPath = 1;
switch (file) {
case "main":
config = main;
break;
case "events":
config = events;
break;
case "conditions":
config = conditions;
break;
case "journal":
config = journal;
break;
case "items":
config = items;
break;
case "objectives":
config = objectives;
break;
case "conversations":
// conversations go one level deeper
if (parts.length < 3) {
return null;
}
config = conversations.get(parts[1]);
startPath = 2;
break;
default:
break;
}
// if config accessor wasn't found, return null
if (config == null) {
return null;
}
// retrieve the string from the path
StringBuilder newPath = new StringBuilder();
for (int i = startPath; i < parts.length; i++) {
newPath.append(parts[i]);
if (i < parts.length - 1) {
newPath.append('.');
}
}
return config.getConfig().getString(newPath.toString(), null);
}
/**
* Returns a string with inserted variables
*
* @param address
* address of the string
* @return the string
*/
public String getString(String address) {
String value = getRawString(address);
if (value == null) {
return null;
}
if (!value.contains("$")) {
return value;
}
// handle "$this$" variables
value = value.replace("$this$", name);
// handle the rest
while (true) {
int start = value.indexOf('$');
int end = value.indexOf('$', start + 1);
if (start == -1 || end == -1) {
break;
}
String varName = value.substring(start + 1, end);
String varVal = main.getConfig().getString("variables." + varName);
if (varVal == null) {
Debug.error(String.format("Variable %s not defined in package %s", varName, name));
return null;
} else if (varVal
.matches("^\\$[a-zA-Z0-9]+\\$->\\(\\-?\\d+\\.?\\d*;\\-?\\d+\\.?\\d*;\\-?\\d+\\.?\\d*\\)$")) {
// handle location variables
// parse the inner location
String innerVarName = varVal.substring(1, varVal.indexOf('$', 2));
String innerVarVal = main.getConfig().getString("variables." + innerVarName);
if (innerVarVal == null) {
Debug.error(String.format("Location variable %s is not defined, in variable %s, package %s.",
innerVarName, varName, name));
return null;
}
if (!innerVarVal.matches("^\\-?\\d+;\\-?\\d+;\\-?\\d+;.+$")) {
Debug.error(
String.format("Inner variable %s is not valid location, in variable %s, package %s.",
innerVarName, varName, name));
return null;
}
double x1, y1, z1;
String rest;
try {
int i = innerVarVal.indexOf(';');
x1 = Double.parseDouble(innerVarVal.substring(0, i));
int j = innerVarVal.indexOf(';', i + 1);
y1 = Double.parseDouble(innerVarVal.substring(i + 1, j));
int k = innerVarVal.indexOf(';', j + 1);
z1 = Double.parseDouble(innerVarVal.substring(j + 1, k));
// rest is world + possible other arguments
rest = innerVarVal.substring(k, innerVarVal.length());
} catch (NumberFormatException e) {
Debug.error(String.format(
"Could not parse coordinates in inner variable %s in variable %s in package %s",
innerVarName, varName, name));
return null;
}
// parse the vector
double x2, y2, z2;
try {
int s = varVal.indexOf('(');
int i = varVal.indexOf(';');
int j = varVal.indexOf(';', i + 1);
int e = varVal.indexOf(')');
x2 = Double.parseDouble(varVal.substring(s + 1, i));
y2 = Double.parseDouble(varVal.substring(i + 1, j));
z2 = Double.parseDouble(varVal.substring(j + 1, e));
} catch (NumberFormatException e) {
Debug.error(String.format("Could not parse vector inlocation variable %s in package %s",
varName, name));
return null;
}
double x3 = x1 + x2, y3 = y1 + y2, z3 = z1 + z2;
value = value.replace("$" + varName + "$", String.format("%.2f;%.2f;%.2f%s", x3, y3, z3, rest));
} else {
value = value.replace("$" + varName + "$", varVal);
}
}
return value;
}
public boolean setString(String address, String value) {
// prepare the address
String[] parts = address.split("\\.");
if (parts.length < 2) {
return false;
}
// get the right file
String file = parts[0];
ConfigAccessor config = null;
int startPath = 1;
switch (file) {
case "main":
config = main;
break;
case "events":
config = events;
break;
case "conditions":
config = conditions;
break;
case "journal":
config = journal;
break;
case "items":
config = items;
break;
case "objectives":
config = objectives;
break;
case "conversations":
// conversations go one level deeper
if (parts.length < 3) {
return false;
}
config = conversations.get(parts[1]);
startPath = 2;
break;
default:
break;
}
// if config accessor wasn't found, return false
if (config == null) {
return false;
}
// retrieve the string from the path
StringBuilder newPath = new StringBuilder();
for (int i = startPath; i < parts.length; i++) {
newPath.append(parts[i]);
if (i < parts.length - 1) {
newPath.append('.');
}
}
config.getConfig().set(newPath.toString(), value);
config.saveConfig();
return true;
}
/**
* @return the main configuration of the package
*/
public ConfigAccessor getMain() {
return main;
}
/**
* @return the events config
*/
public ConfigAccessor getEvents() {
return events;
}
/**
* @return the conditions config
*/
public ConfigAccessor getConditions() {
return conditions;
}
/**
* @return the journal config
*/
public ConfigAccessor getJournal() {
return journal;
}
/**
* @return the items config
*/
public ConfigAccessor getItems() {
return items;
}
/**
* @return the objectives config
*/
public ConfigAccessor getObjectives() {
return objectives;
}
/**
* @return the config with custom settings
*/
public ConfigAccessor getCustom() {
return custom;
}
/**
* @param name
* name of the conversation to search for
* @return the conversation config
*/
public ConfigAccessor getConversation(String name) {
return conversations.get(name);
}
/**
* @return the set of conversation names
*/
public Set<String> getConversationNames() {
return conversations.keySet();
}
/**
* @return the name of this package
*/
public String getName() {
return name;
}
/**
* @return the folder which contains this package
*/
public File getFolder() {
return folder;
}
}