PLearn 0.1
|
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> > "; 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 by " << 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, "&", "&"); 00593 search_replace(s, "<", "<"); 00594 search_replace(s, ">", ">"); 00595 search_replace(s, "\"", """); 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> </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 :