package me.sashie.skriptyaml.skript; import java.util.Arrays; import java.util.List; import javax.annotation.Nullable; import org.bukkit.event.Event; import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import me.sashie.skriptyaml.SkriptYaml; import me.sashie.skriptyaml.utils.StringUtil; import me.sashie.skriptyaml.utils.yaml.YAMLProcessor; @Name("YAML Comments/header") @Description("Gets, sets, deletes comments or the header of a cached yaml file" + "\n - Headers don't contain '#' so add it yourself if you want it" + "\n - Comments can only be at root level ie. 'root' not 'root.something'" + "\n - Both comment and header expressions can be set to multiple elements" + "\n - This expression does not save to file" + "\n - Option to have an extra line or not depending if you use comment or header" + "\n - Any 'extra lines' are removed when deleting comments/headers") @Examples({ "set the comments of yaml node \"test\" from \"config\" to \"First line\" and \"Second line\"", "delete the comments of yaml node \"test\" from \"config\"", " ", "set {_header::*} to \"First line\" and \"Second line\"", "set the comments at the top of \"config\" to {_header::*}", "delete the comments at the top of \"config\"", " ", "set the header of \"config\" to {_header::*}" }) @Since("1.1.0") public class ExprYamlComments extends SimpleExpression<Object> { static { Skript.registerExpression(ExprYamlComments.class, Object.class, ExpressionType.SIMPLE, "[the] comment[s] (of|from) [y[a]ml] node[s] %strings% (of|in|from) %string% [(1¦with [an] extra line)]", "[the] (comment[s] (at|on) [the] top of |header (of|from)) %string% [(1¦with [an] extra line)]"); } private Expression<String> paths, file; private int mark; private static enum States { COMMENT, HEADER } private States state; @Override public Class<? extends Object> getReturnType() { return Object.class; } @Override public boolean isSingle() { return false; } @Override public String toString(@Nullable Event event, boolean b) { return state.toString().toLowerCase() + (state == States.COMMENT ? " for path " + this.paths.toString(event, b) : "") + " from yaml " + this.file.toString(event, b); } @Override @Nullable protected Object[] get(Event event) { final String name = this.file.getSingle(event); final String path = this.paths.getSingle(event); if (!SkriptYaml.YAML_STORE.containsKey(name)) { SkriptYaml.warn("No yaml by the name '" + name + "' has been loaded"); return null; } YAMLProcessor config = SkriptYaml.YAML_STORE.get(name); String s = null; if (state == States.COMMENT) s = config.getComment(path); else if (state == States.HEADER) s = config.getHeader(); if (s == null) return null; List<String> list = Arrays.asList(s.split("\\r?\\n")); return list.toArray(new String[list.size()]); } @Override public void change(Event event, Object[] delta, Changer.ChangeMode mode) { final String name = StringUtil.checkSeparator(this.file.getSingle(event)); String[] paths = null; if (!SkriptYaml.YAML_STORE.containsKey(name)) { SkriptYaml.warn("No yaml by the name '" + name + "' has been loaded"); return; } YAMLProcessor config = SkriptYaml.YAML_STORE.get(name); if (state == States.COMMENT) { paths = this.paths.getAll(event); if (mode == ChangeMode.SET) { String[] comments = new String[delta.length]; for (String p : paths) { if (!p.contains(".")) { if (config.getMap().containsKey(p)) config.setComment(p, this.mark == 1 ? true : false, toStringArray(delta, comments)); else SkriptYaml.warn("'" + p + "' is not a valid path in '" + name + "'"); } else { SkriptYaml.warn("Comments can only be added to root paths not '" + p + "' in '" + name + "'" ); } } } else if (mode == ChangeMode.DELETE || mode == ChangeMode.RESET) { for (String p : paths) { if (config.getMap().containsKey(p)) { String n = null; config.setComment(p, false, n); } } } } else if (state == States.HEADER) { if (mode == ChangeMode.SET) { config.setHeader(toStringArray(delta, new String[delta.length])); config.setExtraHeaderLine(this.mark == 1 ? true : false); } else if (mode == ChangeMode.DELETE || mode == ChangeMode.RESET) { String n = null; config.setHeader(n); config.setExtraHeaderLine(false); } } } private String[] toStringArray(Object[] input, String[] output) { for (int i = 0; i < input.length; i++) output[i] = (String) input[i]; return output; } @Override public Class<?>[] acceptChange(final Changer.ChangeMode mode) { if (mode == Changer.ChangeMode.DELETE || mode == Changer.ChangeMode.RESET || mode == Changer.ChangeMode.SET) { return CollectionUtils.array(Object[].class); } return null; } @SuppressWarnings("unchecked") @Override public boolean init(Expression<?>[] e, int matchedPattern, Kleenean isDelayed, ParseResult parse) { if (matchedPattern == 0) { state = States.COMMENT; paths = (Expression<String>) e[0]; file = (Expression<String>) e[1]; } else { state = States.HEADER; file = (Expression<String>) e[0]; } this.mark = parse.mark; return true; } }