feisty meow concerns codebase  2.140
xml_generator.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : xml_generator *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 2007-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "parser_bits.h"
16 #include "string_manipulation.h"
17 #include "xml_generator.h"
18 
19 #include <basis/astring.h>
20 #include <structures/stack.h>
22 
23 using namespace basis;
24 using namespace structures;
25 
26 namespace textual {
27 
28 #undef LOG
29 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s);
30 
32 
33 class tag_info
34 {
35 public:
36  astring _tag_name;
37  string_table _attribs;
38 
39  tag_info() {}
40 
41  tag_info(const astring &tag_name, const string_table &attribs)
42  : _tag_name(tag_name), _attribs(attribs) {}
43 };
44 
46 
47 class tag_stack : public stack<tag_info>
48 {
49 public:
50  tag_stack() : stack<tag_info>(0) {}
51 };
52 
54 
55 xml_generator::xml_generator(int mods)
56 : _tags(new tag_stack),
57  _accumulator(new astring),
58  _human_read(mods & HUMAN_READABLE),
59  _clean_chars(mods & CLEAN_ILLEGAL_CHARS),
60  _indentation(2)
61 {
62 }
63 
65 {
66  WHACK(_tags);
67  WHACK(_accumulator);
68 }
69 
70 const char *xml_generator::outcome_name(const outcome &to_name)
71 {
72  switch (to_name.value()) {
73  case ERRONEOUS_TAG: return "ERRONEOUS_TAG";
74  default: return common::outcome_name(to_name);
75  }
76 }
77 
78 void xml_generator::set_indentation(int to_indent)
79 {
80  if (to_indent <= 0) to_indent = 1;
81  _indentation = to_indent;
82 }
83 
85 {
86  _accumulator->reset();
87 // we need a reset on stack.
88  while (_tags->pop() == common::OKAY) {}
89 }
90 
92 {
93  astring to_return;
94  generate(to_return);
95  return to_return;
96 }
97 
99 {
100  close_all_tags();
101  generated = "<?xml version=\"1.0\"?>"; // first string is the version.
102  if (_human_read) generated += parser_bits::platform_eol_to_chars();
103  generated += *_accumulator;
104 }
105 
107 {
108  string_table junk;
109  return open_tag(tag_name, junk);
110 }
111 
113  const string_table &attributes)
114 {
115  tag_info new_item(tag_name, attributes);
116  print_open_tag(new_item, HEADER_TAG);
117  return OKAY;
118 }
119 
120 outcome xml_generator::open_tag(const astring &tag_name, const string_table &attributes)
121 {
122  tag_info new_item(tag_name, attributes);
123  print_open_tag(new_item);
124  _tags->push(new_item);
125  return OKAY;
126 }
127 
129 {
130  if (_tags->elements() < 1) return NOT_FOUND;
131  // check to see that it's the right one to close.
132  if (_tags->top()._tag_name != tag_name) return ERRONEOUS_TAG;
133  print_close_tag(tag_name);
134  _tags->pop();
135  return OKAY;
136 }
137 
139 {
140  while (_tags->elements()) {
141  close_tag(_tags->top()._tag_name);
142  }
143 }
144 
146 {
147  if (_human_read) {
148  astring indentata = string_manipulation::indentation(_indentation);
149  int num_indents = _tags->elements();
150  for (int i = 0; i < num_indents; i++)
151  *_accumulator += indentata;
152  }
153  if (_clean_chars)
154  *_accumulator += clean_reserved(content);
155  else
156  *_accumulator += content;
157  if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
158  return OKAY;
159 }
160 
161 void xml_generator::print_open_tag(const tag_info &to_print, int type)
162 {
163  bool is_header = false;
164  if (type == HEADER_TAG) is_header = true;
165 
166  if (_human_read) {
167 //hmmm: starting to look like a nice macro for this stuff, param is num levs.
168  astring indentata = string_manipulation::indentation(_indentation);
169  int num_indents = _tags->elements();
170  for (int i = 0; i < num_indents; i++)
171  *_accumulator += indentata;
172  }
173 
174  if (is_header)
175  *_accumulator += "<?";
176  else
177  *_accumulator += "<";
178  if (_clean_chars)
179  *_accumulator += clean_reserved(to_print._tag_name);
180  else
181  *_accumulator += to_print._tag_name;
182  for (int i = 0; i < to_print._attribs.symbols(); i++) {
183  astring name, content;
184  to_print._attribs.retrieve(i, name, content);
185  if (_clean_chars) {
186  // flush out badness if we were told to.
187  clean_reserved_mod(name, true); // clean spaces.
188  clean_reserved_mod(content);
189  }
190  *_accumulator += " ";
191  *_accumulator += name;
192  *_accumulator += "=\"";
193  *_accumulator += content;
194  *_accumulator += "\"";
195  }
196  if (is_header)
197  *_accumulator += "?>";
198  else
199  *_accumulator += ">";
200  if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
201 }
202 
203 void xml_generator::print_close_tag(const astring &tag_name)
204 {
205  if (_human_read) {
206  astring indentata = string_manipulation::indentation(_indentation);
207  int num_indents = _tags->elements() - 1;
208  for (int i = 0; i < num_indents; i++)
209  *_accumulator += indentata;
210  }
211  *_accumulator += "</";
212  if (_clean_chars)
213  *_accumulator += clean_reserved(tag_name);
214  else
215  *_accumulator += tag_name;
216  *_accumulator += ">";
217  if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
218 }
219 
220 #define PLUGIN_REPLACEMENT(posn, repl_string) { \
221  to_modify.zap(posn, posn); \
222  to_modify.insert(posn, repl_string); \
223  posn += int(strlen(repl_string)) - 1; \
224 }
225 
227  bool replace_spaces)
228 {
229 //could this set live somewhere?
230  const char *quot = "&quot;";
231  const char *amp = "&amp;";
232  const char *lt = "&lt;";
233  const char *gt = "&gt;";
234  const char *apos = "&apos;";
235  const char *space = "_";
236  // was going to use %20 but that still won't parse in an attribute name.
237 
238  for (int i = 0; i < to_modify.length(); i++) {
239  switch (to_modify[i]) {
240  case '"': PLUGIN_REPLACEMENT(i, quot); break;
241  case '&': PLUGIN_REPLACEMENT(i, amp); break;
242  case '<': PLUGIN_REPLACEMENT(i, lt); break;
243  case '>': PLUGIN_REPLACEMENT(i, gt); break;
244  case '\'': PLUGIN_REPLACEMENT(i, apos); break;
245  case ' ': if (replace_spaces) PLUGIN_REPLACEMENT(i, space); break;
246  default: continue;
247  }
248  }
249 }
250 
252  bool replace_spaces)
253 {
254  astring to_return = to_modify;
255  clean_reserved_mod(to_return, replace_spaces);
256  return to_return;
257 }
258 
259 } //namespace.
260 
261 
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
void reset()
clears out the contents string.
Definition: astring.h:202
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
int value() const
Definition: outcome.h:51
An abstraction that represents a stack data structure.
Definition: stack.h:30
Provides a symbol_table that holds strings as the content.
Definition: string_table.h:32
static const char * platform_eol_to_chars()
provides the characters that make up this platform's line ending.
Definition: parser_bits.cpp:59
static basis::astring indentation(int spaces)
Returns a string made of white space that is "spaces" long.
basis::outcome add_content(const basis::astring &content)
stores content into the currently opened tag.
static basis::astring clean_reserved(const basis::astring &to_modify, bool replace_spaces=false)
returns a cleaned version of "to_modify" to make it XML appropriate.
static const char * outcome_name(const basis::outcome &to_name)
reports the string version of "to_name".
static void clean_reserved_mod(basis::astring &to_modify, bool replace_spaces=false)
ensures that "to_modify" contains only characters valid for XML.
basis::outcome close_tag(const basis::astring &tag_name)
closes a previously added "tag_name".
basis::outcome open_tag(const basis::astring &tag_name, const structures::string_table &attributes)
adds a tag with "tag_name" and the "attributes", if any.
basis::astring generate()
writes the current state into a string and returns it.
basis::outcome add_header(const basis::astring &tag_name, const structures::string_table &attributes)
adds an xml style header with the "tag_name" and "attributes".
void close_all_tags()
a wide-bore method that closes all outstanding tags.
void reset()
throws out all accumulated information.
void set_indentation(int to_indent)
sets the number of spaces to indent for the human readable form.
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#define PLUGIN_REPLACEMENT(posn, repl_string)