// @(#)root/html:$Id: THtml.cxx 21476 2007-12-19 10:51:52Z brun $
// Author: Nenad Buncic (18/10/95), Axel Naumann <mailto:axel@fnal.gov> (09/28/01)

/*************************************************************************
 * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "THtml.h"

#include "Riostream.h"
#include "TBaseClass.h"
#include "TClass.h"
#include "TClassDocOutput.h"
#include "TClassEdit.h"
#include "TClassTable.h"
#include "TDocInfo.h"
#include "TDocOutput.h"
#include "TEnv.h"
#include "TInterpreter.h"
#include "TObjString.h"
#include "TRegexp.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TThread.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <set>
#include <fstream>

THtml *gHtml = 0;

namespace {
   class THtmlThreadInfo {
   public:
      THtmlThreadInfo(THtml* html, bool force): fHtml(html), fForce(force) {}
      Bool_t GetForce() const {return fForce;}
      THtml* GetHtml() const {return fHtml;}

   private:
      THtml* fHtml;
      Bool_t fForce;
   };
};


////////////////////////////////////////////////////////////////////////////////
/* BEGIN_HTML
<p>The THtml class is designed to easily document
classes, code, and code related text files (like change logs). It generates HTML 
pages conforming to the XHTML 1.0 transitional specifications; an example of 
these pages is ROOT's own <a href="http://root.cern.ch/root/html/ClassIndex.html">
reference guide</a>. This page was verified to be valid XHTML 1.0 transitional, 
which proves that all pages generated by THtml can be valid, as long as the user 
provided XHTML (documentation, header, etc) is valid. You can check the current 
THtml by clicking this icon: 
<a href="http://validator.w3.org/check?uri=referer"><img
        src="http://www.w3.org/Icons/valid-xhtml10"
        alt="Valid XHTML 1.0 Transitional" height="31" width="88" style="border: none;"/></a></p>
Overview:
<ol style="list-style-type: upper-roman;">
  <li><a href="#usage">Usage</a></li>
  <li><a href="#conf">Configuration</a>
  <ol><li><a href="#conf:input">Input files</a></li>
  <li><a href="#conf:output">Output directory</a></li>
  <li><a href="#conf:liblink">Linking other documentation</a></li>
  <li><a href="#conf:classdoc">Recognizing class documentation</a></li>
  <li><a href="#conf:tags">Author, copyright, etc.</a></li>
  <li><a href="#conf:header">Header and footer</a></li>
  <li><a href="#conf:search">Links to searches, home page, ViewCVS</a></li>
  <li><a href="#conf:charset">HTML Charset</a></li>
  </ol></li>
  <li><a href="#syntax">Documentation syntax</a>
  <ol><li><a href="#syntax:classdesc">Class description</a></li>
  <li><a href="#syntax:classidx">Class index</a></li>
  <li><a href="#syntax:meth">Method documentation</a></li>
  <li><a href="#syntax:datamem">Data member documentation</a></li>
  </ol></li>
  <li><a href="#directive">Documentation directives</a>
  <ol><li><a href="#directive:html"><tt>BEGIN<!-- -->_HTML</tt> <tt>END<!-- -->_HTML</tt>: include 'raw' HTML</a></li>
  <li><a href="#directive:macro"><tt>BEGIN<!-- -->_MACRO</tt> <tt>END<!-- -->_MACRO</tt>: include a picture generated by a macro</a></li>
  <li><a href="#directive:latex"><tt>BEGIN<!-- -->_LATEX</tt> <tt>END<!-- -->_LATEX</tt>: include a latex picture</a></li>
  </ol></li>
  <li><a href="#index">Product and module index</a></li>
  <li><a href="#aux">Auxiliary files: style sheet, JavaScript, help page</a></li>
  <li><a href="#charts">Class Charts</a></li>
  <li><a href="#confvar">Configuration variables</a></li>
  <li><a href="#how">Behind the scenes</a></li>
</ol>


<h3><a name="usage">I. Usage</a></h3>
These are typical things people do with THtml:
<pre>
    root[] <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> html;                // create a <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> object
    root[] html.MakeAll();             // generate documentation for all changed classes
</pre>
or to run on just a few classes:
<pre>
    root[] <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> html;                // create a <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> object
    root[] html.MakeIndex();           // create auxilliary files (style sheet etc) and indices
    root[] html.MakeClass("TMyClass"); // create documentation for TMyClass only
</pre>
To "beautify" (i.e. create links to documentation for class names etc) some text 
file or macro, use:
<pre>
    root[] html.Convert( "hsimple.C", "Histogram example" )
</pre>


<h3><a name="conf">II. Configuration</a></h3>
Most configuration options can be set as a call to THtml, or as a TEnv variable,
which you can set in your .rootrc.

<h4><a name="conf:input">II.1 Input files</a></h4>

<p>In your .rootrc, define Root.Html.SourceDir to point to directories containing 
.cxx and .h files (see: <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>) 
of the classes you want to document, or call THtml::SetSourceDir()</p>

<p>Example:</p><pre>
  Root.Html.SourceDir:  .:src:include
  Root.Html.Root:       http://root.cern.ch/root/html</pre>


<h4><a name="conf:output">II.2 Output directory</a></h4>

<p>The output directory can be specified using the Root.Html.OutputDir
configuration variable (default value: "htmldoc"). If that directory 
doesn't exist <a href="http://root.cern.ch/root/html/THtml.html">THtml</a>
will create it.</p>

<p>Example:</p><pre>
  Root.Html.OutputDir:         htmldoc</pre>

<h4><a name="conf:liblink">II.3 Linking other documentation</a></h4>

<p>When trying to document a class, THtml searches for a source file in 
the directories set via SetSourceDir(). If it cannot find it, it assumes
that this class must have been documented before. Based on the library
this class is defined in, it checks the configuration variable
<tt>Root.Html.LibName</tt>, and creates a link using its value.
Alternatively, you can set these URLs via THtml::SetLibURL().</p>

<p>Example:<br/>
If a class MyClass is defined in class mylibs/libMyLib.so, and .rootrc
contains</p><pre>
  Root.Html.MyLib: ../mylib/</pre>
<p>THtml will create a link to "../mylib/MyClass.html".</p>

<p>The library name association can be set up using the rootmap facility.
For the library in the example above, which contains a dictionary 
generated from the linkdef MyLinkdef.h, the command to generate the
rootmap file is</p>
<pre>  $ rlibmap -f -r rootmap -l mylib/libMyLib.so -d libCore.so -c MyLinkdef.h</pre>
<p>Here, <tt>-r</tt> specifies that the entries for libMyLib should be updated,
<tt>-l</tt> specifies the library we're dealing with, <tt>-d</tt> its 
dependencies, and <tt>-c</tt> its linkdef. The rootmap file must be within
one of the <tt>LD_LIBRARY_PATH</tt> (or <tt>PATH</tt> for Windows) directories
when ROOT is started, otherwise ROOT will not use it.</p>

<h4><a name="conf:classdoc">II.4 Recognizing class documentation</a></h4>

<p>The class documentation has to appear in the header file containing the
class, right in front of its declaration. It is introduced by a string
defined by Root.Html.Description or SetClassDocTag(). See the section on 
<a href="#syntax">documentation syntax</a> for further details.</p>

<p>Example:</p><pre>
  Root.Html.Description:       //____________________</pre>

<p>The class documentation will show which include statement is to be used 
and which library needs to be linked to access it.
The include file name is determined via 
<a href="http://root.cern.ch/root/html/TClass.html#TClass:GetDeclFileName">
TClass::GetDeclFileName()</a>;
leading parts are removed if they match any of the ':' separated entries in 
THtml::GetIncludePath().</p>

<h4><a name="conf:tags">II.5 Author, copyright, etc.</a></h4>

<p>During the conversion, 
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will look for 
some strings ("tags") in the source file, which have to appear right in
front of e.g. the author's name, copyright notice, etc. These tags can be
defined with the following environment variables: Root.Html.Author,
Root.Html.LastUpdate and Root.Html.Copyright, or with
SetAuthorTag(), SetLastUpdateTag(), SetCopyrightTag().</p>

<p>If the LastUpdate tag is not found, the current date and time are used.
This is useful when using
<a href="http://root.cern.ch/root/html/THtml.html#THtml:MakeAll">THtml::MakeAll()</a>'s 
default option force=kFALSE, in which case 
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates 
documentation only for changed classes.</p>

Authors can be a comma separated list of author entries. Each entry has
one of the following two formats
<ul><li><tt>Name (non-alpha)</tt>.
<p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate an 
HTML link for <tt>Name</tt>, taking the Root.Html.XWho configuration
variable (defaults to "http://consult.cern.ch/xwho/people?") and adding 
all parts of the name with spaces replaces by '+'. Non-alphanumerical 
characters are printed out behind <tt>Name</tt>.</p>

<p>Example:</p>
<tt>// Author: Enrico Fermi</tt> appears in the source file.
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate the link
<tt>http://consult.cern.ch/xwho/people?Enrico+Fermi</tt>. This works well for
people at CERN.</li>

<li><tt>Name &lt;link&gt; Info</tt>.
<p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate 
an HTML link for <tt>Name</tt> as specified by <tt>link</tt> and print 
<tt>Info</tt> behind <tt>Name</tt>.</p>

<p>Example:</p>
<tt>// Author: Enrico Fermi &lt;http://www.enricos-home.it&gt;</tt> or<br/>
<tt>// Author: Enrico Fermi &lt;mailto:enrico@fnal.gov&gt;</tt> in the
source file. That's world compatible.</li>
</ul>

<p>Example (with defaults given):</p><pre>
      Root.Html.Author:     // Author:
      Root.Html.LastUpdate: // @(#)
      Root.Html.Copyright:  * Copyright
      Root.Html.XWho:       http://consult.cern.ch/xwho/people?</pre>


<h4><a name="conf:header">II.6 Header and footer</a></h4>

<p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates 
a default header and footer for all pages. You can
specify your own versions with the configuration variables Root.Html.Header
and Root.Html.Footer, or by calling SetHeader(), SetFooter().
Both variables default to "", using the standard Root
versions. If it has a "+" appended, <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will
write both versions (user and root) to a file, for the header in the order
1st root, 2nd user, and for the footer 1st user, 2nd root (the root
versions containing "&lt;html&gt;" and &lt;/html&gt; tags, resp).</p>

<p>If you want to replace root's header you have to write a file containing
all HTML elements necessary starting with the &lt;doctype&gt; tag and ending with
(and including) the &lt;body&gt; tag. If you add your header it will be added
directly after Root's &lt;body&gt; tag. Any occurrence of the string <tt>%TITLE%</tt>
in the user's header file will be replaced by
a sensible, automatically generated title. If the header is generated for a
class, occurrences of <tt>%CLASS%</tt> will be replaced by the current class's name,
<tt>%SRCFILE%</tt> and <tt>%INCFILE%</tt> by the name of the source and header file, resp.
(as given by <a href="http://root.cern.ch/root/html/TClass.html#TClass:GetImplFileLine">TClass::GetImplFileName()</a>,
<a href="http://root.cern.ch/root/html/TClass.html#TClass:GetImplFileLine">TClass::GetDeclFileName()</a>).
If the header is not generated for a class, they will be replaced by "".</p>

<p>Root's footer starts with the tag &lt;!--SIGNATURE--&gt;. It includes the
author(s), last update, copyright, the links to the Root home page, to the
user home page, to the index file (ClassIndex.html), to the top of the page
and <tt>this page is automatically generated</tt> infomation. It ends with the
tags <tt>&lt;/body&gt;&lt;/html&gt;</tt>. If you want to replace it, 
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will search for some
tags in your footer: Occurrences of the strings <tt>%AUTHOR%</tt>, <tt>%UPDATE%</tt>, and
<tt>%COPYRIGHT%</tt> are replaced by their
corresponding values before writing the html file. The <tt>%AUTHOR%</tt> tag will be
replaced by the exact string that follows Root.Html.Author, no link
generation will occur.</p>


<h4><a name="conf:search">II.7 Links to searches, home page, ViewCVS</a></h4>

<p>Additional parameters can be set by Root.Html.Homepage (address of the
user's home page), Root.Html.SearchEngine (search engine for the class
documentation), Root.Html.Search (search URL, where %u is replaced by the 
referer and %s by the escaped search expression), and a ViewCVS base URL 
Root.Html.ViewCVS. For the latter, the file name is appended or, if 
the ViewCVS contains %f, it %f is replaced by the file name.
All values default to "".</p>

<p>Examples:</p><pre>
      Root.Html.Homepage:     http://www.enricos-home.it
      Root.Html.SearchEngine: http://root.cern.ch/root/Search.phtml
      Root.Html.Search:       http://www.google.com/search?q=%s+site%3A%u</pre>


<h4><a name="conf:charset">II.8 HTML Charset</a></h4>

<p>XHTML 1.0 transitional recommends the specification of the charset in the
content type meta tag, see e.g. <a href="http://www.w3.org/TR/2002/REC-xhtml1-20020801/">http://www.w3.org/TR/2002/REC-xhtml1-20020801/</a>
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates it for the HTML output files. It defaults to ISO-8859-1, and
can be changed using Root.Html.Charset.</p>

<p>Example:</p><pre>
      Root.Html.Charset:      EUC-JP</pre>

<h3><a name="syntax">III. Documentation syntax</a></h3>
<h4><a name="syntax:classdesc">III.1 Class description</a></h4>

<p>A class description block, which must be placed before the first
member function, has a following form:</p>
<pre>
////////////////////////////////////////////////////////////////
//                                                            //
// TMyClass                                                   //
//                                                            //
// This is the description block.                             //
//                                                            //
////////////////////////////////////////////////////////////////
</pre>
<p>The environment variable Root.Html.Description 
(see: <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>) contains
the delimiter string (default value: <tt>//_________________</tt>). It means
that you can also write your class description block like this:</p>
<pre>
   //_____________________________________________________________
   // A description of the class starts with the line above, and
   // will take place here !
   //
</pre>
<p>Note that <b><i>everything</i></b> until the first non-commented line is considered
as a valid class description block.</p>

<h4><a name="syntax:classidx">III.2 Class index</a></h4>

<p>All classes to be documented will have an entry in the ClassIndex.html,
showing their name with a link to their documentation page and a miniature
description. This discription for e.g. the class MyClass has to be given
in MyClass's header as a comment right after ClassDef(MyClass, n).</p>

<h4><a name="syntax:meth">III.3 Method documentation</a></h4>
<p>A member function description block starts immediately after '{'
and looks like this:</p>
<pre>
   void TWorld::HelloWorldFunc(string *text)
   {
      // This is an example of description for the
      // TWorld member function

      helloWorld.Print( text );
   }
</pre>
Like in a class description block, <b><i>everything</i></b> until the first
non-commented line is considered as a valid member function
description block.

If the rootrc variable <tt>Root.Html.DescriptionStyle</tt> is set to 
<tt>Doc++</tt> THtml will also look for method documentation in front of
the function implementation. This feature is not recommended; source code
making use of this does not comply to the ROOT documentation standards, which
means future versions of THtml might not support it anymore.

<h4><a name="syntax:datamem">III.4 Data member documentation</a></h4>

<p>Data members are documented by putting a C++ comment behind their 
declaration in the header file, e.g.</p>
<pre>
   int fIAmADataMember; // this is a data member
</pre>


<h3><a name="directive">IV. Documentation directives</a></h3>
<em>NOTE that THtml does not yet support nested directives 
(i.e. latex inside html etc)!</em>

<h4><a name="directive:html">IV.1 <tt>BEGIN<!-- -->_HTML</tt> <tt>END<!-- -->_HTML</tt>: include 'raw' HTML</a></h4>

<p>You can insert pure html code into your documentation comments. During the
generation of the documentation, this code will be inserted as is
into the html file.</p>
<p>Pure html code must be surrounded by the keywords 
<tt>BEGIN<!-- -->_HTML</tt> and <tt>END<!-- -->_HTML</tt>, where the
case is ignored.
An example of pure html code is this class description you are reading right now.
THtml uses a 
<a href="http://root.cern.ch/root/html/TDocHtmlDirective.html">TDocHtmlDirective</a>
object to process this directive.</p>

<h4><a name="directive:macro">IV.2 <tt>BEGIN<!-- -->_MACRO</tt> <tt>END<!-- -->_MACRO</tt>: include a picture generated by a macro</a></h4>

<p>THtml can create images from scripts. You can either call an external 
script by surrounding it by "begin_macro"/"end_macro", or include an unnamed
macro within these keywords. The macro should return a pointer to an object;
this object will then be saved as a GIF file.</p>
<p>Objects deriving from 
<a href="http://root.cern.ch/root/html/TGObject.html">TGObject</a> (GUI elements)
will need to run in graphics mode (non-batch). You must specify this as a parameter:
"Begin_macro(GUI)...".
To create a second tab that displays the source of the macro you can specify
the argument "Begin_macro(source)...".
Of course you can combine them,
e.g. as "Begin_macro(source,gui)...". 
THtml uses a 
<a href="http://root.cern.ch/root/html/TDocMacroDirective.html">TDocMacroDirective</a>
object to process this directive.</p>
<p>This is an example:</p> END_HTML
BEGIN_MACRO(source)
{
  TCanvas* macro_example_canvas = new TCanvas("macro_example_canvas", "", 150, 150);
  macro_example_canvas->SetBorderSize(0);
  macro_example_canvas->SetFillStyle(1001);
  macro_example_canvas->SetFillColor(kWhite);
  macro_example_canvas->cd();
  TArc* macro_example_arc = new TArc(0.5,0.32,0.11,180,360);
  macro_example_arc->Draw();
  TEllipse* macro_example_ellipsis = new TEllipse(0.42,0.58,0.014,0.014,0,360,0);
  macro_example_ellipsis->Draw();
  macro_example_ellipsis = new TEllipse(0.58,0.58,0.014,0.014,0,360,0);
  macro_example_ellipsis->Draw();
  macro_example_ellipsis = new TEllipse(0.50,0.48,0.22,0.32,0,360,0);
  macro_example_ellipsis->Draw();
  TLine* macro_example_line = new TLine(0.48,0.53,0.52,0.41);
  macro_example_line->Draw();
  return macro_example_canvas;
}
END_MACRO

BEGIN_HTML
<h4><a name="directive:latex">IV.3 <tt>BEGIN<!-- -->_LATEX</tt> <tt>END<!-- -->_LATEX</tt>: include a latex picture</a></h4>

<p>You can specify <a href="http://root.cern.ch/root/html/TLatex.html">TLatex</a>
style text and let THtml convert it into an image by surrounding it by "Begin_Latex", "End_Latex".
You can have multiple lines, and e.g. align each line at the '=' sign by passing
the argument <tt>separator='='</tt>. You can also specify how to align these parts;
if you want the part left of the separator to be right aligned, and the right part
to be left aligned, you could specify <tt>align='rl'</tt>. 
THtml uses a <a href="http://root.cern.ch/root/html/TDocLatexDirective.html">TDocLatexDirective</a>
object to process the directive.
This is an example output with arguments <tt>separator='=', align='rl'</tt>:</p>
END_HTML BEGIN_LATEX(separator='=', align='rl')#kappa(x)^{2}=sin(x)^{x}
x=#chi^{2} END_LATEX

BEGIN_HTML

<h3><a name="index">V. Product and module index</a></h3>

<p><a href="#THtml:MakeIndex">THtml::MakeIndex()</a> will generate index files for classes
and types, all modules, and the product which you can set by 
<a href="#THtml:SetProductName">THtml::SetProductName()</a>.
THtml will make use of external documentation in the module and product index,
either by linking it or by including it.
For the product THtml will include files found in the directory defined by
<a href="#THtml:SetProductDocDir">THtml::SetProductDocDir()</a>.
The files for modules are searched based on the source file directory of the 
module's classes; the (possibly relative) path set by 
<a href="#THtml:SetModuleDocPath">THtml::SetModuleDocPath()</a> will guide THtml
to the files.</p>

<p>A filename starting with "index." will be included in the index page;
all other files will be linked.
Only files ending on <tt>.html</tt> or <tt>.txt</tt> will be taken into account;
the text files will first be run through 
<a href="#THtml:Convert">THtml::Convert()</a>.
You can see an example <a href="http://root.cern.ch/root/html/HIST_Index.html">here</a>;
the part between "Index of HIST classes" and "Jump to" is created by parsing
the module's doc directory.</p>

<h3><a name="aux">VI. Auxiliary files: style sheet, JavaScript, help page</a></h3>

<p>The documentation pages share a common set of javascript and CSS files. They
are generated automatically when running <a href="#THtml:MakeAll">MakeAll()</a>; 
they can be generated on 
demand by calling <a href="#THtml:CreateAuxiliaryFiles">CreateAuxiliaryFiles()</a>.</p>


<h3><a name="charts">VII. Class Charts</a></h3>
THtml can generate a number of graphical representations for a class, which
are displayed as a tabbed set of imaged ontop of the class description.
It can show the inheritance, inherited and hidden members, directly and 
indirectly included files, and library dependencies.

These graphs are generated using the <a href="http://www.graphviz.org/">Graphviz</a>
package. You can install it from <a href="http://www.graphviz.org">http://www.graphviz.org</a>.
You can either put it into your $PATH, or tell THtml where to find it by calling
<a href="#THtml:SetDotDir">SetDotDir()</a>.


<h3><a name="confvar">VIII. Configuration variables</a></h3>

<p>Here is a list of all configuration variables that are known to THtml.
You can set them in your .rootrc file, see 
<a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>.</p>

<pre>
  Root.Html.OutputDir    (default: htmldoc)
  Root.Html.SourceDir    (default: .:src/:include/)
  Root.Html.Author       (default: // Author:) - start tag for authors
  Root.Html.LastUpdate   (default: // @(#)) - start tag for last update
  Root.Html.Copyright    (default:  * Copyright) - start tag for copyright notice
  Root.Html.Description  (default: //____________________ ) - start tag for class descr
  Root.Html.HomePage     (default: ) - URL to the user defined home page
  Root.Html.Header       (default: ) - location of user defined header
  Root.Html.Footer       (default: ) - location of user defined footer
  Root.Html.Root         (default: ) - URL of Root's class documentation
  Root.Html.SearchEngine (default: ) - link to the search engine
  Root.Html.Search       (defualt: ) - link to search by replacing "%s" with user input
  Root.Html.ViewCVS      (default: ) - URL of ViewCVS base
  Root.Html.XWho         (default: http://consult.cern.ch/xwho/people?) - URL of CERN's xWho
  Root.Html.Charset      (default: ISO-8859-1) - HTML character set
</pre>

<h3><a name="how">IX. Behind the scene</a></h3>

<p>Internally, THtml is just an API class that sets up the list of known
classes, and forwards API invocations to the "work horses".
<a href="http://root.cern.ch/root/html/TDocOutput.html">TDocOutput</a>
generates the output by letting a
<a href="http://root.cern.ch/root/html/TDocParser.html">TDocParser</a>
object parse the sources, which in turn invokes objects deriving from
<a href="http://root.cern.ch/root/html/TDocDirective.html">TDocDirective</a>
to process directives.</p>

END_HTML */
////////////////////////////////////////////////////////////////////////////////

