xml_generator.cpp

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

Generated on Sat Aug 30 04:31:58 2008 for HOOPLE Libraries by  doxygen 1.5.1