// $Id: tixml_base.cpp,v 1.8 2018/01/03 21:33:06 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: Mary Kuhner, Jon Yamato, and Joseph Felsenstein */


#include "errhandling.h"
#include "stringx.h"
#include "tinyxml.h"
#include "tixml_base.h"
#include <climits>

const std::string tibasestr::EXTRA_TAG_0     =   "incorrect xml: extra tag \"";
const std::string tibasestr::EXTRA_TAG_1     =   "\".";
const std::string tibasestr::MISSING_TAG_0   =   "incorrect xml: missing tag \"";
const std::string tibasestr::MISSING_TAG_1   =   "\".";
const std::string tibasestr::NOT_DOUBLE_0    =   "incorrect xml: tag \"";
const std::string tibasestr::NOT_DOUBLE_1    =   "\" is not a double.";
const std::string tibasestr::NOT_LONG_0      =   "incorrect xml: tag \"";
const std::string tibasestr::NOT_LONG_1      =   "\" is not an integer.";
const std::string tibasestr::NOT_SIZE_T_0    =   "incorrect xml: tag \"";
const std::string tibasestr::NOT_SIZE_T_1    =   "\" is not a non-negative integer.";

TiXmlElement *
ti_singleElement(TiXmlElement* ancestor, std::string nodeName,bool required)
{
    TiXmlNode * firstChild = ancestor->IterateChildren(nodeName,NULL);
    if(firstChild == NULL)
    {
        if(required)
        {
            incorrect_xml_missing_tag e(tibasestr::MISSING_TAG_0+nodeName+tibasestr::MISSING_TAG_1,nodeName);
            throw e;
        }
        return NULL;
    }
    TiXmlNode * secondChild = ancestor->IterateChildren(nodeName,firstChild);
    if (secondChild != NULL)
    {
        incorrect_xml_extra_tag e(tibasestr::EXTRA_TAG_0+nodeName+tibasestr::EXTRA_TAG_1,nodeName);
        throw e;
        return NULL;
    }
    return firstChild->ToElement();
}

TiXmlElement *
ti_optionalChild(TiXmlElement* ancestor, std::string nodeName)
{
    return ti_singleElement(ancestor,nodeName,false);
}

TiXmlElement *
ti_requiredChild(TiXmlElement* ancestor, std::string nodeName)
{
    return ti_singleElement(ancestor,nodeName,true);
}

std::string
ti_nodeText(TiXmlElement * node)
{
    std::string outstring;
    TiXmlNode * child = NULL;
    while((child = node->IterateChildren(child)))
    {
        TiXmlHandle handle(child);
        if(handle.Text())
        {
            outstring += child->Value();
        }
    }
    StripLeadingSpaces(outstring);
    StripTrailingSpaces(outstring);
    return outstring;
}

bool
ti_hasAttribute(TiXmlElement * node, std::string attrName)
{
    const std::string * attrValue = node->Attribute(attrName);
    return (attrValue != NULL);
}

std::string
ti_attributeValue(TiXmlElement * node, std::string attrName)
{
    const std::string * attrValue = node->Attribute(attrName);
    if(attrValue == NULL) return std::string("");
    return (*attrValue);
}

double
ti_double_from_text(TiXmlElement * node)
{
    std::string nodeText = ti_nodeText(node);
    double value = DBL_BIG;
    try
    {
        value = ProduceDoubleOrBarf(nodeText);
    }
    catch(data_error f)
    {
        incorrect_xml_not_double e(tibasestr::NOT_DOUBLE_0+nodeText+tibasestr::NOT_DOUBLE_1,nodeText);
        throw e;
    }
    return value;
}

long
ti_long_from_text(TiXmlElement * node) throw(incorrect_xml)
{
    std::string nodeText = ti_nodeText(node);
    long value = LONG_MAX;
    try
    {
        value = ProduceLongOrBarf(nodeText);
    }
    catch(data_error f)
    {
        incorrect_xml_not_long e(tibasestr::NOT_LONG_0+nodeText+tibasestr::NOT_LONG_1,nodeText);
        throw e;
    }
    return value;
}

size_t
ti_size_t_from_text(TiXmlElement * node) throw(incorrect_xml)
{
    long value = ti_long_from_text(node);
    if(value < 0)
    {
        std::string nodeText = ti_nodeText(node);
        incorrect_xml_not_size_t e(tibasestr::NOT_SIZE_T_0+nodeText+tibasestr::NOT_SIZE_T_1,nodeText);
        throw e;
    }
    return (size_t)value;
}

std::vector<TiXmlElement *>
ti_optionalChildren(TiXmlElement* ancestor, std::string nodeName)
{
    std::vector<TiXmlElement *> returnVec;
    TiXmlNode * child = NULL;
    while((child = ancestor->IterateChildren(nodeName, child)))
    {
        returnVec.push_back(child->ToElement());
    }
    return returnVec;
}

std::vector<TiXmlElement *>
ti_requiredChildren(TiXmlElement* ancestor, std::string nodeName)
{
    std::vector<TiXmlElement *> returnVec = ti_optionalChildren(ancestor,nodeName);
    if(returnVec.empty())
    {
        incorrect_xml_missing_tag e(tibasestr::MISSING_TAG_0+nodeName+tibasestr::MISSING_TAG_1,nodeName);
        throw e;
    }
    return returnVec;
}

//____________________________________________________________________________________