ClassImp(THtml)
//______________________________________________________________________________
THtml::THtml(): fIncludePath("include"), fFoundDot(-1), 
   fCounterFormat("%12s %5s %s"),
   fProductName("(UNKNOWN PRODUCT)"), fProductDocDir("doc"),
   fMacroPath("../doc/macros:."), fModuleDocPath("../doc"),
   fThreadedClassIter(0), fMakeClassMutex(0)

{
   // Create a THtml object.
   // In case output directory does not exist an error
   // will be printed and gHtml stays 0 also zombie bit will be set.

   // get prefix for source directory
   fSourcePrefix = gEnv->GetValue("Root.Html.SourcePrefix", "");

   // check for source directory
   fSourceDir = gEnv->GetValue("Root.Html.SourceDir", "./:src/:include/");

   // check for output directory
   fOutputDir = gEnv->GetValue("Root.Html.OutputDir", "htmldoc");

   fXwho = gEnv->GetValue("Root.Html.XWho", "http://consult.cern.ch/xwho/people?");
   fROOTURL = gEnv->GetValue("Root.Html.Root", "http://root.cern.ch/root/html");
   fClassDocTag = gEnv->GetValue("Root.Html.Description", "//____________________");
   fAuthorTag = gEnv->GetValue("Root.Html.Author", "// Author:");
   fLastUpdateTag = gEnv->GetValue("Root.Html.LastUpdate", "// @(#)");
   fCopyrightTag = gEnv->GetValue("Root.Html.Copyright", "* Copyright");
   fHeader = gEnv->GetValue("Root.Html.Header", "");
   fFooter = gEnv->GetValue("Root.Html.Footer", "");
   fHomepage = gEnv->GetValue("Root.Html.Homepage", "");
   fSearchStemURL = gEnv->GetValue("Root.Html.Search", "");
   fSearchEngine = gEnv->GetValue("Root.Html.SearchEngine", "");
   fViewCVS = gEnv->GetValue("Root.Html.ViewCVS", "");
   fCharset = gEnv->GetValue("Root.Html.Charset", "ISO-8859-1");
   fDocStyle = gEnv->GetValue("Root.Html.DescriptionStyle", "");

   fClasses.SetOwner();
   fModules.SetOwner();
   // insert html object in the list of special ROOT objects
   if (!gHtml) {
      gHtml = this;
      gROOT->GetListOfSpecials()->Add(gHtml);
   }

}


