package com.oreilly.jent.people.xml;

/**
 * In general, you may use the code in this book in your programs and 
 * documentation. You do not need to contact us for permission unless 
 * you're reproducing a significant portion of the code. For example, 
 * writing a program that uses several chunks of code from this book does 
 * not require permission. Selling or distributing a CD-ROM of examples 
 * from O'Reilly books does require permission. Answering a question by 
 * citing this book and quoting example code does not require permission. 
 * Incorporating a significant amount of example code from this book into 
 * your product's documentation does require permission.
 * 
 * We appreciate, but do not require, attribution. An attribution usually 
 * includes the title, author, publisher, and ISBN. For example: 
 * 
 *   "Java Enterprise in a Nutshell, Third Edition, 
 *    by Jim Farley and William Crawford 
 *    with Prakash Malani, John G. Norman, and Justin Gehtland. 
 *    Copyright 2006 O'Reilly Media, Inc., 0-596-10142-2."
 *  
 *  If you feel your use of code examples falls outside fair use or the 
 *  permission given above, feel free to contact us at 
 *  permissions@oreilly.com.
 */

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.oreilly.jent.people.InvalidSearchException;
import com.oreilly.jent.people.PersistenceException;
import com.oreilly.jent.people.Person;
import com.oreilly.jent.people.PersonDAO;
import com.oreilly.jent.people.util.ConfigSet;

/**
 * Class: XMLPersonDAO
 * 
 * An implementation of a PersonDAO that reads all of its person data
 * out of an XML file, located using the "xml-file" element in the 
 * parameters section of the config file.
 * 
 * @author <a href="mailto:jim@jimfarley.org">Jim Farley</a>
 *
 */
public class XMLPersonDAO extends PersonDAO {
    private static Logger sLog = 
        Logger.getLogger(XMLPersonDAO.class.getName());
    // The Person objects loaded from the XML data file
    private Collection mPeople = new ArrayList();

    /**
     * Initialize the DAO from the config info.  This DAO implementation
     * requires the JNDI name of a DataSource that points to the person
     * database.  This name has to be given as the parameter "ds-name" in
     * the "parameters" element of the config file.
     */
    public void init(ConfigSet config) {
        try {
            // Try to parse the XML file referenced by the xml-file element 
            String xmlFileName = config.getParameter("dao.parameters.xml-file");
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            File xmlFile = new File(xmlFileName);
            Document personData = builder.parse(xmlFile);
            mPeople = parseData(personData);
        }
        catch (ParserConfigurationException pce) {
            sLog.severe("Failed to parse XML file: " + pce.getMessage());
        }
        catch (SAXException se) {
            sLog.severe("Failed to parse XML file: " + se.getMessage());
        }
        catch (IOException ie) {
            sLog.severe("Failed to load XML file: " + ie.getMessage());
            ie.printStackTrace();
        }
    }
    
    /**
     * Search the people loaded from the XML file.
     */
    public Collection findPeople(Map searchParams)
            throws InvalidSearchException, PersistenceException {
        // Extract the search parameters from the map
        String emailArg = (String)searchParams.get(PersonDAO.EMAIL);
        String fnameArg = (String)searchParams.get(PersonDAO.FIRST_NAME);
        String lnameArg = (String)searchParams.get(PersonDAO.LAST_NAME);
        
        // Check for no search arguments, which is an invalid search
        if ((emailArg == null || emailArg.length() == 0) &&
            (fnameArg == null || fnameArg.length() == 0) &&
            (lnameArg == null || lnameArg.length() == 0)) {
            throw new InvalidSearchException("No arguments provided");
        }
        // Check each person for a match
        ArrayList people = new ArrayList();
        Iterator pIter = mPeople.iterator();
        while (pIter.hasNext()) {
            Person p = (Person)pIter.next();
            boolean match = false;
            // First check the names
            if ((fnameArg != null && fnameArg.length() > 0 &&
                 p.getFirstName() != null &&
                 p.getFirstName().indexOf(fnameArg) >= 0) ||
                (lnameArg != null && lnameArg.length() > 0 &&
                 p.getLastName() != null &&
                 p.getLastName().indexOf(lnameArg) >= 0)) { 
                match = true;
                people.add(p);
            }
            // If names didn't match, check email addresses
            if (!match && emailArg != null && emailArg.length() > 0) {
                String[] addrs = p.getEmailAddresses();
                for (int i = 0; i < addrs.length && !match; i++) {
                    if (addrs[i].indexOf(emailArg) >= 0) {
                        match = true;
                        people.add(p);
                    }
                }
            }
        }
        return people;
    }

    /* (non-Javadoc)
     * @see oreilly.jent.people.PersonDAO#findPerson(java.lang.String)
     */
    public Person findPerson(String id) throws InvalidSearchException,
            PersistenceException {
        // TODO Auto-generated method stub
        return null;
    }

    /* (non-Javadoc)
     * @see oreilly.jent.people.PersonDAO#findPersonByAcct(java.lang.String)
     */
    public Person findPersonByAcct(String acctID)
            throws InvalidSearchException, PersistenceException {
        // TODO Auto-generated method stub
        return null;
    }
    
    private Collection parseData(Document data) {
        ArrayList people = new ArrayList();
        NodeList pNodes = data.getElementsByTagName("person");
        for (int i = 0; i < pNodes.getLength(); i++) {
            try {
                // Wrap each person's node in the XML doc with a ConfigSet
                Node pn = pNodes.item(i);
                Document d = 
                    DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
                Node pnc = d.importNode(pn, true);
                d.appendChild(pnc);
                
                ConfigSet ps = new ConfigSet(d);
                // Make a person from the node data
                Person p = new Person();
                p.setFirstName(ps.getParameter("person.first-name"));
                p.setLastName(ps.getParameter("person.last-name"));
                p.addEmailAddress(ps.getParameter("person.email-address"));
                people.add(p);
            }
            catch (DOMException de) {
               sLog.severe("XML issue while parsing people data file: " +
                           de.getMessage());
            }
            catch (ParserConfigurationException pce) {
                sLog.severe("XML issue while parsing people data file: " +
                            pce.getMessage());
            }
            catch (FactoryConfigurationError fce) {
                sLog.severe("Error creating document builder: " +
                            fce.getMessage());
            }
        }
        return people;
    }
}
