package crazypants.enderio.machine.enchanter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import net.minecraft.enchantment.Enchantment;
import org.apache.commons.io.IOUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import crazypants.enderio.Log;
import crazypants.enderio.config.Config;
import crazypants.enderio.machine.recipe.RecipeConfig;
import crazypants.enderio.machine.recipe.RecipeConfigParser;
import crazypants.enderio.machine.recipe.RecipeInput;
public class EnchanterRecipeParser extends DefaultHandler {
private static final String CORE_FILE_NAME = "EnchanterRecipes_Core.xml";
private static final String CUSTOM_FILE_NAME = "EnchanterRecipes_User.xml";
public static List<EnchanterRecipe> loadRecipeConfig() {
File coreFile = new File(Config.configDirectory, CORE_FILE_NAME);
String defaultVals = null;
try {
defaultVals = readRecipes(coreFile, CORE_FILE_NAME, true);
} catch (IOException e) {
Log.error("Could not load default recipes file " + coreFile + " from EnderIO jar: " + e.getMessage());
e.printStackTrace();
return null;
}
if(!coreFile.exists()) {
Log.error("Could not load default recipes from " + coreFile + " as the file does not exist.");
return null;
}
List<EnchanterRecipe> result = null;
try {
result = parse(defaultVals);
} catch (Exception e) {
Log.error("Error parsing " + CORE_FILE_NAME);
return null;
}
File userFile = new File(Config.configDirectory, CUSTOM_FILE_NAME);
String userConfigStr = null;
try {
userConfigStr = readRecipes(userFile, CUSTOM_FILE_NAME, false);
if(userConfigStr == null || userConfigStr.trim().length() == 0) {
Log.error("Empty user config file: " + userFile.getAbsolutePath());
} else {
List<EnchanterRecipe> userConfig = parse(userConfigStr);
merge(result, userConfig);
}
} catch (Exception e) {
Log.error("Could not load user defined recipes from file: " + CUSTOM_FILE_NAME);
e.printStackTrace();
}
return result;
}
public static void merge(List<EnchanterRecipe> core, List<EnchanterRecipe> userConfig) {
for (EnchanterRecipe rec : userConfig) {
removeFromList(rec.getEnchantment(), core);
}
core.addAll(userConfig);
}
private static void removeFromList(Enchantment enchantment, List<EnchanterRecipe> recipes) {
if(enchantment == null) {
return;
}
ListIterator<EnchanterRecipe> iter = recipes.listIterator();
while (iter.hasNext()) {
EnchanterRecipe rec = iter.next();
if(rec != null && rec.getEnchantment() != null && rec.getEnchantment().getName().equals(enchantment.getName())) {
Log.info("Replacing enchater recipe based on user config for enchantment " + enchantment.getName());
iter.remove();
}
}
}
private static String readRecipes(File copyTo, String fileName, boolean replaceIfExists) throws IOException {
if(!replaceIfExists && copyTo.exists()) {
return readStream(new FileInputStream(copyTo));
}
InputStream in = RecipeConfig.class.getResourceAsStream("/assets/enderio/config/" + fileName);
if(in == null) {
Log.error("Could load default Enchanter recipes.");
throw new IOException("Could not resource /assets/enderio/config/" + fileName + " form classpath. ");
}
String output = readStream(in);
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(copyTo, false));
writer.write(output.toString());
} finally {
IOUtils.closeQuietly(writer);
}
return output.toString();
}
private static String readStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder output = new StringBuilder();
try {
String line = reader.readLine();
while (line != null) {
output.append(line);
output.append("\n");
line = reader.readLine();
}
} finally {
IOUtils.closeQuietly(reader);
}
return output.toString();
}
public static List<EnchanterRecipe> parse(String str) throws Exception {
StringReader reader = new StringReader(str);
InputSource is = new InputSource(reader);
try {
return parse(is);
} finally {
reader.close();
}
}
public static List<EnchanterRecipe> parse(InputSource is) throws Exception {
EnchanterRecipeParser parser = new EnchanterRecipeParser();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(parser);
xmlReader.parse(is);
return parser.getResult();
}
public static final String ELEMENT_ENCHANTMENT = "enchantment";
public static final String ELEMENT_ITEM_STACK = "itemStack";
public static final String AT_NAME = "name";
private static final String AT_LEVEL = "costPerLevel";
private List<EnchanterRecipe> result = new ArrayList<EnchanterRecipe>();
private Enchantment curEnchantment = null;
private int curLevelCost = -1;
private boolean enchantmentFound = true;
private RecipeInput curInput = null;
private List<EnchanterRecipe> getResult() {
return result;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if(ELEMENT_ENCHANTMENT.equals(localName)) {
curEnchantment = EnchanterRecipe.getEnchantmentFromName(attributes.getValue(AT_NAME));
curLevelCost = RecipeConfigParser.getIntValue(AT_LEVEL, attributes, -1);
if(curLevelCost == -1) {
Log.warn("Cost per level not found for enchantment with name " + attributes.getValue(AT_NAME) + " when parsing enchanter recipes.");
curEnchantment = null;
} else if(curEnchantment == null) {
Log.warn("Could not find enchantment with name " + attributes.getValue(AT_NAME) + " when parsing enchanter recipes.");
enchantmentFound = false;
} else {
enchantmentFound = true;
}
} else if(ELEMENT_ITEM_STACK.equals(localName)) {
if(curEnchantment == null) {
if(enchantmentFound) {
Log.error("EnchanterRecipeParser: Encontered an item stack outside an enchantment element.");
}
} else if(curInput != null) {
Log.error("EnchanterRecipeParser: Multiple input stacks found within the enchantment tag for " + curEnchantment.getName());
} else {
curInput = RecipeConfigParser.getItemStack(attributes);
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(ELEMENT_ENCHANTMENT.equals(localName)) {
if(curEnchantment != null) {
if(curInput == null) {
Log.error("Valid input found for enchantment " + curEnchantment.getName() + " not found.");
} else {
EnchanterRecipe rec = new EnchanterRecipe(curInput, curEnchantment, curLevelCost);
if(rec.isValid()) {
result.add(rec);
}
}
}
curInput = null;
curEnchantment = null;
enchantmentFound = true;
}
}
@Override
public void warning(SAXParseException e) throws SAXException {
Log.warn("Warning parsing Enchanter config file: " + e.getMessage());
}
@Override
public void error(SAXParseException e) throws SAXException {
Log.error("Error parsing Enchanter config file: " + e.getMessage());
e.printStackTrace();
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
Log.error("Error parsing Enchanter config file: " + e.getMessage());
e.printStackTrace();
}
}