/*
 * @author  : Elangovan
 * @version : 1.0
 *
 * Development Environment : Oracle9i JDeveloper
 * Name of the File        : XMLUtils.java
 *
 * Creation / Modification History
 *    Elangovan           26-Apr-2002        Created
 *
 */
package oracle.otnsamples.ibfbs.utils;

// XML proccessing
import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import java.io.File;
import java.io.StringReader;

import java.net.URL;
import java.net.MalformedURLException;

import java.util.Date;
import java.util.HashMap;
import java.util.ArrayList;

import java.text.DateFormat;

// JAXP 1.0
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

import oracle.xml.parser.v2.DOMParser;
import oracle.xml.parser.v2.XMLParser;
import oracle.xml.parser.v2.XMLParseException;
import oracle.xml.parser.schema.XSDBuilder;
import oracle.xml.parser.schema.XMLSchema;

import oracle.otnsamples.ibfbs.control.URLMapping;
import oracle.otnsamples.ibfbs.control.ExceptionMapping;

/**
 * This is a utility class used to perform XML operations.
 * All methods are static.
 *
 * @author Elangovan
 * @version 1.0
 * @since 1.0
 */
public class XMLUtils {

  /**
   * This method parses the given XML file and populates a HashMap with the URL
   * mappings generated from the XML file. JAXP 1.0 is used for parsing the XML
   * files.
   *
   * @param fileUrl File path to load the file
   * @return HashMap containg the url mappings
   * @exception Exception if loading URL(Control) mappings file fails
   * @since 1.0
   */
  public static HashMap parseControlFile(String fileUrl) throws Exception {

    // Hashmap to hold URL mappings
    HashMap urlMappings = null;

    try {

      // Initialize SAXParserFactory
      SAXParserFactory saxFactory = SAXParserFactory.newInstance();

      // Create an instance of SAXParser
      SAXParser saxParser = saxFactory.newSAXParser();

      // Initialize URLMappingHandler (this class extends DefaultHandler)
      URLMappingHandler urlMapHandler = new URLMappingHandler();

      // Parse the XML file
      saxParser.parse(fileUrl, urlMapHandler);

      // Get URLMappings
      urlMappings = urlMapHandler.getMappings();

      System.out.println("Loading URL Mappings - done");
    } catch (Exception ex) {
      System.out.println("Error parsing control file : " + ex.toString());
    }

    return urlMappings;
  }

  /**
   * This method parses the given XML file and populates a HashMap with the
   * exception mappings generated from the XML file. JAXP 1.0 is used for
   * parsing the XML file.
   *
   * @param fileUrl File Path to load the file
   * @return HashMap containing the exception mappings
   * @exception Exception if loading Exception mappings file fails
   * @since 1.0
   */
  public static HashMap parseExceptionFile(String fileUrl) throws Exception {

    // Hashmap to hold exception mappings
    HashMap excpMappings = null;
    try {

      // Initialize SAXParserFactory
      SAXParserFactory saxFactory = SAXParserFactory.newInstance();

      // Create an instance of SAXParser
      SAXParser saxParser = saxFactory.newSAXParser();

      // Initialize ExceptionMappings Handler (this class extends DefaultHandler)
      ExceptionMappingHandler excpMapHandler = new ExceptionMappingHandler();

      // Parse the XML file
      saxParser.parse(fileUrl, excpMapHandler);

      // Get the mappings from handler
      excpMappings = excpMapHandler.getMappings();

      System.out.println("Loading Exception Mappings - done");
    } catch (Exception ex) {
      System.out.println("Error parsing exception file : " + ex.toString());
    }

    return excpMappings;
  }

  /**
   * Parses the Node and returns the node value corresponding to the tag name.
   *
   * @param node Node to be parsed
   * @param name node name
   * @return node value
   * @since 1.0
   */
  public static String getNodeValueByName(Node node, String name) {

    // Node value
    String value = null;
    try {
      if (node != null) {

        // Get all the child nodes
        NodeList children = node.getChildNodes();

        // Number of child nodes
        int childLen = children.getLength();

        // Traverse the child nodes to get the required node
        for (int ctr = 0; ctr < childLen; ctr++) {
          Node child = children.item(ctr);

          // If this node matches the given Node name
          if ((child != null) && (child.getNodeName() != null)
                  && child.getNodeName().equals(name)) {

            // Get the Text node
            Node grandChild = child.getFirstChild();

            // Get the node value
            if (grandChild.getNodeValue() != null) {
              value = grandChild.getNodeValue();

              break;
            }
          }
        }
      }
    } catch (NullPointerException ne) {  // No match found
      value = null;
    }

    return value;
  }

