xml_generator.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : xml_generator                                                     *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 2007-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "parser_bits.h"
00016 #include "string_manipulation.h"
00017 #include "xml_generator.h"
00018 
00019 #include <basis/astring.h>
00020 #include <structures/stack.h>
00021 #include <structures/string_table.h>
00022 
00023 using namespace basis;
00024 using namespace structures;
00025 
00026 namespace textual {
00027 
00028 #undef LOG
00029 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s);
00030 
00032 
00033 class tag_info
00034 {
00035 public:
00036   astring _tag_name;
00037   string_table _attribs;
00038 
00039   tag_info() {}
00040 
00041   tag_info(const astring &tag_name, const string_table &attribs)
00042       : _tag_name(tag_name), _attribs(attribs) {}
00043 };
00044 
00046 
00047 class tag_stack : public stack<tag_info>
00048 {
00049 public:
00050   tag_stack() : stack<tag_info>(0) {}
00051 };
00052 
00054 
00055 xml_generator::xml_generator(int mods)
00056 : _tags(new tag_stack),
00057   _accumulator(new astring),
00058   _human_read(mods & HUMAN_READABLE),
00059   _clean_chars(mods & CLEAN_ILLEGAL_CHARS),
00060   _indentation(2)
00061 {
00062 }
00063 
00064 xml_generator::~xml_generator()
00065 {
00066   WHACK(_tags);
00067   WHACK(_accumulator);
00068 }
00069 
00070 const char *xml_generator::outcome_name(const outcome &to_name)
00071 {
00072   switch (to_name.value()) {
00073     case ERRONEOUS_TAG: return "ERRONEOUS_TAG";
00074     default: return common::outcome_name(to_name);
00075   }
00076 }
00077 
00078 void xml_generator::set_indentation(int to_indent)
00079 {
00080   if (to_indent <= 0) to_indent = 1;
00081   _indentation = to_indent;
00082 }
00083 
00084 void xml_generator::reset()
00085 {
00086   _accumulator->reset();
00087 // we need a reset on stack.
00088   while (_tags->pop() == common::OKAY) {}
00089 }
00090 
00091 astring xml_generator::generate()
00092 {
00093   astring to_return;
00094   generate(to_return);
00095   return to_return;
00096 }
00097 
00098 void xml_generator::generate(astring &generated)
00099 {
00100   close_all_tags();
00101   generated = "<?xml version=\"1.0\"?>";  // first string is the version.
00102   if (_human_read) generated += parser_bits::platform_eol_to_chars();
00103   generated += *_accumulator;
00104 }
00105 
00106 outcome xml_generator::open_tag(const astring &tag_name)
00107 {
00108   string_table junk;
00109   return open_tag(tag_name, junk);
00110 }
00111 
00112 outcome xml_generator::add_header(const astring &tag_name,
00113     const string_table &attributes)
00114 {
00115   tag_info new_item(tag_name, attributes);
00116   print_open_tag(new_item, HEADER_TAG);
00117   return OKAY;
00118 }
00119 
00120 outcome xml_generator::open_tag(const astring &tag_name, const string_table &attributes)
00121 {
00122   tag_info new_item(tag_name, attributes);
00123   print_open_tag(new_item);
00124   _tags->push(new_item);
00125   return OKAY;
00126 }
00127 
00128 outcome xml_generator::close_tag(const astring &tag_name)
00129 {
00130   if (_tags->elements() < 1) return NOT_FOUND;
00131   // check to see that it's the right one to close.
00132   if (_tags->top()._tag_name != tag_name) return ERRONEOUS_TAG;
00133   print_close_tag(tag_name);
00134   _tags->pop();
00135   return OKAY;
00136 }
00137 
00138 void xml_generator::close_all_tags()
00139 {
00140   while (_tags->elements()) {
00141     close_tag(_tags->top()._tag_name);
00142   }
00143 }
00144 
00145 outcome xml_generator::add_content(const astring &content)
00146 {
00147   if (_human_read) {
00148     astring indentata = string_manipulation::indentation(_indentation);
00149     int num_indents = _tags->elements();
00150     for (int i = 0; i < num_indents; i++) 
00151      *_accumulator += indentata;
00152   }
00153   if (_clean_chars) 
00154     *_accumulator += clean_reserved(content);
00155   else
00156     *_accumulator += content;
00157   if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
00158   return OKAY;
00159 }
00160 
00161 void xml_generator::print_open_tag(const tag_info &to_print, int type)
00162 {
00163   bool is_header = false;
00164   if (type == HEADER_TAG) is_header = true;
00165 
00166   if (_human_read) {
00167 //hmmm: starting to look like a nice macro for this stuff, param is num levs.
00168     astring indentata = string_manipulation::indentation(_indentation);
00169     int num_indents = _tags->elements();
00170     for (int i = 0; i < num_indents; i++) 
00171      *_accumulator += indentata;
00172   }
00173 
00174   if (is_header)
00175     *_accumulator += "<?";
00176   else
00177     *_accumulator += "<";
00178   if (_clean_chars)
00179     *_accumulator += clean_reserved(to_print._tag_name);
00180   else
00181     *_accumulator += to_print._tag_name;
00182   for (int i = 0; i < to_print._attribs.symbols(); i++) {
00183     astring name, content;
00184     to_print._attribs.retrieve(i, name, content);
00185     if (_clean_chars) {
00186       // flush out badness if we were told to.
00187       clean_reserved_mod(name, true);  // clean spaces.
00188       clean_reserved_mod(content);
00189     }
00190     *_accumulator += " ";
00191     *_accumulator += name;
00192     *_accumulator += "=\"";
00193     *_accumulator += content;
00194     *_accumulator += "\"";
00195   }
00196   if (is_header)
00197     *_accumulator += "?>";
00198   else
00199     *_accumulator += ">";
00200   if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
00201 }
00202 
00203 void xml_generator::print_close_tag(const astring &tag_name)
00204 {
00205   if (_human_read) {
00206     astring indentata = string_manipulation::indentation(_indentation);
00207     int num_indents = _tags->elements() - 1;
00208     for (int i = 0; i < num_indents; i++) 
00209      *_accumulator += indentata;
00210   }
00211   *_accumulator += "</";
00212   if (_clean_chars)
00213     *_accumulator += clean_reserved(tag_name);
00214   else
00215     *_accumulator += tag_name;
00216   *_accumulator += ">";
00217   if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars();
00218 }
00219 
00220 #define PLUGIN_REPLACEMENT(posn, repl_string) { \
00221   to_modify.zap(posn, posn); \
00222   to_modify.insert(posn, repl_string); \
00223   posn += int(strlen(repl_string)) - 1; \
00224 }
00225 
00226 void xml_generator::clean_reserved_mod(astring &to_modify,
00227     bool replace_spaces)
00228 {
00229 //could this set live somewhere?
00230   const char *quot = "&quot;";
00231   const char *amp = "&amp;";
00232   const char *lt = "&lt;";
00233   const char *gt = "&gt;";
00234   const char *apos = "&apos;";
00235   const char *space = "_";
00236     // was going to use %20 but that still won't parse in an attribute name.
00237 
00238   for (int i = 0; i < to_modify.length(); i++) {
00239     switch (to_modify[i]) {
00240       case '"': PLUGIN_REPLACEMENT(i, quot); break;
00241       case '&': PLUGIN_REPLACEMENT(i, amp); break;
00242       case '<': PLUGIN_REPLACEMENT(i, lt); break;
00243       case '>': PLUGIN_REPLACEMENT(i, gt); break;
00244       case '\'': PLUGIN_REPLACEMENT(i, apos); break;
00245       case ' ': if (replace_spaces) PLUGIN_REPLACEMENT(i, space); break;
00246       default: continue;
00247     }
00248   }
00249 }
00250 
00251 astring xml_generator::clean_reserved(const astring &to_modify,
00252     bool replace_spaces)
00253 {
00254   astring to_return = to_modify;
00255   clean_reserved_mod(to_return, replace_spaces);
00256   return to_return;
00257 }
00258 
00259 } //namespace.
00260 
00261 
Generated on Sat Jan 28 04:22:33 2012 for hoople2 project by  doxygen 1.6.3