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
23using namespace basis;
24using namespace structures;
25
26namespace textual {
27
28#undef LOG
29#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s);
30
32
33class tag_info
34{
35public:
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
47class tag_stack : public stack<tag_info>
48{
49public:
50 tag_stack() : stack<tag_info>(0) {}
51};
52
54
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
70const 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
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{
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
120outcome 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
161void 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
203void 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
static const char * outcome_name(const outcome &to_name)
Returns a string representation of the outcome "to_name".
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.
static const char * platform_eol_to_chars()
provides the characters that make up this platform's line ending.
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".
xml_generator(int modifiers=HUMAN_READABLE|CLEAN_ILLEGAL_CHARS)
creates an xml generator with the specified behavior.
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)