  /**
   * Creates a URL object corresponding to the given file path.
   *
   * @param fileName file path
   * @return URL object of the path
   * @since 1.0
   */
  static URL createURL(String fileName) {

    URL url = null;
    try {

      // Create a URL with the given file name
      url = new URL(fileName);
    } catch (MalformedURLException ex) {

      // Try creating a URL with Operating specific file separator
      File f = new File(fileName);
      try {
        String path = f.getAbsolutePath();
        String fs   = System.getProperty("file.separator");
        if (fs.length() == 1) {
          char sep = fs.charAt(0);
          if (sep != '/') {
            path = path.replace(sep, '/');
          }

          if (path.charAt(0) != '/') {
            path = '/' + path;
          }
        }

        path = "file://" + path;
        url  = new URL(path);
      } catch (MalformedURLException e) {  // Invalid URL
        System.out.println("Cannot create url for: " + fileName);
      }
    }

    return url;
  }

  /**
   * Returns the int value of the given text node.
   *
   * @param node text node
   * @return int value of the text node
   * @since 1.0
   */
  public static int getIntValue(Node node) {

    if (node.getNodeType() == Node.TEXT_NODE) {
      return Integer.parseInt(node.getNodeValue());
    } else {
      return 0;
    }
  }

  /**
   * Returns the float value of the given text node.
   *
   * @param node text node
   * @return float value of text node
   * @since 1.0
   */
  public static float getFloatValue(Node node) {

    if (node.getNodeType() == Node.TEXT_NODE) {
      return Float.parseFloat(node.getNodeValue());
    }  else {
      return 0;
    }
  }

  /**
   * Returns the date value of the given text node.
   *
   * @param node text node
   * @return date value of text node
   * @since 1.0
   */
  public static Date getDateValue(Node node) {

    Date dt = null;
    if (node.getNodeType() == Node.TEXT_NODE) {
      try {
        dt = DateFormat.getDateInstance().parse(node.getNodeValue());
      } catch (java.text.ParseException px) {  // Invalid Date format

        // do nothing
      }
    }

    return dt;
  }

  /**
   * Returns the XMLschema for the given string url.
   *
   * @param url string representation of the schema url
   * @return XML Schema of the given string url
   * @since 1.0
   */
  private static XMLSchema getXMLSchema(String url) {

    XMLSchema schemadoc = null;
    try {

      // Create an XSDBuilder instance
      XSDBuilder builder = new XSDBuilder();

      // Build an XMLSchema object from an XMLSchema document
      schemadoc = (XMLSchema) builder.build(createURL(url));
    } catch (Exception ex) {  // Build failed
      System.out.println(" Couldn't build schema object : " + ex);
    }

    return schemadoc;
  }

  /**
   * Parses the string representation,validates with the given schema file
   * and returns the XML Document.
   *
   * @param xmlStr string representaion of the XML Document
   * @param url url of schema file
   * @return XML Document constructed from the string
   * @since 1.0
   */
  public static Document stringToXML(String xmlStr, String url) {

    // Initialize DOM Parser
    DOMParser dp     = new DOMParser();
    Document  xmldoc = null;

    // Set Schema Object for Validation
    dp.setXMLSchema(getXMLSchema(url));
    dp.setValidationMode(XMLParser.SCHEMA_VALIDATION);
    dp.setPreserveWhitespace(true);

    try {

      // Parse the file through the Reader instance and validate with
      // schema object
      StringReader input = new StringReader(xmlStr);
      dp.parse(input);

      // Get the Root element of the parsed document
      xmldoc = dp.getDocument();
    } catch (XMLParseException pe) {
      System.out.println("Parser Exception: " + pe.toString());
    } catch (Exception e) {
      System.out.println("NonParserException: " + e.toString());
    }

    return xmldoc;
  }
}  // End of class - XMLUtils

/**
 * This class extends the DefaultHandler class and overrides
 * the standard callback methods. This class parses the given XML file
 * and populates the URLMappings in a Hashmap.
 *
 * @version 1.0
 * @since 1.0
 */
class URLMappingHandler extends DefaultHandler {

  // Hashmap to hold URLMappings
  private HashMap hmap = new HashMap();

  // Instance of URLMap, currently being populated.
  private URLMapping urlMap = null;

  // Name of the last tag encountered by startElement(..) method
  private String lastTag = null;

  // List of roles for the current URLMap
  private ArrayList roles = new ArrayList();

  /**
   * Returns the mappings that have been parsed and loaded in Hashmap.
   *
   * @return HashMap containing URLMappings.
   * @since 1.0
   */
  public HashMap getMappings() {
    return hmap;
  }

  /**
   * Clears the Hashmap.
   *
   * @since 1.0
   */
  public void clearMappings() {
    hmap.clear();
  }

