// ********************************************************************** // // Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include #include #include #include #include #include #include #ifdef _WIN32 # include #else # include #endif #include #include using namespace std; using namespace Slice; using namespace IceUtil; using namespace IceUtilInternal; namespace Slice { void generate(const UnitPtr& unit, const string& dir, const string& header, const string& footer, const string& indexHeader, const string& indexFooter, const string& imageDir, const string& logoURL, const string& searchAction, unsigned indexCount, unsigned warnSummary) { unit->mergeModules(); // // I don't want the top-level module to be sorted, therefore no // p->sort() before or after the p->sortContents(). // unit->sortContents(false); GeneratorBase::setOutputDir(dir); GeneratorBase::setHeader(header); GeneratorBase::setFooter(footer); GeneratorBase::setImageDir(imageDir); GeneratorBase::setLogoURL(logoURL); GeneratorBase::setSearchAction(searchAction); GeneratorBase::setIndexCount(indexCount); GeneratorBase::warnSummary(warnSummary); // // The file visitor first runs over the tree and records // the names of all files in this documentation set. // This information is used later to check whether a referenced // symbol is defined in this documentation set, as opposed to // being defined in an included file that is not part of this // documentation set. If the former, we can generate a link // to the symbol; if the latter, we cannot. // Files files; FileVisitor tv(files); unit->visit(&tv, false); // // Generate the start page. // StartPageVisitor spv(files); unit->visit(&spv, false); // // Generate the table of contents. // TOCVisitor tocv(files, indexHeader, indexFooter); unit->visit(&tocv, false); tocv.generate(); // // Generate the individual HTML pages. // GeneratorBase::setSymbols(tocv.symbols()); PageVisitor v(files); unit->visit(&v, false); } } string Slice::GeneratorBase::_dir = "."; string Slice::GeneratorBase::_header1; string Slice::GeneratorBase::_header2; string Slice::GeneratorBase::_footer; string Slice::GeneratorBase::_imageDir; string Slice::GeneratorBase::_logoURL; string Slice::GeneratorBase::_searchAction; size_t Slice::GeneratorBase::_indexCount = 0; size_t Slice::GeneratorBase::_warnSummary = 0; ContainedList Slice::GeneratorBase::_symbols; // // Set the output directory, creating it if necessary. // void Slice::GeneratorBase::setOutputDir(const string& dir) { if(!dir.empty()) { _dir = dir; makeDir(_dir); } } // // Get the headers. If "header" is empty, use a default header. // If a header file is specified, it is expected to include // and to contain a "TITLE" placeholder line (in column 1, no leading // or trailing white space). The actual document title is later substituted // where that TITLE placeholder appears. // void Slice::GeneratorBase::setHeader(const string& header) { getHeaders(header, _header1, _header2); } // // Get the footer. If "footer" is empty, use a default footer. // The footer is expected to include . // void Slice::GeneratorBase::setFooter(const string& footer) { _footer = getFooter(footer); } // // Set the directory for style sheet images. // void Slice::GeneratorBase::setImageDir(const string& imageDir) { _imageDir = imageDir; } // // Set URL for logo image, if any. // void Slice::GeneratorBase::setLogoURL(const string& logoURL) { _logoURL = logoURL; } // // Set search action, if any. // void Slice::GeneratorBase::setSearchAction(const string& searchAction) { _searchAction = searchAction; } // // Set the threshold at which we start generating sub-indexes. // If a page has fewer entries than this, we don't generate a // sub-index. (For example, with "ic" set to 3, we generate // a sub-index only if, say, a structure has 3 or more members. // void Slice::GeneratorBase::setIndexCount(int ic) { _indexCount = ic; } // // If n > 0, we print a warning if a summary sentence exceeds n characters. // void Slice::GeneratorBase::warnSummary(int n) { _warnSummary = n; } void Slice::GeneratorBase::setSymbols(const ContainedList& symbols) { _symbols = symbols; } Slice::GeneratorBase::GeneratorBase(XMLOutput& o, const Files& files) : _out(o), _files(files) { } Slice::GeneratorBase::~GeneratorBase() { } // // Open a file for writing in the output directory (the output directory // is created if necessary) and write the HTML header into the file. // void Slice::GeneratorBase::openDoc(const string& file, const string& title, const string& header, const string& /*footer*/) { makeDir(_dir); openStream(_dir + "/" + file); string h1; string h2; if(header.empty()) { _out << _header1; } else { getHeaders(header, h1, h2); _out << h1; } _out << title; if(header.empty()) { _out << _header2; } else { _out << h2; } _out.inc(); _out.inc(); } // // Open an HTML file for writing for the specified construct. The // path name of the file is relative to the output directory and // is constructed from the Slice scoped name. Sub-directories are // created as needed and the header is written to the file. // void Slice::GeneratorBase::openDoc(const ContainedPtr& c) { string path = _dir; StringList components = getContainer(c); StringList::size_type num = 0; for(StringList::const_iterator i = components.begin(); i != components.end(); ++i) { path += "/" + (IceUtilInternal::toLower(*i) == "index" ? "_index" : *i); ++num; if(num < components.size()) { makeDir(path); } } path += ".html"; openStream(path); _out << _header1; _out << c->scoped().substr(2); // Text for title element. _out << _header2; _out.inc(); _out.inc(); } // // Close an open HTML file after writing the footer. // void Slice::GeneratorBase::closeDoc(const string& footer) { _out.dec(); _out.dec(); _out << nl << (!footer.empty() ? footer : _footer); _out << nl; } void Slice::GeneratorBase::start(const std::string& element, const std::string& classes) { string s = element; if(!classes.empty()) { s += " class=\"" + classes + "\""; } _out << se(s); } void Slice::GeneratorBase::end() { _out << ee; } void Slice::GeneratorBase::printComment(const ContainedPtr& p, const SyntaxTreeBasePtr& source, const string& deprecateReason, bool forIndex) { #ifndef NDEBUG int indent = _out.currIndent(); #endif string comment = getComment(p, source, false, forIndex); StringList par = getTagged("param", comment); StringList ret = getTagged("return", comment); StringList throws = getTagged("throws", comment); StringList see = getTagged("see", comment); string::size_type pos = comment.find_last_not_of(" \t\r\n"); if(pos != string::npos) { comment.erase(pos + 1); start("p"); _out.zeroIndent(); _out << nl << comment; _out.restoreIndent(); end(); } if(!deprecateReason.empty()) { start("p", "Deprecated"); _out << nl << deprecateReason; end(); } assert(_out.currIndent() == indent); if(!par.empty()) { start("h4"); _out << "Parameters"; end(); start("dl"); for(StringList::const_iterator q = par.begin(); q != par.end(); ++q) { string term; pos = q->find_first_of(" \t\r\n"); if(pos != string::npos) { term = q->substr(0, pos); } string item; pos = q->find_first_not_of(" \t\r\n", pos); if(pos != string::npos) { item = q->substr(pos); } start("dt", "Symbol"); _out << term; end(); start("dd"); _out << nl << item; end(); } end(); } if(!ret.empty()) { start("h4"); _out << "Return Value"; end(); start("p"); _out << ret.front(); end(); } if(!throws.empty()) { start("h4"); _out << "Exceptions"; end(); start("dl"); for(StringList::const_iterator q = throws.begin(); q != throws.end(); ++q) { string term; pos = q->find_first_of(" \t\r\n"); if(pos != string::npos) { term = q->substr(0, pos); } string item; pos = q->find_first_not_of(" \t\r\n", pos); if(pos != string::npos) { item = q->substr(pos); } start("dt", "Symbol"); _out << getURL(toSliceID(term, source->definitionContext()->filename()), source, false); end(); start("dd"); _out << nl << item; end(); } end(); } ClassList derivedClasses; ClassDefPtr def = ClassDefPtr::dynamicCast(p); if(def) { derivedClasses = p->unit()->findDerivedClasses(def); } if(!derivedClasses.empty()) { start("h4"); _out << "Derived Classes and Interfaces"; end(); start("dl"); for(ClassList::const_iterator q = derivedClasses.begin(); q != derivedClasses.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, source, false, false); end(); } end(); } ExceptionList derivedExceptions; ExceptionPtr ex = ExceptionPtr::dynamicCast(p); if(ex) { derivedExceptions = p->unit()->findDerivedExceptions(ex); if(!derivedExceptions.empty()) { start("h4"); _out << "Derived Exceptions"; end(); start("dl"); for(ExceptionList::const_iterator q = derivedExceptions.begin(); q != derivedExceptions.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, source, false, false); end(); } end(); } ContainedList usedBy; usedBy = p->unit()->findUsedBy(ex); if(!usedBy.empty()) { start("h4"); _out << "Used By"; end(); start("dl"); // // We first extract the symbol names from the used-by list and sort // them, otherwise the symbols appear in random order. // StringList sl; for(ContainedList::const_iterator q = usedBy.begin(); q != usedBy.end(); ++q) { sl.push_back(getURL(*q, source, false, false)); } sl.sort(); for(StringList::const_iterator r = sl.begin(); r != sl.end(); ++r) { start("dt", "Symbol"); _out << *r; end(); } end(); } } ContainedList usedBy; ConstructedPtr constructed; if(def) { constructed = def->declaration(); } else { constructed = ConstructedPtr::dynamicCast(p); } if(constructed) { usedBy = p->unit()->findUsedBy(constructed); } if(!usedBy.empty()) { // // We first accumulate the strings in a list instead of printing // each stringified entry in the usedBy list. This is necessary because // the usedBy list can contain operations and parameters. But getURL() // on a parameter returns the string for the parameter's operation, so // we can end up printing the same operation name more than once. // StringList strings; for(ContainedList::const_iterator q = usedBy.begin(); q != usedBy.end(); ++q) { strings.push_back(getURL(*q, source, false, false)); } strings.sort(); strings.unique(); start("h4"); _out << "Used By"; end(); start("dl"); for(list::const_iterator p = strings.begin(); p != strings.end(); ++p) { start("dt", "Symbol"); _out << *p; end(); } end(); } if(!see.empty()) { start("h4"); _out << "See Also"; end(); start("dl"); for(StringList::const_iterator q = see.begin(); q != see.end(); ++q) { start("dt", "Symbol"); _out << getURL(toSliceID(*q, source->definitionContext()->filename()), source, false); end(); } end(); } assert(_out.currIndent() == indent); } void Slice::GeneratorBase::printMetaData(const ContainedPtr& p) { list metaData = p->getMetaData(); if(!metaData.empty()) { _out << "["; list::const_iterator q = metaData.begin(); while(q != metaData.end()) { _out << " \"" << *q << "\""; if(++q != metaData.end()) { _out << ","; } } _out << " ]" << nl; } } void Slice::GeneratorBase::printSummary(const ContainedPtr& p, const ContainerPtr& module, bool deprecated, bool forIndex) { ContainerPtr container = ContainerPtr::dynamicCast(p); if(!container) { container = p->container(); } if(module) { container = module; } string summary = getComment(p, container, true, forIndex); _out << nl << summary; if(deprecated) { start("p", "Deprecated"); _out << nl << "Deprecated."; end(); } } void Slice::GeneratorBase::printHeaderFooter(const ContainedPtr& c) { const string scoped = c->scoped(); ContainedList::const_iterator prev = _symbols.end(); ContainedList::const_iterator pos = _symbols.begin(); while(pos != _symbols.end()) { if((*pos)->scoped() == scoped) { break; } prev = pos++; } ContainedList::const_iterator next = pos == _symbols.end() ? _symbols.end() : ++pos; const bool isFirst = prev == _symbols.end(); const bool isLast = next == _symbols.end(); string prevLink; string prevClass; if(!isFirst) { prevLink = getPath(*prev, c) + ".html"; prevClass = "Button"; } else { prevClass = "ButtonGrey"; } string nextLink; string nextClass; if(!isLast) { nextLink = getPath(*next, c) + ".html"; nextClass = "Button"; } else { nextClass = "ButtonGrey"; } string upLink; string upClass; bool topLevel = UnitPtr::dynamicCast(c->container()); if(!topLevel) { upLink = getPath(c->container(), c) + ".html"; upClass = "Button"; } else { upClass = "ButtonGrey"; } string homeLink = getPath(0, c); if(!homeLink.empty()) { homeLink += "/"; } homeLink += "index.html"; string indexLink = getPath(0, c); if(!indexLink.empty()) { indexLink += "/"; } indexLink += "_sindex.html"; string imageDir = getImageDir(); string prevImage = imageDir.empty() ? "Previous" : (isFirst ? "prevx.gif" : "prev.gif"); string nextImage = imageDir.empty() ? "Next" : (isLast ? "nextx.gif" : "next.gif"); string upImage = imageDir.empty()? "Up" : (!topLevel ? "up.gif" : "upx.gif"); string homeImage = imageDir.empty() ? "Home" : "home.gif"; string indexImage = imageDir.empty() ? "Index" : "index.gif"; if(!imageDir.empty()) { string path = getPath(0, c); if(!path.empty()) { path += "/"; } path += imageDir + "/"; prevImage = "\"Previous\""; nextImage = "\"Next\""; upImage = "\"Up\""; homeImage = "\"Home\""; indexImage = "\"Index\""; } _out << nl << ""; start("table", "HeaderFooter"); start("tr"); start("td align=\"left\""); start("table"); start("tr"); start("td"); _out << "" << homeImage << ""; end(); // td if(!imageDir.empty() || !isFirst) { start("td"); if(!isFirst) { _out << ""; } _out << prevImage; if(!isFirst) { _out << ""; } end(); } if(!imageDir.empty() || !topLevel) { start("td"); if(!topLevel) { _out << ""; } _out << upImage; if(!topLevel) { _out << ""; } end(); } if(!imageDir.empty() || !isLast) { start("td"); if(!isLast) { _out << ""; } _out << nextImage; if(!isLast) { _out << ""; } end(); } start("td"); _out << "" << indexImage << ""; end(); end(); // tr end(); // table end(); // td start("td align=\"center\""); printSearch(); end(); start("td align=\"right\""); printLogo(c); end(); end(); // tr end(); // table _out << nl << "" << nl; } void Slice::GeneratorBase::printSearch() { if(!_searchAction.empty()) { start("table"); start("tr"); start("td"); _out << nl << "
"; _out.inc(); start("div"); _out << nl << ""; _out << nl << ""; _out.dec(); end(); _out << nl << "
"; end(); // td end(); // tr end(); // table } } void Slice::GeneratorBase::printLogo(const ContainedPtr& c) { string imageDir = getImageDir(); if(!imageDir.empty()) { string path = getURL(0, c, false, false); if(!path.empty()) { path += "/"; } path += imageDir + "/logo.gif"; start("table", "LogoTable"); start("tr"); start("td"); if(!_logoURL.empty()) { _out << ""; } _out << "\"Logo\""; if(!_logoURL.empty()) { _out << ""; } end(); end(); end(); } } bool Slice::GeneratorBase::checkFile(const SyntaxTreeBasePtr& p) { // // Determine whether the file in which the given element is defined is included in this documentation set. // return _files.find(p->definitionContext()->filename()) != _files.end(); } ContainedPtr Slice::GeneratorBase::getPageElement(const SyntaxTreeBasePtr& p) { ContainedPtr r = ContainedPtr::dynamicCast(p); if(r) { // // Determine the syntax tree entity that represents the page on which the definition is contained. // switch(r->containedType()) { case Contained::ContainedTypeSequence: // Described on the enclosing module's page. case Contained::ContainedTypeDictionary: // Described on the enclosing module's page. case Contained::ContainedTypeOperation: // Described on the class/interface page. case Contained::ContainedTypeDataMember: // Described on the class/struct/exception page. case Contained::ContainedTypeConstant: // Described on the enclosing module's page. r = ContainedPtr::dynamicCast(r->container()); assert(r); break; case Contained::ContainedTypeParamDecl: // Described on the class/interface page. { r = ContainedPtr::dynamicCast(r->container()); assert(r); OperationPtr op = OperationPtr::dynamicCast(r); assert(op); r = ContainedPtr::dynamicCast(op->container()); assert(r); break; } case Contained::ContainedTypeEnumerator: // Described on the enum page. r = EnumeratorPtr::dynamicCast(r)->type(); break; case Contained::ContainedTypeEnum: case Contained::ContainedTypeModule: case Contained::ContainedTypeException: case Contained::ContainedTypeStruct: // // These types have their own pages. // break; case Contained::ContainedTypeClass: { ClassDeclPtr c = ClassDeclPtr::dynamicCast(p); if(c) { assert(c->definition()); // May be nil if the class is declared but not defined. } break; } } } return r; } string Slice::GeneratorBase::getPath(const SyntaxTreeBasePtr& target, const SyntaxTreeBasePtr& source) { // // We need a link to get from the page containing the source to the page containing the target. // ContainedPtr t = getPageElement(target); ContainedPtr s = getPageElement(source); string path; // // First compute a path from source to the top. // if(s) { // // Start out one level up. Top-level modules still have a container (the Unit) but we // don't want to include that level. // ContainedPtr c = ContainedPtr::dynamicCast(s->container()); while(c) { if(!path.empty()) { path += "/"; } path += ".."; c = ContainedPtr::dynamicCast(c->container()); } } // // Now add a path to the target. // if(t) { StringList l = toStringList(t); for(StringList::iterator p = l.begin(); p != l.end(); ++p) { if(!path.empty()) { path += "/"; } path += (IceUtilInternal::toLower(*p) == "index" ? "_index" : *p); } } return path; } string Slice::GeneratorBase::getURL(const SyntaxTreeBasePtr& target, const SyntaxTreeBasePtr& source, bool asTarget, bool shortName) { string anchor; string linkpath; string s; static const char* builtinTable[] = { "byte", "bool", "short", "int", "long", "float", "double", "string", "Object", "Object*", "LocalObject", "Value" }; SyntaxTreeBasePtr t = target; BuiltinPtr builtin = BuiltinPtr::dynamicCast(t); if(builtin) { s = builtinTable[builtin->kind()]; return s; } ProxyPtr proxy = ProxyPtr::dynamicCast(t); if(proxy) { if(checkFile(proxy)) { // // We can only link to classes that are defined. // if(proxy->_class()->definition()) { linkpath = getPath(proxy->_class(), source); } } s = getScopedMinimized(proxy->_class(), source, shortName); } ClassDeclPtr cl = ClassDeclPtr::dynamicCast(t); if(cl) { // // We can only link to classes that are defined. // if(cl->definition() && checkFile(cl)) { linkpath = getPath(cl->definition(), source); } s = getScopedMinimized(cl, source, shortName); } ExceptionPtr ex = ExceptionPtr::dynamicCast(t); if(ex) { if(checkFile(ex)) { linkpath = getPath(ex, source); } s = getScopedMinimized(ex, source, shortName); } StructPtr st = StructPtr::dynamicCast(t); if(st) { if(checkFile(st)) { linkpath = getPath(st, source); } s = getScopedMinimized(st, source, shortName); } EnumeratorPtr en = EnumeratorPtr::dynamicCast(t); if(en) { if(checkFile(en)) { anchor = getAnchor(en); linkpath = getPath(en, source); } s = getScopedMinimized(en, source, shortName); } OperationPtr op = OperationPtr::dynamicCast(t); if(op) { if(checkFile(op)) { anchor = getAnchor(op); linkpath = getPath(op, source); } s = getScopedMinimized(op, source, shortName); } ParamDeclPtr pd = ParamDeclPtr::dynamicCast(t); if(pd) { op = OperationPtr::dynamicCast(pd->container()); assert(op); if(checkFile(pd)) { anchor = getAnchor(op); linkpath = getPath(op, source); } s = getScopedMinimized(op, source, shortName); } if(!t) { linkpath = getPath(t, source); } else if(s.empty()) { ContainedPtr contained = ContainedPtr::dynamicCast(t); assert(contained); if(checkFile(t)) { if(!(EnumPtr::dynamicCast(t) || ModulePtr::dynamicCast(t) || ClassDefPtr::dynamicCast(t))) { anchor = getAnchor(contained); } linkpath = getPath(t, source); } s = getScopedMinimized(contained, source, shortName); } if(linkpath.empty() && anchor.empty()) { if(proxy) { s += '*'; } return s; } string ret = ""; ret += s; ret += ""; if(proxy) { ret += '*'; } return ret; } string Slice::GeneratorBase::getURL(const string& str, const SyntaxTreeBasePtr& source, bool asTarget) { ContainerPtr container = ContainerPtr::dynamicCast(source); if(!container) { ContainedPtr contained = ContainedPtr::dynamicCast(source); if(contained) { container = contained->container(); } } if(container) { TypeList types = container->lookupType(str, false); if(!types.empty()) { return getURL(types.front(), source, asTarget, false); } ContainedList contList = container->lookupContained(str, false); if(!contList.empty()) { return getURL(contList.front(), source, asTarget, false); } } // // If we can't find the string, printing it in typewriter font is the best we can do. // return "" + str + ""; } string Slice::GeneratorBase::getComment(const ContainedPtr& contained, const SyntaxTreeBasePtr& source, bool summary, bool forIndex) { size_t summarySize = 0; string s = contained->comment(); string comment; for(unsigned int i = 0; i < s.size(); ++i) { // // TODO: Remove old-style link processing once we no longer support the [ident] syntax for links. // if(s[i] == '\\' && i + 1 < s.size() && s[i + 1] == '[') { comment += '['; ++summarySize; ++i; } else if(s[i] == '[') { string literal; for(++i; i < s.size(); ++i) { if(s[i] == ']') { break; } literal += s[i]; } size_t sz = 0; comment += getURL(literal, source, false); summarySize += sz; // // TODO: Remove this warning once we no longer support the old javadoc syntax. // string fileName = contained->file(); if(_warnOldCommentFiles.find(fileName) == _warnOldCommentFiles.end()) { _warnOldCommentFiles.insert(fileName); consoleErr << fileName << ": warning: file contains old-style link syntax: `[" << literal << "]'" << endl; } } else if(s[i] == '{') { static const string atLink = "{@link"; string::size_type pos = s.find(atLink, i); if(pos != i) { comment += '{'; ++summarySize; continue; } string::size_type endpos = s.find('}', pos); if(endpos == string::npos) { continue; } string literal = s.substr(pos + atLink.size(), endpos - pos - atLink.size()); size_t sz = 0; comment += getURL(toSliceID(literal, contained->file()), source, false); summarySize += sz; i = static_cast(endpos); } else if(summary && s[i] == '.' && (i + 1 >= s.size() || isspace(static_cast(s[i + 1])))) { comment += '.'; ++summarySize; break; } else { comment += s[i]; ++summarySize; } } if(summary && _warnSummary && summarySize > _warnSummary) { consoleErr << contained->file() << ": warning: summary size (" << summarySize << ") exceeds " << _warnSummary << " characters: `" << comment << "'" << endl; } return comment; } string Slice::GeneratorBase::getAnchor(const SyntaxTreeBasePtr& p) { StringList symbols = getContained(p); string anchor; for(StringList::const_iterator i = symbols.begin(); i != symbols.end(); ++i) { if(i != symbols.begin()) { anchor += "::"; } anchor += *i; } return anchor; } string Slice::GeneratorBase::getImageDir() { return _imageDir; } string Slice::GeneratorBase::getLogoURL() { return _logoURL; } void Slice::GeneratorBase::openStream(const string& path) { _out.open(path.c_str()); if(!_out.isOpen()) { ostringstream os; os << "cannot open file `" << path << "': " << strerror(errno); throw FileException(__FILE__, __LINE__, os.str()); } FileTracker::instance()->addFile(path); } void Slice::GeneratorBase::closeStream() { _out.close(); } string Slice::GeneratorBase::containedToId(const ContainedPtr& contained, bool /*asTarget*/) { assert(contained); string scoped = contained->scoped(); if(scoped[0] == ':') { scoped.erase(0, 2); } string id; id.reserve(scoped.size()); for(unsigned int i = 0; i < scoped.size(); ++i) { if(scoped[i] == ':') { id += '.'; ++i; } else { id += scoped[i]; } } // // A link name cannot start with a period. // if(id[0] == '.') { id.erase(0, 1); } return '"' + id + '"'; } StringList Slice::GeneratorBase::getTagged(const string& tag, string& comment) { StringList result; string::size_type begin = 0; while(begin < comment.size()) { begin = comment.find("@" + tag, begin); if(begin == string::npos) { return result; } string::size_type pos1 = comment.find_first_not_of(" \t\r\n", begin + tag.size() + 1); if(pos1 == string::npos) { comment.erase(begin); return result; } string::size_type pos2 = comment.find('@', pos1); string line = comment.substr(pos1, pos2 - pos1); comment.erase(begin, pos2 - 1 - begin); string::size_type pos3 = line.find_last_not_of(" \t\r\n"); if(pos3 != string::npos) { line.erase(pos3 + 1); } result.push_back(line); } return result; } string Slice::GeneratorBase::getScopedMinimized(const SyntaxTreeBasePtr& target, const SyntaxTreeBasePtr& source, bool shortName) { ContainedPtr t = ContainedPtr::dynamicCast(target); assert(t); if(shortName) { return t->name(); } string scoped = t->scoped(); ContainedPtr s = ContainedPtr::dynamicCast(source); if(!s) // Source is the global module { return scoped.substr(2); } do { string scoped2 = s->scoped(); scoped2 += "::"; if(scoped.find(scoped2) == 0) { return scoped.substr(scoped2.size()); } s = ContainedPtr::dynamicCast(s->container()); } while(s); return scoped; } StringList Slice::GeneratorBase::getContained(const SyntaxTreeBasePtr& p) { StringList result; if(!p) { return result; } SyntaxTreeBasePtr c = p; do { ContainedPtr contained = ContainedPtr::dynamicCast(c); assert(contained); result.push_front(contained->name()); c = contained->container(); } while(!ContainerPtr::dynamicCast(c)); return result; } StringList Slice::GeneratorBase::getContainer(const SyntaxTreeBasePtr& p) { StringList result; if(!p) { return result; } ContainedPtr contained = ContainedPtr::dynamicCast(p); while(contained && !ModulePtr::dynamicCast(contained) && !ExceptionPtr::dynamicCast(contained) && !ClassDefPtr::dynamicCast(contained) && !StructPtr::dynamicCast(contained) && !EnumPtr::dynamicCast(contained)) { contained = ContainedPtr::dynamicCast(contained->container()); } while(contained) { result.push_front(contained->name()); contained = ContainedPtr::dynamicCast(contained->container()); } return result; } // // TODO: remove warnOldStyleIdent() function once we no longer support // old-style javadoc comments ([...] instead of {@link ...} and // X::Y::Z instead of X.Y#Z). // // void Slice::GeneratorBase::warnOldStyleIdent(const string& str, const string& fileName) { string newName; string::size_type next = 0; if(str.size() > 2 && str[0] == ':' && str[1] == ':') { next = 2; } int numIdents = 0; string::size_type endpos; while((endpos = str.find("::", next)) != string::npos) { if(numIdents != 0) { newName += "."; } newName += str.substr(next, endpos - next); ++numIdents; next = endpos; if(next != string::npos) { next += 2; } } if(numIdents != 0) { newName += "."; } newName += str.substr(next); if(_warnOldCommentFiles.find(fileName) == _warnOldCommentFiles.end()) { _warnOldCommentFiles.insert(fileName); string::size_type pos; pos = newName.rfind('.'); string alternateName; string lastName; if(pos != string::npos) { alternateName = newName; alternateName[pos] = '#'; lastName = newName.substr(pos + 1); } consoleErr << fileName << ": warning: file contains old-style identifier syntax: `" << str << "'." << " Use `'" << newName << "'"; if(!alternateName.empty()) { consoleErr << " or `" << alternateName << "' if `" << lastName << "' is a member"; } consoleErr << endl; } } // // Convert a string of the form X.Y#Z into X::Y::Z (#Z converts to Z). // TODO: Remove the filename parameter once we no longer support old-style javadoc comments. // string Slice::GeneratorBase::toSliceID(const string& str, const string& filename) { const string s = IceUtilInternal::trim(str); string result; string::size_type pos; string::size_type next = 0; while((pos = s.find_first_of(".#", next)) != string::npos) { result += s.substr(next, pos - next); if(s[pos] != '#' || pos != 0) { result += "::"; } next = ++pos; } result += s.substr(next); // // TODO: Remove the warning once we no longer support the old-style // javadoc syntax. // if(str.find("::") != string::npos) { warnOldStyleIdent(s, filename); } return result; } StringList Slice::GeneratorBase::toStringList(const ContainedPtr& c) { return toStringList(c->scoped()); } StringList Slice::GeneratorBase::toStringList(const string& scoped) { assert(scoped.size() > 2); assert(scoped[0] == ':'); assert(scoped[1] == ':'); StringList ids; string::size_type next = 2; string::size_type endpos; while((endpos = scoped.find("::", next)) != string::npos) { ids.push_back(scoped.substr(next, endpos - next)); next = endpos; if(next != string::npos) { ++next; ++next; } } ids.push_back(scoped.substr(next)); return ids; } void Slice::GeneratorBase::makeDir(const string& dir) { IceUtilInternal::structstat st; if(!IceUtilInternal::stat(dir, &st)) { if(!(st.st_mode & S_IFDIR)) { ostringstream os; os << "failed to create package directory `" << dir << "': file already exists and is not a directory"; throw FileException(__FILE__, __LINE__, os.str()); } return; } if(IceUtilInternal::mkdir(dir, 0777) != 0) { ostringstream os; os << "cannot create directory `" << dir << "': " << strerror(errno); throw FileException(__FILE__, __LINE__, os.str()); } FileTracker::instance()->addDirectory(dir); } string Slice::GeneratorBase::readFile(const string& file) { ifstream in(streamFilename(file).c_str()); if(!in) { ostringstream os; os << "cannot open file `" << file << "': " << strerror(errno); throw FileException(__FILE__, __LINE__, os.str()); } ostringstream result; string line; getline(in, line); while(!line.empty()) { result << line << '\n'; getline(in, line); } return result.str(); } void Slice::GeneratorBase::getHeaders(const string& header, string& h1, string& h2) { if(header.empty()) { ostringstream hdr1; XMLOutput O1(hdr1); O1 << ""; O1 << sp; O1 << nl << ""; O1 << se("html"); O1 << se("head"); O1 << se("title") << nl; h1 = hdr1.str(); // _header1 and _header2 store the bit preceding and following the title. // _header1, the title text, and _header2 are written by openDoc(). ostringstream hdr2; XMLOutput O2(hdr2); O2.inc(); O2.inc(); O2 << nl << ""; O2.dec(); O2 << nl << ""; O2 << nl << ""; h2 = hdr2.str(); } else { readFile(header, h1, h2); } } string Slice::GeneratorBase::getFooter(const string& footer) { ostringstream ftr; XMLOutput O(ftr); if(footer.empty()) { O << " "; } else { O << readFile(footer); } O << nl << ""; return ftr.str(); } void Slice::GeneratorBase::readFile(const string& file, string& part1, string& part2) { ifstream in(streamFilename(file).c_str()); if(!in) { ostringstream os; os << "cannot open file `" << file << "': " << strerror(errno); throw FileException(__FILE__, __LINE__, os.str()); } string line; bool foundTitle = false; ostringstream p1; while(!foundTitle && getline(in, line)) { if(line == "TITLE") { foundTitle = true; } else { p1 << line << '\n'; } } part1 = p1.str(); if(!foundTitle) { string err = "no TITLE marker in `" + file + "'"; throw err; } ostringstream p2; p2 << endl; while(getline(in, line)) { p2 << line << '\n'; } part2 = p2.str(); } Slice::StartPageGenerator::StartPageGenerator(const Files& files) : GeneratorBase(_out, files) { openDoc("index.html", "Slice API Documentation"); } Slice::StartPageGenerator::~StartPageGenerator() { ::std::sort(_modules.begin(), _modules.end()); printHeaderFooter(); _out << nl << ""; _out << nl << "
"; start("h1"); _out << "Slice API Documentation"; end(); start("h2"); _out << "Modules"; end(); start("dl"); for(ModuleDescriptions::const_iterator i = _modules.begin(); i != _modules.end(); ++i) { start("dt", "Symbol"); _out << i->first; end(); start("dd"); _out << i->second; end(); } end(); _out << nl << "
"; printHeaderFooter(); closeDoc(); } void Slice::StartPageGenerator::generate(const ModulePtr& m) { string name = getURL(m, 0, false, false); string comment = getComment(m, m, true, true); _modules.push_back(make_pair(name, comment)); } void Slice::StartPageGenerator::printHeaderFooter() { start("table", "HeaderFooter"); start("tr"); start("td align=\"left\""); start("table"); start("tr"); start("td"); string imageDir = getImageDir(); if(imageDir.empty()) { _out << "Index"; } else { string src = imageDir + "/index.gif"; _out << "\"Index"; } end(); // td end(); // tr end(); // table end(); // td start("td align=\"center\""); printSearch(); end(); // td if(!imageDir.empty()) { start("td align=\"right\""); start("table"); start("tr"); start("td"); string logoURL = getLogoURL(); if(!logoURL.empty()) { _out << ""; } _out << "\"Logo\""; if(!logoURL.empty()) { _out << ""; } end(); // td end(); // tr end(); // table end(); // td } end(); // tr end(); // table } Slice::FileVisitor::FileVisitor(Files& files) : _files(files) { } bool Slice::FileVisitor::visitUnitStart(const UnitPtr&) { return true; } bool Slice::FileVisitor::visitModuleStart(const ModulePtr& m) { _files.insert(m->file()); return true; } bool Slice::FileVisitor::visitExceptionStart(const ExceptionPtr& e) { _files.insert(e->file()); return false; } bool Slice::FileVisitor::visitClassDefStart(const ClassDefPtr& c) { _files.insert(c->file()); return false; } void Slice::FileVisitor::visitClassDecl(const ClassDeclPtr& c) { _files.insert(c->file()); } bool Slice::FileVisitor::visitStructStart(const StructPtr& s) { _files.insert(s->file()); return false; } void Slice::FileVisitor::visitSequence(const SequencePtr& s) { _files.insert(s->file()); } void Slice::FileVisitor::visitDictionary(const DictionaryPtr& d) { _files.insert(d->file()); } void Slice::FileVisitor::visitEnum(const EnumPtr& e) { _files.insert(e->file()); } Slice::StartPageVisitor::StartPageVisitor(const Files& files) : _spg(files) { } bool Slice::StartPageVisitor::visitUnitStart(const UnitPtr&) { return true; } bool Slice::StartPageVisitor::visitModuleStart(const ModulePtr& m) { _spg.generate(m); return false; } TOCGenerator::TOCGenerator(const Files& files, const string& header, const string& footer) : GeneratorBase(_out, files) { _footer = footer; openDoc("_sindex.html", "Slice API Index", header, footer); start("h1"); _out << "Slice API Index"; end(); start("table", "ExpandCollapseButtonTable"); start("tbody"); start("tr"); start("td"); _out << ""; end(); start("td"); _out << ""; end(); end(); end(); end(); } void TOCGenerator::generate(const ModulePtr& m) { _modules.push_back(m); } void TOCGenerator::writeTOC() { _modules.sort(); _out << nl << "
    "; _out.inc(); for(ModuleList::const_iterator i = _modules.begin(); i != _modules.end(); ++i) { writeEntry(*i); } _out.dec(); _out << nl << "
