#include #include #include #include using namespace std; using namespace IceUtilInternal; namespace Confluence { EndElement ee; } // ---------------------------------------------------------------------- // ConfluenceOutput // ---------------------------------------------------------------------- const string Confluence::ConfluenceOutput::TEMP_ESCAPER_START = "$$$$$$$"; const string Confluence::ConfluenceOutput::TEMP_ESCAPER_END = "!!!!!!!"; Confluence::ConfluenceOutput::ConfluenceOutput() : OutputBase(), _se(false), _text(false), _escape(false), _listMarkers(""), _commentListMarkers("") { } Confluence::ConfluenceOutput::ConfluenceOutput(ostream& os) : OutputBase(os), _se(false), _text(false), _escape(false), _listMarkers(""), _commentListMarkers("") { } Confluence::ConfluenceOutput::ConfluenceOutput(const char* s) : OutputBase(s), _se(false), _text(false), _escape(false), _listMarkers(""), _commentListMarkers("") { } void Confluence::ConfluenceOutput::print(const char* s) { if(_se) { _se = false; } _text = true; if(_escape) { string escaped = escape(s); OutputBase::print(escaped.c_str()); } else { OutputBase::print(s); } } string Confluence::ConfluenceOutput::escapeComment(string comment) { list< pair > escaperLimits = getMarkerLimits(comment); string escapeChars = "\\{}-_+*|[]"; //for each escape character for(string::iterator i = escapeChars.begin(); i < escapeChars.end(); ++i) { string c(1, *i); string replacement; if(c == "\\") { replacement = "\\\\"; } else if(c == "{") { replacement = "\\{"; } else if(c == "}") { replacement = "\\}"; } else if(c == "-") { replacement = "\\-"; } else if(c == "*") { replacement = "\\*"; } else if(c == "|") { replacement = "\\|"; } else if(c == "_") { replacement = "\\_"; } else if(c == "+") { replacement = "\\+"; } else if(c == "[") { replacement = "\\["; } else if(c == "]") { replacement = "\\]"; } size_t pos = comment.find(c); //for each position of a found escape character while(pos != string::npos) { pair *region = NULL; //is this pos in an escaped section? for(list >::iterator i = escaperLimits.begin(); i != escaperLimits.end(); ++i) { if(pos >= i->first && pos <= i->second) { region = &*i; break; } } if(region == NULL) { comment.replace(pos, c.size(), replacement); pos = comment.find(c, pos+replacement.size()); } else { //skip ahead past the marked section pos = comment.find(c, region->second+1); } } } comment = removeMarkers(comment); return comment; } string Confluence::ConfluenceOutput::convertCommentHTML(string comment) { comment = escapeComment(comment); bool italics = false; size_t tagStart = comment.find("<"); while(tagStart != string::npos) { size_t tagEnd = comment.find(">", tagStart); string tag = comment.substr(tagStart + 1, tagEnd - (tagStart + 1)); string replacement = ""; bool isEndTag = tag[0] == '/'; if(isEndTag) { //strip slash character tag.erase(remove(tag.begin(), tag.end(), '/'), tag.end()); } size_t spacepos = tag.find(" "); list > attributes; string rest; if(spacepos != string::npos) { //get the rest and seperate into attrs rest = tag.substr(spacepos); //get just the tag tag = tag.substr(0, spacepos); size_t nextSpace = 0; size_t lastSpace = 0; do { lastSpace = nextSpace; nextSpace = rest.find(" ", lastSpace+1); //rest starts with a space string setting; if(nextSpace == string::npos) { setting = rest.substr(lastSpace); } else { setting = rest.substr(lastSpace, nextSpace-lastSpace); } size_t eqPos = setting.find("="); if(eqPos != string::npos) { string aName = setting.substr(1, eqPos-1); string aVal = setting.substr(eqPos+1); //remove quotes from val size_t qPos = aVal.find("\""); while(qPos != string::npos) { aVal.erase(qPos, 1); qPos = aVal.find("\""); } pair p = make_pair(aName, aVal); attributes.push_back(p); } else { //bad attribute, ignore } } while(nextSpace != string::npos); } if(tag == "tt") { if(!isEndTag) { replacement = "{{"; } else { replacement = "}}"; } } else if(tag == "p") { //special case: Some classes add markup for(list >::iterator i = attributes.begin(); i != attributes.end(); ++i) { if(i->first == "class" && i->second == "Note") { italics = true; break; } if(i->first == "class" && i->second == "Deprecated") { italics = true; break; } } if(!isEndTag) { if(italics) { replacement = "\n\n_"; } else { replacement = "\n\n"; } } else { if(italics) { replacement = "_\n\n"; italics = false; } else { replacement = "\n\n"; } } } else if(tag == "ol") { if(!isEndTag) { if(_commentListMarkers.empty()) { replacement = "\n"; } _commentListMarkers.append("#"); } else { _commentListMarkers.erase(_commentListMarkers.size()-1); } } else if(tag == "ul") { if(!isEndTag) { if(_commentListMarkers.empty()) { replacement = "\n"; } _commentListMarkers.append("*"); } else { _commentListMarkers.erase(_commentListMarkers.size()-1); } } else if(tag == "li") { if(!isEndTag) { ostringstream oss; oss << "\n" << _commentListMarkers << " "; replacement = oss.str(); } //do nothing for end tag } else if(tag == "dl") { if(!isEndTag) { replacement = "\n"; } else { replacement = "\n"; } } else if(tag == "dt") { if(!isEndTag) { replacement = ""; } else { replacement = " "; } } else if(tag == "dd") { if(!isEndTag) { replacement = "--- "; } else { replacement = "\n"; } } else if(tag == "em") { if(!isEndTag) { replacement = "_"; } else { replacement = "_"; } } else { replacement = "*{{UNRECOGNIZED MARKUP: " + tag + "}}*"; } //apply replacement if(tag == "p") { comment.erase(tagStart, tagEnd + 1 - tagStart); size_t displace = comment.find_first_not_of(" \n\r\t", tagStart); //skip ahead over whitespace comment.insert(displace, replacement); } else { comment.replace(tagStart, tagEnd + 1 - tagStart, replacement); //don't skip whitespace } //special case: terminate

