/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.util;

import java.io.*;
import javax.xml.parsers.*;
import org.apache.log4j.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.silverwrist.util.DOMElementHelper;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.except.*;

public class XMLLoader
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Category logger = Category.getInstance(XMLLoader.class);

  private static XMLLoader self = null;

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  private XMLLoader()
  { // do nothing
  } // end constructor

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  public final Document loadConfigDocument(String filename) throws ConfigException
  {
    if (logger.isDebugEnabled())
      logger.debug("loadConfigDocument(\"" + filename + "\")...");

    try
    { // create a simple DOM parser by using the Java XML parsing API
      DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
      fac.setNamespaceAware(false);
      fac.setValidating(false);
      DocumentBuilder parser = fac.newDocumentBuilder();

      // access the config file and parse it into our config data tree
      Document rc = parser.parse(new File(filename));
      if (logger.isDebugEnabled())
	logger.debug("config file \"" + filename + "\" loaded successfully");
      return rc;

    } // end try
    catch (FactoryConfigurationError fce)
    { // if the document builder factory could not be created
      logger.fatal("Parser factory configuration error: " + fce.getMessage(),fce);
      throw new ConfigException("XML parser factory could not be created - " + fce.getMessage());

    } // end catch
    catch (ParserConfigurationException pce)
    { // if the XML parser itself could not be created
      logger.fatal("Parser configuration error: " + pce.getMessage(),pce);
      throw new ConfigException("XML parser could not be created - " + pce.getMessage(),pce);

    } // end catch
    catch (SAXException se)
    { // if the XML parser choked on our document
      if (se instanceof SAXParseException)
      { // we have a detailed message - make a proper exception
	SAXParseException spe = (SAXParseException)se;
	logger.fatal("Config file error [" + filename + ":" + spe.getLineNumber() + ","
		     + spe.getColumnNumber() + "]: " + spe.getMessage(),spe);
	throw new ConfigException("Configuration file error: " + spe.getMessage() + " at line "
				  + spe.getLineNumber() + ", column " + spe.getColumnNumber(),spe);

      } // end if
      else
      { // generic exception - just send up a simple error message
	logger.fatal("Config file error [" + filename + "]: " + se.getMessage(),se);
	throw new ConfigException("Configuration file error - " + se.getMessage(),se);

      } // end else

    } // end catch
    catch (IOException ioe)
    { // error reading the config file itself off the disk
      logger.fatal("IO error reading config: " + ioe.getMessage(),ioe);
      throw new ConfigException("unable to read config file \"" + filename + "\" - " + ioe.getMessage(),ioe);

    } // end catch

  } // end loadConfigDocument

  public final Document loadPostData(InputStream stm) throws ValidationException
  {
    if (logger.isDebugEnabled())
      logger.debug("loadPostData()...");

    try
    { // create a simple DOM parser by using the Java XML parsing API
      DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
      fac.setNamespaceAware(false);
      fac.setValidating(false);
      DocumentBuilder parser = fac.newDocumentBuilder();

      // access the config file and parse it into our config data tree
      Document rc = parser.parse(stm);
      if (logger.isDebugEnabled())
	logger.debug("post data loaded successfully");
      return rc;

    } // end try
    catch (FactoryConfigurationError fce)
    { // if the document builder factory could not be created
      logger.error("Parser factory configuration error: " + fce.getMessage(),fce);
      throw new ValidationException("XML parser factory could not be created - " + fce.getMessage());

    } // end catch
    catch (ParserConfigurationException pce)
    { // if the XML parser itself could not be created
      logger.error("Parser configuration error: " + pce.getMessage(),pce);
      throw new ValidationException("XML parser could not be created - " + pce.getMessage(),pce);

    } // end catch
    catch (SAXException se)
    { // if the XML parser choked on our document
      if (se instanceof SAXParseException)
      { // we have a detailed message - make a proper exception
	SAXParseException spe = (SAXParseException)se;
	logger.error("Error in post data [" + spe.getLineNumber() + "," + spe.getColumnNumber() + "]: "
		     + spe.getMessage(),spe);
	throw new ValidationException("Error in post data: " + spe.getMessage() + " at line "
				      + spe.getLineNumber() + ", column " + spe.getColumnNumber(),spe);

      } // end if
      else
      { // generic exception - just send up a simple error message
	logger.error("Error in post data: " + se.getMessage(),se);
	throw new ValidationException("Error in post data - " + se.getMessage(),se);

      } // end else

    } // end catch
    catch (IOException ioe)
    { // error reading the post data itself out of the buffer
      logger.error("IO error reading post data: " + ioe.getMessage(),ioe);
      throw new ValidationException("unable to read post data - " + ioe.getMessage(),ioe);

    } // end catch

  } // end loadPostData

  public final Element configGetRootElement(Document doc, String expected_name) throws ConfigException
  {
    Element rc = doc.getDocumentElement();
    if (rc.getTagName().equals(expected_name))
      return rc;  // we're OK
    logger.fatal("config document is not a <" + expected_name + "/> document (actual root tag: <"
		 + rc.getTagName() + "/>)");
    throw new ConfigException("document is not a <" + expected_name + "/> document",rc);

  } // end configGetRootElement

  public final Element postGetRootElement(Document doc, String expected_name) throws ValidationException
  {
    Element rc = doc.getDocumentElement();
    if (rc.getTagName().equals(expected_name))
      return rc;  // we're OK
    logger.fatal("posted document is not a <" + expected_name + "/> document (actual root tag: <"
		 + rc.getTagName() + "/>)");
    throw new ValidationException("root element is not \"" + expected_name + "\"");

  } // end postGetRootElement

  public final String configGetText(DOMElementHelper h) throws ConfigException
  {
    String rc = h.getElementText();
    if (rc!=null)
      return rc;  // we're OK
    logger.fatal("<" + h.getElement().getTagName() + "/> has no value");
    throw new ConfigException("no data value found in <" + h.getElement().getTagName() + "/>",h.getElement());

  } // end configGetText

  public final String configGetText(Element elt) throws ConfigException
  {
    return configGetText(new DOMElementHelper(elt));

  } // end configGetText

  public final String configGetSubElementText(DOMElementHelper h, String elt_name) throws ConfigException
  {
    String rc = h.getSubElementText(elt_name);
    if (rc!=null)
      return rc;  // we're OK
    logger.fatal("<" + h.getElement().getTagName() + "/> has no <" + elt_name + "/> element");
    throw new ConfigException("no <" + elt_name + "/> element found in <" + h.getElement().getTagName() + "/>",
			      h.getElement());

  } // end configGetSubElementText

  public final String postGetSubElementText(DOMElementHelper h, String elt_name) throws ValidationException
  {
    String rc = h.getSubElementText(elt_name);
    if (rc!=null)
      return rc;  // we're OK
    logger.fatal("<" + h.getElement().getTagName() + "/> has no <" + elt_name + "/> element");
    throw new ValidationException("no \"" + elt_name + "\" element found in \""
				  + h.getElement().getTagName() + "\"");

  } // end postGetSubElementText

  public final String configGetSubElementText(Element elt, String subelt_name) throws ConfigException
  {
    return configGetSubElementText(new DOMElementHelper(elt),subelt_name);

  } // end configGetSubElementText

  public final String postGetSubElementText(Element elt, String subelt_name) throws ValidationException
  {
    return postGetSubElementText(new DOMElementHelper(elt),subelt_name);

  } // end postGetSubElementText

  public final Element configGetSubSection(DOMElementHelper h, String sect_name) throws ConfigException
  {
    Element rc = h.getSubElement(sect_name);
    if (rc!=null)
      return rc;  // we're OK
    logger.fatal("<" + h.getElement().getTagName() + "/> has no <" + sect_name + "/> section");
    throw new ConfigException("no <" + sect_name + "/> section found in <" + h.getElement().getTagName()
			      + "/>",h.getElement());

  } // end configGetSubElementText

  public final Element postGetSubSection(DOMElementHelper h, String sect_name) throws ValidationException
  {
    Element rc = h.getSubElement(sect_name);
    if (rc!=null)
      return rc;  // we're OK
    logger.fatal("<" + h.getElement().getTagName() + "/> has no <" + sect_name + "/> element");
    throw new ValidationException("no \"" + sect_name + "\" element found in \""
				  + h.getElement().getTagName() + "\"");

  } // end postGetSubSection

  public final Element configGetSubSection(Element sect, String subsect_name) throws ConfigException
  {
    return configGetSubSection(new DOMElementHelper(sect),subsect_name);

  } // end configGetSubSection

  public final Element postGetSubSection(Element sect, String subsect_name) throws ValidationException
  {
    return postGetSubSection(new DOMElementHelper(sect),subsect_name);

  } // end postGetSubSection

  public final void configVerifyNodeName(Node n, String name, Element enclosing_sect) throws ConfigException
  {
    if (n.getNodeName().equals(name))
      return;
    logger.fatal("unknown node <" + n.getNodeName() + "/> seen inside <" + enclosing_sect.getTagName() + "/>");
    throw new ConfigException("unknown node name <" + n.getNodeName() + "/> in <"
			      + enclosing_sect.getTagName() + "/>",enclosing_sect);
    
  } // end configVerifyNodeName

  public final void configVerifyNodeName(Node n, String name) throws ConfigException
  {
    configVerifyNodeName(n,name,(Element)(n.getParentNode()));

  } // end configVerifyNodeName

  public final void configVerifyTagName(Element tag, String name) throws ConfigException
  {
    if (tag.getTagName().equals(name))
      return;
    logger.fatal("expected a <" + name + "/> element here, but got a <" + tag.getTagName() + "/>");
    throw new ConfigException("element must be a <" + name + "/>",tag);

  } // end configVerifyTagName

  public final String configGetAttribute(Element elt, String attr_name) throws ConfigException
  {
    String rc = elt.getAttribute(attr_name);
    if (!(StringUtil.isStringEmpty(rc)))
      return rc;
    logger.fatal("no " + attr_name + "= attribute found in <" + elt.getTagName() + "/> element");
    throw new ConfigException("no " + attr_name + "= attribute found in <" + elt.getTagName()
			      + "/> element",elt);

  } // end configGetAttribute

  public final int configGetAttributeInt(Element elt, String attr_name) throws ConfigException
  {
    String tmp = elt.getAttribute(attr_name);
    if (StringUtil.isStringEmpty(tmp))
    { // the attribute is not present
      logger.fatal("no " + attr_name + "= attribute found in <" + elt.getTagName() + "/> element");
      throw new ConfigException("no " + attr_name + "= attribute found in <" + elt.getTagName()
				+ "/> element",elt);

    } // end if

    try
    { // parse out the integer value
      return Integer.parseInt(tmp.trim());

    } // end try
    catch (NumberFormatException nfe)
    { // but it's not a valid integer - throw something else!
      logger.fatal(attr_name + "= attribute in <" + elt.getTagName() + "/> element is not a valid integer");
      throw new ConfigException(attr_name + "= attribute in <" + elt.getTagName()
				+ "/> element is not a valid integer",elt);

    } // end catch

  } // end configGetAttributeInt

  /*--------------------------------------------------------------------------------
   * External static operations
   *--------------------------------------------------------------------------------
   */

  public static final synchronized XMLLoader get()
  {
    if (self==null)
      self = new XMLLoader();
    return self;

  } // end get

} // end class XMLLoader