//______________________________________________________________________________
THtml::~THtml()
{
// Default destructor

   fClasses.Clear();
   fModules.Clear();
   if (gHtml == this) {
      gROOT->GetListOfSpecials()->Remove(gHtml);
      gHtml = 0;
   }
}

//______________________________________________________________________________
void THtml::AddMacroPath(const char* path)
{
   // Add path to the directories to be searched for macro files
   // that are to be executed via the TDocMacroDirective
   // ("Begin_Macro"/"End_Macro"); relative to the source file
   // that the directive is run on.

   const char pathDelimiter = 
#ifdef R__WIN32
      ';';
#else
      ':';
#endif
   fMacroPath += pathDelimiter;
   fMacroPath += path;
}


//______________________________________________________________________________
void THtml::CreateAuxiliaryFiles() const
{
   // copy CSS, javascript file, etc to the output dir
   CreateJavascript();
   CreateStyleSheet();
   CopyFileFromEtcDir("HELP.html");
}

//______________________________________________________________________________
const char* THtml::GetEtcDir()
{
// Get the directory containing THtml's auxiliary files ($ROOTSYS/etc/html)

   if (fEtcDir.Length())
      return fEtcDir;

   R__LOCKGUARD(GetMakeClassMutex());

   fEtcDir = "html";

#ifdef ROOTETCDIR
   gSystem->PrependPathName(ROOTETCDIR, fEtcDir);
#else
   gSystem->PrependPathName("etc", fEtcDir);
# ifdef ROOTPREFIX
   gSystem->PrependPathName(ROOTPREFIX, fEtcDir);
# else
   if (getenv("ROOTSYS"))
      gSystem->PrependPathName(getenv("ROOTSYS"), fEtcDir);
# endif
#endif

   return fEtcDir;
}


