// $Id$
/*
 * Copyright 2010 Institute for Systems Biology
 *                Seattle, Washington, USA.
 * 
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package biotextEngine.xmlparsers.medline;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import biotextEngine.xmlparsers.GenericXMLParser;
import biotextEngine.xmlparsers.NodeHandler;

/**
 * The main driving class of the MedlineCitation. Includes elements and
 * structures relevant to root element.
 * 
 * @author Ariel Schwartz
 * @author Gaurav Bhalotia
 */
public class MedlineCitation extends NodeHandler {

    String pmid;
    private static PreparedStatement pstmt_delete = null;

    private static boolean isIncremental = true;

    /*
     * Default constructor
     * @param xmlFileName The name of the file that is being parsed, to be
     * stored in the record for medline_citation
     */
    public MedlineCitation(String xmlFileName) throws Exception {

        ignoreDuplicateKeyError = true;

        /*
         * The table in which the parsed entries are to be entered from this
         * node
         */
        tableName = "medline_citation";

        /* The node name being handled by this class */
        xmlNodeName = "MedlineCitation";

        /* The various column names in the table */
        String[] columnNameDef = {
                "pmid",
                "date_created",
                "date_completed",
                "date_revised",
                "pub_model",
                "issn",
                "issn_type",
                "cited_medium",
                "volume",
                "issue",
                "pub_date_year",
                "pub_date_month",
                "pub_date_day",
                "pub_date_season",
                "medline_date",
                "journal_title",
                "iso_abbreviation",
                "article_title",
                "medline_pgn",
                "abstract_text",
                "copyright_info",
                "article_affiliation",
                "article_author_list_comp_yn",
                "data_bank_list_comp_yn",
                "grantlist_complete_yn",
                "vernacular_title",
                "date_of_electronic_publication",
                "elec_pub_official_date_yn",
                "country",
                "medline_ta",
                "nlm_unique_id",
                "issn_linking",
                "xml_file_name",
                "number_of_references",
                "keyword_list_owner",
                "citation_owner",
                "citation_status"
        };

        columnName = columnNameDef;

        /*
         * The corresponding XML tags, The tags names starts from the current
         * xmlnode
         */
        String[] xmlElementNameDef = {
                "MedlineCitation.PMID",
                "MedlineCitation.DateCreated",
                "MedlineCitation.DateCompleted",
                "MedlineCitation.DateRevised",
                "MedlineCitation.Article.PubModel",
                "MedlineCitation.Article.Journal.ISSN",
                "MedlineCitation.Article.Journal.ISSN.IssnType",
                "MedlineCitation.Article.Journal.JournalIssue.CitedMedium",
                "MedlineCitation.Article.Journal.JournalIssue.Volume",
                "MedlineCitation.Article.Journal.JournalIssue.Issue",
                "MedlineCitation.Article.Journal.JournalIssue.PubDate.Year",
                "MedlineCitation.Article.Journal.JournalIssue.PubDate.Month",
                "MedlineCitation.Article.Journal.JournalIssue.PubDate.Day",
                "MedlineCitation.Article.Journal.JournalIssue.PubDate.Season",
                "MedlineCitation.Article.Journal.JournalIssue.PubDate.MedlineDate",
                "MedlineCitation.Article.Journal.Title",
                "MedlineCitation.Article.Journal.ISOAbbreviation",
                "MedlineCitation.Article.ArticleTitle",
                "MedlineCitation.Article.Pagination.MedlinePgn",
                "MedlineCitation.Article.Abstract.AbstractText",
                "MedlineCitation.Article.Abstract.CopyrightInformation",
                "MedlineCitation.Article.Affiliation",
                "MedlineCitation.Article.AuthorList.CompleteYN",
                "MedlineCitation.Article.DataBankList.CompleteYN",
                "MedlineCitation.Article.GrantList.CompleteYN",
                "MedlineCitation.Article.VernacularTitle",
                "MedlineCitation.Article.ElectronicPubDate",
                "MedlineCitation.Article.ElectronicPubDate.OfficialDateYN",
                "MedlineCitation.MedlineJournalInfo.Country",
                "MedlineCitation.MedlineJournalInfo.MedlineTA",
                "MedlineCitation.MedlineJournalInfo.NlmUniqueID",
                "MedlineCitation.MedlineJournalInfo.ISSNLinking",
                "XmlFileName",
                "MedlineCitation.NumberOfReferences",
                "MedlineCitation.KeywordList.Owner",
                "MedlineCitation.Owner",
                "MedlineCitation.Status"
        };

        xmlElementName = xmlElementNameDef;

        /* The SQL types for the various columns above */
        int columnTypeDef[] = {
                Types.INTEGER,  // pmid
                Types.DATE,     // date_created
                Types.DATE,     // date_completed
                Types.DATE,     // date_revised
                Types.VARCHAR,  // pub_model
                Types.CHAR,     // issn
                Types.VARCHAR,  // issn_type
                Types.VARCHAR,  // cited_medium
                Types.VARCHAR,  // volume
                Types.VARCHAR,  // issue
                Types.VARCHAR,  // pub_date_year
                Types.VARCHAR,  // pub_date_month
                Types.VARCHAR,  // pub_date_day
                Types.VARCHAR,  // pub_date_season
                Types.VARCHAR,  // medline_date
                Types.VARCHAR,  // journal_title
                Types.VARCHAR,  // iso_abbreviation
                Types.VARCHAR,  // article_title
                Types.VARCHAR,  // medline_pgn
                Types.CLOB,     // abstract_text
                Types.VARCHAR,  // copyright_info
                Types.VARCHAR,  // article_affiliation
                Types.CHAR,     // article_author_list_comp_yn
                Types.CHAR,     // data_bank_list_comp_yn
                Types.CHAR,     // grantlist_complete_yn
                Types.VARCHAR,  // vernacular_title
                Types.DATE,     // date_of_electronic_publication
                Types.CHAR,     // elec_pub_official_date_yn
                Types.VARCHAR,  // country
                Types.VARCHAR,  // medline_ta
                Types.VARCHAR,  // nlm_unique_id
                Types.VARCHAR,  // issn_linking
                Types.VARCHAR,  // xml_file_name
                Types.INTEGER,  // number_of_references
                Types.VARCHAR,  // keyword_list_owner
                Types.VARCHAR,  // citation_owner
                Types.VARCHAR   // citation_status
        };

        columnType = columnTypeDef;
        initialize();

        /* Add any data that does not come through XML to the hashtable */
        putColumnValue("XmlFileName", xmlFileName);
    }

