PLearn 0.1
HTMLHelpCommand.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // HTMLHelpCommand.cc
00004 //
00005 // Copyright (C) 2004-2006 Nicolas Chapados 
00006 // 
00007 // Redistribution and use in source and binary forms, with or without
00008 // modification, are permitted provided that the following conditions are met:
00009 // 
00010 //  1. Redistributions of source code must retain the above copyright
00011 //     notice, this list of conditions and the following disclaimer.
00012 // 
00013 //  2. Redistributions in binary form must reproduce the above copyright
00014 //     notice, this list of conditions and the following disclaimer in the
00015 //     documentation and/or other materials provided with the distribution.
00016 // 
00017 //  3. The name of the authors may not be used to endorse or promote
00018 //     products derived from this software without specific prior written
00019 //     permission.
00020 // 
00021 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00022 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00023 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00024 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00025 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00026 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00027 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00028 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00029 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031 // 
00032 // This file is part of the PLearn library. For more information on the PLearn
00033 // library, go to the PLearn Web site at www.plearn.org
00034 
00035 /* *******************************************************      
00036  * $Id: HTMLHelpCommand.cc 7042 2007-05-09 23:44:20Z saintmlx $ 
00037  ******************************************************* */
00038 
00039 // Authors: Nicolas Chapados
00040 
00043 #define PL_LOG_MODULE_NAME "HTMLHelpCommand"
00044 
00045 #include <plearn/base/Object.h>
00046 #include <time.h>
00047 #include <fstream>
00048 #include <set>
00049 #include <boost/regex.hpp>
00050 
00051 #include <plearn/base/general.h>
00052 #include <plearn/base/TypeFactory.h>
00053 #include <plearn/base/stringutils.h>
00054 #include <plearn/io/pl_log.h>
00055 #include "PLearnCommandRegistry.h"
00056 #include "plearn_main.h"                     // For version_string
00057 #include "HTMLHelpCommand.h"
00058 #include <plearn/base/tostring.h>
00059 #include <plearn/base/HelpSystem.h>
00060 
00061 namespace PLearn {
00062 using namespace std;
00063 
00064 //#####  HTMLHelpConfig  ######################################################
00065 
00066 PLEARN_IMPLEMENT_OBJECT(
00067     HTMLHelpConfig,
00068     "Configuration options for HTMLHelpCommand",
00069     "Specifies generation options for HTML documentation."
00070     );
00071 
00072 void HTMLHelpConfig::declareOptions(OptionList& ol)
00073 {
00074     declareOption(ol, "output_dir", &HTMLHelpConfig::output_dir,
00075                   OptionBase::buildoption,
00076                   "Directory where the .html files should be generated");
00077 
00078     declareOption(ol, "html_index_document", &HTMLHelpConfig::html_index_document,
00079                   OptionBase::buildoption,
00080                   "Filename containing the main index file (index.html).  If missing,\n"
00081                   "a default index is generated.  Note that the prolog and epilog are\n"
00082                   "always generated for this file.");
00083     
00084     declareOption(ol, "html_prolog_document", &HTMLHelpConfig::html_prolog_document,
00085                   OptionBase::buildoption,
00086                   "Filename containing the \"top\" of the HTML document (including an\n"
00087                   "opening <body> tag");
00088                 
00089     declareOption(ol, "html_epilog_document", &HTMLHelpConfig::html_epilog_document,
00090                   OptionBase::buildoption,
00091                   "Filename containing the \"bottom\" of the HTML document (including the\n"
00092                   "closing </body> tag");
00093                 
00094     inherited::declareOptions(ol);
00095 }
00096 
00097 
00098 //#####  HTMLHelpCommand per se  ##############################################
00099   
00102 PLearnCommandRegistry HTMLHelpCommand::reg_(new HTMLHelpCommand);
00103 
00104 HTMLHelpCommand::HTMLHelpCommand():
00105     PLearnCommand("htmlhelp",
00106                   "Output HTML-formatted help for PLearn",
00107                   "This command provides basic HTML formatting for PLearn\n"
00108                   "commands and classes.  The command takes a single argument,\n"
00109                   "a filename containing an HTMLHelpConfig object giving the\n"
00110                   "configuration options for HTML generation." )
00111 {}
00112 
00114 void HTMLHelpCommand::run(const vector<string>& args)
00115 {
00116     if (args.size() != 1) {
00117         cout << helpmsg << endl;
00118     }
00119 
00120     const string& fname = args[0];
00121     config = dynamic_cast<HTMLHelpConfig*>(macroLoadObject(fname));
00122     if (!config)
00123         PLERROR("HTMLHelpCommand::run: the file '%s' must contain an "
00124                 "object of type HTMLHelpConfig", fname.c_str());
00125 
00126     helpCommands();
00127     helpClasses();
00128     helpIndex();
00129 }
00130 
00132 void HTMLHelpCommand::copySnippet(ostream& os, const string& document)
00133 {
00134     ifstream f(document.c_str());
00135     if (f)
00136         while (f) {
00137             int c = f.get();
00138             if (c > 0)
00139                 os.put(c);
00140         }
00141     else
00142         PLERROR("HTMLHelpCommand::copySnippet: cannot open file '%s' for reading",
00143                 document.c_str());
00144 }
00145 
00146 
00147 //#####  helpIndex  ###########################################################
00148 
00149 void HTMLHelpCommand::helpIndex()
00150 {
00151     PLASSERT( config );
00152     ofstream os((config->output_dir + "/" + "index.html").c_str());
00153     helpIndex(os, config);
00154 }
00155 
00156 void HTMLHelpCommand::helpIndex(ostream& os, const HTMLHelpConfig* config)
00157 {
00158     copySnippet(os, config->html_prolog_document);
00159 
00160     if (config->html_index_document.empty()) {
00161         os << "<div class=\"cmdname\">" << endl
00162            << "Welcome to PLearn User-Level Class and Commands Help" << endl
00163            << "</div>"
00164            << "<div class=\"cmdhelp\">" << endl
00165            << "<ul>" << endl
00166            << "  <li> <a href=\"class_index.html\">Class Index</a>" << endl
00167            << "  <li> <a href=\"command_index.html\">Command Index</a>" << endl
00168            << "</ul></div>" << endl;
00169     }
00170     else
00171         copySnippet(os, config->html_index_document);
00172 
00173     os << generated_by() << endl;
00174 
00175     copySnippet(os, config->html_epilog_document);
00176 }
00177   
00178 
00179 //#####  helpCommands  ######################################################
00180 
00181 void HTMLHelpCommand::helpCommands()
00182 {
00183     PLASSERT( config );
00184     ofstream os((config->output_dir + "/" + "command_index.html").c_str());
00185     helpCommands(os, config);
00186     
00187     for(PLearnCommandRegistry::command_map::iterator
00188             it  = PLearnCommandRegistry::commands().begin(),
00189             end = PLearnCommandRegistry::commands().end()
00190             ; it != end ; ++it)
00191     {
00192         // Generate help for this command
00193         helpOnCommand(it->first);
00194     }
00195 }
00196 
00197 void HTMLHelpCommand::helpCommands(ostream& os, const HTMLHelpConfig* config)
00198 {
00199     copySnippet(os, config->html_prolog_document);
00200   
00201     os << "<div class=\"generaltable\">" << endl
00202        << "<h2>Index of Available Commands</h2>" << endl
00203        << "<table cellspacing=\"0\" cellpadding=\"0\">" << endl;
00204 
00205     int i=2;                                   
00206     vector<string> args(1);
00207     for(PLearnCommandRegistry::command_map::iterator
00208             it  = PLearnCommandRegistry::commands().begin(),
00209             end = PLearnCommandRegistry::commands().end()
00210             ; it != end ; ++it, ++i)
00211     {
00212         string helpurl = string("command_") + it->first + ".html";
00213         string helphtml = "<a href=\"" + helpurl + "\">" + it->first + "</a>";
00214         os << string("  <tr class=\"") + (i%2 == 0? "even" : "odd") + "\">" << endl
00215            << "    <td>" + helphtml + "</td>"
00216            << "<td>" + it->second->description + "</td></tr>" << endl;
00217     }
00218     os << "</table>" << endl
00219        << "</div>" << endl;
00220 
00221     os << generated_by() << endl;
00222   
00223     copySnippet(os, config->html_epilog_document);
00224 }
00225 
00226 
00227 //#####  HelpOnCommand  #####################################################
00228 
00229 void HTMLHelpCommand::helpOnCommand(const string& theCommand)
00230 {
00231     PLASSERT( config );
00232     ofstream os((config->output_dir + "/" + "command_"+theCommand+".html").c_str());
00233     helpOnCommand(theCommand, os, config);
00234 }
00235 
00236 void HTMLHelpCommand::helpOnCommand(const string& theCommand, ostream& os,
00237                                     const HTMLHelpConfig* config)
00238 {
00239     copySnippet(os, config->html_prolog_document);
00240   
00241     PLearnCommandRegistry::command_map::iterator
00242         it  = PLearnCommandRegistry::commands().find(theCommand),
00243         end = PLearnCommandRegistry::commands().end();
00244     if (it == end)
00245         PLERROR("HTMLHelpCommand::helpOnCommand: no help for command '%s'",
00246                 theCommand.c_str());
00247     else
00248         os << "<div class=\"cmdname\">"
00249            << quote(theCommand) << "</div>" << endl
00250            << "<div class=\"cmddescr\">"
00251            << quote(it->second->description) << "</div>" << endl
00252            << "<div class=\"cmdhelp\">"
00253            << format_free_text(quote(it->second->helpmsg)) << "</div>" << endl;
00254 
00255     os << generated_by() << endl;
00256   
00257     copySnippet(os, config->html_epilog_document);
00258 }
00259 
00260 
00261 //#####  HelpClasses  #######################################################
00262 
00263 void HTMLHelpCommand::helpClasses()
00264 {
00265     PLASSERT( config );
00266     ofstream os((config->output_dir + "/" + "class_index.html").c_str());
00267     helpClasses(os, config);
00268     
00269     for(TypeMap::const_iterator
00270             it  = TypeFactory::instance().getTypeMap().begin(),
00271             end = TypeFactory::instance().getTypeMap().end()
00272             ; it != end ; ++it)
00273     {
00274         // Generate help for this class
00275         helpOnClass(it->first);
00276     }
00277 }
00278 
00279 void HTMLHelpCommand::helpClasses(ostream& os, const HTMLHelpConfig* config)
00280 {
00281     copySnippet(os, config->html_prolog_document);
00282   
00283     os << "<div class=\"generaltable\">" << endl
00284        << "<h2>Index of Available Classes</h2>" << endl
00285        << "<table cellspacing=\"0\" cellpadding=\"0\">" << endl;
00286 
00287     vector<string> args(1);
00288     int i=0;
00289     for(TypeMap::const_iterator
00290             it  = TypeFactory::instance().getTypeMap().begin(),
00291             end = TypeFactory::instance().getTypeMap().end()
00292             ; it != end ; ++it)
00293     {
00294         string helpurl = string("class_") + it->first + ".html?level=" 
00295             + tostring(OptionBase::getCurrentOptionLevel());
00296         string helphtml = "<a href=\"" + helpurl + "\">" + it->first + "</a>";
00297         os << string("  <tr class=\"") + (i++%2 == 0? "even" : "odd") + "\">" << endl
00298            << "    <td>" + helphtml + "</td>" << endl
00299            << "    <td>" + it->second.one_line_descr + "</td></tr>" << endl;
00300     }
00301     os << "</table>" << endl
00302        << "</div>" << endl;
00303 
00304     os << generated_by() << endl;
00305     copySnippet(os, config->html_epilog_document);
00306 }
00307 
00308 
00309 //#####  HelpOnClass  #######################################################
00310 
00311 string HTMLHelpCommand::flagsAndLevelHeading(const string& classname) const
00312 {
00313     string str= "<div class=\"levelnav\">";
00314 /*
00315  * Don't display option flags for now
00316     str+= "OptionFlags:";
00317     const OptionBase::StrToFlagMap& flag_map= OptionBase::getStrToFlagMap();
00318     for(OptionBase::StrToFlagMap::const_iterator it= flag_map.begin();
00319         it != flag_map.end(); ++it)
00320     {
00321         str+= "<a href=\"toggle_flag_" + tostring(it->second) + "\">";
00322         str+= it->second & OptionBase::getCurrentFlags() ? "[+]" : "[-]";
00323         str+= it->first + "</a>";
00324     }
00325     str+= "<nbsp><nbsp><nbsp><nbsp>";
00326 */
00327     str+= "OptionLevel:";
00328     const OptionBase::LevelToStrMap& lev_map= OptionBase::getLevelToStrMap();
00329     for(OptionBase::LevelToStrMap::const_iterator it= lev_map.begin();
00330         it != lev_map.end(); ++it)
00331     {
00332         if(it->first == OptionBase::getCurrentOptionLevel())
00333             str+= "<b>" + it->second + "</b>";
00334         else
00335             str+= "<a href=\"class_" + classname + ".html?level=" 
00336                 + tostring(it->first) + "\">" + it->second + "</a>";
00337     }
00338     str+= "</div>";
00339     return str;
00340 }
00341 
00342 
00343 void HTMLHelpCommand::helpOnClass(const string& classname)
00344 {
00345     PLASSERT( config );
00346     ofstream out((config->output_dir + "/class_" + classname + ".html").c_str());
00347     helpOnClass(classname, out, config);
00348 }
00349 
00350 void HTMLHelpCommand::helpOnClass(const string& classname, ostream& out,
00351                                   const HTMLHelpConfig* config)
00352 {
00353     copySnippet(out, config->html_prolog_document);
00354 
00355     const TypeMap& type_map = TypeFactory::instance().getTypeMap();
00356     TypeMap::const_iterator it = type_map.find(classname);
00357     TypeMap::const_iterator itend = type_map.end();
00358 
00359     if(it==itend)
00360         PLERROR("Object type %s unknown.\n"
00361                 "Did you #include it, does it call the IMPLEMENT_NAME_AND_DEEPCOPY macro?\n"
00362                 "and has it indeed been linked with your program?", classname.c_str());
00363 
00364     const TypeMapEntry& entry = it->second;
00365     Object* obj = 0;
00366 
00367     out << flagsAndLevelHeading(classname);
00368 
00369     // Determine list of parent classes and print it out as crumbtrail
00370     TVec<string> parents = parent_classes(classname);
00371     out << "<div class=\"crumbtrail\">";
00372     for (int i=0, n=parents.size() ; i<n ; ++i) {
00373         if (i < n-1)
00374             out << "<a href=\"class_" + parents[i] + ".html?level="
00375                 + tostring(OptionBase::getCurrentOptionLevel()) + "\">"
00376                 << parents[i] << "</a>&nbsp;&gt; ";
00377         else
00378             out << parents[i];
00379     }
00380     out << "</div>";
00381 
00382     // Output general information
00383     out << "<div class=\"classname\">"
00384         << quote(classname)
00385         << "</div>" << endl
00386         << "<div class=\"classdescr\">"
00387         << quote(entry.one_line_descr)
00388         << "</div>" << endl
00389         << "<div class=\"classhelp\">"
00390         << highlight_known_classes(format_free_text(quote(entry.multi_line_help)))
00391         << "</div>" << endl;
00392   
00393     if(entry.constructor) // it's an instantiable class
00394         obj = (*entry.constructor)();
00395     else
00396         out << "<div class=\"classhelp\"><b>Note:</b>"
00397             << quote(classname)
00398             << " is a base-class with pure virtual methods that cannot be instantiated directly.\n" 
00399             << "(default values for build options can only be displayed for instantiable classes, \n"
00400             << " so you'll only see question marks here.)</div>\n"
00401             << endl;
00402 
00403     // Output list of options
00404     out << "<div class=\"generaltable\">" << endl
00405         << "<h2>List of All Options</h2>" << endl
00406         << "<table cellspacing=\"0\" cellpadding=\"0\">" << endl;
00407   
00408     OptionList& options = (*entry.getoptionlist_method)();    
00409 
00410     int index = 0;
00411     for( OptionList::iterator olIt = options.begin(); olIt!=options.end();
00412          ++olIt ) {
00413 
00414         string descr    = (*olIt)->description();
00415         string optname  = (*olIt)->optionname();
00416         string opttype  = (*olIt)->optiontype();
00417         string optlevel = (*olIt)->levelString();
00418         string defclass = "";
00419         string defaultval = "?";
00420 
00421         // Determine the flag rendering
00422         string flag_string = join((*olIt)->flagStrings(), " | ");
00423 
00424         if((*olIt)->level() > OptionBase::getCurrentOptionLevel()
00425             || 0 == ((*olIt)->flags() & OptionBase::getCurrentFlags()))
00426             continue;
00427         if(obj) // it's an instantiable class
00428         {
00429             defaultval = (*olIt)->defaultval(); 
00430             if(defaultval=="")
00431                 defaultval = (*olIt)->writeIntoString(obj);
00432             defclass = (*olIt)->optionHolderClassName(obj);
00433         }
00434         out << string("  <tr class=\"") + (index++ % 2 == 0? "even" : "odd")
00435                                         + "\">" << endl
00436             << "    <td>"
00437             << "<div class=\"opttype\">" << highlight_known_classes(quote(opttype))
00438             << "</div>" << endl
00439             << "    <div class=\"optname\">" << quote(optname) << "</div>" << endl;
00440         if (defaultval != "?")
00441             out << "    <div class=\"optvalue\">= " << quote(defaultval) << "</div>" << endl;
00442         out << "    <div class=\"opttype\"><i>(" << flag_string << ")</i></div>" << endl;
00443         out << "    <div class=\"optlevel\"><i>(OptionLevel: " << optlevel << ")</i></div>" << endl;
00444         out << "    </td>" << endl;
00445         if (removeblanks(descr) == "")
00446             descr = "(no description)";
00447         out << "    <td>"
00448             << highlight_known_classes(format_free_text(quote(descr)));
00449         if (defclass != "") {
00450             out << "    <span class=\"fromclass\">"
00451                 << "(defined&nbsp;by&nbsp;" << highlight_known_classes(defclass) << ")"
00452                 << "</span>" << endl;
00453         }
00454         out << "    </td>" << endl
00455             << "  </tr>" << endl;
00456     }
00457     if (index == 0)
00458         out << "<tr><td>This class does not specify any build options.</td></tr>"
00459             << endl;
00460 
00461     out << "</table></div>" << endl;
00462 
00463     if(obj)
00464         delete obj;
00465 
00466     // Output instantiable derived classes
00467     out << "<div class=\"generaltable\">" << endl
00468         << "<h2>List of Instantiable Derived Classes</h2>" << endl
00469         << "<table cellspacing=\"0\" cellpadding=\"0\">" << endl;
00470 
00471     index = 0;
00472     for(it = type_map.begin(); it!=itend; ++it)
00473     {
00474         const TypeMapEntry& e = it->second;
00475         if(e.constructor && it->first!=classname)
00476         {
00477             Object* o = (*e.constructor)();
00478             if( (*entry.isa_method)(o) ) {
00479                 string helpurl = string("class_") + it->first + ".html"
00480                     + "?level=" + tostring(OptionBase::getCurrentOptionLevel());
00481                 out << string("  <tr class=\"") +
00482                        (index++%2 == 0? "even" : "odd") + "\">" << endl
00483                     << "    <td><a href=\"" << helpurl << "\">"
00484                     << quote(it->first) << "</a></td><td>" << quote(e.one_line_descr)
00485                     << "    </td>"
00486                     << "  </tr>" << endl;
00487             }
00488             if(o)
00489                 delete o;
00490         }
00491     }
00492     if (index == 0)
00493         out << "<tr><td>This class does not have instantiable derived classes.</td></tr>"
00494             << endl;
00495 
00496     out << "</table></div>" << endl;
00497 
00498     // Output remote-callable methods
00499     const RemoteMethodMap* rmm = &(*entry.get_remote_methods)();
00500     out << "<div class=\"rmitable\">" << endl
00501         << "<h2>List of Remote-Callable Methods</h2>" << endl
00502         << "<table cellspacing=\"0\" cellpadding=\"0\">" << endl;
00503     index = 0;
00504     while (rmm) {
00505         RemoteMethodMap::MethodMap::const_iterator
00506             rmmIt = rmm->begin(), end = rmm->end();
00507         for ( ; rmmIt != end ; ++rmmIt ) {
00508             const string& method_name = rmmIt->first.first;
00509             const RemoteTrampoline* t = rmmIt->second;
00510             PLASSERT( t );
00511             const RemoteMethodDoc& doc = t->documentation();
00512 
00513             // Generate the method signature and argument-list table in HTML form
00514             string return_type = highlight_known_classes(quote(doc.returnType()));
00515             string args = "";
00516             string arg_table = "";
00517             list<ArgDoc>::const_iterator
00518                 adit = doc.argListDoc().begin(), adend = doc.argListDoc().end();
00519             list<string>::const_iterator
00520                 tyit = doc.argListType().begin(), tyend = doc.argListType().end();
00521 
00522             int j=0;
00523             for ( ; tyit != tyend ; ++tyit) {
00524                 string arg_type = highlight_known_classes(quote(*tyit));
00525                 string arg_name = "";
00526                 string arg_doc  = "(no documentation)";
00527                 if (adit != adend) {
00528                     arg_name = quote(adit->m_argument_name);
00529                     arg_doc  = highlight_known_classes(format_free_text(adit->m_doc));
00530                     ++adit;
00531                 }
00532                 if (! args.empty())
00533                     args += ", ";
00534                 args += arg_type + ' ' + arg_name;
00535 
00536                 if (! arg_table.empty())
00537                     arg_table += "</tr><tr><td></td>";
00538 
00539                 string td1_class = (++j % 2 == 0? "argnameeven" : "argnameodd");
00540                 string td2_class = (  j % 2 == 0? "argdoceven"  : "argdocodd");
00541                 
00542                 arg_table +=
00543                       "  <td class=\"" + td1_class + "\">" + arg_type + ' ' + arg_name + "</td>"
00544                     + "  <td class=\"" + td2_class + "\">" + arg_doc  + "</td>";
00545             }
00546 
00547             string tr_class = (index++ == 0? "first" : "others");
00548             out << "<tr class=\"" << tr_class << +  + "\">" << endl
00549                 << "<td colspan=\"3\"><div class=\"rmiprototype\">"
00550                 << return_type
00551                 << " <span class=\"rmifuncname\">" << method_name << "</span>"
00552                 << '(' << args << ')' << "</div>" << endl
00553                 << "</tr><tr><td></td><td colspan=\"2\">" << doc.bodyDoc()
00554                 << "</td></tr>" << endl;
00555 
00556             if (! arg_table.empty())
00557                 out << "<tr>" << endl
00558                     << "<td class=\"rmititle\">Arguments</td>" << endl
00559                     << arg_table << "</tr>" << endl;
00560 
00561             string td1_class = (++j % 2 == 0? "argnameeven" : "argnameodd");
00562             string td2_class = (  j % 2 == 0? "argdoceven"  : "argdocodd");
00563             
00564             if (! doc.returnType().empty() || ! doc.returnDoc().empty()) {
00565                 string form_ret = highlight_known_classes(format_free_text(doc.returnDoc()));
00566                 out << "<tr>" << endl
00567                     << "<td class=\"rmititle\">Returns</td>" << endl
00568                     << "<td class=\"" + td1_class + "\">" << return_type << "</td>"
00569                     << "<td class=\"" + td2_class + "\">" << form_ret  << "</td>" << endl
00570                     << "</tr>"
00571                     << endl;
00572             }
00573         }
00574 
00575         // Inspect base classes
00576         rmm = rmm->inheritedMethods();
00577     }
00578     if (index == 0)
00579         out << "<tr><td>This class does not define any remote-callable methods.</td></tr>"
00580             << endl;
00581 
00582     out << "</table></div>" << endl;
00583 
00584     
00585     out << generated_by() << endl;
00586     copySnippet(out, config->html_epilog_document);
00587 }
00588 
00589 
00590 string HTMLHelpCommand::quote(string s) const
00591 {
00592     search_replace(s, "&", "&amp;");
00593     search_replace(s, "<", "&lt;");
00594     search_replace(s, ">", "&gt;");
00595     search_replace(s, "\"", "&quot;");
00596     return s;
00597 }
00598 
00599 
00600 TVec<string> HTMLHelpCommand::parent_classes(string classname) const
00601 {
00602     const TypeMap& type_map = TypeFactory::instance().getTypeMap();
00603     TVec<string> parents;
00604     while (classname != "") {
00605         TypeMap::const_iterator it = type_map.find(classname);
00606         if (it == type_map.end()) {
00607             PLWARNING("HTMLHelpCommand::parent_classes: cannot find class '%s'",
00608                       classname.c_str());
00609 
00610             static bool printed_parents = false;
00611             if (! printed_parents) {
00612                 MODULE_LOG << "Registered classes are:" << endl;
00613                 for (TypeMap::const_iterator types_it = type_map.begin(),
00614                          types_end = type_map.end() ; types_it != types_end ; ++types_it)
00615                     MODULE_LOG << types_it->first << endl;
00616                 printed_parents = true;
00617             }
00618             break;
00619         }
00620         parents.insert(0,classname);
00621         if (classname != it->second.parent_class) // Object is its own parent
00622             classname = it->second.parent_class;
00623         else break;
00624     }
00625     return parents;
00626 }
00627 
00628 
00629 string HTMLHelpCommand::highlight_known_classes(string typestr) const
00630 {
00631     vector<string> tokens = split(typestr, " \t\n\r<>,.';:\"");
00632     set<string> replaced; // Carry out replacements for a given token only once
00633     const TypeMap& type_map = TypeFactory::instance().getTypeMap();
00634     vector<string>::size_type n=tokens.size();
00635     for (unsigned int i=0; i<n ; ++i) {
00636         TypeMap::const_iterator it = type_map.find(tokens[i]);
00637         if (it != type_map.end() && replaced.find(tokens[i]) == replaced.end()) {
00638             replaced.insert(tokens[i]);
00639       
00640             // ensure we only match whole words with the regular expression
00641             const boost::regex e("\\<" + tokens[i] + "\\>");
00642             const string repl_str("<a href=\"class_$&.html\\?level="
00643                                   + tostring(OptionBase::getCurrentOptionLevel()) 
00644                                   +"\">$&</a>");
00645             typestr = regex_replace(typestr, e, repl_str, boost::match_default | boost::format_default);
00646         }
00647     }
00648     return typestr;
00649 }
00650 
00651 
00652 string HTMLHelpCommand::format_free_text(string text) const
00653 {
00654     // sort of DWIM HTML formatting for free-text; cannot use split since it
00655     // eats up consecutive delimiters
00656     text = removeblanks(text);
00657     size_t curpos = 0, curnl = text.find('\n');
00658     bool ul_active = false;
00659     bool start_paragraph = false;
00660     string finallines;
00661     for ( ; curpos != string::npos ;
00662           curpos = curnl+(curnl!=string::npos), curnl = text.find('\n', curpos) ) {
00663         string curline = text.substr(curpos, curnl-curpos);
00664 
00665         // step 1: check if the line starts with a '-': if so, start a new <UL>
00666         // if not in one, or extend one if so
00667         if (removeblanks(curline).substr(0,1) == "-" ||
00668             removeblanks(curline).substr(0,1) == "*" )
00669         {
00670             curline = removeblanks(curline).substr(1);
00671             if (! ul_active)
00672                 curline = "<ul><li>" + curline;
00673             else
00674                 curline = "<li>" + curline;
00675             start_paragraph = false;
00676             ul_active = true;
00677         }
00678 
00679         // otherwise, a line that starts with some whitespace within a list
00680         // just extends the previous <li> :: don't touch it
00681         else if (ul_active && (curline == "" ||
00682                                curline.substr(0,1) == " " ||
00683                                curline.substr(0,1) == "\t")) {
00684             /* do nothing */
00685         }
00686 
00687         // otherwise, normal processing
00688         else {
00689             // any line that is empty or starts with some whitespace gets its own <br>
00690             if (removeblanks(curline) == "") {
00691                 // Don't start new paragraph right away; wait until we
00692                 // encounter some text that's neither a <ul> or a <pre>
00693                 start_paragraph = true;
00694                 curline = "";
00695             }
00696             else if (curline[0] == ' ' || curline[0] == '\t') {
00697                 start_paragraph = false;
00698                 curline = "<pre>" + curline + "</pre>";
00699             }
00700 
00701             // if we were processing a list, close it first
00702             if (ul_active) {
00703                 curline = "</ul>" + curline;
00704                 ul_active = 0;
00705             }
00706         }
00707 
00708         if (!curline.empty() && start_paragraph) {
00709             finallines += "<p>";
00710             start_paragraph = false;
00711         }
00712         
00713         finallines += curline + "\n";
00714     }
00715 
00716     // Close any pending open blocks
00717     if (ul_active)
00718         finallines += "</ul>\n";
00719   
00720     // Finally join the lines
00721     return make_http_hyperlinks(finallines);
00722 }
00723 
00724 
00725 string HTMLHelpCommand::make_http_hyperlinks(string text) const
00726 {
00727     // Find elements of the form XYZ://x.y.z/a/b/c and make them into
00728     // hyperlink. An issue is to determine when
00729     static const char* recognized_protocols[] = 
00730         { "http://", "https://", "ftp://", "mailto:" };        // for now...
00731     static const vector<string> protocols_vector(
00732         recognized_protocols,
00733         recognized_protocols + sizeof(recognized_protocols) / sizeof(recognized_protocols[0]));
00734 
00735     // Match everything that starts with the recognized protocol up to the
00736     // following whitespace, excluding trailing punctuation if any.
00737     // Make sure the URL is NOT enclosed in quotes
00738     static const boost::regex e( string("(?!\")") + "(" +
00739                                  "(?:" + join(protocols_vector, "|") + ")" +
00740                                  "\\S+(?:\\w|/)" +
00741                                  ")" + "(?!\")" + "([[:punct:]]*\\s|$)");
00742     const string repl_str("<a href=\"$1\">$1</a>$2");
00743     text = regex_replace(text, e, repl_str, boost::match_default | boost::format_default);
00744     return text;
00745 }
00746 
00747 
00748 string HTMLHelpCommand::generated_by() const
00749 {
00750     time_t curtime = time(NULL);
00751     struct tm *broken_down_time = localtime(&curtime);
00752     const int SIZE = 100;
00753     char time_buffer[SIZE];
00754     strftime(time_buffer,SIZE,"%Y/%m/%d %H:%M:%S",broken_down_time);
00755 
00756     return string("<p>&nbsp;</p><address>Generated on " ) +
00757         time_buffer + " by " + version_string() + "</address>";
00758 }
00759 
00760 
00761 } // end of namespace PLearn
00762 
00763 
00764 /*
00765   Local Variables:
00766   mode:c++
00767   c-basic-offset:4
00768   c-file-style:"stroustrup"
00769   c-file-offsets:((innamespace . 0)(inline-open . 0))
00770   indent-tabs-mode:nil
00771   fill-column:79
00772   End:
00773 */
00774 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines