// $Id: parsetreeschema.cpp,v 1.7 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 <cassert>
#include <iostream>

#include "errhandling.h"
#include "parsetreeschema.h"
#include "xml_strings.h"

//------------------------------------------------------------------------------------

ElementInfo::ElementInfo(const string child)
    :
    m_child(child),
    m_required(false),
    m_oneOnly(false),
    m_deprecated(true)
{
}

ElementInfo::ElementInfo(const string child, bool required, bool oneOnly)
    :
    m_child(child),
    m_required(required),
    m_oneOnly(oneOnly),
    m_deprecated(false)
{
}

ElementInfo::~ElementInfo()
{
}

const string &
ElementInfo::GetChild() const
{
    return m_child;
}

bool
ElementInfo::GetRequired() const
{
    return m_required;
}

bool
ElementInfo::GetOneOnly() const
{
    return m_oneOnly;
}

bool
ElementInfo::GetDeprecated() const
{
    return m_deprecated;
}

string
ElementInfo::DebugDump() const
{
    return "elem";
}

string
AttributeInfo::DebugDump() const
{
    return "attr";
}

bool
ElementInfoCompare::operator()(const ElementInfo e1, const ElementInfo e2) const
{
    string s1 = e1.GetChild();
    string s2 = e2.GetChild();
    int val = s1.compare(s2);
    return (val < 0);
}

bool
AttributeInfoCompare::operator()(const AttributeInfo a1, const AttributeInfo a2) const
{
    string s1 = a1.GetName();
    string s2 = a2.GetName();
    int val = s1.compare(s2);
    return (val < 0);
}

AttributeInfo::AttributeInfo(const string name, bool required)
    :
    m_name(name),
    m_required(required)
{
}

AttributeInfo::~AttributeInfo()
{
}

const string &
AttributeInfo::GetName() const
{
    return m_name;
}

bool
AttributeInfo::GetRequired() const
{
    return m_required;
}

VerificationInfo::VerificationInfo()
{
}

VerificationInfo::~VerificationInfo()
{
}

attrSet::const_iterator
VerificationInfo::findAttr(const string attrName) const
{
    AttributeInfo a(attrName,false);
    return m_attributeInfo.find(a);
}

elemSet::const_iterator
VerificationInfo::findElem(const string elemName) const
{
    ElementInfo e(elemName,false,false);
    return m_elementInfo.find(e);
}

std::set<string>
VerificationInfo::RequiredAttributes() const
{
    std::set<string> requireds;

    for(attrSet::const_iterator i = m_attributeInfo.begin();
        i != m_attributeInfo.end();
        i++)
    {
        const AttributeInfo & attrRef = *i;
        if(attrRef.GetRequired())
        {
            requireds.insert(attrRef.GetName());
        }
    }
    return requireds;
}

std::set<string>
VerificationInfo::RequiredElements() const
{
    std::set<string> requireds;

    for(elemSet::const_iterator i = m_elementInfo.begin();
        i != m_elementInfo.end();
        i++)
    {
        const ElementInfo & elemRef = *i;
        if(elemRef.GetRequired())
        {
            requireds.insert(elemRef.GetChild());
        }
    }
    return requireds;
}

bool
VerificationInfo::AllowsAttribute(string attrName) const
{
    attrSet::const_iterator iter = findAttr(attrName);
    return (iter != m_attributeInfo.end());
}

bool
VerificationInfo::AllowsElement(string elemName) const
{
    elemSet::const_iterator iter = findElem(elemName);
    return (iter != m_elementInfo.end());
}

bool
VerificationInfo::AllowsAdditionalElements(string elemName) const
{
    elemSet::const_iterator iter = findElem(elemName);
    if (iter != m_elementInfo.end())
    {
        const ElementInfo & elem = *iter;
        return !(elem.GetOneOnly());
    }
    return false;

}

bool
VerificationInfo::DeprecatedElement(string elemName) const
{
    elemSet::const_iterator iter = findElem(elemName);
    if (iter != m_elementInfo.end())
    {
        const ElementInfo & elem = *iter;
        return (elem.GetDeprecated());
    }
    return false;

}

void
VerificationInfo::AddAttribute(bool required, string parentName, string name)
{
    attrSet::const_iterator i = findAttr(name);
    if(i != m_attributeInfo.end())
    {
        throw implementation_error(xmlstr::XML_IERR_DUP_ATTR_0
                                   + name
                                   + xmlstr::XML_IERR_DUP_ATTR_1
                                   + parentName
                                   + xmlstr::XML_IERR_DUP_ATTR_2);
    }
    m_attributeInfo.insert(AttributeInfo(name,required));
}

void
VerificationInfo::AddElement(bool required, bool onlyOne,
                             string parentName, string name)
{
    elemSet::const_iterator i = findElem(name);
    if(i != m_elementInfo.end())
    {
        throw implementation_error(xmlstr::XML_IERR_DUP_CHILD_0
                                   + name
                                   + xmlstr::XML_IERR_DUP_CHILD_1
                                   + parentName
                                   + xmlstr::XML_IERR_DUP_CHILD_2);
    }
    m_elementInfo.insert(ElementInfo(name,required,onlyOne));
}

