package me.sashie.skriptyaml.utils.yaml; import java.util.Calendar; import java.util.LinkedHashMap; import java.util.Map; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.util.Vector; import org.yaml.snakeyaml.constructor.AbstractConstruct; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.YAMLException; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.ScalarNode; import org.yaml.snakeyaml.nodes.Tag; import ch.njol.skript.util.Color; import ch.njol.skript.util.Date; import ch.njol.skript.util.Time; import ch.njol.skript.util.Timespan; import ch.njol.skript.util.WeatherType; import me.sashie.skriptyaml.api.ConstructedClass; public class SkriptYamlConstructor extends SafeConstructor { public SkriptYamlConstructor() { this.yamlConstructors.put(new Tag("!skriptclass"), new ConstructSkriptClass()); this.yamlConstructors.put(new Tag("!vector"), new ConstructVector()); this.yamlConstructors.put(new Tag("!location"), new ConstructLocation()); this.yamlConstructors.put(new Tag("!skriptdate"), new ConstructSkriptDate()); //this.yamlConstructors.put(Tag.TIMESTAMP, new ConstructSkriptDate()); this.yamlConstructors.put(new Tag("!skripttime"), new ConstructSkriptTime()); this.yamlConstructors.put(new Tag("!skripttimespan"), new ConstructSkriptTimespan()); this.yamlConstructors.put(new Tag("!skriptcolor"), new ConstructSkriptColor()); this.yamlConstructors.put(new Tag("!skriptweather"), new ConstructSkriptWeather()); this.yamlConstructors.put(Tag.MAP, new ConstructCustomObject()); } public void register(String tag, ConstructedClass<?> cc) { this.yamlConstructors.put(new Tag("!" + tag), cc); } /* public Map<Object, Object> constructMapping(MappingNode node) { Map<Object, Object> mapping = this.newMap(node); this.constructMapping2ndStep(node, mapping); return mapping; } */ public Map<Object, Object> constructMap(MappingNode node) { return constructMapping(node); } private class ConstructCustomObject extends ConstructYamlMap { @Override public Object construct(Node node) { if (node.isTwoStepsConstruction()) { throw new YAMLException("Unexpected referential mapping structure. Node: " + node); } Map<?, ?> raw = (Map<?, ?>) super.construct(node); if (raw.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) { Map<String, Object> typed = new LinkedHashMap<String, Object>(raw.size()); for (Map.Entry<?, ?> entry : raw.entrySet()) { typed.put(entry.getKey().toString(), entry.getValue()); } try { return ConfigurationSerialization.deserializeObject(typed); } catch (IllegalArgumentException ex) { throw new YAMLException("Could not deserialize object", ex); } } return raw; } @Override public void construct2ndStep(Node node, Object object) { throw new YAMLException("Unexpected referential mapping structure. Node: " + node); } } private class ConstructVector extends AbstractConstruct { @Override public Object construct(Node node) { final Map<Object, Object> values = constructMapping((MappingNode) node); Double x = (Double) values.get("x"); Double y = (Double) values.get("y"); Double z = (Double) values.get("z"); if (x == null || y == null || z == null) return null; return new Vector(x, y, z); } } private class ConstructLocation extends AbstractConstruct { @Override public Object construct(Node node) { final Map<Object, Object> values = constructMapping((MappingNode) node); String w = (String) values.get("world"); Double x = (Double) values.get("x"); Double y = (Double) values.get("y"); Double z = (Double) values.get("z"); Double yaw = (Double) values.get("yaw"); Double pitch = (Double) values.get("pitch"); if (w == null | x == null || y == null || z == null || yaw == null || pitch == null) return null; return new Location(Bukkit.getServer().getWorld(w), x, y, z, (float) yaw.doubleValue(), (float) pitch.doubleValue()); } } private class ConstructSkriptClass extends AbstractConstruct { @Override public Object construct(Node node) { final Map<Object, Object> values = constructMapping((MappingNode) node); String type = (String) values.get("type"); String data = (String) values.get("data"); if (type == null || data == null) return null; return new SkriptClass(type, data).deserialize(); } } private final static Pattern TIMESTAMP_REGEXP = Pattern.compile( "^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \t]*(?:Z|([-+][0-9][0-9]?)(?::([0-9][0-9])?)?))?)?$"); private final static Pattern YMD_REGEXP = Pattern.compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$"); public static class ConstructSkriptDate extends AbstractConstruct { private Calendar calendar; public Calendar getCalendar() { return calendar; } @Override public Object construct(Node node) { ScalarNode scalar = (ScalarNode) node; String nodeValue = scalar.getValue(); Matcher match = YMD_REGEXP.matcher(nodeValue); if (match.matches()) { String year_s = match.group(1); String month_s = match.group(2); String day_s = match.group(3); calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); calendar.clear(); calendar.set(Calendar.YEAR, Integer.parseInt(year_s)); // Java's months are zero-based... calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); // x calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s)); return new Date(calendar.getTime().getTime()); } else { match = TIMESTAMP_REGEXP.matcher(nodeValue); if (!match.matches()) { throw new YAMLException("Unexpected timestamp: " + nodeValue); } String year_s = match.group(1); String month_s = match.group(2); String day_s = match.group(3); String hour_s = match.group(4); String min_s = match.group(5); // seconds and milliseconds String seconds = match.group(6); String millis = match.group(7); if (millis != null) { seconds = seconds + "." + millis; } double fractions = Double.parseDouble(seconds); int sec_s = (int) Math.round(Math.floor(fractions)); int usec = (int) Math.round((fractions - sec_s) * 1000); // timezone String timezoneh_s = match.group(8); String timezonem_s = match.group(9); TimeZone timeZone; if (timezoneh_s != null) { String time = timezonem_s != null ? ":" + timezonem_s : "00"; timeZone = TimeZone.getTimeZone("GMT" + timezoneh_s + time); } else { // no time zone provided timeZone = TimeZone.getTimeZone("UTC"); } calendar = Calendar.getInstance(timeZone); calendar.set(Calendar.YEAR, Integer.parseInt(year_s)); // Java's months are zero-based... calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s)); calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour_s)); calendar.set(Calendar.MINUTE, Integer.parseInt(min_s)); calendar.set(Calendar.SECOND, sec_s); calendar.set(Calendar.MILLISECOND, usec); return new Date(calendar.getTime().getTime()); } } } public static class ConstructSkriptTime extends AbstractConstruct { @Override public Object construct(Node node) { ScalarNode scalar = (ScalarNode) node; String nodeValue = scalar.getValue(); return Time.parse(nodeValue); } } public static class ConstructSkriptTimespan extends AbstractConstruct { @Override public Object construct(Node node) { ScalarNode scalar = (ScalarNode) node; String nodeValue = scalar.getValue(); return Timespan.parse(nodeValue); } } public static class ConstructSkriptColor extends AbstractConstruct { @Override public Object construct(Node node) { ScalarNode scalar = (ScalarNode) node; String nodeValue = scalar.getValue(); return Color.byName(nodeValue); } } public static class ConstructSkriptWeather extends AbstractConstruct { @Override public Object construct(Node node) { ScalarNode scalar = (ScalarNode) node; String nodeValue = scalar.getValue(); return WeatherType.parse(nodeValue); } } }