package com.brew.brewshop.xml;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.brew.brewshop.R;
import com.brew.brewshop.fragments.RecipeListFragment;
import com.brew.brewshop.storage.recipes.BeerStyle;
import com.brew.brewshop.storage.recipes.HopAddition;
import com.brew.brewshop.storage.recipes.HopUsage;
import com.brew.brewshop.storage.recipes.MaltAddition;
import com.brew.brewshop.storage.recipes.Quantity;
import com.brew.brewshop.storage.recipes.Recipe;
import com.brew.brewshop.storage.recipes.Yeast;
import com.brew.brewshop.storage.style.BjcpCategory;
import com.brew.brewshop.storage.style.BjcpCategoryList;
import com.brew.brewshop.storage.style.BjcpCategoryStorage;
import com.brew.brewshop.storage.style.BjcpSubcategory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
public class BeerXMLWriter extends AsyncTask<OutputStream, Integer, Integer> {
Recipe[] recipes;
RecipeListFragment parentFragment = null;
Context mContext = null;
ProgressDialog progressDialog;
BjcpCategoryList mBjcpCategoryList;
String TAG = BeerXMLWriter.class.getName();
public BeerXMLWriter(RecipeListFragment parentFragment, Recipe[] recipes) {
this.parentFragment = parentFragment;
this.mContext = parentFragment.getActivity();
this.progressDialog = new ProgressDialog(mContext);
this.mBjcpCategoryList = new BjcpCategoryStorage(mContext).getStyles();
this.recipes = recipes;
}
public BeerXMLWriter(Context context, Recipe[] recipes) {
this.mContext = context;
this.mBjcpCategoryList = new BjcpCategoryStorage(mContext).getStyles();
this.recipes = recipes;
}
@Override
protected void onPreExecute() {
//set message of the dialog
progressDialog.setMessage(mContext.getString(R.string.save_recipe_progress));
//show dialog
progressDialog.show();
}
@Override
protected void onPostExecute(Integer success) {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
parentFragment.savedRecipes(success);
}
public int writeRecipes(File outputFile) throws IOException {
FileOutputStream outputStream = new FileOutputStream(outputFile);
return writeRecipes(outputStream, false);
}
public int writeRecipes(OutputStream recipeOutputStream, boolean publish) throws IOException {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = null;
try {
dBuilder = dbFactory.newDocumentBuilder();
} catch (ParserConfigurationException e1) {
Log.e(TAG, "Couldn't create DocumentBuilderFactory", e1);
return -1;
}
Document recipeDocument = null;
XPath xp = null;
// Create the Recipe Node
recipeDocument = dBuilder.newDocument();
Element recipesElement = recipeDocument.createElement("RECIPES");
int success = 0;
for (int i = 0; i < recipes.length; i++) {
if (publish) {
publishProgress(i, recipes.length);
}
Recipe recipe = recipes[i];
try {
Element recipeElement = writeRecipe(recipe, recipeDocument);
if (recipeElement != null) {
recipesElement.appendChild(recipeElement);
}
success++;
} catch (IOException ioe) {
Log.e(TAG, "Couldn't add recipe", ioe);
}
}
recipeDocument.appendChild(recipesElement);
try {
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(recipeDocument);
xp = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xp.evaluate(
"//text()[normalize-space(.)='']", recipeDocument,
XPathConstants.NODESET);
for (int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
node.getParentNode().removeChild(node);
}
StreamResult configResult = new StreamResult(recipeOutputStream);
transformer.transform(source, configResult);
} catch (TransformerConfigurationException e) {
Log.e(TAG, "Could not transform config file", e);
} catch (TransformerException e) {
Log.e(TAG, "Could not transformer file", e);
} catch (XPathExpressionException e) {
e.printStackTrace();
} finally {
recipeOutputStream.close();
}
return success;
}
public Element writeRecipe(Recipe recipe, Document recipeDocument) throws IOException {
Element recipeElement = recipeDocument.createElement("RECIPE");
// Generic recipe stuff
Element tElement = recipeDocument.createElement("NAME");
tElement.setTextContent(recipe.getName());
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("TYPE");
//tElement.setTextContent(recipe.getType());
tElement.setTextContent("Partial Mash");
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("BREWER");
tElement.setTextContent(recipe.getBrewerName());
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("BATCH_SIZE");
tElement.setTextContent("" + Quantity.convertUnit("gallons US", "litres", recipe.getBatchVolume()));
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("BOIL_SIZE");
tElement.setTextContent("" + Quantity.convertUnit("gallons US", "litres", recipe.getBoilVolume()));
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("BOIL_TIME");
tElement.setTextContent("" + recipe.getBoilTime());
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("EFFICIENCY");
tElement.setTextContent("" + recipe.getEfficiency());
recipeElement.appendChild(tElement);
tElement = recipeDocument.createElement("NOTES");
tElement.setTextContent(recipe.getNotes());
recipeElement.appendChild(tElement);
Element hopsElement = recipeDocument.createElement("HOPS");
for (HopAddition hopAddition : recipe.getHops()) {
Element hopElement = createHopElement(hopAddition, recipeDocument);
hopsElement.appendChild(hopElement);
}
recipeElement.appendChild(hopsElement);
Element fermentablesElement = recipeDocument.createElement("FERMENTABLES");
for (MaltAddition maltAddition : recipe.getMalts()) {
Element fermentableElement = createFermentableElement(maltAddition, recipeDocument);
fermentablesElement.appendChild(fermentableElement);
}
recipeElement.appendChild(fermentablesElement);
Element yeastsElement = recipeDocument.createElement("YEASTS");
for (Yeast yeast : recipe.getYeast()) {
Element yeastElement = createYeastElement(yeast, recipeDocument);
yeastsElement.appendChild(yeastElement);
}
recipeElement.appendChild(yeastsElement);
recipeElement.appendChild(createStyleElement(recipe.getStyle(), recipeDocument));
return recipeElement;
}
private Element createHopElement(HopAddition hopAddition, Document recipeDocument) {
Element hopElement = recipeDocument.createElement("HOP");
Element tElement = recipeDocument.createElement("NAME");
tElement.setTextContent(hopAddition.getHop().getName());
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("VERSION");
tElement.setTextContent("1");
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("ALPHA");
tElement.setTextContent("" + hopAddition.getHop().getPercentAlpha());
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("AMOUNT");
tElement.setTextContent("" + hopAddition.getWeight().getKilograms());
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("USE");
tElement.setTextContent(hopAddition.getUsage().toString().replaceAll("_", " "));
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("TIME");
if (hopAddition.getUsage() == HopUsage.DRY_HOP) {
double days = hopAddition.getDryHopDays() * 24 * 60;
tElement.setTextContent("" + days);
} else {
tElement.setTextContent("" + hopAddition.getBoilTime());
}
hopElement.appendChild(tElement);
/*
For later
tElement = recipeDocument.createElement("NOTES");
tElement.setTextContent("" + hopAddition.getNotes());
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("TYPE");
tElement.setTextContent("" + hopAddition.getType());
hopElement.appendChild(tElement);
tElement = recipeDocument.createElement("FORM");
tElement.setTextContent("" + hopAddition.getForm()());
hopElement.appendChild(tElement);
*/
return hopElement;
}
private Element createFermentableElement(MaltAddition maltAddition, Document recipeDocument) {
Element fermentableElement = recipeDocument.createElement("FERMENTABLE");
Element tElement = recipeDocument.createElement("VERSION");
tElement.setTextContent("1");
fermentableElement.appendChild(tElement);
tElement = recipeDocument.createElement("NAME");
tElement.setTextContent(maltAddition.getMalt().getName());
fermentableElement.appendChild(tElement);
tElement = recipeDocument.createElement("TYPE");
if (maltAddition.getMalt().isMashed()) {
tElement.setTextContent("Grain");
} else {
tElement.setTextContent("Extract");
}
fermentableElement.appendChild(tElement);
tElement = recipeDocument.createElement("AMOUNT");
tElement.setTextContent("" + maltAddition.getWeight().getKilograms());
fermentableElement.appendChild(tElement);
double gravity = maltAddition.getMalt().getGravity();
double yield = ((gravity - 1) / (BeerXMLCommon.SUCROSE_GRAVITY - 1)) * 100;
tElement = recipeDocument.createElement("YIELD");
tElement.setTextContent("" + yield);
fermentableElement.appendChild(tElement);
tElement = recipeDocument.createElement("COLOR");
tElement.setTextContent("" + maltAddition.getMalt().getColor());
fermentableElement.appendChild(tElement);
return fermentableElement;
}
private Element createYeastElement(Yeast yeast, Document recipeDocument) {
Element yeastElement = recipeDocument.createElement("YEAST");
Element tElement = recipeDocument.createElement("VERSION");
tElement.setTextContent("1");
yeastElement.appendChild(tElement);
tElement = recipeDocument.createElement("NAME");
tElement.setTextContent(yeast.getName());
yeastElement.appendChild(tElement);
tElement = recipeDocument.createElement("ATTENUATION");
tElement.setTextContent("" + yeast.getAttenuation());
yeastElement.appendChild(tElement);
tElement = recipeDocument.createElement("TYPE");
tElement.setTextContent("ALE");
yeastElement.appendChild(tElement);
tElement = recipeDocument.createElement("FORM");
tElement.setTextContent("DRY");
yeastElement.appendChild(tElement);
tElement = recipeDocument.createElement("AMOUNT");
tElement.setTextContent("1");
yeastElement.appendChild(tElement);
return yeastElement;
}
private Element createStyleElement(BeerStyle style, Document recipeDocument) {
Element styleElement = recipeDocument.createElement("STYLE");
BjcpCategory bjcpCategory = mBjcpCategoryList.findByName(style.getStyleName());
if (bjcpCategory == null) {
return styleElement;
}
BjcpSubcategory bjcpSubcategory = bjcpCategory.findSubcategoryByName(style.getSubstyleName());
Element tElement = recipeDocument.createElement("VERSION");
tElement.setTextContent("1");
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("NAME");
tElement.setTextContent(style.getDisplayName());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("CATEGORY");
tElement.setTextContent(style.getStyleName());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("CATEGORY_NUMBER");
tElement.setTextContent("" + bjcpCategory.getId());
styleElement.appendChild(tElement);
if (bjcpSubcategory != null) {
tElement = recipeDocument.createElement("STYLE_LETTER");
tElement.setTextContent(bjcpSubcategory.getLetter());
styleElement.appendChild(tElement);
}
tElement = recipeDocument.createElement("STYLE_GUIDE");
tElement.setTextContent(style.getStyleGuide());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("TYPE");
tElement.setTextContent(style.getType());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("OG_MIN");
tElement.setTextContent("" + style.getOgMin());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("OG_MAX");
tElement.setTextContent("" + style.getOgMax());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("FG_MIN");
tElement.setTextContent("" + style.getFgMin());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("FG_MAX");
tElement.setTextContent("" + style.getFgMax());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("IBU_MIN");
tElement.setTextContent("" + style.getIbuMin());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("IBU_MAX");
tElement.setTextContent("" + style.getIbuMax());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("COLOR_MIN");
tElement.setTextContent("" + style.getSrmMin());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("COLOR_MAX");
tElement.setTextContent("" + style.getSrmMax());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("ABV_MIN");
tElement.setTextContent("" + style.getAbvMin());
styleElement.appendChild(tElement);
tElement = recipeDocument.createElement("ABV_MAX");
tElement.setTextContent("" + style.getAbvMax());
styleElement.appendChild(tElement);
return styleElement;
}
@Override
protected Integer doInBackground(OutputStream... outputStreams) {
try {
return writeRecipes(outputStreams[0], true);
} catch (IOException ioe) {
return -1;
}
}
@Override
protected void onProgressUpdate(Integer... progress) {
int current = progress[0];
int total = progress[1];
String message;
if (total > 1) {
message = String.format(mContext.getString(R.string.save_recipes_progress), current, total);
} else {
message = mContext.getString(R.string.save_recipe_progress);
}
if (progressDialog != null) {
progressDialog.setMessage(message);
} else {
Log.i(TAG, message);
}
}
}