1 /*****************************************************************************\
3 * Name : xml_generator *
4 * Author : Chris Koeritz *
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 \*****************************************************************************/
15 #include "parser_bits.h"
16 #include "string_manipulation.h"
17 #include "xml_generator.h"
19 #include <basis/astring.h>
20 #include <structures/stack.h>
21 #include <structures/string_table.h>
23 using namespace basis;
24 using namespace structures;
29 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s);
37 string_table _attribs;
41 tag_info(const astring &tag_name, const string_table &attribs)
42 : _tag_name(tag_name), _attribs(attribs) {}
47 class tag_stack : public stack<tag_info>
50 tag_stack() : stack<tag_info>(0) {}
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),
64 xml_generator::~xml_generator()
70 const char *xml_generator::outcome_name(const outcome &to_name)
72 switch (to_name.value()) {
73 case ERRONEOUS_TAG: return "ERRONEOUS_TAG";
74 default: return common::outcome_name(to_name);
78 void xml_generator::set_indentation(int to_indent)
80 if (to_indent <= 0) to_indent = 1;
81 _indentation = to_indent;
84 void xml_generator::reset()
86 _accumulator->reset();
87 // we need a reset on stack.
88 while (_tags->pop() == common::OKAY) {}
91 astring xml_generator::generate()
98 void xml_generator::generate(astring &generated)
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;
106 outcome xml_generator::open_tag(const astring &tag_name)
109 return open_tag(tag_name, junk);
112 outcome xml_generator::add_header(const astring &tag_name,
113 const string_table &attributes)
115 tag_info new_item(tag_name, attributes);
116 print_open_tag(new_item, HEADER_TAG);
120 outcome xml_generator::open_tag(const astring &tag_name, const string_table &attributes)
122 tag_info new_item(tag_name, attributes);
123 print_open_tag(new_item);
124 _tags->push(new_item);
128 outcome xml_generator::close_tag(const astring &tag_name)
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);
138 void xml_generator::close_all_tags()
140 while (_tags->elements()) {
141 close_tag(_tags->top()._tag_name);
145 outcome xml_generator::add_content(const astring &content)
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;
154 *_accumulator += clean_reserved(content);
156 *_accumulator += content;
157 if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
161 void xml_generator::print_open_tag(const tag_info &to_print, int type)
163 bool is_header = false;
164 if (type == HEADER_TAG) is_header = true;
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;
175 *_accumulator += "<?";
177 *_accumulator += "<";
179 *_accumulator += clean_reserved(to_print._tag_name);
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);
186 // flush out badness if we were told to.
187 clean_reserved_mod(name, true); // clean spaces.
188 clean_reserved_mod(content);
190 *_accumulator += " ";
191 *_accumulator += name;
192 *_accumulator += "=\"";
193 *_accumulator += content;
194 *_accumulator += "\"";
197 *_accumulator += "?>";
199 *_accumulator += ">";
200 if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
203 void xml_generator::print_close_tag(const astring &tag_name)
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;
211 *_accumulator += "</";
213 *_accumulator += clean_reserved(tag_name);
215 *_accumulator += tag_name;
216 *_accumulator += ">";
217 if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
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; \
226 void xml_generator::clean_reserved_mod(astring &to_modify,
229 //could this set live somewhere?
230 const char *quot = """;
231 const char *amp = "&";
232 const char *lt = "<";
233 const char *gt = ">";
234 const char *apos = "'";
235 const char *space = "_";
236 // was going to use %20 but that still won't parse in an attribute name.
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;
251 astring xml_generator::clean_reserved(const astring &to_modify,
254 astring to_return = to_modify;
255 clean_reserved_mod(to_return, replace_spaces);