//______________________________________________________________________________
TClassDocInfo *THtml::GetNextClass()
{
   // Return the next class to be generated for MakeClassThreaded.

   if (!fThreadedClassIter) return 0;

   R__LOCKGUARD(GetMakeClassMutex());

   TClassDocInfo* classinfo = 0;
   while ((classinfo = (TClassDocInfo*)(*fThreadedClassIter)())
          && !classinfo->IsSelected());

   if (!classinfo) {
      delete fThreadedClassIter;
      fThreadedClassIter = 0;
   }

   fCounter.Form("%5d", fClasses.GetSize() - fThreadedClassCount++);

   return classinfo;
}


//______________________________________________________________________________
const char* THtml::GetURL(const char* lib /*=0*/) const
{
   // Get the documentation URL for library lib.
   // If lib == 0 or no documentation URL has been set for lib, return the ROOT 
   // documentation URL. The return value is always != 0.

   R__LOCKGUARD(GetMakeClassMutex());

   if (lib && strlen(lib)) {
      std::map<std::string, TString>::const_iterator iUrl = fLibURLs.find(lib);
      if (iUrl != fLibURLs.end()) return iUrl->second;
      return gEnv->GetValue(TString("Root.Html.") + lib, fROOTURL);
   }
   return fROOTURL;
}

//______________________________________________________________________________
Bool_t THtml::HaveDot()
{
   // Check whether dot is available in $PATH or in the directory set 
   // by SetDotPath()

   if (fFoundDot != -1) 
      return (Bool_t)fFoundDot;

   R__LOCKGUARD(GetMakeClassMutex());

   Info("HaveDot", "Checking for Graphviz (dot)...");
   TString runDot("dot");
   if (fDotDir.Length())
      gSystem->PrependPathName(fDotDir, runDot);
   runDot += " -V";
   if (gDebug > 3)
      Info("HaveDot", "Running: %s", runDot.Data());
   if (gSystem->Exec(runDot)) {
      fFoundDot = 0;
      return kFALSE;
   }
   fFoundDot = 1;
   return kTRUE;

}