void
VerificationInfo::AddDeprecatedElement(string parentName, string name)
{
    elemSet::const_iterator i = findElem(name);
    if(i != m_elementInfo.end())
    {
        throw implementation_error(xmlstr::XML_IERR_DUP_CHILD_0
                                   + name
                                   + xmlstr::XML_IERR_DUP_CHILD_1
                                   + parentName
                                   + xmlstr::XML_IERR_DUP_CHILD_2);
    }
    m_elementInfo.insert(ElementInfo(name));
}

string
VerificationInfo::DebugDump() const
{
    string retVal = "";
    elemSet::const_iterator i;
    for(i=m_elementInfo.begin(); i != m_elementInfo.end(); i++)
    {
        const ElementInfo & elem = *i;
        retVal += elem.GetChild();
        retVal += " ";
    }
    return retVal;
}

void
ParseTreeSchema::AddAttribute(bool required, string parent, string attr)
{
    std::map<string,VerificationInfo>::iterator i = m_schema.find(parent);
    if(i == m_schema.end())
    {
        m_schema[parent] = VerificationInfo();
        i = m_schema.find(parent);
        assert(i != m_schema.end());
    }
    ((*i).second).AddAttribute(required,parent,attr);
}

void
ParseTreeSchema::AddTag(string tagName)
{
    // find the entry for tagName
    std::map<string,VerificationInfo>::iterator i = m_schema.find(tagName);
    if(i != m_schema.end())
        // it already exists
    {
        throw implementation_error(xmlstr::XML_IERR_DUP_TAG_0
                                   + tagName
                                   + xmlstr::XML_IERR_DUP_TAG_1);
    }

    // create new item
    m_schema[tagName] = VerificationInfo();
}

void
ParseTreeSchema::AddSubtag( bool required,
                            bool onlyOne,
                            string parent,
                            string child)
{
    // find the schema entry for parent
    std::map<string,VerificationInfo>::iterator i = m_schema.find(parent);
    if(i == m_schema.end())
    {
        throw implementation_error(xmlstr::XML_IERR_NO_PARENT_TAG_0
                                   + child
                                   + xmlstr::XML_IERR_NO_PARENT_TAG_1
                                   + parent
                                   + xmlstr::XML_IERR_NO_PARENT_TAG_2);

    }

    // add new info to parent
    ((*i).second).AddElement(required,onlyOne,parent,child);

    // add blank tag for child if necessary
    if(m_schema.find(child) == m_schema.end())
    {
        AddTag(child);
    }
}

void
ParseTreeSchema::DeprecatedSubtag(string parent, string child)
{
    // find the schema entry for parent
    std::map<string,VerificationInfo>::iterator i = m_schema.find(parent);
    if(i == m_schema.end())
    {
        throw implementation_error(xmlstr::XML_IERR_NO_PARENT_TAG_0
                                   + child
                                   + xmlstr::XML_IERR_NO_PARENT_TAG_1
                                   + parent
                                   + xmlstr::XML_IERR_NO_PARENT_TAG_2);

    }

    // add new info to parent
    ((*i).second).AddDeprecatedElement(parent,child);

    // add blank tag for child if necessary
    if(m_schema.find(child) == m_schema.end())
    {
        AddTag(child);
    }
}

void
ParseTreeSchema::DebugDump(const string header) const
{
    std::cout << header << std::endl;
    std::map<string,VerificationInfo>::const_iterator i;
    for(i=m_schema.begin(); i != m_schema.end(); i++)
    {
        const string tagName = (*i).first;
        const VerificationInfo & veri = (*i).second;

        std::cout << "\t" << tagName << " " << veri.DebugDump() << std::endl;
    }
}

ParseTreeSchema::ParseTreeSchema()
{
}

ParseTreeSchema::~ParseTreeSchema()
{
}

const VerificationInfo &
ParseTreeSchema::getInfo(const string parent,int lineNo) const
{
    std::map<string,VerificationInfo>::const_iterator i = m_schema.find(parent);
    if(i == m_schema.end())
    {
        throw unrecognized_tag_error(parent,lineNo);
    }
    return (*i).second;
}

bool
ParseTreeSchema::AllowsAttribute(const string parent, const string attr, int lineNo) const
{
    return getInfo(parent,lineNo).AllowsAttribute(attr);
}

bool
ParseTreeSchema::AllowsElement(const string parent, const string child, int lineNo) const
{
    return getInfo(parent,lineNo).AllowsElement(child);
}

bool
ParseTreeSchema::AllowsAdditionalElements(const string parent, const string child, int lineNo) const
{
    return getInfo(parent,lineNo).AllowsAdditionalElements(child);
}

bool
ParseTreeSchema::DeprecatedElement(const string parent, const string child, int lineNo) const
{
    return getInfo(parent,lineNo).DeprecatedElement(child);
}

std::set<string>
ParseTreeSchema::RequiredAttributes(const string parent, int lineNo) const
{
    return getInfo(parent,lineNo).RequiredAttributes();
}

std::set<string>
ParseTreeSchema::RequiredElements(const string parent, int lineNo) const
{
    return getInfo(parent,lineNo).RequiredElements();
}

//____________________________________________________________________________________
