Audacity 3.2.0
XMLFileReader.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 XMLFileReader.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//*******************************************************************/
15
16#include "XMLFileReader.h"
17
18#include <wx/defs.h>
19#include <wx/ffile.h>
20#include <wx/log.h>
21
22#include <string.h>
23
24#include "expat.h"
25
27{
28 mParser = XML_ParserCreate(NULL);
29 XML_SetUserData(mParser, (void *)this);
30 XML_SetElementHandler(mParser, startElement, endElement);
31 XML_SetCharacterDataHandler(mParser, charHandler);
32 mBaseHandler = NULL;
33 mHandler.reserve(128);
34}
35
37{
38 XML_ParserFree(mParser);
39}
40
42 const FilePath &fname)
43{
44 wxFFile theXMLFile(fname, wxT("rb"));
45 if (!theXMLFile.IsOpened()) {
46 mErrorStr = XO("Could not open file: \"%s\"").Format( fname );
47 return false;
48 }
49
50 mBaseHandler = baseHandler;
51
52 const size_t bufferSize = 16384;
53 char buffer[16384];
54 int done = 0;
55 do {
56 size_t len = fread(buffer, 1, bufferSize, theXMLFile.fp());
57 done = (len < bufferSize);
58 if (!XML_Parse(mParser, buffer, len, done)) {
59
60 // Embedded error string from expat doesn't translate (yet)
61 // We could make a table of XOs if we wanted so that it could
62 // If we do, uncomment the second constructor argument so it's not
63 // a verbatim string
65 XML_ErrorString(XML_GetErrorCode(mParser)) // , {}
66 );
67
68 mErrorStr = XO("Error: %s at line %lu").Format(
70 (long unsigned int)XML_GetCurrentLineNumber(mParser)
71 );
72
73 theXMLFile.Close();
74 return false;
75
76// If we did want to handle every single parse error, these are they....
77/*
78 XML_L("out of memory"),
79 XML_L("syntax error"),
80 XML_L("no element found"),
81 XML_L("not well-formed (invalid token)"),
82 XML_L("unclosed token"),
83 XML_L("partial character"),
84 XML_L("mismatched tag"),
85 XML_L("duplicate attribute"),
86 XML_L("junk after document element"),
87 XML_L("illegal parameter entity reference"),
88 XML_L("undefined entity"),
89 XML_L("recursive entity reference"),
90 XML_L("asynchronous entity"),
91 XML_L("reference to invalid character number"),
92 XML_L("reference to binary entity"),
93 XML_L("reference to external entity in attribute"),
94 XML_L("XML or text declaration not at start of entity"),
95 XML_L("unknown encoding"),
96 XML_L("encoding specified in XML declaration is incorrect"),
97 XML_L("unclosed CDATA section"),
98 XML_L("error in processing external entity reference"),
99 XML_L("document is not standalone"),
100 XML_L("unexpected parser state - please send a bug report"),
101 XML_L("entity declared in parameter entity"),
102 XML_L("requested feature requires XML_DTD support in Expat"),
103 XML_L("cannot change setting once parsing has begun"),
104 XML_L("unbound prefix"),
105 XML_L("must not undeclare prefix"),
106 XML_L("incomplete markup in parameter entity"),
107 XML_L("XML declaration not well-formed"),
108 XML_L("text declaration not well-formed"),
109 XML_L("illegal character(s) in public id"),
110 XML_L("parser suspended"),
111 XML_L("parser not suspended"),
112 XML_L("parsing aborted"),
113 XML_L("parsing finished"),
114 XML_L("cannot suspend in external parameter entity"),
115 XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"),
116 XML_L("reserved prefix (xmlns) must not be declared or undeclared"),
117 XML_L("prefix must not be bound to one of the reserved namespace names")
118*/
119 }
120 } while (!done);
121
122 theXMLFile.Close();
123
124 // Even though there were no parse errors, we only succeed if
125 // the first-level handler actually got called, and didn't
126 // return false.
127 if (mBaseHandler)
128 return true;
129 else {
130 mErrorStr = XO("Could not load file: \"%s\"").Format( fname );
131 return false;
132 }
133}
134
136 const wxString &xmldata)
137{
138 auto utf8 = xmldata.ToUTF8();
139 const char *buffer = utf8.data();
140 int len = utf8.length();
141
142 mBaseHandler = baseHandler;
143
144 if (!ParseBuffer(baseHandler, utf8.data(), utf8.length(), true))
145 return false;
146
147 // Even though there were no parse errors, we only succeed if
148 // the first-level handler actually got called, and didn't
149 // return false.
150 if (!mBaseHandler)
151 {
152 mErrorStr = XO("Could not parse XML");
153 return false;
154 }
155
156 return true;
157}
158
160 XMLTagHandler* baseHandler, const MemoryStream& xmldata)
161{
162 mBaseHandler = baseHandler;
163
164 for (auto chunk : xmldata)
165 {
166 if (!ParseBuffer(baseHandler, static_cast<const char*>(chunk.first), chunk.second, false))
167 return false;
168 }
169
170 if (!ParseBuffer(baseHandler, nullptr, 0, true))
171 return false;
172
173 if (!mBaseHandler)
174 {
175 mErrorStr = XO("Could not parse XML");
176 return false;
177 }
178
179 return true;
180}
181
183{
184 return mErrorStr;
185}
186
188{
189 return mLibraryErrorStr;
190}
191
192// static
193void XMLFileReader::startElement(void *userData, const char *name,
194 const char **atts)
195{
196 XMLFileReader *This = (XMLFileReader *)userData;
197 Handlers &handlers = This->mHandler;
198
199 if (handlers.empty()) {
200 handlers.push_back(This->mBaseHandler);
201 }
202 else {
203 if (XMLTagHandler *const handler = handlers.back())
204 handlers.push_back(handler->ReadXMLChild(name));
205 else
206 handlers.push_back(NULL);
207 }
208
209 if (XMLTagHandler *& handler = handlers.back()) {
210 This->mCurrentTagAttributes.clear();
211
212 while(*atts)
213 {
214 const char* name = *atts++;
215 const char* value = *atts++;
216
217 This->mCurrentTagAttributes.emplace_back(
218 std::string_view(name), XMLAttributeValueView(std::string_view(value)));
219 }
220
221 if (!handler->HandleXMLTag(name, This->mCurrentTagAttributes)) {
222 handler = nullptr;
223 if (handlers.size() == 1)
224 This->mBaseHandler = nullptr;
225 }
226 }
227}
228
229// static
230void XMLFileReader::endElement(void *userData, const char *name)
231{
232 XMLFileReader *This = (XMLFileReader *)userData;
233 Handlers &handlers = This->mHandler;
234
235 if (XMLTagHandler *const handler = handlers.back())
236 handler->ReadXMLEndTag(name);
237
238 handlers.pop_back();
239}
240
241// static
242void XMLFileReader::charHandler(void *userData, const char *s, int len)
243{
244 XMLFileReader *This = (XMLFileReader *)userData;
245 Handlers &handlers = This->mHandler;
246
247 if (XMLTagHandler *const handler = handlers.back())
248 handler->ReadXMLContent(s, len);
249}
250
252 XMLTagHandler* baseHandler, const char* buffer, size_t len, bool isFinal)
253{
254 if (!XML_Parse(mParser, buffer, len, isFinal))
255 {
256
257 // Embedded error string from expat doesn't translate (yet)
258 // We could make a table of XOs if we wanted so that it could
259 // If we do, uncomment the second constructor argument so it's not
260 // a verbatim string
262 Verbatim(XML_ErrorString(XML_GetErrorCode(mParser)) // , {}
263 );
264
265 mErrorStr = XO("Error: %s at line %lu")
266 .Format(
268 (long unsigned int)XML_GetCurrentLineNumber(mParser));
269
270 wxLogMessage(
271 wxT("ParseString error: %s\n===begin===%s\n===end==="),
272 mErrorStr.Debug(), buffer);
273
274 return false;
275 }
276
277 return true;
278}
wxT("CloseDown"))
XO("Cut/Copy/Paste")
wxString FilePath
Definition: Project.h:21
wxString name
Definition: TagsEditor.cpp:166
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
A low overhead memory stream with O(1) append, low heap fragmentation and a linear memory view.
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Debug() const
Format as an English string for debugging logs and developers' eyes, not for end users.
A view into an attribute value. The class does not take the ownership of the data.
Reads a file and passes the results through an XMLTagHandler.
Definition: XMLFileReader.h:19
bool ParseString(XMLTagHandler *baseHandler, const wxString &xmldata)
const TranslatableString & GetErrorStr() const
static void startElement(void *userData, const char *name, const char **atts)
bool ParseMemoryStream(XMLTagHandler *baseHandler, const MemoryStream &xmldata)
bool Parse(XMLTagHandler *baseHandler, const FilePath &fname)
const TranslatableString & GetLibraryErrorStr() const
static void endElement(void *userData, const char *name)
std::vector< XMLTagHandler * > Handlers
Definition: XMLFileReader.h:49
Handlers mHandler
Definition: XMLFileReader.h:50
TranslatableString mErrorStr
Definition: XMLFileReader.h:51
XML_Parser mParser
Definition: XMLFileReader.h:47
AttributesList mCurrentTagAttributes
Definition: XMLFileReader.h:55
XMLTagHandler * mBaseHandler
Definition: XMLFileReader.h:48
static void charHandler(void *userData, const char *s, int len)
bool ParseBuffer(XMLTagHandler *baseHandler, const char *buffer, size_t len, bool isFinal)
TranslatableString mLibraryErrorStr
Definition: XMLFileReader.h:52
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:42