#include #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 (!strcmp(tag.c_str(), "tt")) { if (!isEndTag) { replacement = "{{"; } else { replacement = "}}"; } } else if (!strcmp(tag.c_str(), "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 (!strcmp(tag.c_str(), "ol")) { if (!isEndTag) { if (_commentListMarkers.empty()) { replacement = "\n"; } _commentListMarkers.append("#"); } else { _commentListMarkers.erase(_commentListMarkers.size()-1); } } else if (!strcmp(tag.c_str(), "ul")) { if (!isEndTag) { if (_commentListMarkers.empty()) { replacement = "\n"; } _commentListMarkers.append("*"); } else { _commentListMarkers.erase(_commentListMarkers.size()-1); } } else if (!strcmp(tag.c_str(), "li")) { if (!isEndTag) { ostringstream oss; oss << "\n" << _commentListMarkers << " "; replacement = oss.str(); } //do nothing for end tag } else if (!strcmp(tag.c_str(), "dl")) { if (!isEndTag) { replacement = "\n"; } else { replacement = "\n"; } } else if (!strcmp(tag.c_str(), "dt")) { if (!isEndTag) { replacement = ""; } else { replacement = " "; } } else if (!strcmp(tag.c_str(), "dd")) { if (!isEndTag) { replacement = "--- "; } else { replacement = "\n"; } } else if (!strcmp(tag.c_str(), "em")) { if (!isEndTag) { replacement = "_"; } else { replacement = "_"; } } else { replacement = "*{{UNRECOGNIZED MARKUP: " + tag + "}}*"; } //apply replacement if (!strcmp(tag.c_str(), "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; } const char *tagname; string::size_type tagpos = element.find_first_of(" "); tagname = element.substr(0, tagpos).c_str(); if (!strcmp(tagname, "p")) { _out << "\n"; } else if (!strcmp(tagname, "b")) { _out << "*"; } else if (!strcmp(tagname, "panel")) { _out << "{panel}"; } else if (!strcmp(tagname, "blockquote")) { _out << "{section}{column:width=10px}{column} {column}"; } else if (!strcmp(tagname, "dl")) { _out << "\n"; } else if (!strcmp(tagname, "dt")) { _out << ""; } else if (!strcmp(tagname, "dd")) { _out << "--- "; } else if (!strcmp(tagname, "table")) { _out << "{table}\n"; } else if (!strcmp(tagname, "tr")) { _out << "{tr}\n"; } else if (!strcmp(tagname, "td")) { _out << "{td}"; } else if (!strcmp(tagname, "th")) { _out << "{th}"; } else if (!strcmp(tagname, "div")) { _out << "{div}"; } else if (!strcmp(tagname, "span")) { _out << "{span}"; } else if (!strcmp(tagname, "ol")) { if (_listMarkers.empty()) { _out << "\n"; } _listMarkers.append("#"); } else if (!strcmp(tagname, "ul")) { if (_listMarkers.empty()) { _out << "\n"; } _listMarkers.append("*"); } else if (!strcmp(tagname, "li")) { _out << "\n" << _listMarkers << " "; } else if (!strcmp(tagname, "hr")) { _out << "----"; } else if (!strcmp(tagname, "h1")) { _out << "\nh1. "; } else if (!strcmp(tagname, "h2")) { _out << "\nh2. "; } else if (!strcmp(tagname, "h3")) { _out << "\nh3. "; } else if (!strcmp(tagname, "h4")) { _out << "\nh4. "; } else if (!strcmp(tagname, "h5")) { _out << "\nh5. "; } else if (!strcmp(tagname, "h6")) { _out << "\nh6. "; } else if (!strcmp(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; } const char *tagname; string::size_type tagpos = element.find_first_of(" "); tagname = element.substr(0, tagpos).c_str(); if (!strcmp(tagname, "p")) { _out << "\n"; } else if (!strcmp(tagname, "b")) { _out << "*"; } else if (!strcmp(tagname, "panel")) { _out << "{panel}\n"; } else if (!strcmp(tagname, "blockquote")) { _out << "{column}{section}\n"; } else if (!strcmp(tagname, "dl")) { _out << "\n"; } else if (!strcmp(tagname, "dt")) { _out << " "; } else if (!strcmp(tagname, "dd")) { _out << "\n"; } else if (!strcmp(tagname, "table")) { _out << "{table}\n"; } else if (!strcmp(tagname, "tr")) { _out << "{tr}\n"; } else if (!strcmp(tagname, "td")) { _out << "{td}\n"; } else if (!strcmp(tagname, "th")) { _out << ""; } else if (!strcmp(tagname, "div")) { _out << "{div}"; } else if (!strcmp(tagname, "span")) { _out << "{span}"; } else if (!strcmp(tagname, "ol")) { _listMarkers.erase(_listMarkers.size()-1); if (_listMarkers.empty()) { _out << "\n"; } } else if (!strcmp(tagname, "ul")) { _listMarkers.erase(_listMarkers.size()-1); if (_listMarkers.empty()) { _out << "\n"; } } else if (!strcmp(tagname, "li")) { //nothing to do } else if (!strcmp(tagname, "hr")) { _out << "\n\n"; } else if (!strcmp(tagname, "h1")) { _out << "\n\n"; } else if (!strcmp(tagname, "h2")) { _out << "\n\n"; } else if (!strcmp(tagname, "h3")) { _out << "\n\n"; } else if (!strcmp(tagname, "h4")) { _out << "\n\n"; } else if (!strcmp(tagname, "h5")) { _out << "\n\n"; } else if (!strcmp(tagname, "h6")) { _out << "\n\n"; } else if (!strcmp(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(); //leak? } 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(); //leak? } 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((unsigned int)start, (unsigned int)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; }