// // Copyright (c) ZeroC, Inc. All rights reserved. // #include #include #include #include #include #if defined(_MSC_VER) # pragma warning(disable:4456) // shadow # pragma warning(disable:4457) // shadow # pragma warning(disable:4459) // shadow # pragma warning(disable:4100) // unreferenced parameter #elif defined(__clang__) # pragma clang diagnostic ignored "-Wshadow" # pragma clang diagnostic ignored "-Wshadow-all" # pragma clang diagnostic ignored "-Wunused-parameter" #elif defined(__GNUC__) # pragma GCC diagnostic ignored "-Wshadow" # pragma GCC diagnostic ignored "-Wunused-parameter" #endif 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 string& s) { if(_se) { _se = false; } _text = true; if(_escape) { string escaped = escape(s); OutputBase::print(escaped); } 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 = ICE_NULLPTR; // 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 == ICE_NULLPTR) { 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 separate 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 { consoleErr << "getMarkerLimits 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; }