/*
 * BIConfigReader.java
 *
 * Copyright (c) 2003, Oracle Corporation. All rights reserved. Oracle grants
 * you ("Licensee") a non-exclusive, royalty-free, worldwide license to use,
 * modify and redistribute this software in source and binary code form, provided
 * that (i) this copyright notice and license appear on all copies of the
 * software; (ii) Licensee does not utilize the software in a manner disparaging
 * to Oracle; and (iii) Licensee does not utilize the software in a manner that
 * violates the terms of any Licensee agreement with Oracle.
 */
package samples.javaclient.analyzer;

import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;

import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XMLElement;
import oracle.xml.parser.v2.DOMParser;
import oracle.xml.parser.v2.XMLParseException;

/**
 * Reads the configuration file.
 * This class uses java.io classes, to read the configuration file, and
 * oracle.xml.parser.v2 classes, to parse the xml.
 * <p>
 * The {@link AnalyzerConnection} uses this class for access to the
 * configuration file.
 */
public class BIConfigReader
{
    /**
     * The root element in the BIConfig file (&lt;BIConfig&gt;).
     */
    private XMLElement m_root;
    /**
     * The PersistenceConnection element in the BIConfig file.
     */
    private PersistenceConnection m_perConn;
    /**
     * The OLAPConnection element in the BIConfig file.
     */
    private OLAPConnection m_olapConn;

    /**
     * Constructor.
     * 
     * @param configXML The configuration file.
     * 
     * @throws IOException If there is a problem opening the configuration file.
     * @throws SAXException If a general SAX exception occurs in parsing the XML.
     */
    public BIConfigReader(String configXML) throws IOException, SAXException
    {
        InputStream _is = null;
        InputStreamReader _reader = null;
        
        try
        {
            if (configXML.startsWith("/"))
                configXML = configXML.substring(1);

            // first read the file (either through jar file or filesystem)
            _is = Thread.currentThread().getContextClassLoader().getResourceAsStream(configXML);
            if (_is == null)
            {
                File _file = new File(configXML);
                if (_file.exists())
                    _is = new FileInputStream(_file);
                else
                    throw new FileNotFoundException("File does not exist!");
            }

            // now parse the file
             _reader = new InputStreamReader(_is, "UTF8");

            DOMParser _parser = new DOMParser();
            _parser.setPreserveWhitespace(false);
            _parser.parse(_reader);
        
            XMLDocument _doc = (XMLDocument)_parser.getDocument();
            m_root = (XMLElement)_doc.getDocumentElement();
        }
        finally
        {
            // close all the streams
            if (_is != null)
                _is.close();

            if (_reader != null)
                _reader.close();
        }
    }

    /**
     * Gets connection information that is in the &lt;PersistenceConnection&gt;
     * element.
     * 
     * @return Information about the connection to the BI Beans Catalog.
     */
    public PersistenceConnection getPersistenceConnection()
    {
        if (m_perConn == null)
        {
            if (m_root != null)
            {
                // assume only one Persistence connection
                // always returns the first one
                NodeList _list = m_root.getChildrenByTagName("PersistenceConnection");
                if (_list.getLength() > 0)
                {
                    XMLElement _perElem = (XMLElement)_list.item(0);                    
                    if (_perElem != null)
                        m_perConn = new PersistenceConnection(_perElem);
                }
            }
        }

        return m_perConn;
    }

    /**
     * Gets connection information that is in the &lt;OLAPConnection&gt; element.
     * 
     * @return Information about the connection to the OLAP Catalog.
     */
    public OLAPConnection getOLAPConnection()
    {
        if (m_olapConn == null)
        {
            if (m_root != null)
            {
                // assume only one OLAP connection
                // always returns the first one
                NodeList _list = m_root.getChildrenByTagName("OLAPConnection");
                if (_list.getLength() > 0)
                {
                    XMLElement _olapElem = (XMLElement)_list.item(0);                    
                    if (_olapElem != null)
                        m_olapConn = new OLAPConnection(_olapElem);
                }
            }
        }

        return m_olapConn;
    }