//______________________________________________________________________________
void THtml::Convert(const char *filename, const char *title,
                    const char *dirname /*= ""*/, const char *relpath /*= "../"*/)
{
// It converts a single text file to HTML
//
//
// Input: filename - name of the file to convert
//        title    - title which will be placed at the top of the HTML file
//        dirname  - optional parameter, if it's not specified, output will
//                   be placed in htmldoc/examples directory.
//        relpath  - optional parameter pointing to the THtml generated doc 
//                   on the server, relative to the current page.
//
//  NOTE: Output file name is the same as filename, but with extension .html
//

   gROOT->GetListOfGlobals(kTRUE);        // force update of this list
   CreateListOfClasses("*");

   const char *dir;

   // if it's not defined, make the "examples" as a default directory
   if (!*dirname) {
      gSystem->ExpandPathName(fOutputDir);
      dir = gSystem->ConcatFileName(fOutputDir, "examples");
   } else
      dir = dirname;

   // create directory if necessary
   if (gSystem->AccessPathName(dir))
      gSystem->MakeDirectory(dir);

   // find a file
   char *realFilename =
       gSystem->Which(fSourceDir, filename, kReadPermission);

   if (!realFilename) {
      Error("Convert", "Can't find file '%s' !", filename);
      return;
   }

   // open source file
   ifstream sourceFile;
   sourceFile.open(realFilename, ios::in);

   delete[]realFilename;
   realFilename = 0;

   if (!sourceFile.good()) {
      Error("Convert", "Can't open file '%s' !", realFilename);
      return;
   }

   if (gSystem->AccessPathName(dir)) {
      Error("Convert",
            "Directory '%s' doesn't exist, or it's write protected !", dir);
      return;
   }
   char *tmp1 =
       gSystem->ConcatFileName(dir, GetFileName(filename));

   TDocOutput output(*this);
   output.Convert(sourceFile, tmp1, title, relpath);

   if (tmp1)
      delete[]tmp1;
   tmp1 = 0;
}


//______________________________________________________________________________
void THtml::GetModuleName(TString& modulename, const char* filename) const 
{
   // Returns the module a class with filename belongs to.
   // For ROOT, this is determined by MODULE/src/*.cxx or MODULE/inc/*.h. 
   // Math/GenVector (MATHCORE) and Math/Matrix (SMATRIX) get special
   // treatment.
   // All classes not fitting into this layout are assigned to the
   // module USER.

   size_t offset = 0;
   if (filename[0] == '.' && (filename[1] == '/' || filename[1] == '\\'))
      offset = 2;

   modulename = filename + offset;
   const char* posSlash = strchr(filename + offset, '/');
   const char *srcdir = 0;
   if (posSlash) {
      // for new ROOT install the impl file name has the form: base/src/TROOT.cxx
      srcdir = strstr(posSlash, "/src/");
      
      // if impl is unset, check for decl and see if it matches
      // format "base/inc/TROOT.h" - in which case it's not a USER
      // class, but a BASE class.
      if (!srcdir) srcdir=strstr(posSlash, "/inc/");
   } else srcdir = 0;

   if (srcdir && srcdir == posSlash) {
      modulename.Remove(srcdir - (filename + offset), modulename.Length());
      modulename.ToUpper();
   } else {
      if (posSlash && !strncmp(posSlash,"/Math/GenVector/", 16))
         modulename = "MATHCORE";
      else if (posSlash && !strncmp(posSlash,"/Math/Matrix", 12))
         modulename = "SMATRIX";
      else
         modulename = "USER";
   }
}

//______________________________________________________________________________
void  THtml::GetModuleNameForClass(TString& module, TClass* cl) const
{
   // Return the module name for a given class.
   // Use the cached information from fClasses.

   module = "(UNKNOWN)";
   TClassDocInfo* cdi = (TClassDocInfo*)fClasses.FindObject(cl->GetName());
   if (!cdi || !cdi->GetModule())
      return;
   module = cdi->GetModule()->GetName();
}

//______________________________________________________________________________
void THtml::CreateListOfClasses(const char* filter)
{
// Create the list of all known classes

   if (fClasses.GetSize() && fClassFilter == filter)
      return;

   Info("CreateListOfClasses", "Initializing - this might take a while...");
   // get total number of classes
   Int_t totalNumberOfClasses = gClassTable->Classes();

   // allocate memory
   fClasses.Clear();
   fModules.Clear();

   fClassFilter = filter;

   // start from begining
   gClassTable->Init();

   TString reg = filter;
   TRegexp re(reg, kTRUE);

   for (Int_t i = 0; i < totalNumberOfClasses; i++) {

      // get class name
      const char *cname = gClassTable->Next();
      TString s = cname;

      // This is a hack for until after Cint and Reflex are one.
      if (strstr(cname, "__gnu_cxx::")) continue;

      // get class & filename - use TROOT::GetClass, as we also
      // want those classes without decl file name!
      TClass *classPtr = TClass::GetClass((const char *) cname, kTRUE);
      if (!classPtr) continue;

      TString srcGuess;
      TString hdrGuess;
      const char *impname=GetImplFileName(classPtr);
      if (!impname || !impname[0]) {
         impname = GetDeclFileName(classPtr);
         if (impname && !impname[0]) {
            // no impl, no decl - might be a cintex dict
            // use namespace to decrypt path.
            TString impnameString(cname);
            TObjArray* arrScopes = impnameString.Tokenize("::");

            // for A::B::C, we assume B to be the module, 
            // b/inc/B/C.h the header, and b/src/C.cxx the source.
            TIter iScope(arrScopes, kIterBackward);
            TObjString *osFile   = (TObjString*)iScope();
            TObjString *osModule = 0;
            if (osFile) osModule = (TObjString*)iScope();

            if (osModule) {
               hdrGuess = osModule->String();
               hdrGuess.ToLower();
               hdrGuess += "/inc/";
               hdrGuess += osModule->String();
               hdrGuess += "/";
               hdrGuess += osFile->String();
               hdrGuess += ".h";
               char* realFile = gSystem->Which(fSourceDir, hdrGuess, kReadPermission);
               if (realFile) {
                  delete realFile;
                  fGuessedDeclFileNames[classPtr] = hdrGuess.Data();
                  impname = hdrGuess.Data();
                  
                  // only check for source if we've found the header!
                  srcGuess = osModule->String();
                  srcGuess.ToLower();
                  srcGuess += "/src/";
                  srcGuess += osFile->String();
                  srcGuess += ".cxx";
                  realFile = gSystem->Which(fSourceDir, srcGuess, kReadPermission);
                  if (realFile) {
                     delete realFile;
                     fGuessedImplFileNames[classPtr] = srcGuess.Data();
                     impname = srcGuess.Data();
                  }
               }
            }
            delete arrScopes;
         }
      }

      if (!impname || !impname[0]) {
         cout << "WARNING class " << cname <<
            " has no implementation file name !" << endl;
         continue;
      }

      if (strstr(impname,"prec_stl/")) continue;
      //if (strstr(cname, "ROOT::") && !strstr(cname,"Math::")
      //    && !strstr(cname,"Reflex::") && !strstr(cname,"Cintex::"))
      //   continue;

      TString htmlfilename;
      GetHtmlFileName(classPtr, htmlfilename);
      TClassDocInfo* cdi = new TClassDocInfo(classPtr, htmlfilename.Data());
      cdi->SetSelected(!(filter && filter[0] && strcmp(filter,"*") && s.Index(re) == kNPOS));
      char* realFile = gSystem->Which(fSourceDir, impname, kReadPermission); // delete at end of block
      cdi->SetHaveSource((realFile));

      fClasses.Add(cdi);

      TString modulename;
      GetModuleName(modulename, impname);
      if (!modulename.Length() || modulename == "USER") 
         GetModuleNameForClass(modulename, classPtr);
      if (modulename == "(UNKNOWN)") modulename = "USER";
      if (!modulename.Length() || modulename == "USER") {
         modulename = classPtr->GetSharedLibs();
         Ssiz_t pos = modulename.Index(' ');
         if (pos != kNPOS)
            modulename.Remove(pos, modulename.Length());
         if (modulename.BeginsWith("lib"))
            modulename.Remove(0,3);
         pos = modulename.Index('.');
         if (pos != kNPOS)
            modulename.Remove(pos, modulename.Length());
         modulename.ToUpper();
      }
      if (!modulename.Length())
         if (strstr(cname, "::SMatrix<") || strstr(cname, "::SVector<"))
            modulename = "SMATRIX";
         else if (strstr(cname, "::TArrayProxy<") || strstr(cname, "::TClaArrayProxy<")
                  || strstr(cname, "::TImpProxy<") || strstr(cname, "::TClaImpProxy<"))
            modulename = "TREEPLAYER";
      if (!modulename.Length())
         modulename = "USER";
      
      TModuleDocInfo* module = (TModuleDocInfo*) fModules.FindObject(modulename);
      if (!module) {
         module = new TModuleDocInfo(modulename);

         TString moduledir;
         if (modulename == "MATHCORE")
            moduledir = "mathcore/src";
         else if (modulename == "MATHMORE")
            moduledir = "mathmore/src";
         else if (modulename == "REFLEX")
            moduledir = "reflex/src";
         else if (modulename == "TMVA")
            moduledir = "tmva/src";
         else if (modulename == "SMATRIX")
            moduledir = "smatrix/src";
         if (moduledir.Length())
            module->SetSourceDir(moduledir);

         module->SetSelected(kFALSE);
         fModules.Add(module);
      }
      if (module) {
         if (!strcmp(module->GetName(), "REFLEX")
             // take class doc from header, so ignore sources:
             //|| !strcmp(module->GetName(), "MATHCORE")
             //|| !strcmp(module->GetName(), "MATHMORE")
             ) {
            TString srcFile = gSystem->BaseName(impname);
            srcFile.ReplaceAll(".h", ".cxx");
            gSystem->PrependPathName(module->GetSourceDir(), srcFile);
            if (!gSystem->AccessPathName(srcFile))
               SetImplFileName(classPtr, srcFile);
         }

         module->AddClass(cdi);
         cdi->SetModule(module);
         if (cdi->HaveSource() && cdi->IsSelected())
            module->SetSelected();
         if (cdi->HaveSource() && !module->GetSourceDir().Length()) {
            TString realfile(GetImplFileName(classPtr));
            if (gSystem->FindFile(fSourceDir, realfile, kReadPermission))
               module->SetSourceDir(gSystem->DirName(realfile));
         }
      }
      delete[] realFile;
   }

   fClasses.Sort();
   fModules.Sort();

   if (fProductName == "(UNKNOWN PRODUCT)" 
      && fModules.FindObject("BASE") 
      && fModules.FindObject("CONT") 
      && fModules.FindObject("RINT")
      && gProgName && strstr(gProgName, "root"))
      // if we have these modules we're probably building the root doc
      fProductName = "ROOT";

   if (fProductName == "(UNKNOWN PRODUCT)")
      Warning("CreateListOfClasses", "Product not set. You should call gHtml->SetProduct(\"MyProductName\");");

   Info("CreateListOfClasses", "Initializing - DONE.");
}