  /**
   * This method is notified when a start tag is encountered.
   *
   * @param namespaceURI namespace URI
   * @param sName local name of the tag
   * @param qName quantified name of the tag
   * @param attrs Attribute list of the tag
   * @since 1.0
   */
  public void startElement(String namespaceURI, String sName, String qName,
                           Attributes attrs) {

    String tagName = sName;

    // If Namespace aware
    if ("".equals(sName) || (sName == null)) {
      tagName = qName;
    }

    // A new event was encountered, initialize a new URLMapping
    if (tagName.equals("Event")) {

      // Start a new URL Mapping
      urlMap = new URLMapping();

    } else {
      lastTag = tagName;
    }


  }

  /**
   * This method is notified when a end tag is encountered.
   *
   * @param namespaceURI namespace URI
   * @param sName local name of the tag
   * @param qName quantified name of the tag
   * @since 1.0
   */
  public void endElement(String namespaceURI, String sName, String qName) {

    String tagName = sName;

    // If namespace aware
    if ("".equals(sName) || (sName == null)) {
      tagName = qName;
    }

    // If end of a URL mapping
    if (tagName.equals("Event")) {

      // Put the  URL Mapping into the Hashmap
      hmap.put(urlMap.getEventName(), urlMap);

    } else if (tagName.equals("Roles")) {

        // Load the roles into the URLMap
        String[] role = (String[]) roles.toArray(new String[roles.size()]);
        urlMap.setRoles(role);
        roles.clear();

    }
    // Reset last tag
    lastTag = null;

  }

  /**
   * This method is notified when the characters in-between tags is encountered.
   *
   * @param ch Character array of the text
   * @param offset start position in the char array
   * @param len length of the characters to be taken from the char array
   * @since 1.0
   */
  public void characters(char[] ch, int offset, int len) {

    if (len == 0 || lastTag==null)   return;

    String tagValue = new String(ch, offset, len);

    if (lastTag.equals("Name"))

      urlMap.setEventName(tagValue);

    else if (lastTag.equals("Class"))

      urlMap.setClassName(tagValue);

    else if (lastTag.equals("Method"))

      urlMap.setMethodName(tagValue);

    else if (lastTag.equals("Screen"))

      urlMap.setNextScreen(tagValue);

    else if (lastTag.equals("Role"))
      roles.add(tagValue);

  }

}  // End of class URLMappingHandler

/**
 * This class extends the DefaultHandler class and overrides
 * the standard callback methods. This class parses the given XML file
 * and populates the ExceptionMappings in a Hashmap.
 *
 * @version 1.0
 * @since 1.0
 */
class ExceptionMappingHandler extends DefaultHandler {

  // Hashmap to hold ExceptionMappings
  private HashMap hmap = new HashMap();

  // ExceptionMap that is currently being constructed
  private ExceptionMapping excpMap = null;

  // Name of the last tag encountered by startElement(..)
  private String lastTag = null;

  /**
   * Returns the mappings that have been parsed and loaded in Hashmap.
   *
   * @return HashMap containing ExceptionMappings.
   * @since 1.0
   */
  public HashMap getMappings() {
    return hmap;
  }

  /**
   * Clears the Hashmap.
   *
   * @since 1.0
   */
  public void clearMappings() {
    hmap.clear();
  }

  /**
   * This method is notified when a start tag is encountered.
   *
   * @param namespaceURI namespace URI
   * @param sName local name of the tag
   * @param qName quantified name of the tag
   * @param attrs Attribute list of the tag
   * @since 1.0
   */
  public void startElement(String namespaceURI, String sName, String qName,
                           Attributes attrs) {

    String tagName = sName;
    if ("".equals(sName) || (sName == null)) {
      tagName = qName;
    }

    if (tagName.equals("Exception")) {

      // Start a new Exception Mapping
      excpMap = new ExceptionMapping();
    }
    else {
      lastTag = tagName;
    }
  }

  /**
   * This method is notified when a end tag is encountered.
   *
   * @param namespaceURI namespace URI
   * @param sName local name of the tag
   * @param qName quantified name of the tag
   * @since 1.0
   */
  public void endElement(String namespaceURI, String sName, String qName) {

    String tagName = sName;
    if ("".equals(sName) || (sName == null)) {
      tagName = qName;
    }

    if (tagName.equals("Exception")) {

      // Put the  URL Mapping into the Hashmap
      hmap.put(excpMap.getClassName(), excpMap);
    }

    lastTag = null;
  }

  /**
   * This method is notified when the characters in-between tags is encountered.
   *
   * @param ch Character array of the text
   * @param offset start position in the char array
   * @param len length of the characters to be taken from the char array
   * @since 1.0
   */
  public void characters(char[] ch, int offset, int len) {

    if (len == 0 || lastTag==null)    return;


    String tagValue = new String(ch, offset, len);

    if (lastTag.equals("Class"))

      excpMap.setClassName(tagValue);

    else if (lastTag.equals("WebErrorPage"))

      excpMap.setNextWebScreen(tagValue);

    else if (lastTag.equals("MobileErrorPage"))

      excpMap.setNextMobileScreen(tagValue);
  }

}