"; _symbols.sort(); _symbols.unique(); string f = getFooter(_footer); closeDoc(getFooter(_footer)); } const ContainedList& TOCGenerator::symbols() const { return _symbols; } void TOCGenerator::writeEntry(const ContainedPtr& c) { ContainedList cl; ModulePtr m = ModulePtr::dynamicCast(c); if(m) { cl = m->contents(); } EnumPtr en = EnumPtr::dynamicCast(c); if(en) { EnumeratorList enumerators = en->enumerators(); for(EnumeratorList::const_iterator i = enumerators.begin(); i != enumerators.end(); ++i) { cl.push_back(*i); } } StructPtr s = StructPtr::dynamicCast(c); if(s) { DataMemberList dml = s->dataMembers(); for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i) { cl.push_back(*i); } } ExceptionPtr e = ExceptionPtr::dynamicCast(c); if(e) { DataMemberList dml = e->dataMembers(); for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i) { cl.push_back(*i); } } ClassDefPtr cdef = ClassDefPtr::dynamicCast(c); if(!cdef) { ClassDeclPtr cdec = ClassDeclPtr::dynamicCast(c); if(cdec) { cdef = cdec->definition(); } } if(cdef) { DataMemberList dml = cdef->dataMembers(); for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i) { cl.push_back(*i); } OperationList ol = cdef->operations(); for(OperationList::const_iterator j = ol.begin(); j != ol.end(); ++j) { cl.push_back(*j); } } start("li"); if(!cl.empty()) { cl.sort(); cl.unique(); _out << nl << getURL(c, 0, false, true); start("ul"); for(ContainedList::const_iterator i = cl.begin(); i != cl.end(); ++i) { writeEntry(*i); } end(); } else { _out << nl << getURL(c, 0, false, true); } if(ModulePtr::dynamicCast(c) || ExceptionPtr::dynamicCast(c) || ClassDefPtr::dynamicCast(c) || StructPtr::dynamicCast(c) || EnumPtr::dynamicCast(c)) { _symbols.push_back(c); } else if(ClassDeclPtr::dynamicCast(c)) { ContainedPtr definition = ClassDeclPtr::dynamicCast(c)->definition(); if(definition) { _symbols.push_back(definition); } } end(); } TOCVisitor::TOCVisitor(const Files& files, const string& header, const string& footer) : _tg(files, header, footer) { } bool TOCVisitor::visitUnitStart(const UnitPtr&) { return true; } bool TOCVisitor::visitModuleStart(const ModulePtr& m) { _tg.generate(m); return false; } void TOCVisitor::generate() { _tg.writeTOC(); } const ContainedList& TOCVisitor::symbols() const { return _tg.symbols(); } Slice::ModuleGenerator::ModuleGenerator(XMLOutput& o, const Files& files) : GeneratorBase(o, files) { } void Slice::ModuleGenerator::generate(const ModulePtr& m) { #ifndef NDEBUG int indent = _out.currIndent(); #endif openDoc(m); printHeaderFooter(m); _out << nl << "
"; start("h1", "Symbol"); _out << m->scoped().substr(2); end(); string metadata, deprecateReason; if(m->findMetaData("deprecate", metadata)) { deprecateReason = "This module is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } start("h2"); _out << "Overview"; end(); start("h3", "Synopsis"); printMetaData(m); _out << "module " << m->name(); end(); printComment(m, m, deprecateReason, true); visitContainer(m); _out << nl << "
"; printHeaderFooter(m); closeDoc(); assert(_out.currIndent() == indent); } void Slice::ModuleGenerator::visitContainer(const ContainerPtr& p) { #ifndef NDEBUG int indent = _out.currIndent(); #endif ModuleList modules = p->modules(); if(!modules.empty()) { start("h2"); _out << "Module Index"; end(); start("dl"); for(ModuleList::const_iterator q = modules.begin(); q != modules.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); ClassList classesAndInterfaces = p->classes(); ClassList classes; ClassList interfaces; remove_copy_if(classesAndInterfaces.begin(), classesAndInterfaces.end(), back_inserter(classes), ::IceUtil::constMemFun(&ClassDef::isInterface)); remove_copy_if(classesAndInterfaces.begin(), classesAndInterfaces.end(), back_inserter(interfaces), not1(::IceUtil::constMemFun(&ClassDef::isInterface))); if(!classes.empty()) { start("h2"); _out << "Class Index"; end(); start("dl"); for(ClassList::const_iterator q = classes.begin(); q != classes.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); if(!interfaces.empty()) { start("h2"); _out << "Interface Index"; end(); start("dl"); for(ClassList::const_iterator q = interfaces.begin(); q != interfaces.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); ExceptionList exceptions = p->exceptions(); if(!exceptions.empty()) { start("h2"); _out << "Exception Index"; end(); start("dl"); for(ExceptionList::const_iterator q = exceptions.begin(); q != exceptions.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); StructList structs = p->structs(); if(!structs.empty()) { start("h2"); _out << "Structure Index"; end(); start("dl"); for(StructList::const_iterator q = structs.begin(); q != structs.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); SequenceList sequences = p->sequences(); if(!sequences.empty()) { start("h2"); _out << "Sequence Index"; end(); start("dl"); for(SequenceList::const_iterator q = sequences.begin(); q != sequences.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); DictionaryList dictionaries = p->dictionaries(); if(!dictionaries.empty()) { start("h2"); _out << "Dictionary Index"; end(); start("dl"); for(DictionaryList::const_iterator q = dictionaries.begin(); q != dictionaries.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); ConstList consts = p->consts(); if(!consts.empty()) { start("h2"); _out << "Constant Index"; end(); start("dl"); for(ConstList::const_iterator q = consts.begin(); q != consts.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); EnumList enums = p->enums(); if(!enums.empty()) { start("h2"); _out << "Enumeration Index"; end(); start("dl"); for(EnumList::const_iterator q = enums.begin(); q != enums.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, p, false, false); end(); start("dd"); string metadata; printSummary(*q, p, (*q)->findMetaData("deprecate", metadata), true); end(); } end(); } assert(_out.currIndent() == indent); if(!sequences.empty()) { start("h2"); _out << "Sequences"; end(); for(SequenceList::const_iterator q = sequences.begin(); q != sequences.end(); ++q) { start("dl"); start("dt"); start("span", "Synopsis"); printMetaData(*q); if((*q)->isLocal()) { _out << "local "; } TypePtr type = (*q)->type(); _out << "sequence<" << getURL(type, p, false, false) << "> " << getURL(*q, p, true, false); end(); end(); start("dd"); string metadata, deprecateReason; if((*q)->findMetaData("deprecate", metadata)) { deprecateReason = "This type is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } printComment(*q, p, deprecateReason, true); end(); end(); } } if(!dictionaries.empty()) { start("h2"); _out << "Dictionaries"; end(); for(DictionaryList::const_iterator q = dictionaries.begin(); q != dictionaries.end(); ++q) { start("dl"); start("dt"); start("span", "Synopsis"); printMetaData(*q); if((*q)->isLocal()) { _out << "local "; } TypePtr keyType = (*q)->keyType(); TypePtr valueType = (*q)->valueType(); _out << "dictionary<" << getURL(keyType, p, false, false) << ", " << getURL(valueType, p, false, false) << "> " << getURL(*q, p, true, false); end(); end(); start("dd"); string metadata, deprecateReason; if((*q)->findMetaData("deprecate", metadata)) { deprecateReason = "This type is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } printComment(*q, p, deprecateReason, true); end(); end(); } } if(!consts.empty()) { start("h2"); _out << "Constants"; end(); for(ConstList::const_iterator q = consts.begin(); q != consts.end(); ++q) { start("dl"); start("dt"); start("span", "Synopsis"); _out << "const " << getURL((*q)->type(), p, false, false) << " " << getURL(*q, p, true, false) << " = " << (*q)->literal() << ";"; end(); end(); start("dd"); string metadata, deprecateReason; if((*q)->findMetaData("deprecate", metadata)) { deprecateReason = "This type is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } printComment(*q, p, deprecateReason, true); end(); end(); } } } Slice::ExceptionGenerator::ExceptionGenerator(XMLOutput& o, const Files& files) : GeneratorBase(o, files) { } void Slice::ExceptionGenerator::generate(const ExceptionPtr& e) { #ifndef NDEBUG int indent = _out.currIndent(); #endif openDoc(e); printHeaderFooter(e); _out << nl << "
"; start("h1", "Symbol"); _out << e->scoped().substr(2); end(); string metadata, deprecateReason; bool deprecatedException = e->findMetaData("deprecate", metadata); if(deprecatedException) { deprecateReason = "This module is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } start("h2"); _out << "Overview"; end(); start("h3", "Synopsis"); printMetaData(e); if(e->isLocal()) { _out << "local "; } _out << "exception " << e->name(); ExceptionPtr base = e->base(); if(base) { _out.inc(); _out << nl << "extends "; _out.inc(); _out << nl << getURL(base, e, false, false); _out.dec(); _out.dec(); } end(); printComment(e, e, deprecateReason); DataMemberList dataMembers = e->dataMembers(); if(_indexCount > 0 && dataMembers.size() >= _indexCount) { start("h2"); _out << "Data Member Index"; end(); start("dl"); for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, e, false, false); end(); start("dd"); string metadata; printSummary(*q, e, (*q)->findMetaData("deprecate", metadata), false); end(); } end(); } if(!dataMembers.empty()) { start("h2"); _out << "Data Members"; end(); start("dl"); for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { start("dt", "Symbol"); _out << "name() << "\"/>"; printMetaData(*q); if((*q)->optional()) { _out << "optional(" << (*q)->tag() << ") "; } TypePtr type = (*q)->type(); _out << getURL(type, e, false, false) << " " << (*q)->name() << ";"; end(); start("dd"); string reason; metadata.clear(); if(deprecatedException || (*q)->findMetaData("deprecate", metadata)) { reason = "This member is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { reason = metadata.substr(10); } } printComment(*q, e, reason); end(); } end(); } _out << nl << "
"; printHeaderFooter(e); closeDoc(); assert(_out.currIndent() == indent); } Slice::ClassGenerator::ClassGenerator(XMLOutput& o, const Files& files) : GeneratorBase(o, files) { } void Slice::ClassGenerator::generate(const ClassDefPtr& c) { #ifndef NDEBUG int indent = _out.currIndent(); #endif openDoc(c); printHeaderFooter(c); _out << nl << "
"; start("h1", "Symbol"); _out << c->scoped().substr(2); end(); string metadata, deprecateReason; bool deprecatedClass = c->findMetaData("deprecate", metadata); if(deprecatedClass) { deprecateReason = "This "; deprecateReason += c->isInterface() ? "interface" : "class"; deprecateReason += " is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } start("h2"); _out << "Overview"; end(); start("h3", "Synopsis"); printMetaData(c); if(c->isLocal()) { _out << "local "; } _out << (c->isInterface() ? "interface" : "class" )<< " " << c->name(); ClassList bases = c->bases(); if(!bases.empty() && !bases.front()->isInterface()) { _out << " extends " << getURL(bases.front(), c, false, false); bases.pop_front(); } if(!bases.empty()) { _out << (c->isInterface() ? " extends " : " implements "); ClassList::const_iterator q = bases.begin(); while(q != bases.end()) { _out << getURL(*q, c, false, false); if(++q != bases.end()) { _out << ", "; } } } end(); printComment(c, c, deprecateReason); OperationList operations = c->operations(); if(_indexCount > 0 && operations.size() >= _indexCount) { start("h2"); _out << "Operation Index"; end(); start("dl"); for(OperationList::const_iterator q = operations.begin(); q != operations.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, c, false, false); end(); start("dd"); string metadata; printSummary(*q, c, (*q)->findMetaData("deprecate", metadata), false); end(); } end(); } DataMemberList dataMembers = c->dataMembers(); if(_indexCount > 0 && dataMembers.size() >= _indexCount) { start("h2"); _out << "Data Member Index"; end(); start("dl"); for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, c, false, false); end(); start("dd"); string metadata; printSummary(*q, c, (*q)->findMetaData("deprecate", metadata), false); end(); } end(); } if(!operations.empty()) { start("h2"); _out << "Operations"; end(); for(OperationList::const_iterator q = operations.begin(); q != operations.end(); ++q) { start("h3", "Synopsis"); _out << "
name() << "\"/>"; const TypePtr returnType = (*q)->returnType(); if((*q)->returnIsOptional()) { _out << "optional(" << (*q)->returnTag() << ") "; } _out << (returnType ? getURL(returnType, c, false, false) : string("void")) << " " << (*q)->name() << "("; ParamDeclList params = (*q)->parameters(); ParamDeclList::const_iterator r = params.begin(); while(r != params.end()) { if((*r)->isOutParam()) { _out << "out "; } if((*r)->optional()) { _out << "optional(" << (*r)->tag() << ") "; } _out << getURL((*r)->type(), *q, false, false) << " " << (*r)->name(); if(++r != params.end()) { _out << ", "; } } _out << ")"; ExceptionList throws = (*q)->throws(); if(!throws.empty()) { _out << " throws "; ExceptionList::const_iterator t = throws.begin(); while(t != throws.end()) { _out << getURL(*t, c, false, false); if(++t != throws.end()) { _out << ", "; } } } end(); string reason; metadata.clear(); if(deprecatedClass || (*q)->findMetaData("deprecate", metadata)) { reason = "This operation is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { reason = metadata.substr(10); } } printComment(*q, c, reason); } } if(!dataMembers.empty()) { start("h2"); _out << "Data Members"; end(); for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { start("h3", "Synopsis"); _out << "name() << "\"/>"; printMetaData(*q); if((*q)->optional()) { _out << "optional(" << (*q)->tag() << ") "; } TypePtr type = (*q)->type(); _out << getURL(type, c, false, false) << " " << (*q)->name() << ";"; end(); string reason; metadata.clear(); if(deprecatedClass || (*q)->findMetaData("deprecate", metadata)) { reason = "This member is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { reason = metadata.substr(10); } } printComment(*q, c, reason); } } _out << nl << "
"; printHeaderFooter(c); closeDoc(); assert(_out.currIndent() == indent); } Slice::StructGenerator::StructGenerator(XMLOutput& o, const Files& files) : GeneratorBase(o, files) { } void Slice::StructGenerator::generate(const StructPtr& s) { #ifndef NDEBUG int indent = _out.currIndent(); #endif openDoc(s); printHeaderFooter(s); _out << nl << "
"; start("h1", "Symbol"); _out << s->scoped().substr(2); end(); string metadata, deprecateReason; bool deprecatedException = s->findMetaData("deprecate", metadata); if(deprecatedException) { deprecateReason = "This module is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } start("h2"); _out << "Overview"; end(); start("h3", "Synopsis"); printMetaData(s); if(s->isLocal()) { _out << "local "; } _out << "struct " << s->name(); end(); printComment(s, s, deprecateReason); DataMemberList dataMembers = s->dataMembers(); if(_indexCount > 0 && dataMembers.size() >= _indexCount) { start("h2"); _out << "Data Member Index"; end(); start("dl"); for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { start("dt", "Symbol"); _out << getURL(*q, s, false, false); end(); start("dd"); string metadata; printSummary(*q, s, (*q)->findMetaData("deprecate", metadata), false); end(); } end(); } if(!dataMembers.empty()) { start("h2"); _out << "Data Members"; end(); start("dl"); for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { start("dt", "Symbol"); _out << "
name() << "\"/>"; printMetaData(*q); TypePtr type = (*q)->type(); _out << getURL(type, s, false, false) << " " << (*q)->name() << ";"; end(); start("dd"); string reason; metadata.clear(); if(deprecatedException || (*q)->findMetaData("deprecate", metadata)) { reason = "This member is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { reason = metadata.substr(10); } } printComment(*q, s, reason); end(); } end(); } _out << nl << "
"; printHeaderFooter(s); closeDoc(); assert(_out.currIndent() == indent); } Slice::EnumGenerator::EnumGenerator(XMLOutput& o, const Files& files) : GeneratorBase(o, files) { } void Slice::EnumGenerator::generate(const EnumPtr& e) { #ifndef NDEBUG int indent = _out.currIndent(); #endif openDoc(e); printHeaderFooter(e); _out << nl << "
"; start("h1", "Symbol"); _out << e->scoped().substr(2); end(); string metadata, deprecateReason; bool deprecatedException = e->findMetaData("deprecate", metadata); if(deprecatedException) { deprecateReason = "This enumeration is deprecated."; if(metadata.find("deprecate:") == 0 && metadata.size() > 10) { deprecateReason = metadata.substr(10); } } start("h2"); _out << "Overview"; end(); start("h3", "Synopsis"); printMetaData(e); if(e->isLocal()) { _out << "local "; } _out << "enum " << e->name(); end(); printComment(e, e, deprecateReason); EnumeratorList enumerators = e->enumerators(); if(!enumerators.empty()) { start("h2"); _out << "Enumerators"; end(); start("dl"); for(EnumeratorList::const_iterator q = enumerators.begin(); q != enumerators.end(); ++q) { start("dt", "Symbol"); _out << (*q)->name(); end(); start("dd"); string reason; // // Enumerators do not support metadata. // printComment(*q, e, reason); end(); } end(); } closeDoc(); _out << nl << "
"; printHeaderFooter(e); assert(_out.currIndent() == indent); } Slice::PageVisitor::PageVisitor(const Files& files) : _files(files) { } bool Slice::PageVisitor::visitUnitStart(const UnitPtr&) { return true; } bool Slice::PageVisitor::visitModuleStart(const ModulePtr& m) { XMLOutput O; ModuleGenerator mg(O, _files); mg.generate(m); return true; } bool Slice::PageVisitor::visitExceptionStart(const ExceptionPtr& e) { XMLOutput O; ExceptionGenerator eg(O, _files); eg.generate(e); return true; } bool Slice::PageVisitor::visitClassDefStart(const ClassDefPtr& c) { XMLOutput O; ClassGenerator cg(O, _files); cg.generate(c); return true; } bool Slice::PageVisitor::visitStructStart(const StructPtr& s) { XMLOutput O; StructGenerator sg(O, _files); sg.generate(s); return true; } void Slice::PageVisitor::visitEnum(const EnumPtr& e) { XMLOutput O; EnumGenerator eg(O, _files); eg.generate(e); }