//______________________________________________________________________________
void THtml::CreateListOfTypes()
{
// Create index of all data types

   TDocOutput output(*this);
   output.CreateTypeIndex();

}

//______________________________________________________________________________
Bool_t THtml::CopyFileFromEtcDir(const char* filename) const {
   // Copy a file from $ROOTSYS/etc/html into GetOutputDir()

   R__LOCKGUARD(GetMakeClassMutex());

   TString outFile(filename);

   TString inFile(outFile);
   gSystem->PrependPathName("html", inFile);
#ifndef ROOTETCDIR
   gSystem->PrependPathName("etc", inFile);
   gSystem->PrependPathName(gRootDir, inFile);
#else
   gSystem->PrependPathName(ROOTETCDIR, inFile);
#endif

   gSystem->PrependPathName(GetOutputDir(), outFile);

   if (gSystem->CopyFile(inFile, outFile, kTRUE) != 0) {
      Warning("CopyFileFromEtcDir", "Could not copy %s to %s", inFile.Data(), outFile.Data());
      return kFALSE;
   }

   return kTRUE;
}

//______________________________________________________________________________
void THtml::CreateHierarchy()
{
   // Create the inheritance hierarchy diagram for all classes
   TDocOutput output(*this);
   output.CreateHierarchy();
}

//______________________________________________________________________________
void THtml::CreateJavascript() const {
   // Write the default ROOT style sheet.
   CopyFileFromEtcDir("ROOT.js");
}

//______________________________________________________________________________
void THtml::CreateStyleSheet() const {
   // Write the default ROOT style sheet.
   CopyFileFromEtcDir("ROOT.css");
   CopyFileFromEtcDir("shadowAlpha.png");
   CopyFileFromEtcDir("shadow.gif");
}



//______________________________________________________________________________
void THtml::GetDerivedClasses(TClass* cl, std::map<TClass*, Int_t>& derived) const
{
   // fill derived with all classes inheriting from cl and their inheritance 
   // distance to cl

   TIter iClass(&fClasses);
   TClassDocInfo* cdi = 0;
   while ((cdi = (TClassDocInfo*) iClass())) {
      TClass* candidate = cdi->GetClass();
      if (!candidate) continue;
      if (candidate != cl && candidate->InheritsFrom(cl)) {
         Int_t level = 0;
         TClass* currentBaseOfCandidate = candidate;
         while (currentBaseOfCandidate != cl) {
            TList* bases = currentBaseOfCandidate->GetListOfBases();
            if (!bases) continue;
            TIter iBase(bases);
            TBaseClass* base = 0;
            while ((base = (TBaseClass*) iBase())) {
               TClass* clBase = base->GetClassPointer();
               if (clBase && clBase->InheritsFrom(cl)) {
                  ++level;
                  currentBaseOfCandidate = clBase;
               }
            }
         }
         derived[candidate] = level;
      }
   }
}

//______________________________________________________________________________
const char *THtml::GetFileName(const char *filename) const
{
// It discards any directory information inside filename
//
//
//  Input: filename - pointer to the file name
//
// Output: pointer to the string containing just a file name
//         without any other directory information, i.e.
//         '/usr/root/test.dat' will return 'test.dat'
//

   if (!filename || !filename[0]) return "";
   return gSystem->BaseName(filename);
}

//______________________________________________________________________________
void THtml::GetSourceFileName(TString& filename)
{
   // Find the source file. If filename contains a path it will be used
   // together with the possible source prefix. If not found we try
   // old algorithm, by stripping off the path and trying to find it in the
   // specified source search path.

   TString found(filename);

   if (strchr(filename, '/') 
#ifdef WIN32
   || strchr(filename, '\\')
#endif
   ){
      TString found(fSourcePrefix);
      if (found.Length())
         gSystem->PrependPathName(found, filename);
      gSystem->FindFile(fSourceDir, filename, kReadPermission);
      if (filename.Length())
         return;
   }

   filename = GetFileName(filename);
   if (filename.Length())
      gSystem->FindFile(fSourceDir, filename, kReadPermission);
}

//______________________________________________________________________________
void THtml::GetHtmlFileName(TClass * classPtr, TString& filename) const
{
// Return real HTML filename
//
//
//  Input: classPtr - pointer to a class
//         filename - string containing a full name
//         of the corresponding HTML file after the function returns. 
//

   filename.Remove(0);
   if (!classPtr) return;

   const char* cFilename = GetImplFileName(classPtr);
   if (!cFilename || !cFilename[0])
      cFilename = GetDeclFileName(classPtr);

   // classes without Impl/DeclFileName don't have docs,
   // and classes without docs don't have output file names
   if (!cFilename || !cFilename[0])
      return;

   TString libName;
   const char *colon = strchr(cFilename, ':');
   if (colon)
      // old version, where source file name is prepended by "TAG:"
      libName = TString(cFilename, colon - cFilename);
   else
      // New version, check class's libname.
      // If libname is dir/libMyLib.so, check Root.Html.MyLib
      // If libname is myOtherLib.so.2.3, check Root.Html.myOtherLib
      // (i.e. remove directories, "lib" prefix, and any "extension")
      if (classPtr->GetSharedLibs()) {
         // first one is the class's lib
         TString libname(classPtr->GetSharedLibs());
         Ssiz_t posSpace = libname.First(' ');
         if (posSpace != kNPOS)
            libname.Remove(posSpace, libname.Length());
         TString libnameBase = gSystem->BaseName(libname);
         if (libnameBase.BeginsWith("lib"))
            libnameBase.Remove(0, 3);
         Ssiz_t posExt = libnameBase.First('.');
         if (posExt != '.')
            libnameBase.Remove(posExt, libnameBase.Length());
         if (libnameBase.Length())
            libName = libnameBase;
      }

   filename = cFilename;
   TString htmlFileName;
   if (!filename.Length() ||
      !gSystem->FindFile(fSourceDir, filename, kReadPermission)) {
      htmlFileName = GetURL(libName);
   } else
      htmlFileName = "./";

   if (htmlFileName.Length()) {
      filename = htmlFileName;
      TString className(classPtr->GetName());
      TDocOutput output(*const_cast<THtml*>(this));
      output.NameSpace2FileName(className);
      gSystem->PrependPathName(filename, className);
      filename = className;
      filename.ReplaceAll("\\", "/");
      filename += ".html";
   } else filename.Remove(0);
}

//______________________________________________________________________________
const char* THtml::GetHtmlFileName(const char* classname) const
{
   // Get the html file name for a class named classname.
   // Returns 0 if the class is not documented.
   TClassDocInfo* cdi = (TClassDocInfo*) fClasses.FindObject(classname);
   if (cdi)
      return cdi->GetHtmlFileName();
   return 0;
}

//______________________________________________________________________________
TClass *THtml::GetClass(const char *name1) const
{
//*-*-*-*-*Return pointer to class with name*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*      =================================
   if(!name1 || !name1[0]) return 0;
   // no doc for internal classes
   if (strstr(name1,"ROOT::")==name1) {
      Bool_t ret = kTRUE;
      if (!strncmp(name1 + 6,"Math", 4))   ret = kFALSE;
      if (!strncmp(name1 + 6,"Reflex", 6)) ret = kFALSE;
      if (!strncmp(name1 + 6,"Cintex", 6)) ret = kFALSE;
      if (ret) return 0;
   }

   TClassDocInfo* cdi = (TClassDocInfo*)fClasses.FindObject(name1);
   if (!cdi) return 0;
   TClass *cl=cdi->GetClass();
   // hack to get rid of prec_stl types
   // TClassEdit checks are far too slow...
   /*
   if (cl && GetDeclFileName(cl) &&
       strstr(GetDeclFileName(cl),"prec_stl/"))
      cl = 0;
   */
   if (cl && GetDeclFileName(cl) && GetDeclFileName(cl)[0])
      return cl;
   return 0;
}

//______________________________________________________________________________
const char* THtml::GetDeclFileName(TClass * cl) const
{
   // Return declaration file name

   R__LOCKGUARD(GetMakeClassMutex());
   std::map<TClass*,std::string>::const_iterator iClDecl = fGuessedDeclFileNames.find(cl);
   if (iClDecl == fGuessedDeclFileNames.end()) return cl->GetDeclFileName();
   return iClDecl->second.c_str();
}

//______________________________________________________________________________
const char* THtml::GetImplFileName(TClass * cl) const
{
   // Return implementation file name

   R__LOCKGUARD(GetMakeClassMutex());
   std::map<TClass*,std::string>::const_iterator iClImpl = fGuessedImplFileNames.find(cl);
   if (iClImpl == fGuessedImplFileNames.end()) return cl->GetImplFileName();
   return iClImpl->second.c_str();
}

//______________________________________________________________________________
const TString& THtml::GetOutputDir(Bool_t createDir /*= kTRUE*/) const
{
   // Return the output directory as set by SetOutputDir().
   // Create it if it doesn't exist and if createDir is kTRUE.

   if (createDir) {
      R__LOCKGUARD(GetMakeClassMutex());

      gSystem->ExpandPathName(const_cast<THtml*>(this)->fOutputDir);
      Long64_t sSize;
      Long_t sId, sFlags, sModtime;
      Int_t st = gSystem->GetPathInfo(fOutputDir, &sId, &sSize, &sFlags, &sModtime);
      if (st || !(sFlags & 2))
         if (st == 0)
            Error("GetOutputDir", "output directory %s is an existing file",
                  fOutputDir.Data());
         else if (gSystem->MakeDirectory(fOutputDir) == -1)
            Error("GetOutputDir", "output directory %s does not exist and can't create it", fOutputDir.Data());
   }
   return fOutputDir;
}

//______________________________________________________________________________
Bool_t THtml::IsNamespace(const TClass*cl)
{
   // Check whether cl is a namespace
   return (cl->Property() & kIsNamespace);
}

//______________________________________________________________________________
void THtml::LoadAllLibs()
{
   // Load all libraries known to ROOT via the rootmap system.

   TEnv* mapfile = gInterpreter->GetMapfile();
   if (!mapfile || !mapfile->GetTable()) return;

   std::set<std::string> direct;
   std::set<std::string> indirect;
   
   TEnvRec* rec = 0;
   TIter iEnvRec(mapfile->GetTable());
   while ((rec = (TEnvRec*) iEnvRec())) {
      TString libs = rec->GetValue();
      TString lib;
      Ssiz_t pos = 0;
      bool first = true;
      while (libs.Tokenize(lib, pos))
         if (first) {
            // ignore libCore - it's already loaded
            if (lib.BeginsWith("libCore"))
               continue;

            // first one, i.e. direct
            direct.insert(lib.Data());
            first = false;
         } else
            indirect.insert(lib.Data());
   }
   TString allLibs;
   for (std::set<std::string>::iterator iDirect = direct.begin();
        iDirect != direct.end();) {
      std::set<std::string>::iterator next = iDirect;
      ++next;
      if (indirect.find(*iDirect) != indirect.end())
         direct.erase(iDirect);
      else allLibs += *iDirect + "* ";
      iDirect = next;
   }
   for (std::set<std::string>::iterator iIndirect = indirect.begin();
        iIndirect != indirect.end(); ++iIndirect)
      allLibs += *iIndirect + " ";
   if (gHtml && gDebug > 2)
      gHtml->Info("LoadAllLibs", "Loading libraries %s\n", allLibs.Data());

   for (std::set<std::string>::iterator iDirect = direct.begin();
        iDirect != direct.end(); ++iDirect)
      gSystem->Load(iDirect->c_str());
}


