Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RelaxNG.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "RelaxNG.h"
21 
22 #include "lib/timer.h"
23 #include "lib/utf8.h"
24 #include "ps/CLogger.h"
25 
26 #include <libxml/relaxng.h>
27 #include <map>
28 
29 TIMER_ADD_CLIENT(xml_validation);
30 
31 /*
32  * libxml2 leaks memory when parsing schemas: https://bugzilla.gnome.org/show_bug.cgi?id=615767
33  * To minimise that problem, keep a global cache of parsed schemas, so we don't
34  * leak an indefinitely large amount of memory when repeatedly restarting the simulation.
35  */
36 
38 {
39 public:
41 
42  RelaxNGSchema(const std::string& grammar)
43  {
44  xmlRelaxNGParserCtxtPtr ctxt = xmlRelaxNGNewMemParserCtxt(grammar.c_str(), (int)grammar.size());
45  m_Schema = xmlRelaxNGParse(ctxt);
46  xmlRelaxNGFreeParserCtxt(ctxt);
47 
48  if (m_Schema == NULL)
49  LOGERROR(L"RelaxNGValidator: Failed to compile schema");
50  }
51 
53  {
54  if (m_Schema)
55  xmlRelaxNGFree(m_Schema);
56  }
57 };
58 
59 static std::map<std::string, shared_ptr<RelaxNGSchema> > g_SchemaCache;
61 
63  m_Schema(NULL)
64 {
65 }
66 
68 {
69 }
70 
71 bool RelaxNGValidator::LoadGrammar(const std::string& grammar)
72 {
73  TIMER_ACCRUE(xml_validation);
74 
75  shared_ptr<RelaxNGSchema> schema;
76 
77  {
78  CScopeLock lock(g_SchemaCacheLock);
79  std::map<std::string, shared_ptr<RelaxNGSchema> >::iterator it = g_SchemaCache.find(grammar);
80  if (it == g_SchemaCache.end())
81  {
82  schema = shared_ptr<RelaxNGSchema>(new RelaxNGSchema(grammar));
83  g_SchemaCache[grammar] = schema;
84  }
85  else
86  {
87  schema = it->second;
88  }
89  }
90 
91  m_Schema = schema->m_Schema;
92 
93  if (!m_Schema)
94  return false;
95  return true;
96 }
97 
98 bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document)
99 {
100  std::string docutf8 = "<?xml version='1.0' encoding='utf-8'?>" + utf8_from_wstring(document);
101 
102  return ValidateEncoded(filename, docutf8);
103 }
104 
105 bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document)
106 {
107  TIMER_ACCRUE(xml_validation);
108 
109  if (!m_Schema)
110  {
111  LOGERROR(L"RelaxNGValidator: No grammar loaded");
112  return false;
113  }
114 
115  xmlDocPtr doc = xmlReadMemory(document.c_str(), (int)document.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
116  if (doc == NULL)
117  {
118  LOGERROR(L"RelaxNGValidator: Failed to parse document");
119  return false;
120  }
121 
122  xmlRelaxNGValidCtxtPtr ctxt = xmlRelaxNGNewValidCtxt(m_Schema);
123  int ret = xmlRelaxNGValidateDoc(ctxt, doc);
124  xmlRelaxNGFreeValidCtxt(ctxt);
125  xmlFreeDoc(doc);
126 
127  if (ret == 0)
128  {
129  return true;
130  }
131  else if (ret > 0)
132  {
133  LOGERROR(L"RelaxNGValidator: Validation failed");
134  return false;
135  }
136  else
137  {
138  LOGERROR(L"RelaxNGValidator: Internal error %d", ret);
139  return false;
140  }
141 }
#define LOGERROR
Definition: CLogger.h:35
std::string utf8_from_wstring(const std::wstring &src, Status *err)
opposite of wstring_from_utf8
Definition: utf8.cpp:208
Locks a CMutex over this object&#39;s lifetime.
Definition: ThreadUtil.h:73
xmlRelaxNGPtr m_Schema
Definition: RelaxNG.cpp:40
xmlDoc * xmlDocPtr
Definition: Xeromyces.h:40
static std::map< std::string, shared_ptr< RelaxNGSchema > > g_SchemaCache
Definition: RelaxNG.cpp:59
A non-recursive mutual exclusion lock.
Definition: ThreadUtil.h:45
#define TIMER_ACCRUE(client)
Measure the time taken to execute code up until end of the current scope; bill it to the given TimerC...
Definition: timer.h:389
xmlRelaxNGPtr m_Schema
Definition: RelaxNG.h:37
RelaxNGSchema(const std::string &grammar)
Definition: RelaxNG.cpp:42
bool Validate(const std::wstring &filename, const std::wstring &document)
Definition: RelaxNG.cpp:98
xmlRelaxNG * xmlRelaxNGPtr
Definition: RelaxNG.h:22
static CMutex g_SchemaCacheLock
Definition: RelaxNG.cpp:60
#define TIMER_ADD_CLIENT(id)
&quot;allocate&quot; a new TimerClient that will keep track of the total time billed to it, along with a descri...
Definition: timer.h:308
bool LoadGrammar(const std::string &grammar)
Definition: RelaxNG.cpp:71
bool ValidateEncoded(const std::wstring &filename, const std::string &document)
Definition: RelaxNG.cpp:105