// $Id: XmlParserUtil.cpp,v 1.8 2018/01/03 21:32:57 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

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.

*/

/* Authors: Patrick Colacurcio, Peter Beerli, Mary Kuhner, Jon Yamato 
/* Authors: Patrick Colacurcio, Peter Beerli, Mary Kuhner, Jon Yamato 
and Joseph Felsenstein */


#include <sstream>

#include "Converter_XmlParserUtil.h"
#include "Converter_DataSourceException.h"
#include "Converter_DataSourceException.h"

#ifdef DMALLOC_FUNC_CHECK
#include "/usr/local/include/dmalloc.h"
#endif

using namespace std;

XmlParserUtil::XmlParserUtil() {}

XmlParserUtil::~XmlParserUtil() {}

string
XmlParserUtil::getNextTag (istream& is, map<string,string>& tagInfo)
{
    int i = 0;
    int ch;
    int firstCh;
    string buffer;

    int beginChar = '<';

    skipToChar(is, beginChar);

    //  if it's a comment, skip it.
    firstCh=is.get();
    if ((ch=is.get()) == '!')
    {
        return getNextTag(is, tagInfo);
    }
    else
    {
        is.putback(ch);
        is.putback(firstCh);
    }

    while ((ch=is.get()) != EOF )
    {
        if ( (ch != '>') )
        {
            i = 1;
            buffer += ch;
        }
        else if ( i == 1 )
            break;
    }
    if (ch == EOF)
    {
        if (i == 0)
        {
            // putback the EOF
            is.putback(ch);
            return buffer;
        }
        else
        {
            // throw here
            is.putback(ch);
            return buffer;
        }
    }
    if(i == 1)
    {
        // dont putback the last '>'
        // but stick it into the buffer.
        buffer += ">";
        // is.putback(ch);
    }
    else
        // throw here
        return "";

    bool startTag = stripTag(buffer, tagInfo);

    if (startTag)
    {
        m_tagStack.push_back(tagInfo["TagName"]);
    }
    else
    {
        if (m_tagStack.empty())
        {
            // TODO Error
            // Break
            return "";
        }
        string lastTag = m_tagStack.back();
        m_tagStack.pop_back();
        if (lastTag != tagInfo["TagName"])
        {
            string err = "Invalid XML.  Unbalanced Tags: <" + lastTag + ">...<" +
                tagInfo["TagName"] + ">.";
            throw FileFormatError(err);
        }
    }
    return buffer;

}                               // XmlParserUtil::getNextTag

bool
XmlParserUtil::stripTag(string& tag, map<string, string>& tagInfo) const
{
    istringstream tagStream(tag);

    int ch;
    bool startTag;

    // pull the initial '<'
    if ((ch = tagStream.get()) != '<')
    {
        string err = "Invalid XML.  Could not parse tag: " + tag;
        throw FileFormatError(err);
    }

    skipWhiteSpace(tagStream);

    // if the next character is a '/' note that this is an starttag
    if ((ch = tagStream.get()) == '/')
    {
        startTag = false;
    }
    else
    {
        // put back whatever it was...
        tagStream.putback(ch);
        startTag = true;
    }

    // Next, get the next token.  (the tagName)
    string tagName;
    bool success = getToken(tagStream, tagName);

    if (!success)
    {
        string err = "Invalid XML.  Could not parse tagName: " + tag;
        throw FileFormatError(err);
    }

    tagInfo.insert(make_pair(string("TagName"), tagName));

    // LAME! -plc
    string startTagString = "true";
    if (!startTag)
    {
        startTagString = "false";
    }

    tagInfo.insert(make_pair(string("IsStartTag"), startTagString));

    skipWhiteSpace(tagStream);

    string attributeName;
    string attributeValue;

    bool getName = true;
    bool inQuotes = false;

    //  Now, get the attributes.
    while (((ch=tagStream.get()) != EOF) && (ch != '>'))
    {
        if ( (ch != '=') && (ch != '"') )
        {
            if (getName)
            {
                attributeName += ch;
            }
            else
            {
                if (inQuotes)
                {
                    attributeValue += ch;
                }
            }
        }
        else if (ch == '=')
        {
            getName = false;
        }
        else if (ch == '"')
        {
            if (inQuotes)
            {
                inQuotes = false;
                getName = true;
                skipWhiteSpace(tagStream);
                tagInfo.insert(make_pair(attributeName, attributeValue));
                attributeName = "";
                attributeValue = "";
            }
            else
            {
                inQuotes = true;
            }
        }
    }
    return startTag;
}                               // XmlParserUtil::stripTag

bool
XmlParserUtil::getTagValue (istream& is, string& buffer)
{
    string currentLocation = getLocation();

    int ch;

    while ((ch=is.get()) != EOF )
    {
        if ( (ch != '<') )
        {
            buffer += ch;
        }
        else
        {
            map<string, string> tagInfo;  // Note that we don't actually use this here.
            is.putback(ch);
            string nextTag = getNextTag(is, tagInfo);
            // If we get back to less than where (stcompwise, that is),
            // we started, stackwise, we're done.
            if (currentLocation > getLocation() )
            {
                break;
            }
            // otherwise, add the tag info to the deal.
            buffer += nextTag;
        }
    }
    if (ch == EOF)
    {
        return false;
    }
    else
    {
        return true;
    }
}                               // XmlParserUtil::getTagValue

string
XmlParserUtil::getLocation() const
{
    string curDict;  //  The current sub tree that we're in.
    //  eg.  "lamarc forces replication"

    if (m_tagStack.empty())
        return "";

    vector<string>::const_iterator it;
    for (it = m_tagStack.begin(); it != m_tagStack.end(); it++)
    {
        curDict += *it + " ";
    }

    return curDict;
}                               // XmlParserUtil::getLocatio

string
XmlParserUtil::getTopNodeName() const
{
    return m_tagStack.back();
}                               // XmlParserUtil::getTopNodeNam

//____________________________________________________________________________________