    /**
     * Writes both the config file and the Catalog DAD file.
     * 
     * @param configFile The full path name of the configuration file.
     * @param catalogFile The full path name of the DAD file.
     */
    public void writeToLocalFile(String configFile, String catalogFile) throws IOException 
    {
        // Write configuration file
        FileOutputStream _fos = new FileOutputStream(configFile);
        XMLDocument _doc = (XMLDocument)m_root.getOwnerDocument();
        _doc.print(_fos);
        _fos.flush();
        _fos.close();

        DADFile dad = getPersistenceConnection().getDADFile();
        FileWriter fw = new FileWriter (catalogFile);
        fw.write("Username:" + dad.getUserName() + "\r\n");
        fw.write("Password:" + dad.getPassword());
        fw.flush();
        fw.close();        
        
    }

    // test routine
    /**
     * Test this class.
     */
    public static void main(String[] args)
    {
        try
        {
            // BIConfigReader _reader = new BIConfigReader(args[0]);
            BIConfigReader _reader = new BIConfigReader("c:/temp/biconfig.xml");
            
            PersistenceConnection _perConn = _reader.getPersistenceConnection();
            DADFile _perDAD  = _perConn.getDADFile();
            System.out.println("Persistence Connection");
            System.out.println("======================");
            System.out.println("Host: "+_perConn.getHostName());
            System.out.println("Port: "+_perConn.getPortNumber());
            System.out.println("SID: "+_perConn.getSID());
            if (_perDAD != null)
            {
                System.out.println("User name: "+_perDAD.getUserName());
                System.out.println("Password: "+_perDAD.getPassword());
            }
            System.out.println();
            
            OLAPConnection _olapConn = _reader.getOLAPConnection();
            DADFile _olapDAD = _olapConn.getDADFile();
            System.out.println("OLAP Connection");
            System.out.println("======================");
            System.out.println("Host: "+_olapConn.getHostName());
            System.out.println("Port: "+_olapConn.getPortNumber());
            System.out.println("SID: "+_olapConn.getSID());
            if (_olapDAD != null)
            {
                System.out.println("User name: "+_olapDAD.getUserName());
                System.out.println("Password: "+_olapDAD.getPassword());
            }

            // change info and write to new file
            _perConn.setHostName("ccchow-pc3");
            _perConn.setPortNumber("1521");
            _perConn.setSID("orcl");

            _olapConn.setDADFileName("/olap.dad");
            _olapConn.setHostName("klhanson-sun");

            // should write the file to the project output directory
            _reader.writeToLocalFile("c:/temp/potter.xml", "c:/temp/harry.dad");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}


// class that encapsulates information about a Persistence connection
/**
 * BI Beans Catalog connection information from the configuration file.
 * This class encapsulates the attributes from the 
 * &lt;PersistenceConnection&gt; element in the configuration file.
 * <p>
 * The {@link AnalyzerConnection} uses this class for access to the
 * BI Beans Catalog information in the configuration file.
 */
class PersistenceConnection extends AbstractConnection
{
    /**
     * Constructor.
     * 
     * @param root The root element in the configuration file (&lt;BIConfig&gt;).
     */
    PersistenceConnection(XMLElement root) {
        super(root);
    }

    /**
     * Gets the PCID attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @return The value of the PCID attribute.
     */
    public String getID() {
        return _root.getAttribute("PCID");
    }

    /**
     * Gets the JdbcDriverType attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @return The value of the JdbcDriverType attribute.
     */
    public String getJdbcDriverType() {
        return _root.getAttribute("JdbcDriverType");
    }

    /**
     * Sets the jdbcDriverType attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @param driverType The value to set for the JdbcDriverType attribute.
     */
    public void setJdbcDriverType(String driverType) {
        _root.setAttribute("JdbcDriverType", driverType);    
    }

    /**
     * Gets the PersistenceType attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @return The value of the PersistenceType attribute.
     */
    public String getPersistenceType() {
        return _root.getAttribute("PersistenceType");
    }

    /**
     * Sets the PersistenceType attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @param persistenceType The value for the PersistenceType attribute.
     */
    public void setPersistenceType(String persistenceType) {
        _root.setAttribute("PersistenceType", persistenceType);    
    }
    
    /**
     * Gets the RootFolder attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @return The value of the RootFolder attribute.
     */
    public String getRootFolder() {
        return _root.getAttribute("RootFolder");
    }

    /**
     * Sets the RootFolder attribute of the &lt;PersistenceConnection&gt;.
     * 
     * @param rootFolder The value of the RootFolder attribute.
     */
    public void setRootFolder(String rootFolder) {
        _root.setAttribute("RootFolder", rootFolder);
    }
}

// class that encapsulates information about an OLAP connection
/**
 * OLAP Catalog connection information from the configuration file.
 * This class encapsulates the attributes from the 
 * &lt;OLAPConnection&gt; element in the configuration file.
 * <p>
 * The {@link AnalyzerConnection} uses this class for access to the
 * OLAP connection information in the configuration file.
 */
class OLAPConnection extends AbstractConnection {
    /**
     * Constructor.
     * 
     * @param root The root element in the configuration file (&lt;BIConfig&gt;).
     */
    OLAPConnection(XMLElement root) {
        super(root);
    }

    /**
     * Gets the OCID attribute of the &lt;OLAPConnection&gt;.
     * 
     * @return The value of the OCID attribute.
     */
    public String getID() {
        return _root.getAttribute("OCID");
    }
}

/**
 * Base class for PersistenceConnection and OLAPConnection.
 */
abstract class AbstractConnection
{
    /**
     * Root XML element.
     */
    protected XMLElement _root;
    /**
     * DAD file.
     */
    private DADFile m_dadFile;

    /**
     * Constructor.
     * 
     * @param root The root element in the configuration file (&lt;BIConfig&gt;).
     */
    AbstractConnection(XMLElement root) {
        _root = root;
    }

    /**
     * Gets the ID attribute for the connection.
     * 
     * @return The value of the appropriate ID attribute.
     */
    public abstract String getID();
    
    /**
     * Gets the HostName attribute of the connection.
     * 
     * @return The value of the HostName attribute.
     */
    public String getHostName() {
        return _root.getAttribute("HostName");
    }

    /**
     * Sets the HostName attribute.
     * 
     * @param The host name to set as the HostName attribute value.
     */
    public void setHostName(String host) {
        _root.setAttribute("HostName", host);
    }

    /**
     * Gets the PortNumber attribute of the connection.
     * 
     * @return The value of the PortNumber attribute.
     */
    public String getPortNumber() {
        return _root.getAttribute("PortNumber");
    }

    /**
     * Sets the PortNumber attribute of the connection.
     * 
     * @param Port number to set the attribute to.
     */
    public void setPortNumber(String port) {
        _root.setAttribute("PortNumber", port);
    }

    /**
     * Gets the SID attribute of the connection.
     * 
     * @return The value of the SID attribute.
     */
    public String getSID() {
        return _root.getAttribute("SID");
    }

    /**
     * Sets the SID attribute of the connection.
     * 
     * @param sid The SID to set as the SID attribute value.
     */
    public void setSID(String sid) {
        _root.setAttribute("SID", sid);
    }

    /**
     * Gets the &lt;DADFile&gt; element from the configuration file.
     * 
     * @return The DADFile element.
     */
    private XMLElement getDADFileElem() {
        NodeList _list = _root.getChildrenByTagName("DADFile");
        if (_list != null && _list.getLength() > 0)
        {
            XMLElement _dadElem = (XMLElement)_list.item(0);
            return _dadElem;
        }
        return null;
    }

    /**
     * Gets the name of the DAD file.
     * This method gets the Filename attribute of the &lt;DADFile&gt; element.
     * 
     * @return The name of the DAD file.
     */
    public String getDADFileName() {
        XMLElement _dadElem = getDADFileElem();
        if (_dadElem != null)
            return _dadElem.getAttribute("Filename");
        else
            return null;
    }

    /**
     * Sets the name of the DAD file.
     * This method sets the Filename attribute of the &lt;DADFile&gt; element.
     * 
     * @param dad The name of the DAD file, including path information.
     */
    public void setDADFileName(String dad) {
        XMLElement _dadElem = getDADFileElem();
        if (_dadElem != null && dad != null)
            _dadElem.setAttribute("Filename", dad);
    }

    /**
     * Indicates whether the DAD file is obfuscated.
     * 
     * @return <code>true</code> if the Obfuscated attribute of the &lt;DADFile&gt;
     *                           element is true,
     *         <code>false</code> if the Obfuscated attribute is false.
     */
    public boolean isObfuscated() {
        XMLElement _dadElem = getDADFileElem();
        if (_dadElem != null)
            return (new Boolean(_dadElem.getAttribute("Obfuscated"))).booleanValue();
        else
            return false;
    }

    /**
     * Sets the Obfuscated attribute of the &lt;DADFile&gt; element.
     * 
     * @param obfuscated <code>true</code> to set the attribute to true,
     *                   <code>false</code> to set it to false.
     */
    public void setObfuscated(boolean obfuscated)  {
        XMLElement _dadElem = getDADFileElem();
        if (_dadElem != null)
            _dadElem.setAttribute("Obfuscated", (new Boolean(obfuscated)).toString());
    }

    /**
     * Gets the DADFile that has the username and password.
     * 
     * @return The DADFile.
     * 
     * @throws IOException If there is a problem opening the DAD file.
     */
    public DADFile getDADFile() throws IOException  {
        if (m_dadFile != null) 
          return m_dadFile;

        // Create a new DADFile if nec  
        String _dadFileName = getDADFileName();
        
        if (_dadFileName != null)
        {
            m_dadFile = new DADFile(_dadFileName);
            return m_dadFile;
        }

        return null;
    }
}

// encapsulates information about a DAD file
/**
 * Username and password information from the DAD file.
 * <p>
 * The {@link AnalyzerConnection} uses this class for access to the
 * DAD file information in the configuration file.
 */
class DADFile {
    /**
     * User name.
     */
    private String m_userName;
    /**
     * Password.
     */
    private String m_password;

    /**
     * Constructor.
     * 
     * @param The full path name of the DAD file that this object should represent.
     * 
     * @throws IOException If there is a problem reading the DAD file.
     */
    DADFile(String dadFile) throws IOException
    {
        InputStream _is = null;
        BufferedReader _reader = null;
        try
        {
            if (dadFile.startsWith("/"))
                dadFile = dadFile.substring(1);

            // first read the file (either through jar file or filesystem)
            _is = Thread.currentThread().getContextClassLoader().getResourceAsStream(dadFile);
            if (_is == null)
            {
                File _file = new File(dadFile);
                if (_file.exists())
                    _is = new FileInputStream(_file);
                else 
                {
//                    throw new FileNotFoundException("File does not exists!");
                    // Instead of throwing the exception, create the file
                    _file.createNewFile();
                    _is = new FileInputStream(_file);
                }
            }

            _reader = new BufferedReader(new InputStreamReader(_is));
            String _line1 = _reader.readLine();
            String _line2 = null;
            if (_line1 != null)
                _line2 = _reader.readLine();

            if (_line1 != null && _line1.indexOf("Username:") > -1)
                m_userName = _line1.substring("Username:".length());

            if (_line2 != null && _line2.indexOf("Password:") > -1)
                m_password = _line2.substring("Password:".length());
        }
        finally
        {
            if (_is != null)
                _is.close();
            if (_reader != null)
                _reader.close();
        }
    }

    /**
     * Gets the user name.
     * 
     * @return The user name.
     */
    public String getUserName()
    {
        return m_userName;
    }

    /**
     * Sets the user name.
     * 
     * @param userName The user name.
     */
    public void setUserName(String userName) {
      m_userName = userName;
    }    

    /**
     * Gets the password.
     * 
     * @return The password.
     */
    public String getPassword()
    {
        return m_password;
    }

    /**
     * Sets the password.
     * 
     * @param password The password for the DAD file.
     */
    public void setPassword(String password) {
      m_password = password;
    }
}