package de.dhbw.humbuch.util; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Util class for looking up a book by its isbn * <ul> * <li>Standard ISBN API is isbndb.com</li> * <li>Document retrieval, validation and parsing can be overridden in subclass</li> * </ul> * * @author davherrmann */ public class BookLookup { private final static String KEY = "CONBNUOZ"; private final static String LOOKUP_URL = "http://isbndb.com/api/v2/xml/" + KEY + "/book/"; /** * Look up a book by its ISBN * * @param isbn * {@link String} containing the ISBN - all non-numerical * characters are ignored * @return {@link Book} containing the book data * @throws BookNotFoundException * when a book is not found */ public static Book lookup(String isbn) throws BookNotFoundException { Document document = retrieveDocument(buildLookupURL(processISBN(isbn))); validateDocument(document); return parseDocument(document); } /** * Removes all non-numerical characters from the isbn * * @param isbn * {@link String} containing the ISBN * @return {@link String} without all non-numerical characters */ protected static String processISBN(String isbn) { return isbn.replaceAll("[^\\d]", ""); } /** * Builds the URI for the ISBN API * * @param isbn * {@link String} containing the ISBN * @return {@link String} containing the ISBN API URL */ protected static String buildLookupURL(String isbn) { return LOOKUP_URL + isbn; } /** * Retrieve an document from a given URI * * @param uri * {@link String} containing the URI * @return {@link Document} retrieved from the URI * @throws BookNotFoundException * thrown when an error occurs while retrieving the document */ protected static Document retrieveDocument(String uri) throws BookNotFoundException { try { DocumentBuilder documentBuilder = DocumentBuilderFactory .newInstance().newDocumentBuilder(); Document document = documentBuilder.parse(uri); document.getDocumentElement().normalize(); return document; } catch (ParserConfigurationException | SAXException | IOException e) { throw new BookNotFoundException( "Error during retrieving the XML document...", e); } } /** * Checks if the document contains valid data for a book * * @param document * {@link Document} to be validated * @throws BookNotFoundException * thrown when the document contains no valid book data */ protected static void validateDocument(Document document) throws BookNotFoundException { Object error = getNodeValue(document, "error"); if (error != null) { throw new BookNotFoundException("No book found..."); } } /** * Parse a document and extract the book data * * @param document * {@link Document} containing the book data * @return {@link Book} with the extracted data */ protected static Book parseDocument(Document document) { return new Book.Builder(getNodeValue(document, "title")) .author(getNodeValue(document, "name")) .isbn10(getNodeValue(document, "isbn10")) .isbn13(getNodeValue(document, "isbn13")) .publisher(getNodeValue(document, "publisher_name")).build(); } /** * Extract the value of the first node of an element * * @param document * the {@link Document} * @param elementName * name of the element of which the value should be extracted * @return {@link String} containing the content of the element if it * exists, otherwise <code>null</code> */ private static String getNodeValue(Document document, String elementName) { NodeList data = document.getElementsByTagName(elementName); Element element = (Element) data.item(0); if (element != null) { Node node = element.getChildNodes().item(0); if (node != null) { return node.getNodeValue(); } } return null; } /** * Exception indicating a book was not found */ public static class BookNotFoundException extends Exception { private static final long serialVersionUID = -644882332116172763L; public BookNotFoundException() { super(); } public BookNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public BookNotFoundException(String message, Throwable cause) { super(message, cause); } public BookNotFoundException(String message) { super(message); } public BookNotFoundException(Throwable cause) { super(cause); } } /** * POJO holding information about the book */ public static class Book { public final String author; public final String isbn10; public final String isbn13; public final String publisher; public final String title; private Book(Builder builder) { this.author = builder.author; this.isbn10 = builder.isbn10; this.isbn13 = builder.isbn13; this.publisher = builder.publisher; this.title = builder.title; } public static class Builder { private String author; private String isbn10; private String isbn13; private String publisher; private String title; public Builder(String title) { this.title = title; } public Builder author(String author) { this.author = author; return this; } public Builder isbn10(String isbn10) { this.isbn10 = isbn10; return this; } public Builder isbn13(String isbn13) { this.isbn13 = isbn13; return this; } public Builder publisher(String publisher) { this.publisher = publisher; return this; } public Book build() { return new Book(this); } } } }