libupnpp  0.16.0
A C++ wrapper for the Portable UPnP reference library
expatmm.hxx
1 /*
2  * ExpatMM - C++ Wrapper for Expat available at http://expat.sourceforge.net/
3  * Copyright (c) 2006, 2007, 2008, 2009 IntelliTree Solutions llc
4  * Author: Coleman Kane <ckane@intellitree.com>
5  *
6  * Mutilated and forced into single-file solution by <jf@dockes.org>
7  * Copyright (c) 2013-2018 J.F. Dockes
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General
20  * Public License along with this library; if not, please feel free
21  * to contact the author listed above.
22  */
23 #ifndef _EXPATMM_EXPATXMLPARSER_H
24 #define _EXPATMM_EXPATXMLPARSER_H
25 
26 #include <string.h>
27 #include <expat.h>
28 #include <sstream>
29 #include <string>
30 #include <map>
31 #include <vector>
32 
33 #ifdef _MSC_VER
34 #define EXPATMM_SSIZE_T int
35 #else
36 #define EXPATMM_SSIZE_T ssize_t
37 #endif
38 
40 public:
41 
42  /* Create a new parser, using the default Chunk Size */
43  ExpatXMLParser(void) {
44  init();
45  }
46 
47  /* Create a new parser, using a user-supplied chunk size */
48  ExpatXMLParser(size_t chunk_size) {
49  init(chunk_size);
50  }
51 
52  /* Destructor that cleans up xml_buffer and parser */
53  virtual ~ExpatXMLParser(void) {
54  valid_parser = false;
55  if(expat_parser != NULL) {
56  XML_ParserFree(expat_parser);
57  expat_parser = NULL;
58  }
59  if(xml_buffer != NULL) {
60  delete [] xml_buffer;
61  xml_buffer = NULL;
62  }
63  }
64 
65  /*
66  Generic Parser. Most derivations will simply call this, rather
67  than implement their own. This will loop, processing XML data
68  and calling the necessary handler code until an error is encountered.
69  */
70  virtual bool Parse(void) {
71  /* Ensure that the parser is ready */
72  if(!Ready())
73  return false;
74 
75  EXPATMM_SSIZE_T bytes_read;
76  /* Loop, reading the XML source block by block */
77  while((bytes_read = read_block()) >= 0) {
78  if(bytes_read > 0) {
79  XML_Status local_status =
80  XML_Parse(expat_parser, getReadBuffer(), (int)bytes_read,
81  XML_FALSE);
82 
83  if(local_status != XML_STATUS_OK) {
84  set_status(local_status);
85  break;
86  }
87 
88  /* Break on successful "short read", in event of EOF */
89  if(getLastError() == XML_ERROR_FINISHED)
90  break;
91  }
92  }
93 
94  /* Finalize the parser */
95  if((getStatus() == XML_STATUS_OK) ||
96  (getLastError() == XML_ERROR_FINISHED)) {
97  XML_Status local_status =
98  XML_Parse(expat_parser, getBuffer(), 0, XML_TRUE);
99  if(local_status != XML_STATUS_OK) {
100  set_status(local_status);
101  return false;
102  }
103  return true;
104  }
105 
106  /* Return false in the event of an error. The parser is
107  not finalized on error. */
108  return false;
109  }
110 
111  /* Expose status, error, and control codes to users */
112  virtual bool Ready(void) const {
113  return valid_parser;
114  }
115  virtual XML_Error getLastError(void) const {
116  return last_error;
117  }
118  virtual XML_Status getStatus(void) const {
119  return status;
120  }
121  virtual XML_Size getLastErrorLine(void) const {
122  return last_error_line;
123  }
124  virtual XML_Size getLastErrorColumn(void) const {
125  return last_error_column;
126  }
127  virtual std::string getLastErrorMessage(void) const {
128  return last_error_message;
129  }
130 
131 protected:
132  class StackEl {
133  public:
134  StackEl(const char* nm) : name(nm) {}
135  std::string name;
136  XML_Size start_index;
137  std::map<std::string,std::string> attributes;
138  std::string data;
139  };
140  std::vector<StackEl> m_path;
141 
142  virtual XML_Char *getBuffer(void) {
143  return xml_buffer;
144  }
145  virtual const char *getReadBuffer(void) {
146  return xml_buffer;
147  }
148  virtual size_t getBlockSize(void) {
149  return xml_buffer_size;
150  }
151 
152  /* Read XML data.
153  *
154  * Override this to implement your container-specific parser.
155  *
156  * You must:
157  * put new XML data into xml_buffer
158  * set status
159  * set last_error
160  * return the amount of XML_Char's written to xml_buffer
161  *
162  * on error, return < 0. The contents of xml_buffer will be
163  * thrown away in this event, so it is the derived class's
164  * responsibility to reseek the "data cursor" to re-get any
165  * data in the buffer on an error condition.
166  *
167  * Use setReadiness, setStatus, and setLastError to handle
168  * error, status, and control events and codes.
169  *
170  * The default implementation returns "no elements" if it is
171  * ever called. and should be overridden by the derived class.
172  *
173  * Note that, as the actual parser only uses
174  * getBuffer()/getBlockSize()/read_block() (no direct access
175  * to the buffer), you are free to use an entirely different
176  * I/O mechanism, like what does the inputRefXMLParser below.
177  */
178  virtual EXPATMM_SSIZE_T read_block(void) {
179  last_error = XML_ERROR_NO_ELEMENTS;
180  status = XML_STATUS_ERROR;
181  return -1;
182  }
183 
184  virtual void setReadiness(bool ready) {
185  valid_parser = ready;
186  }
187  virtual void setStatus(XML_Status new_status) {
188  status = new_status;
189  }
190  virtual void setLastError(XML_Error new_last_error) {
191  last_error = new_last_error;
192  }
193 
194  /* Methods to be overriden */
195  virtual void StartElement(const XML_Char *, const XML_Char **) {}
196  virtual void EndElement(const XML_Char *) {}
197  virtual void CharacterData(const XML_Char *, int) {}
198  virtual void ProcessingInstruction(const XML_Char *, const XML_Char *) {}
199  virtual void CommentData(const XML_Char *) {}
200  virtual void DefaultHandler(const XML_Char *, int) {}
201  virtual void CDataStart(void) {}
202  virtual void CDataEnd(void) {}
203 
204  /* The handle for the parser (expat) */
205  XML_Parser expat_parser;
206 
207 private:
208 
209  /* Temporary buffer where data is streamed in */
210  XML_Char *xml_buffer;
211  size_t xml_buffer_size;
212 
213  /* Tells if the parser is ready to accept data */
214  bool valid_parser;
215 
216  /* Status and Error codes in the event of unforseen events */
217  void set_status(XML_Status ls) {
218  status = ls;
219  last_error = XML_GetErrorCode(expat_parser);
220  last_error_line = XML_GetCurrentLineNumber(expat_parser);
221  last_error_column= XML_GetCurrentColumnNumber(expat_parser);
222  std::ostringstream oss;
223  oss << XML_ErrorString(last_error) <<
224  " at line " << last_error_line << " column " <<
225  last_error_column;
226  last_error_message = oss.str();
227  }
228 
229  XML_Status status;
230  XML_Error last_error;
231  XML_Size last_error_line{0};
232  XML_Size last_error_column{0};
233  std::string last_error_message;
234 
235  /* Expat callbacks.
236  * The expatmm protocol is to pass (this) as the userData argument
237  * in the XML_Parser structure. These static methods will convert
238  * handlers into upcalls to the instantiated class's virtual members
239  * to do the actual handling work. */
240  static void _element_start_handler(void *userData, const XML_Char *name,
241  const XML_Char **atts) {
242  ExpatXMLParser *me = (ExpatXMLParser*)userData;
243  if(me != NULL) {
244  me->m_path.push_back(StackEl(name));
245  StackEl& lastelt = me->m_path.back();
246  lastelt.start_index = XML_GetCurrentByteIndex(me->expat_parser);
247  for (int i = 0; atts[i] != 0; i += 2) {
248  lastelt.attributes[atts[i]] = atts[i+1];
249  }
250  me->StartElement(name, atts);
251  }
252  }
253  static void _element_end_handler(void *userData, const XML_Char *name) {
254  ExpatXMLParser *me = (ExpatXMLParser*)userData;
255  if(me != NULL) {
256  me->EndElement(name);
257  me->m_path.pop_back();
258  }
259  }
260  static void _character_data_handler(void *userData,
261  const XML_Char *s, int len) {
262  ExpatXMLParser *me = (ExpatXMLParser*)userData;
263  if(me != NULL) me->CharacterData(s, len);
264  }
265  static void _processing_instr_handler(void *userData,
266  const XML_Char *target,
267  const XML_Char *data) {
268  ExpatXMLParser *me = (ExpatXMLParser*)userData;
269  if(me != NULL) me->ProcessingInstruction(target, data);
270  }
271  static void _comment_handler(void *userData, const XML_Char *data) {
272  ExpatXMLParser *me = (ExpatXMLParser*)userData;
273  if(me != NULL) me->CommentData(data);
274  }
275  static void _default_handler(void *userData, const XML_Char *s, int len) {
276  ExpatXMLParser *me = (ExpatXMLParser*)userData;
277  if(me != NULL) me->DefaultHandler(s, len);
278  }
279  static void _cdata_start_handler(void *userData) {
280  ExpatXMLParser *me = (ExpatXMLParser*)userData;
281  if(me != NULL) me->CDataStart();
282  }
283  static void _cdata_end_handler(void *userData) {
284  ExpatXMLParser *me = (ExpatXMLParser*)userData;
285  if(me != NULL) me->CDataEnd();
286  }
287  /* Register our static handlers with the Expat events. */
288  void register_default_handlers() {
289  XML_SetElementHandler(expat_parser, &_element_start_handler,
290  &_element_end_handler);
291  XML_SetCharacterDataHandler(expat_parser, &_character_data_handler);
292  XML_SetProcessingInstructionHandler(expat_parser,
293  &_processing_instr_handler);
294  XML_SetCommentHandler(expat_parser, &_comment_handler);
295  XML_SetCdataSectionHandler(expat_parser, &_cdata_start_handler,
296  &_cdata_end_handler);
297  XML_SetDefaultHandler(expat_parser, &_default_handler);
298  }
299  /* Constructor common code */
300  void init(size_t chunk_size = 0) {
301  valid_parser = false;
302  expat_parser = NULL;
303  xml_buffer_size = chunk_size ? chunk_size : 10240;
304  xml_buffer = new XML_Char[xml_buffer_size];
305  if(xml_buffer == NULL)
306  return;
307  expat_parser = XML_ParserCreate(NULL);
308 
309  if(expat_parser == NULL) {
310  delete [] xml_buffer;
311  xml_buffer = NULL;
312  return;
313  }
314  status = XML_STATUS_OK;
315  last_error = XML_ERROR_NONE;
316 
317  memset(xml_buffer, 0, chunk_size * sizeof(XML_Char));
318 
319  /* Set the "ready" flag on this parser */
320  valid_parser = true;
321  XML_SetUserData(expat_parser, (void*)this);
322  register_default_handlers();
323  }
324 };
325 
328 public:
329  // Beware: we only use a ref to input to minimize copying. This means
330  // that storage for the input parameter must persist until you are done
331  // with the parser object !
332  inputRefXMLParser(const std::string& input)
333  : ExpatXMLParser(1), // Have to allocate a small buf even if not used.
334  m_input(input) {
335  }
336 
337 protected:
338  EXPATMM_SSIZE_T read_block(void) {
339  if (getLastError() == XML_ERROR_FINISHED) {
340  setStatus(XML_STATUS_OK);
341  return -1;
342  }
343  setLastError(XML_ERROR_FINISHED);
344  return m_input.size();
345  }
346  const char *getReadBuffer() {
347  return m_input.c_str();
348  }
349  virtual size_t getBlockSize(void) {
350  return m_input.size();
351  }
352 protected:
353  const std::string& m_input;
354 };
355 
356 #endif /* _EXPATMM_EXPATXMLPARSER_H */
Definition: expatmm.hxx:39
A specialization of ExpatXMLParser that does not copy its input.
Definition: expatmm.hxx:327
Definition: expatmm.hxx:132