//______________________________________________________________________________
void THtml::MakeAll(Bool_t force, const char *filter, int numthreads /*= -1*/)
{
// Produce documentation for all the classes specified in the filter (by default "*")
// To process all classes having a name starting with XX, do:
//        html.MakeAll(kFALSE,"XX*");
// If force=kFALSE (default), only the classes that have been modified since
// the previous call to this function will be generated.
// If force=kTRUE, all classes passing the filter will be processed.
// If numthreads is != -1, use numthreads threads, else decide automatically
// based on the number of CPUs.

   MakeIndex(filter);

   if (numthreads == 1) {
      // CreateListOfClasses(filter); already done by MakeIndex
      TClassDocInfo* classinfo = 0;
      TIter iClassInfo(&fClasses);
      UInt_t count = 0;

      while ((classinfo = (TClassDocInfo*)iClassInfo())) {
         if (!classinfo->IsSelected()) 
            continue;
         fCounter.Form("%5d", fClasses.GetSize() - count++);
         MakeClass(classinfo, force);
      }
   } else {
      if (numthreads == -1) {
         SysInfo_t sysinfo;
         gSystem->GetSysInfo(&sysinfo);
         numthreads = sysinfo.fCpus;
         if (numthreads < 1)
            numthreads = 2;
      }
      fThreadedClassCount = 0;
      fThreadedClassIter = new TIter(&fClasses);
      THtmlThreadInfo hti(this, force);
      if (!fMakeClassMutex && gGlobalMutex) {
         gGlobalMutex->Lock();
         fMakeClassMutex = gGlobalMutex->Factory(kTRUE);
         gGlobalMutex->UnLock();
      }

      TList threads;
      gSystem->Load("libThread");
      while (--numthreads >= 0) {
         TThread* thread = new TThread(MakeClassThreaded, &hti);
         thread->Run();
         threads.Add(thread);
      }

      TIter iThread(&threads);
      TThread* thread = 0;
      Bool_t wait = kTRUE;
      while (wait) {
         while (wait && (thread = (TThread*) iThread()))
            wait &= (thread->GetState() == TThread::kRunningState);
         gSystem->ProcessEvents();
         gSystem->Sleep(500);
      }

      iThread.Reset();
      while ((thread = (TThread*) iThread()))
         thread->Join();
   }
   fCounter.Remove(0);
}


//______________________________________________________________________________
void THtml::MakeClass(const char *className, Bool_t force)
{
// Make HTML files for a single class
//
//
// Input: className - name of the class to process
//
   CreateListOfClasses("*");

   TClassDocInfo* cdi = (TClassDocInfo*)fClasses.FindObject(className);
   if (!cdi) {
      if (!TClassEdit::IsStdClass(className)) // stl classes won't be available, so no warning
         Error("MakeClass", "Unknown class '%s'!", className);
      return;
   }

   MakeClass(cdi, force);
}

//______________________________________________________________________________
void THtml::MakeClass(void *cdi_void, Bool_t force)
{
// Make HTML files for a single class
//
//
// Input: cdi - doc info for class to process
//
   if (!fClasses.GetSize())
      CreateListOfClasses("*");

   TClassDocInfo* cdi = (TClassDocInfo*) cdi_void;
   TClass* currentClass = cdi->GetClass();

   if (!currentClass) {
      if (!TClassEdit::IsStdClass(cdi->GetName())) // stl classes won't be available, so no warning
         Error("MakeClass", "Class '%s' is known, but I cannot find its TClass object!", cdi->GetName());
      return;
   }
   TString htmlFile(cdi->GetHtmlFileName());
   if (htmlFile.Length()
       && (htmlFile.BeginsWith("http://")
           || htmlFile.BeginsWith("https://")
           || gSystem->IsAbsoluteFileName(htmlFile))
       ) {
      htmlFile.Remove(0);
      //printf("CASE skipped, class=%s, htmlFile=%s\n",className,htmlFile);
   }
   if (htmlFile.Length()) {
      TClassDocOutput cdo(*this, currentClass);
      cdo.Class2Html(force);
      cdo.MakeTree(force);
   } else
      Printf(fCounterFormat.Data(), "-skipped-", fCounter.Data(), cdi->GetName());
}


//______________________________________________________________________________
void* THtml::MakeClassThreaded(void* info) {
   // Entry point of worker threads for multi-threaded MakeAll().
   // info points to an (internal) THtmlThreadInfo object containing the current
   // THtml object, and whether "force" was passed to MakeAll().
   // The thread will poll GetNextClass() until no further class is available.

   const THtmlThreadInfo* hti = (const THtmlThreadInfo*)info;
   if (!hti) return 0;
   TClassDocInfo* classinfo = 0;
   while ((classinfo = hti->GetHtml()->GetNextClass()))
      hti->GetHtml()->MakeClass(classinfo, hti->GetForce());

   return 0;
}

//______________________________________________________________________________
void THtml::MakeIndex(const char *filter)
{
   // Create the index files for the product, modules, all types, etc.
   // By default all classes are indexed (if filter="*");
   // to generate an index for all classes starting with "XX", do
   //    html.MakeIndex("XX*");

   CreateListOfClasses(filter);

   TDocOutput output(*this);
   // create indices
   output.CreateTypeIndex();
   output.CreateModuleIndex();
   output.CreateClassIndex();
   output.CreateProductIndex();

   // create a class hierarchy
   output.CreateHierarchy();
}


//______________________________________________________________________________
void THtml::MakeTree(const char *className, Bool_t force)
{
// Make an inheritance tree
//
//
// Input: className - name of the class to process
//

   // create canvas & set fill color
   TClass *classPtr = GetClass(className);

   if (!classPtr) {
      Error("MakeTree", "Unknown class '%s' !", className);
      return;
   }

   TClassDocOutput cdo(*this, classPtr);
   cdo.MakeTree(force);
}

//______________________________________________________________________________
void THtml::SetSourcePrefix(const char *prefix)
{
   // Sets the source prefix, see GetSourceFileName().
   // Also resets the class structure, in case new files can
   // be found after this call.

   fSourcePrefix = prefix;

   // reset class table
   fClasses.Clear();
   fModules.Clear();
   fGuessedDeclFileNames.clear();
   fGuessedImplFileNames.clear();
}

//______________________________________________________________________________
void THtml::SetSourceDir(const char *dir)
{
   // Set the directory containing the source files.
   // The source file for a class MyClass will be searched
   // by prepending dir to the value of
   // MyClass::Class()->GetImplFileName() - which can contain
   // directory information!
   // Also resets the class structure, in case new files can
   // be found after this call.

   fSourceDir = dir;

   // reset class table
   fClasses.Clear();
   fModules.Clear();
   fGuessedDeclFileNames.clear();
   fGuessedImplFileNames.clear();
}

//______________________________________________________________________________
void THtml::SetDeclFileName(TClass* cl, const char* filename)
{
   // Explicitly set a decl file name for TClass cl.
   fGuessedDeclFileNames[cl] = filename;
}

//______________________________________________________________________________
void THtml::SetImplFileName(TClass* cl, const char* filename)
{
   // Explicitly set a impl file name for TClass cl.
   fGuessedImplFileNames[cl] = filename;
}

Last update: Thu Jan 17 08:58:13 2008

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.