1 // $Id: xml.cpp,v 1.124 2010/09/27 03:19:46 bobgian Exp $
2 
3 /*
4   Copyright 2002  Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein
5 
6   This software is distributed free of charge for non-commercial use
7   and is copyrighted.  Of course, we do not guarantee that the software
8   works, and are not responsible for any damage you may cause or have.
9 */
10 
11 #include <cassert>
12 
13 #include "errhandling.h"
14 #include "front_end_warnings.h"
15 #include "stringx.h"
16 #include "tinyxml.h"
17 #include "xml.h"
18 #include "xml_strings.h"
19 
20 using std::string;
21 
22 //------------------------------------------------------------------------------------
23 
XmlErrorSupport(string fileName)24 XmlErrorSupport::XmlErrorSupport(string fileName)
25     :
26     m_fileName(fileName),
27     m_errorMessage("")
28 {
29 }
30 
~XmlErrorSupport()31 XmlErrorSupport::~XmlErrorSupport()
32 {
33 }
34 
35 string
GetFileName()36 XmlErrorSupport::GetFileName()
37 {
38     return m_fileName;
39 }
40 
41 void
ThrowDataError(const string & reason)42 XmlErrorSupport::ThrowDataError(const string& reason)
43 {
44     m_errorMessage.append(xmlstr::XML_ERR_DATA_ERR_0 + reason);
45     incorrect_data e(m_errorMessage);
46     throw e;
47 }
48 
49 void
ThrowDataError(const string & reason,long lineno)50 XmlErrorSupport::ThrowDataError(const string& reason, long lineno)
51 {
52     m_errorMessage.append( xmlstr::XML_ERR_DATA_ERR_0
53                            + xmlstr::XML_ERR_DATA_ERR_1
54                            + m_fileName
55                            + xmlstr::XML_ERR_DATA_ERR_2
56                            + ToString(lineno)
57                            + xmlstr::XML_ERR_DATA_ERR_3
58                            + reason);
59     incorrect_data e(m_errorMessage);
60     throw e;
61 }
62 
63 void
ThrowFileError(const string & reason)64 XmlErrorSupport::ThrowFileError(const string& reason)
65 {
66     m_errorMessage.append(xmlstr::XML_ERR_FILE_ERR + reason);
67     file_error e(m_errorMessage);
68     throw e;
69 }
70 
71 void
ThrowXMLError(const string & reason)72 XmlErrorSupport::ThrowXMLError(const string& reason)
73 {
74     m_errorMessage.append(xmlstr::XML_ERR_0 + reason);
75     incorrect_xml e(m_errorMessage);
76     throw e;
77 }
78 
79 void
ThrowXMLError(const string & reason,long lineno)80 XmlErrorSupport::ThrowXMLError(const string& reason, long lineno)
81 {
82     m_errorMessage.append(xmlstr::XML_ERR_0
83                           + xmlstr::XML_ERR_1
84                           + m_fileName
85                           + xmlstr::XML_ERR_2
86                           + ToString(lineno)
87                           + xmlstr::XML_ERR_3
88                           + reason);
89     incorrect_xml e(m_errorMessage);
90     throw e;
91 }
92 
93 void
ThrowInternalXMLError(const string & reason)94 XmlErrorSupport::ThrowInternalXMLError(const string& reason)
95 {
96     m_errorMessage.append(xmlstr::XML_ERR_INTERNAL + reason);
97     incorrect_xml e(m_errorMessage);
98     throw e;
99 }
100 
101 //------------------------------------------------------------------------------------
102 
XmlParser(ParseTreeSchema & schema,FrontEndWarnings & warnings)103 XmlParser::XmlParser(ParseTreeSchema & schema, FrontEndWarnings & warnings)
104     :
105     XmlErrorSupport(defaults::datafilename),
106     m_schema(schema),
107     m_frontEndWarnings(warnings)
108 {
109 }
110 
~XmlParser()111 XmlParser::~XmlParser()
112 {
113 }
114 
115 void
ParseFileData(string fileName)116 XmlParser::ParseFileData(string fileName)
117 {
118     m_fileName = fileName;
119     m_document.LoadFile(GetFileName());
120     if(m_document.Error())
121     {
122         throw tixml_error(m_document.ErrorDesc());  // EWFIX -- row ??
123     }
124     else
125         // XML parser is happy, but now we need to make
126         // sure that we've got data
127     {
128         TiXmlElement * root = GetRootElement();
129         if(root == NULL)
130         {
131             string msg = GetFileName() + ":"
132                 + xmlstr::XML_ERR_NO_XML_DATA;
133             throw tixml_error(msg);
134         }
135     }
136 
137     checkSchema();
138 }
139 
140 TiXmlElement *
GetRootElement()141 XmlParser::GetRootElement()
142 {
143     return m_document.RootElement();
144 }
145 
146 void
checkSchema()147 XmlParser::checkSchema()
148 {
149     TiXmlElement * rootElement = GetRootElement();
150     TiXmlNode * extraNode = rootElement->NextSibling();
151     while(extraNode != NULL)
152     {
153         if(extraNode->Type() != TiXmlNode::TINYXML_COMMENT)
154         {
155             ThrowXMLError( xmlstr::XML_ERR_EXTRA_TAG_TOP_0
156                            + extraNode->Value()
157                            + xmlstr::XML_ERR_EXTRA_TAG_TOP_1,
158                            extraNode->Row());
159         }
160         extraNode = extraNode->NextSibling();
161     }
162     checkSchema(rootElement);
163 }
164 
165 void
checkAttrPresent(TiXmlElement * elem,string attrName)166 XmlParser::checkAttrPresent(TiXmlElement * elem, string attrName)
167 {
168     const string * attrValue = elem->Attribute(attrName);
169     if(!attrValue)
170     {
171         ThrowXMLError( xmlstr::XML_ERR_ATTR_MISSING_0
172                        +attrName
173                        +xmlstr::XML_ERR_ATTR_MISSING_1
174                        +elem->Value()
175                        +xmlstr::XML_ERR_ATTR_MISSING_2,
176                        elem->Row());
177 
178     }
179 }
180 
181 void
checkElemPresent(TiXmlElement * elem,string elemName)182 XmlParser::checkElemPresent(TiXmlElement * elem, string elemName)
183 {
184     TiXmlNode * firstChild = elem->IterateChildren(elemName,NULL);
185     if(firstChild == NULL)
186     {
187         ThrowXMLError( xmlstr::XML_ERR_ELEM_MISSING_0
188                        +elemName
189                        +xmlstr::XML_ERR_ELEM_MISSING_1
190                        +elem->Value()
191                        +xmlstr::XML_ERR_ELEM_MISSING_2,
192                        elem->Row());
193     }
194 }
195 
196 void
checkAttrAllowed(TiXmlElement * parent,string attrName)197 XmlParser::checkAttrAllowed(TiXmlElement * parent, string attrName)
198 {
199     if(!m_schema.AllowsAttribute(string(parent->Value()),attrName,parent->Row()))
200     {
201         ThrowXMLError(xmlstr::XML_ERR_UNEXPECTED_ATTR_0
202                       + attrName
203                       + xmlstr::XML_ERR_UNEXPECTED_ATTR_1
204                       + parent->Value()
205                       + xmlstr::XML_ERR_UNEXPECTED_ATTR_2,
206                       parent->Row());
207     }
208 }
209 
210 void
checkElemCount(TiXmlElement * parent,string childName)211 XmlParser::checkElemCount(TiXmlElement * parent, string childName)
212 {
213     TiXmlNode * child = parent->IterateChildren(childName,NULL);
214     assert(child != NULL);  // because we found it as child of parent
215 
216     if(!m_schema.AllowsElement(string(parent->Value()),childName,child->Row()))
217     {
218         ThrowXMLError(xmlstr::XML_ERR_UNEXPECTED_TAG_0
219                       + childName
220                       + xmlstr::XML_ERR_UNEXPECTED_TAG_1
221                       + parent->Value()
222                       + xmlstr::XML_ERR_UNEXPECTED_TAG_2,
223                       child->Row());
224     }
225 
226     // now check if there is more than one
227     child = parent->IterateChildren(childName,child);
228     if(child != NULL)
229     {
230         if(!m_schema.AllowsAdditionalElements(string(parent->Value()),childName,child->Row()))
231         {
232             ThrowXMLError(xmlstr::XML_ERR_EXTRA_TAG_0
233                           + childName
234                           + xmlstr::XML_ERR_EXTRA_TAG_1
235                           + parent->Value()
236                           + xmlstr::XML_ERR_EXTRA_TAG_2,
237                           child->Row());
238         }
239     }
240 }
241 
242 void
checkSchema(TiXmlElement * parentElem)243 XmlParser::checkSchema(TiXmlElement * parentElem)
244 {
245 
246     string parentString(parentElem->Value());
247 
248     // check all required attributes are present
249     std::set<string> attrs = m_schema.RequiredAttributes(parentString,parentElem->Row());
250     for(std::set<string>::const_iterator i= attrs.begin(); i != attrs.end(); i++)
251     {
252         checkAttrPresent(parentElem,*i);
253     }
254 
255     // check all required elements are present
256     std::set<string> elems = m_schema.RequiredElements(parentString,parentElem->Row());
257     for(std::set<string>::const_iterator i= elems.begin(); i != elems.end(); i++)
258     {
259         checkElemPresent(parentElem,*i);
260     }
261 
262     // check all attributes present are allowed
263     for(TiXmlAttribute * attr = parentElem->FirstAttribute(); attr != NULL; attr = attr->Next())
264     {
265         string attrName(attr->Name());
266         checkAttrAllowed(parentElem,attrName);
267     }
268 
269     // check all elements present are allowed and in right number
270     TiXmlNode * child = NULL;
271     while ((child = parentElem->IterateChildren(child)))
272     {
273         if(child->Type() == TiXmlNode::TINYXML_ELEMENT)
274         {
275             TiXmlElement * newElem = child->ToElement();
276             string childString(newElem->Value());
277             if(m_schema.DeprecatedElement(parentString,childString,newElem->Row()))
278             {
279                 m_frontEndWarnings.AddWarning(xmlstr::XML_WARN_DEPRECATED_TAG_0
280                                               + childString
281                                               + xmlstr::XML_WARN_DEPRECATED_TAG_1
282                                               + parentString
283                                               + xmlstr::XML_WARN_DEPRECATED_TAG_2
284                                               + ToString(newElem->Row()));
285             }
286             else
287             {
288                 checkElemCount(parentElem,string(childString));
289                 checkSchema(newElem); // recursive check
290             }
291         }
292     }
293 }
294 
295 //____________________________________________________________________________________
296