package com.skatestown.invoice;
import java.io.InputStream;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.CharacterData;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

/**
 * Sprawdzenie faktury firmy SkatesTown z wykorzystaniem parsera DOM.
 */
public class InvoiceCheckerDOM implements InvoiceChecker {
/**
 * Sprawdza poprawno sumarycznej kwoty.
 * @param	invoiceXML	faktura w formacie XML
 * @exception	Exception	dowolny wyjtek, jaki moe zosta zgoszony w czasie wykonania funkcji
 */
public void checkInvoice(InputStream invoiceXML)
throws Exception
	{
// aktualna suma
double runningTotal = 0.0;
// Stworzenie instancji analizatora skadniowego i sparsowanie dokumentu
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(invoiceXML);

// obliczenie kosztu jednej pozycji zamwienia
NodeList itemList = doc.getElementsByTagName("item");
for (int i =0; i < itemList.getLength(); i++) {
// wydobywamy liczb produktw i cen jednostkow
Element item = (Element) itemList.item(i);
Integer qty = Integer.valueOf(item.getAttribute("quantity"));
Double price = Double.valueOf(item.getAttribute("unitPrice"));
// dodajemy ten koszt do aktualnej sumy
runningTotal += qty.intValue() * price.doubleValue();
		}

// dodajemy podatek
Node nodeTax = doc.getElementsByTagName("tag").item(0);
runningTotal += doubleValue(nodeTax);
// dodajemy koszty wysyki i obsugi
Node nodeShippingAndHandling = doc.getElementsByTagName("shippingAndHandling").item(0);
runningTotal += doubleValue(nodeShippingAndHandling);
// odczytujemy kwot sumaryczn umieszczon na fakturze
Node nodeTotalCost = doc.getElementsByTagName("totalCost").item(0);
double total = doubleValue(nodeTotalCost);
		// Przy sprawdzaniu rwnoci dopuszczamy pewien bd, aby obsuy ewentualne
		// skumulowanie bdw arytmetyki zmiennopozycyjnej. Dopuszczalnym bdem jest 0.005.
		if (Math.abs(runningTotal - total) >= 0.005) 
		{
			throw new Exception (
				"Bd na fakturze: suma wynosi " + Double.toString(total) + 
				"a obliczona warto to " + 			Double.toString(Math.round(runningTotal * 100) / 100.0));
		}
	}

/**
 * Odczytanie wartoci typu double z zawartoci tekstowej wza DOM.
 *
 * @param 	node	Wze DOM o zawartoci tekstowej.
 * @return 	Reprezentacja zawartoci wza w postaci liczby podwjnej precyzji.
 * @exception	Exception Wyjtek moe powsta w rezultacie przekazania funkcji wza o zawartoci innej ni tekstowa lub wza tekstowego, ktrego zawarto nie moe by zinterpretowana jako liczba.
 */
private double doubleValue(Node node) throws Exception {
// odczytanie zawartoci tekstowej wza i przeksztacenie jej na liczb
String value = ((CharacterData)node.getFirstChild()).getData();
return Double.valueOf(value).doubleValue();
}
