package com.earth2me.essentials.storage;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.yaml.snakeyaml.Yaml;
public class YamlStorageWriter implements IStorageWriter
{
private transient static final Pattern NON_WORD_PATTERN = Pattern.compile("\\W");
private transient static final Yaml YAML = new Yaml();
private transient final PrintWriter writer;
public YamlStorageWriter(final PrintWriter writer)
{
this.writer = writer;
}
@Override
public void save(final StorageObject object)
{
try
{
writeToFile(object, 0, object.getClass());
}
catch (IllegalArgumentException ex)
{
Logger.getLogger(YamlStorageWriter.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
Logger.getLogger(YamlStorageWriter.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void writeToFile(final Object object, final int depth, final Class clazz) throws IllegalAccessException
{
for (Field field : clazz.getDeclaredFields())
{
final int modifier = field.getModifiers();
if (Modifier.isPrivate(modifier) && !Modifier.isTransient(modifier) && !Modifier.isStatic(modifier))
{
field.setAccessible(true);
final Object data = field.get(object);
if (writeKey(field, depth, data))
{
continue;
}
if (data instanceof StorageObject)
{
writer.println();
writeToFile(data, depth + 1, data.getClass());
}
else if (data instanceof Map)
{
writeMap((Map<Object, Object>)data, depth + 1);
}
else if (data instanceof Collection)
{
writeCollection((Collection<Object>)data, depth + 1);
}
else if (data instanceof Location)
{
writeLocation((Location)data, depth + 1);
}
else
{
writeScalar(data);
writer.println();
}
}
}
}
private boolean writeKey(final Field field, final int depth, final Object data)
{
final boolean commentPresent = writeComment(field, depth);
if (data == null && !commentPresent)
{
return true;
}
writeIndention(depth);
if (data == null && commentPresent)
{
writer.print('#');
}
final String name = field.getName();
writer.print(name);
writer.print(": ");
if (data == null && commentPresent)
{
writer.println();
writer.println();
return true;
}
return false;
}
private boolean writeComment(final Field field, final int depth)
{
final boolean commentPresent = field.isAnnotationPresent(Comment.class);
if (commentPresent)
{
final Comment comments = field.getAnnotation(Comment.class);
for (String comment : comments.value())
{
final String trimmed = comment.trim();
if (trimmed.isEmpty())
{
continue;
}
writeIndention(depth);
writer.print("# ");
writer.print(trimmed);
writer.println();
}
}
return commentPresent;
}
private void writeCollection(final Collection<Object> data, final int depth) throws IllegalAccessException
{
writer.println();
if (data.isEmpty())
{
writer.println();
}
for (Object entry : data)
{
if (entry != null)
{
writeIndention(depth);
writer.print("- ");
if (entry instanceof StorageObject)
{
writer.println();
writeToFile(entry, depth + 1, entry.getClass());
}
else if (entry instanceof Location)
{
writeLocation((Location)entry, depth + 1);
}
else
{
writeScalar(entry);
}
}
}
writer.println();
}
private void writeMap(final Map<Object, Object> data, final int depth) throws IllegalArgumentException, IllegalAccessException
{
writer.println();
if (data.isEmpty())
{
writer.println();
}
for (Entry<Object, Object> entry : data.entrySet())
{
final Object value = entry.getValue();
if (value != null)
{
writeIndention(depth);
writeKey(entry.getKey());
writer.print(": ");
if (value instanceof StorageObject)
{
writer.println();
writeToFile(value, depth + 1, value.getClass());
}
else if (value instanceof Collection)
{
writeCollection((Collection<Object>)value, depth + 1);
}
else if (value instanceof Location)
{
writeLocation((Location)value, depth + 1);
}
else
{
writeScalar(value);
writer.println();
}
}
}
}
private void writeIndention(final int depth)
{
for (int i = 0; i < depth; i++)
{
writer.print(" ");
}
}
private void writeScalar(final Object data)
{
if (data instanceof String || data instanceof Boolean || data instanceof Number)
{
synchronized (YAML)
{
YAML.dumpAll(Collections.singletonList(data).iterator(), writer);
}
}
else if (data instanceof Material)
{
writeMaterial(data);
writer.println();
}
else if (data instanceof MaterialData)
{
writeMaterialData(data);
writer.println();
}
else if (data instanceof ItemStack)
{
writeItemStack(data);
writer.println();
}
else if (data instanceof EnchantmentLevel)
{
writeEnchantmentLevel(data);
writer.println();
}
else
{
throw new UnsupportedOperationException();
}
}
private void writeKey(final Object data)
{
if (data instanceof String || data instanceof Boolean || data instanceof Number)
{
String output = data.toString();
if (NON_WORD_PATTERN.matcher(output).find())
{
writer.print('"');
writer.print(output.replace("\"", "\\\""));
writer.print('"');
}
else
{
writer.print(output);
}
}
else if (data instanceof Material)
{
writeMaterial(data);
}
else if (data instanceof MaterialData)
{
writeMaterialData(data);
}
else if (data instanceof EnchantmentLevel)
{
writeEnchantmentLevel(data);
}
else
{
throw new UnsupportedOperationException();
}
}
private void writeMaterial(final Object data)
{
writer.print(data.toString().toLowerCase(Locale.ENGLISH));
}
private void writeMaterialData(final Object data)
{
final MaterialData matData = (MaterialData)data;
writeMaterial(matData.getItemType());
if (matData.getData() > 0)
{
writer.print(':');
writer.print(matData.getData());
}
}
private void writeItemStack(final Object data)
{
final ItemStack itemStack = (ItemStack)data;
writeMaterialData(itemStack.getData());
writer.print(' ');
writer.print(itemStack.getAmount());
for (Entry<Enchantment, Integer> entry : itemStack.getEnchantments().entrySet())
{
writer.print(' ');
writeEnchantmentLevel(entry);
}
}
private void writeEnchantmentLevel(Object data)
{
final Entry<Enchantment, Integer> enchLevel = (Entry<Enchantment, Integer>)data;
writer.print(enchLevel.getKey().getName().toLowerCase(Locale.ENGLISH));
writer.print(':');
writer.print(enchLevel.getValue());
}
private void writeLocation(final Location entry, final int depth)
{
writer.println();
writeIndention(depth);
writer.print("world: ");
writeScalar(entry.getWorld().getName());
writeIndention(depth);
writer.print("x: ");
writeScalar(entry.getX());
writeIndention(depth);
writer.print("y: ");
writeScalar(entry.getY());
writeIndention(depth);
writer.print("z: ");
writeScalar(entry.getZ());
writeIndention(depth);
writer.print("yaw: ");
writeScalar(entry.getYaw());
writeIndention(depth);
writer.print("pitch: ");
writeScalar(entry.getPitch());
}
}