From c5a707ba7428905432eaeaa7b0756d057efa1393 Mon Sep 17 00:00:00 2001 From: Richard Heck Date: Tue, 7 May 2013 00:19:34 -0400 Subject: [PATCH] Reorganize the TagStack objects. --- src/output_xhtml.cpp | 135 ++++++++++++++++++++++--------------------- src/output_xhtml.h | 27 +++++++-- 2 files changed, 89 insertions(+), 73 deletions(-) diff --git a/src/output_xhtml.cpp b/src/output_xhtml.cpp index b3a23365d1..13e5c19f00 100644 --- a/src/output_xhtml.cpp +++ b/src/output_xhtml.cpp @@ -36,6 +36,9 @@ #include +// Uncomment to activate debugging code. +// #define XHTML_DEBUG + using namespace std; using namespace lyx::support; @@ -43,6 +46,7 @@ namespace lyx { namespace html { + docstring escapeChar(char_type c, XHTMLStream::EscapeSettings e) { docstring str; @@ -196,12 +200,12 @@ docstring CompTag::asTag() const /// //////////////////////////////////////////////////////////////// -XHTMLStream::XHTMLStream(odocstream & os) - : os_(os), escape_(ESCAPE_ALL) +XHTMLStream::XHTMLStream(odocstream & os) + : os_(os), escape_(ESCAPE_ALL) {} -#if 0 +#ifdef XHTML_DEBUG void XHTMLStream::dumpTagStack(string const & msg) const { writeError(msg + ": Tag Stack"); @@ -246,31 +250,29 @@ bool XHTMLStream::closeFontTags() return true; // first, we close any open font tags we can close - html::StartTag curtag = tag_stack_.back(); - while (html::isFontTag(curtag.tag_)) { - os_ << curtag.asEndTag(); + TagPtr curtag = tag_stack_.back(); + while (html::isFontTag(curtag->tag_)) { + os_ << curtag->asEndTag(); tag_stack_.pop_back(); - if (tag_stack_.empty()) - // this probably shouldn't happen, since then the - // font tags weren't in any other tag. but that - // problem will likely be caught elsewhere. - return true; + // this shouldn't happen, since then the font tags + // weren't in any other tag. + LBUFERR(!tag_stack_.empty()); curtag = tag_stack_.back(); } - if (curtag.tag_ == parsep_tag) + if (curtag->tag_ == parsep_tag) return true; // so we've hit a non-font tag. writeError("Tags still open in closeFontTags(). Probably not a problem,\n" "but you might want to check these tags:"); - TagStack::const_reverse_iterator it = tag_stack_.rbegin(); - TagStack::const_reverse_iterator const en = tag_stack_.rend(); + TagDeque::const_reverse_iterator it = tag_stack_.rbegin(); + TagDeque::const_reverse_iterator const en = tag_stack_.rend(); for (; it != en; ++it) { - string const tagname = it->tag_; + string const tagname = (*it)->tag_; if (tagname == parsep_tag) break; - writeError(it->tag_); + writeError((*it)->tag_); } return false; } @@ -278,7 +280,7 @@ bool XHTMLStream::closeFontTags() void XHTMLStream::startParagraph(bool keep_empty) { - pending_tags_.push_back(html::StartTag(parsep_tag)); + pending_tags_.push_back(makeTagPtr(html::StartTag(parsep_tag))); if (keep_empty) clearTagDeque(); } @@ -293,10 +295,9 @@ void XHTMLStream::endParagraph() // clear all pending tags up to and including the parsep tag. // note that we work from the back, because we want to get rid // of everything that hasn't been used. - html::StartTag const cur_tag = pending_tags_.back(); - string const & tag = cur_tag.tag_; + TagPtr const cur_tag = pending_tags_.back(); pending_tags_.pop_back(); - if (tag == parsep_tag) + if (cur_tag->tag_ == parsep_tag) break; } return; @@ -310,13 +311,12 @@ void XHTMLStream::endParagraph() // this case is also normal, if the parsep tag is the last one // on the stack. otherwise, it's an error. while (!tag_stack_.empty()) { - html::StartTag const cur_tag = tag_stack_.back(); - string const & tag = cur_tag.tag_; + TagPtr const cur_tag = tag_stack_.back(); tag_stack_.pop_back(); - if (tag == parsep_tag) + if (cur_tag->tag_ == parsep_tag) break; - writeError("Tag `" + tag + "' still open at end of paragraph. Closing."); - os_ << cur_tag.asEndTag(); + writeError("Tag `" + cur_tag->tag_ + "' still open at end of paragraph. Closing."); + os_ << cur_tag->asEndTag(); } } @@ -324,10 +324,10 @@ void XHTMLStream::endParagraph() void XHTMLStream::clearTagDeque() { while (!pending_tags_.empty()) { - html::StartTag const & tag = pending_tags_.front(); - if (tag.tag_ != parsep_tag) + TagPtr const tag = pending_tags_.front(); + if (tag->tag_ != parsep_tag) // tabs? - os_ << tag.asTag(); + os_ << tag->asTag(); tag_stack_.push_back(tag); pending_tags_.pop_front(); } @@ -391,7 +391,7 @@ XHTMLStream & XHTMLStream::operator<<(html::StartTag const & tag) { if (tag.tag_.empty()) return *this; - pending_tags_.push_back(tag); + pending_tags_.push_back(makeTagPtr(tag)); if (tag.keepempty_) clearTagDeque(); return *this; @@ -403,7 +403,6 @@ XHTMLStream & XHTMLStream::operator<<(html::CompTag const & tag) if (tag.tag_.empty()) return *this; clearTagDeque(); - // tabs? os_ << tag.asTag(); *this << html::CR(); return *this; @@ -420,10 +419,10 @@ XHTMLStream & XHTMLStream::operator<<(html::CR const &) bool XHTMLStream::isTagOpen(string const & stag) const { - TagStack::const_iterator sit = tag_stack_.begin(); - TagStack::const_iterator const sen = tag_stack_.end(); + TagDeque::const_iterator sit = tag_stack_.begin(); + TagDeque::const_iterator const sen = tag_stack_.end(); for (; sit != sen; ++sit) - if (sit->tag_ == stag) + if ((*sit)->tag_ == stag) return true; return false; } @@ -431,10 +430,10 @@ bool XHTMLStream::isTagOpen(string const & stag) const bool XHTMLStream::isTagPending(string const & stag) const { - TagStack::const_iterator sit = pending_tags_.begin(); - TagStack::const_iterator const sen = pending_tags_.end(); + TagDeque::const_iterator sit = pending_tags_.begin(); + TagDeque::const_iterator const sen = pending_tags_.end(); for (; sit != sen; ++sit) - if (sit->tag_ == stag) + if ((*sit)->tag_ == stag) return true; return false; } @@ -450,34 +449,29 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag) if (etag.tag_.empty()) return *this; - // make sure there are tags to be closed - if (tag_stack_.empty()) { - writeError("Tried to close `" + etag.tag_ - + "' when no tags were open!"); - return *this; - } - - // first make sure we're not closing an empty tag + // if this tag is pending, we can simply discard it. if (!pending_tags_.empty()) { - html::StartTag const & stag = pending_tags_.back(); - if (etag.tag_ == stag.tag_) { + + if (etag.tag_ == pending_tags_.back()->tag_) { // we have , so we discard it and remove it // from the pending_tags_. pending_tags_.pop_back(); return *this; } + // there is a pending tag that isn't the one we are trying // to close. + // is this tag itself pending? // non-const iterators because we may call erase(). - TagStack::iterator dit = pending_tags_.begin(); - TagStack::iterator const den = pending_tags_.end(); + TagDeque::iterator dit = pending_tags_.begin(); + TagDeque::iterator const den = pending_tags_.end(); for (; dit != den; ++dit) { - if (dit->tag_ == etag.tag_) { + if ((*dit)->tag_ == etag.tag_) { // it was pending, so we just erase it writeError("Tried to close pending tag `" + etag.tag_ + "' when other tags were pending. Last pending tag is `" - + pending_tags_.back().tag_ + "'. Tag discarded."); + + pending_tags_.back()->tag_ + "'. Tag discarded."); pending_tags_.erase(dit); return *this; } @@ -494,15 +488,22 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag) string estr = "Closing tag `" + etag.tag_ + "' when other tags are pending. Discarded pending tags:\n"; for (dit = pending_tags_.begin(); dit != den; ++dit) - estr += dit->tag_ + "\n"; + estr += (*dit)->tag_ + "\n"; writeError(estr); // clear the pending tags... pending_tags_.clear(); // ...and then just fall through. } + // make sure there are tags to be closed + if (tag_stack_.empty()) { + writeError("Tried to close `" + etag.tag_ + + "' when no tags were open!"); + return *this; + } + // is the tag we are closing the last one we opened? - if (etag.tag_ == tag_stack_.back().tag_) { + if (etag.tag_ == tag_stack_.back()->tag_) { // output it... os_ << etag.asEndTag(); // ...and forget about it @@ -524,15 +525,15 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag) if (html::isFontTag(etag.tag_)) { // it won't be a problem if the other tags open since this one // are also font tags. - TagStack::const_reverse_iterator rit = tag_stack_.rbegin(); - TagStack::const_reverse_iterator ren = tag_stack_.rend(); + TagDeque::const_reverse_iterator rit = tag_stack_.rbegin(); + TagDeque::const_reverse_iterator ren = tag_stack_.rend(); for (; rit != ren; ++rit) { - if (rit->tag_ == etag.tag_) + if ((*rit)->tag_ == etag.tag_) break; - if (!html::isFontTag(rit->tag_)) { + if (!html::isFontTag((*rit)->tag_)) { // we'll just leave it and, presumably, have to close it later. writeError("Unable to close font tag `" + etag.tag_ - + "' due to open non-font tag `" + rit->tag_ + "'."); + + "' due to open non-font tag `" + (*rit)->tag_ + "'."); return *this; } } @@ -542,11 +543,11 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag) // and are being asked to closed em. we want: // this is bold // first, we close the intervening tags... - html::StartTag curtag = tag_stack_.back(); + TagPtr curtag = tag_stack_.back(); // ...remembering them in a stack. - TagStack fontstack; - while (curtag.tag_ != etag.tag_) { - os_ << curtag.asEndTag(); + TagDeque fontstack; + while (etag.tag_ != curtag->tag_) { + os_ << curtag->asEndTag(); fontstack.push_back(curtag); tag_stack_.pop_back(); curtag = tag_stack_.back(); @@ -569,16 +570,16 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag) // at least guarantees proper nesting. writeError("Closing tag `" + etag.tag_ + "' when other tags are open, namely:"); - html::StartTag curtag = tag_stack_.back(); - while (curtag.tag_ != etag.tag_) { - writeError(curtag.tag_); - if (curtag.tag_ != parsep_tag) - os_ << curtag.asEndTag(); + TagPtr curtag = tag_stack_.back(); + while (etag.tag_ != curtag->tag_) { + writeError(curtag->tag_); + if (curtag->tag_ != parsep_tag) + os_ << curtag->asEndTag(); tag_stack_.pop_back(); curtag = tag_stack_.back(); } // curtag is now the one we actually want. - os_ << curtag.asEndTag(); + os_ << curtag->asEndTag(); tag_stack_.pop_back(); return *this; diff --git a/src/output_xhtml.h b/src/output_xhtml.h index 497ffdbce0..99d07be9d7 100644 --- a/src/output_xhtml.h +++ b/src/output_xhtml.h @@ -13,7 +13,9 @@ #define OUTPUT_XHTML_H #include "LayoutEnums.h" + #include "support/docstream.h" +#include "support/shared_ptr.h" #include "support/strfwd.h" #include @@ -83,6 +85,7 @@ struct CompTag { std::string attr_; }; + // trivial struct for output of newlines struct CR{}; @@ -151,14 +154,26 @@ private: void writeError(std::string const &) const; /// odocstream & os_; - /// - typedef std::deque TagStack; - /// holds start tags until we know there is content in them. - TagStack pending_tags_; - /// remembers the history, so we can make sure we nest properly. - TagStack tag_stack_; /// EscapeSettings escape_; + // What we would really like to do here is simply use a + // deque. But we want to store both StartTags and + // sub-classes thereof on this stack, which means we run into the + // so-called polymorphic class problem with the STL. We therefore have + // to use a deque, which leads to the question who will + // own these pointers and how they will be deleted, so we use shared + // pointers. + /// + typedef shared_ptr TagPtr; + typedef std::deque TagDeque; + /// + template + shared_ptr makeTagPtr(T const & tag) + { return shared_ptr(new T(tag)); } + /// + TagDeque pending_tags_; + /// + TagDeque tag_stack_; }; /// -- 2.39.2