    /**
     * The method to handle the event when a new element is found, this is
     * overwriting the method defined in the super class NodeHandler.java
     */
    public void startElement(final String namespaceURI, final String localName,
            final String qName, final Attributes atts) throws SAXException {

        NodeHandler handler = null;

        /*
         * Take decisions based on the element found, if it needs to be handled
         * by a child handler then instantiate an object for the same and set
         * the handler else call the handler from the super class
         */
        try {
            if (currentElement != null) {
                if (pmid == null)
                    pmid = getColumnValue("MedlineCitation.PMID");

                if (currentElement.equals("AuthorList")
                        && qName.equals("Author")) {
                    handler = new Author(pmid);

                } else if (currentElement.equals("AccessionNumberList")
                        && qName.equals("AccessionNumber")) {
                    String dataBankName = getColumnValue("MedlineCitation.Article.DataBankList.DataBank.DataBankName");
                    handler = new DataBank(pmid, dataBankName);

                } else if (currentElement.equals("ChemicalList")
                        && qName.equals("Chemical")) {
                    handler = new Chemical(pmid);

                } else if (currentElement.equals("GeneSymbolList")
                        && qName.equals("GeneSymbol")) {
                    handler = new GeneSymbol(pmid);

                } else if (currentElement.equals("KeywordList")
                        && qName.equals("Keyword")) {
                    handler = new Keyword(pmid);

                } else if (currentElement.equals("PublicationTypeList")
                        && qName.equals("PublicationType")) {
                    handler = new ArticlePublicationType(pmid);

                } else if (currentElement.equals("GrantList")
                        && qName.equals("Grant")) {
                    handler = new Grant(pmid);

                } else if (currentElement.equals("MeshHeadingList")
                        && qName.equals("MeshHeading")) {
                    handler = new MeshHeading(pmid);

                } else if (currentElement.equals("CommentsCorrectionsList")
                        && qName.equals("CommentsCorrections")) {
                    handler = new CommentsCorrections(pmid);

                } else if (qName.equals("CitationSubset")) {
                    handler = new CitationSubset(pmid);

                } else if (qName.equals("Language")) {
                    handler = new ArticleLanguage(pmid);
                    
                } else if (qName.equals("ELocationID")) {
                    handler = new ArticleELocationID(pmid);

                } else if (currentElement.equals("PersonalNameSubjectList")
                        && qName.equals("PersonalNameSubject")) {
                    handler = new PersonalNameSubject(pmid);

                } else if (qName.equals("OtherID")) {
                    handler = new OtherID(pmid);

                } else if (qName.equals("OtherAbstract")) {
                    handler = new OtherAbstract(pmid);

                } else if (qName.equals("SpaceFlightMission")) {
                    handler = new SpaceFlightMission(pmid);

                } else if (currentElement.equals("InvestigatorList")
                        && qName.equals("Investigator")) {
                    handler = new Investigator(pmid);

                } else if (qName.equals("GeneralNote")) {
                    handler = new GeneralNote(pmid);

                } else {
                    super.startElement(namespaceURI, localName, qName, atts);
                }
                
                // if anybody in the previous if block initialized a handler,
                // set it
                if (null != handler) {
                    setContentHandler(
                            handler, namespaceURI, localName, qName, atts);
                }
                
            } else {
                super.startElement(namespaceURI, localName, qName, atts);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            throw new SAXException("Problem creating Child. PMID: " + pmid
                    + " for element " + qName);
        }
    }

    /*
     * Extends the endElement method from the super class NodeHandler checks if
     * a medline citation has ended, in which case flushes the values found
     * including all the childhandler to the database
     */
    public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException {
        super.endElement(namespaceURI, localName, qName);
        if (qName.equals(xmlNodeName)) {
            try {
                updateDB();
            } catch (Exception e) {
                e.printStackTrace();
                throw new SAXException("problem updating the database");
            }
        }
    }

    /**
     * Handles SQLException. Should be overloaded by inheriting classes to
     * handle special cases
     * 
     * @returns true if the exception has been handled, false otherwise
     */
    protected boolean handleSQLException(SQLException e) {
        if (ignoreDuplicateKeyError && e.getErrorCode() == DB2_DUPLICATE_ERROR) {

            if (isIncremental) {
                try {

                    if (pstmt_delete == null) {
                        pstmt_delete = GenericXMLParser
                                .getDbConnection()
                                .prepareStatement(
                                        "DELETE FROM medline_citation where pmid = ?");
                    }

                    pstmt_delete.setInt(1, Integer.parseInt(pmid));

                    pstmt_delete.executeUpdate();

                    /*
                     * Execute the insert again. Note this could cause problems
                     * in multithreaded implementation
                     */
                    pstmt.executeUpdate();
                    return true;
                } catch (SQLException e1) {
                    /* Doesn't work again so give up and report */
                    MedlineParser.eCount++;
                    if (MedlineParser.eCount % 500 == 0) {
                        System.out.println("Total " + MedlineParser.eCount
                                + " Values not inserted");
                    }
                    updateChildren = false;
                    return true;
                }

            } else {

                MedlineParser.eCount++;
                if (MedlineParser.eCount % 500 == 0) {
                    System.out.println("Total " + MedlineParser.eCount
                            + " Values not inserted");
                }
                updateChildren = false;
                return true;
                /*
                 * Don't do anything, the tuple for this primary key has already
                 * been inserted
                 */
            }
        } else {
            System.err.println("ERROR CODE == " + e.getErrorCode());
            return false;
        }
    }

}