(and any italics) on double newline or end of comment size_t dnl = comment.find("\n\n", tagStart+replacement.size()); tagStart = comment.find("<"); if(italics) { if(tagStart == string::npos && dnl == string::npos) { //end italics before javadoc markup size_t atPos = comment.find("@", tagStart+replacement.size()); if(atPos != string::npos) { //found markup. now move to the last non-whitespace char before the markup and end italics string before = comment.substr(0, atPos); size_t endLocation = before.find_last_not_of(" \n\r\t"); comment.insert(endLocation, "_"); italics = false; } else { //no markup; end of comment size_t endLocation = comment.find_last_not_of(" \n\r\t"); comment.insert(endLocation, "_"); italics = false; } } else if(dnl != string::npos && (tagStart == string::npos || dnl < tagStart)) { string before = comment.substr(0, dnl); size_t endLocation = before.find_last_not_of(" \n\r\t"); comment.insert(endLocation, "_"); italics = false; } } } return comment; } void Confluence::ConfluenceOutput::newline() { if(_se) { _se = false; } OutputBase::newline(); } void Confluence::ConfluenceOutput::startElement(const string& element) { string escaped; if(_escape) { escaped = escape(element); } else { escaped = element; } string::size_type tagpos = element.find_first_of(" "); const string tagname = element.substr(0, tagpos).c_str(); if(tagname == "p") { _out << "\n"; } else if(tagname == "b") { _out << "*"; } else if(tagname == "panel") { _out << "{panel}"; } else if(tagname == "blockquote") { _out << "{section}{column:width=10px}{column} {column}"; } else if(tagname == "dl") { _out << "\n"; } else if(tagname == "dt") { _out << ""; } else if(tagname == "dd") { _out << "--- "; } else if(tagname == "table") { _out << "{table}\n"; } else if(tagname == "tr") { _out << "{tr}\n"; } else if(tagname == "td") { _out << "{td}"; } else if(tagname == "th") { _out << "{th}"; } else if(tagname == "div") { _out << "{div}"; } else if(tagname == "span") { _out << "{span}"; } else if(tagname == "ol") { if(_listMarkers.empty()) { _out << "\n"; } _listMarkers.append("#"); } else if(tagname == "ul") { if(_listMarkers.empty()) { _out << "\n"; } _listMarkers.append("*"); } else if(tagname == "li") { _out << "\n" << _listMarkers << " "; } else if(tagname == "hr") { _out << "----"; } else if(tagname == "h1") { _out << "\nh1. "; } else if(tagname == "h2") { _out << "\nh2. "; } else if(tagname == "h3") { _out << "\nh3. "; } else if(tagname == "h4") { _out << "\nh4. "; } else if(tagname == "h5") { _out << "\nh5. "; } else if(tagname == "h6") { _out << "\nh6. "; } else if(tagname == "tt") { _out << "{{"; } else { _out << "{" << escaped << "}"; } _se = true; _text = false; string::size_type pos = element.find_first_of(" \t"); if(pos == string::npos) { _elementStack.push(element); } else { _elementStack.push(element.substr(0, pos)); } ++_pos; // TODO: ??? inc(); _separator = false; } void Confluence::ConfluenceOutput::endElement() { string element = _elementStack.top(); _elementStack.pop(); string escaped; if(_escape) { escaped = escape(element); } else { escaped = element; } string::size_type tagpos = element.find_first_of(" "); const string tagname = element.substr(0, tagpos).c_str(); if(tagname == "p") { _out << "\n"; } else if(tagname == "b") { _out << "*"; } else if(tagname == "panel") { _out << "{panel}\n"; } else if(tagname == "blockquote") { _out << "{column}{section}\n"; } else if(tagname == "dl") { _out << "\n"; } else if(tagname == "dt") { _out << " "; } else if(tagname == "dd") { _out << "\n"; } else if(tagname == "table") { _out << "{table}\n"; } else if(tagname == "tr") { _out << "{tr}\n"; } else if(tagname == "td") { _out << "{td}\n"; } else if(tagname == "th") { _out << ""; } else if(tagname == "div") { _out << "{div}"; } else if(tagname == "span") { _out << "{span}"; } else if(tagname == "ol") { _listMarkers.erase(_listMarkers.size()-1); if(_listMarkers.empty()) { _out << "\n"; } } else if(tagname == "ul") { _listMarkers.erase(_listMarkers.size()-1); if(_listMarkers.empty()) { _out << "\n"; } } else if(tagname == "li") { //nothing to do } else if(tagname == "hr") { _out << "\n\n"; } else if(tagname == "h1") { _out << "\n\n"; } else if(tagname == "h2") { _out << "\n\n"; } else if(tagname == "h3") { _out << "\n\n"; } else if(tagname == "h4") { _out << "\n\n"; } else if(tagname == "h5") { _out << "\n\n"; } else if(tagname == "h6") { _out << "\n\n"; } else if(tagname == "tt") { _out << "}}"; } else { _out << "{" << escaped << "}"; } dec(); --_pos; // TODO: ??? _se = false; _text = false; } string Confluence::ConfluenceOutput::getLinkMarkup(const std::string& url, const std::string& text, const std::string& anchor, const std::string& tip) { ostringstream oss; oss << "["; if(!text.empty()) { oss << text << "|"; } oss << url; if(!anchor.empty()) { oss << "#" << anchor; } if(!tip.empty()) { oss << "|" << tip; } oss << "]"; return oss.str(); } string Confluence::ConfluenceOutput::getImageMarkup(const string& url, const string& title) { ostringstream oss; oss << "!" << url; if(!title.empty()) { oss << "|" << title; } oss << "!"; return oss.str(); } string Confluence::ConfluenceOutput::getAnchorMarkup(const std::string& anchor, const std::string& text) { ostringstream oss; oss << "{anchor:" << anchor << "}"; if(!text.empty()) { oss << text << "\n"; } return oss.str(); } string Confluence::ConfluenceOutput::getNavMarkup(const std::string& prevLink, const std::string& nextLink) { ostringstream oss; oss << "{znav:"; if(!prevLink.empty()) { oss << "prev=" << prevLink << "|"; } if(!nextLink.empty()) { oss << "next=" << nextLink; } oss << "}\n"; oss << "{section}{section}\n"; return oss.str(); } list< pair > Confluence::ConfluenceOutput::getMarkerLimits(const string& str) { list< pair > pairs; size_t start = str.find(TEMP_ESCAPER_START); size_t end; while(start != string::npos) { end = str.find(TEMP_ESCAPER_END, start+TEMP_ESCAPER_START.size()); if(end != string::npos) { pair p = make_pair(static_cast(start), static_cast(end+TEMP_ESCAPER_END.size())); pairs.push_back(p); start = str.find(TEMP_ESCAPER_START, end+TEMP_ESCAPER_END.size()); } else { cerr << "getEscaperLimits FOUND START OF ESCAPE MARKER WITH NO MATCHING END IN STRING:" << endl << str.substr(start) << endl; break; } } return pairs; } string Confluence::ConfluenceOutput::removeMarkers(string str) { //remove starts size_t start = str.find(TEMP_ESCAPER_START); while(start != string::npos) { str.erase(start, TEMP_ESCAPER_START.size()); start = str.find(TEMP_ESCAPER_START, start); } //remove ends size_t end = str.find(TEMP_ESCAPER_END); while(end != string::npos) { str.erase(end, TEMP_ESCAPER_END.size()); end = str.find(TEMP_ESCAPER_END, end); } return str; } void Confluence::ConfluenceOutput::attr(const string& name, const string& value) { // // Precondition: Attributes can only be attached to elements. // assert(_se); _out << " " << name << "=\"" << escape(value) << "\""; } void Confluence::ConfluenceOutput::startEscapes() { _escape = true; } void Confluence::ConfluenceOutput::endEscapes() { _escape = false; } string Confluence::ConfluenceOutput::currentElement() const { if(_elementStack.size() > 0) { return _elementStack.top(); } else { return string(); } } string Confluence::ConfluenceOutput::escape(const string& input) const { string v = input; // // Find out whether there is a reserved character to avoid // conversion if not necessary. // const string allReserved = "<>'\"&{}"; if(v.find_first_of(allReserved) != string::npos) { // // First convert all & to & // size_t pos = 0; while((pos = v.find_first_of('&', pos)) != string::npos) { v.insert(pos+1, "amp;"); pos += 4; } // // Next convert remaining reserved characters. // const string reserved = "<>'\"{}"; pos = 0; while((pos = v.find_first_of(reserved, pos)) != string::npos) { string replace; switch(v[pos]) { case '>': replace = ">"; break; case '<': replace = "<"; break; case '\'': replace = "'"; break; case '"': replace = """; break; case '{': replace = "\\{"; break; case '}': replace = "\\}"; break; default: assert(false); } v.erase(pos, 1); v.insert(pos, replace); pos += replace.size(); } } return v; } Confluence::ConfluenceOutput& Confluence::operator<<(ConfluenceOutput& out, ios_base& (*val)(ios_base&)) { ostringstream s; s << val; out.print(s.str().c_str()); return out; } Confluence::StartElement::StartElement(const string& name) : _name(name) { } const string& Confluence::StartElement::getName() const { return _name; } Confluence::Attribute::Attribute(const string& name, const string& value) : _name(name), _value(value) { } const string& Confluence::Attribute::getName() const { return _name; } const string& Confluence::Attribute::getValue() const { return _value; }