Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Formatter/HTML/core.py
    1| # $Id: core.py,v 1.47 2003/01/20 06:43:02 chalky Exp $
    2| #
    3| # This file is a part of Synopsis.
    4| # Copyright (C) 2000, 2001 Stephen Davies
    5| #
    6| # Synopsis is free software; you can redistribute it and/or modify it
    7| # under the terms of the GNU General Public License as published by
    8| # the Free Software Foundation; either version 2 of the License, or
    9| # (at your option) any later version.
   10| #
   11| # This program is distributed in the hope that it will be useful,
   12| # but WITHOUT ANY WARRANTY; without even the implied warranty of
   13| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14| # General Public License for more details.
   15| #
   16| # You should have received a copy of the GNU General Public License
   17| # along with this program; if not, write to the Free Software
   18| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   19| # 02111-1307, USA.
   20| #
   21| # $Log: core.py,v $
   22| # Revision 1.47  2003/01/20 06:43:02  chalky
   23| # Refactored comment processing. Added AST.CommentTag. Linker now determines
   24| # comment summary and extracts tags. Increased AST version number.
   25| #
   26| # Revision 1.46  2003/01/16 16:48:23  chalky
   27| # Using FileTree now forces FileListing, FileIndexer and FileDetails to be used.
   28| #
   29| # Revision 1.45  2003/01/16 12:46:46  chalky
   30| # Renamed FilePages to FileSource, FileTree to FileListing. Added FileIndexer
   31| # (used to be part of FileTree) and FileDetails.
   32| #
   33| # Revision 1.44  2003/01/02 07:00:58  chalky
   34| # Only use Core.FileTree, refactored FileTree to be quicker and better.
   35| #
   36| # Revision 1.43  2002/12/12 17:25:33  chalky
   37| # Implemented Include support for C++ parser. A few other minor fixes.
   38| #
   39| # Revision 1.42  2002/12/09 04:00:59  chalky
   40| # Added multiple file support to parsers, changed AST datastructure to handle
   41| # new information, added a demo to demo/C++. AST Declarations now have a
   42| # reference to a SourceFile (which includes a filename) instead of a filename.
   43| #
   44| # Revision 1.41  2002/11/16 04:12:33  chalky
   45| # Added strategies for page formatting, and added one to allow template HTML
   46| # files to be used.
   47| #
   48| # Revision 1.40  2002/11/13 02:29:24  chalky
   49| # Support exclude_glob option to exclude files from listings. Remove debug info.
   50| #
   51| # Revision 1.39  2002/11/11 15:19:35  chalky
   52| # More fixes to get demo/C++ sxr working without frames
   53| #
   54| # Revision 1.38  2002/11/02 06:37:37  chalky
   55| # Allow non-frames output, some refactoring of page layout, new modules.
   56| #
   57| # Revision 1.37  2002/10/29 12:43:56  chalky
   58| # Added flexible TOC support to link to things other than ScopePages
   59| #
   60| # Revision 1.36  2002/10/28 17:39:37  chalky
   61| # Cross referencing support
   62| #
   63| # Revision 1.35  2002/10/28 06:12:30  chalky
   64| # Add structs_as_classes option
   65| #
   66| # Revision 1.34  2002/10/27 12:05:44  chalky
   67| # Add 2 verbose levels, make default (1) less verbose
   68| #
   69| # Revision 1.33  2002/10/11 06:02:33  chalky
   70| # Allow GUi to pass config object
   71| #
   72| # Revision 1.32  2002/08/23 04:37:26  chalky
   73| # Huge refactoring of Linker to make it modular, and use a config system similar
   74| # to the HTML package
   75| #
   76| # Revision 1.31  2002/07/04 06:43:18  chalky
   77| # Improved support for absolute references - pages known their full path.
   78| #
   79| # Revision 1.30  2002/07/04 05:16:19  chalky
   80| # Impl output_dir option for config to replace -o argument to HTML formatter.
   81| #
   82| # Revision 1.29  2002/03/14 00:19:47  chalky
   83| # Added demo of template specializations, and fixed HTML formatter to deal with
   84| # angle brackets in class names :)
   85| #
   86| # Revision 1.28  2002/01/13 09:44:51  chalky
   87| # Allow formatted source in GUI
   88| #
   89| # Revision 1.27  2002/01/09 11:43:41  chalky
   90| # Inheritance pics
   91| #
   92| # Revision 1.26  2002/01/09 10:16:35  chalky
   93| # Centralized navigation, clicking links in (html) docs works.
   94| #
   95| # Revision 1.25  2001/11/09 15:35:04  chalky
   96| # GUI shows HTML pages. just. Source window also scrolls to correct line.
   97| #
   98| # Revision 1.24  2001/07/19 05:10:39  chalky
   99| # Use filenames stored in AST object
  100| #
  101| # Revision 1.23  2001/07/19 04:03:05  chalky
  102| # New .syn file format.
  103| #
  104| # Revision 1.22  2001/07/10 14:41:22  chalky
  105| # Make treeformatter config nicer
  106| #
  107| # Revision 1.21  2001/07/10 02:55:51  chalky
  108| # Better comments in some files, and more links work with the nested layout
  109| #
  110| # Revision 1.20  2001/07/05 02:08:35  uid20151
  111| # Changed the registration of pages to be part of a two-phase construction
  112| #
  113| # Revision 1.19  2001/07/04 08:17:48  uid20151
  114| # Comments
  115| #
  116| # Revision 1.18  2001/06/28 07:22:18  stefan
  117| # more refactoring/cleanup in the HTML formatter
  118| #
  119| # Revision 1.17  2001/06/26 04:32:16  stefan
  120| # A whole slew of changes mostly to fix the HTML formatter's output generation,
  121| # i.e. to make the output more robust towards changes in the layout of files.
  122| #
  123| # the rpm script now works, i.e. it generates source and binary packages.
  124| #
  125| # Revision 1.16  2001/06/16 01:29:42  stefan
  126| # change the HTML formatter to not use chdir, as this triggers a but in python's import implementation
  127| #
  128| # Revision 1.15  2001/06/05 05:28:34  chalky
  129| # Some old tree abstraction
  130| #
  131| # Revision 1.14  2001/05/25 13:45:49  stefan
  132| # fix problem with getopt error reporting
  133| #
  134| # Revision 1.13  2001/04/06 02:39:26  chalky
  135| # Get toc in/out from config
  136| #
  137| # Revision 1.12  2001/04/06 01:41:09  chalky
  138| # More verbose verbosity
  139| #
  140| # Revision 1.11  2001/03/29 14:05:33  chalky
  141| # Can give namespace to manager._calculateStart. Timing of pages if verbose
  142| #
  143| # Revision 1.10  2001/02/13 02:54:15  chalky
  144| # Added Name Index page
  145| #
  146| # Revision 1.9  2001/02/12 04:08:09  chalky
  147| # Added config options to HTML and Linker. Config demo has doxy and synopsis styles.
  148| #
  149| # Revision 1.8  2001/02/07 14:13:51  chalky
  150| # Small fixes.
  151| #
  152| # Revision 1.7  2001/02/06 05:13:05  chalky
  153| # Fixes
  154| #
  155| # Revision 1.6  2001/02/05 07:58:39  chalky
  156| # Cleaned up image copying for *JS. Added synopsis logo to ScopePages.
  157| #
  158| # Revision 1.5  2001/02/05 05:26:24  chalky
  159| # Graphs are separated. Misc changes
  160| #
  161| # Revision 1.4  2001/02/01 20:08:35  chalky
  162| # Added types to config
  163| #
  164| # Revision 1.3  2001/02/01 18:36:55  chalky
  165| # Moved TOC out to Formatter/TOC.py
  166| #
  167| # Revision 1.2  2001/02/01 15:23:24  chalky
  168| # Copywritten brown paper bag edition.
  169| #
  170| # Revision 1.1  2001/02/01 14:58:48  chalky
  171| # Modularized HTML formatter a bit :)
  172| #
  173| # These are from the old HTML.py:
  174| # Revision 1.55  2001/01/24 18:05:16  stefan
  175| # extended the HTML submodule loading to allow for external formatters
  176| #
  177| # Revision 1.54  2001/01/24 13:06:48  chalky
  178| # Fixed bug related to /**/ causing following //. to be on same line
  179| #
  180| # Revision 1.53  2001/01/24 12:50:23  chalky
  181| # Fixes to get summary/detail linking again since AST.Visitor changed ...
  182| #
  183| # Revision 1.52  2001/01/24 01:38:36  chalky
  184| # Added docstrings to all modules
  185| #
  186| # Revision 1.51  2001/01/23 00:20:27  chalky
  187| # Added employee.cc demo. Fixed small bug wrt linking to details of variables.
  188| #
  189| # Revision 1.50  2001/01/22 20:14:36  stefan
  190| # forgot two flags in the usage() function
  191| #
  192| # Revision 1.49  2001/01/22 19:54:41  stefan
  193| # better support for help message
  194| #
  195| # Revision 1.48  2001/01/22 17:06:15  stefan
  196| # added copyright notice, and switched on logging
  197| #
  198| 
  199| # HTML2 Formatter by Stephen Davies
  200| # Based on HTML Formatter by Stefan Seefeld
  201| #
  202| # Generates a page for each Module/Class and an inheritance tree
  203| # Also 3-frame format inspired by Javadoc
  204| 
  205| """
  206| Core module for the HTML Formatter.
  207| 
  208| This module is the first to be loaded, and it creates the global 'core.config'
  209| object before creating any pages. It also handles the command line parsing for
  210| this module, and coordinates the actual output generation.
  211| """
  212| 
  213| # System modules
  214| import sys, getopt, os, os.path, string, types, errno, stat, re, time
  215| 
  216| # Synopsis modules
  217| from Synopsis.Config import Base
  218| from Synopsis.Core import AST, Type, Util
  219| from Synopsis.Core.FileTree import FileTree
  220| from Synopsis.Formatter import TOC, ClassTree, xref
  221| from Synopsis.Formatter.HTML import TreeFormatter
  222| 
  223| from Synopsis.Core.Util import import_object
  224| 
  225| verbose=0
  226| 
  227| # Create the Config class before importing any HTML modules
  228| class Config:
  229|     """Central configuration repository for HTML formatter.
  230|     This class holds references to the current formatters, tocs, etc."""
  231|     def __init__(self):
  232|         """Constructor - initialise objects to None."""
  233|         self.ast = None
  234|         self.commentFormatter = None
  235|         self.commentFormatterList = []
  236|         self.types = None # Filled in first thing in format()
  237|         self.toc = None
  238|         self.toc_out = ""
  239|         self.toc_in = []
  240|         self.default_toc = 'ScopePages'
  241|         self.basename = None
  242|         self.datadir = None
  243|         self.stylesheet = ""
  244|         self.stylesheet_file = None
  245|         self.sorter = None
  246|         self.namespace = ""
  247|         self.files = None
  248|         self.files_class = None
  249|         self.pageset = None
  250|         self.sorter = None
  251|         self.classTree = None
  252|         self.structs_as_classes = 0
  253|         self.using_frames = 1
  254|         self.using_module_index = 0
  255|         self.base_dir = ''
  256|         self.start_dir = ''
  257|         self.exclude_globs = []
  258|         self.page_format = ""
  259|         self.treeFormatterClass = TreeFormatter.TreeFormatter
  260|         self.page_contents = "" # page contents frame (top-left)
  261|         self.page_index = "" # page for index frame (left)
  262|         self.page_main = "" # page for main index.html page
  263|         self.pages = [
  264|             'ScopePages', 'ModuleListing', 'ModuleIndexer', 
  265|             'FileListing', 'FileIndexer', 'FileDetails',
  266|             'InheritanceTree', 'InheritanceGraph', 'NameIndex', 'FramesIndex'
  267|         ]
  268|         self.verbose = 0
  269|         self.xref = xref.CrossReferencer()
  270| 
  271| 
  272|     def fillDefaults(self):
  273|         """Fill in empty options with defaults"""
  274|         if not self.sorter:
  275|             import ScopeSorter
  276|             self.sorter = ScopeSorter.ScopeSorter()
  277|     
  278|     def use_config(self, obj):
  279|         """Extracts useful attributes from 'obj' and stores them. The object
  280|         itself is also stored as config.obj"""
  281|         # obj.pages is a list of module names
  282|         self.obj = obj
  283|         if hasattr(obj, 'verbose'): self.verbose = obj.verbose
  284|         options = ('pages', 'sorter', 'datadir', 'stylesheet', 'stylesheet_file',
  285|             'comment_formatters', 'toc_out', 'toc_in', 'tree_formatter',
  286|             'file_layout', 'output_dir', 'structs_as_classes', 'default_toc',
  287|             'base_dir', 'start_dir', 'exclude_globs', 'page_format')
  288|         for option in options:
  289|             if hasattr(obj, option):
  290|                getattr(self, '_config_'+option)(getattr(obj, option))
  291|             elif self.verbose > 1: print "Option",option,"not found in config."
  292| 
  293|     def _config_pages(self, pages):
  294|         "Configures from the given list of pages"
  295|         if type(pages) != types.ListType:
  296|             raise TypeError, "HTML.pages must be a list."
  297|         if self.verbose > 1: print "Using pages:",pages
  298|         self.pages = pages
  299|         if 'FramesIndex' in pages: self.using_frames = 1
  300|         else: self.using_frames = 0
  301| 
  302|     def _config_sorter(self, sorter):
  303|         if self.verbose > 1: print "Using sorter:",sorter
  304|         self.sorter = import_object(sorter)()
  305| 
  306|     def _config_datadir(self, datadir):
  307|         if self.verbose > 1: print "Using datadir:", datadir
  308|         self.datadir = datadir
  309| 
  310|     def _config_output_dir(self, output_dir):
  311|         if self.verbose > 1: print "Using output_dir:", output_dir
  312|         self.basename = output_dir
  313| 
  314|     def _config_stylesheet(self, stylesheet):
  315|         if self.verbose > 1: print "Using stylesheet:", stylesheet
  316|         self.stylesheet = stylesheet
  317| 
  318|     def _config_stylesheet_file(self, stylesheet_file):
  319|         if self.verbose > 1: print "Using stylesheet file:", stylesheet_file
  320|         self.stylesheet_file = stylesheet_file
  321| 
  322|     def _config_comment_formatters(self, comment_formatters):
  323|         if self.verbose > 1: print "Using comment formatters:", comment_formatters
  324|         basePackage = 'Synopsis.Formatter.HTML.CommentFormatter.'
  325|         for formatter in comment_formatters:
  326|             if type(formatter) == types.StringType:
  327|                if CommentFormatter.commentFormatters.has_key(formatter):
  328|                    self.commentFormatterList.append(
  329|                       CommentFormatter.commentFormatters[formatter]()
  330|         )
  331|         else:
  332|                    raise ImportError, "No builtin comment formatter '%s'"%formatter
  333|          else:
  334|                clas = import_object(formatter, basePackage)
  335|                self.commentFormatterList.append(clas())
  336|     
  337|     def _config_toc_in(self, toc_in):
  338|         if self.verbose > 1: print "Will read toc(s) from",toc_in
  339|         self.toc_in = toc_in
  340| 
  341|     def _config_default_toc(self, page):
  342|         if self.verbose > 1: print "Will use page %s for generating default TOC"%page
  343|         self.default_toc = page
  344| 
  345|     def _config_toc_out(self, toc_out):
  346|         if self.verbose > 1: print "Will save toc to",toc_out
  347|         self.toc_out = toc_out
  348| 
  349|     def _config_tree_formatter(self, tree_class):
  350|         if self.verbose > 1: print "Using tree class",tree_class
  351|         clas = import_object(tree_class, basePackage='Synopsis.Formatter.HTML.')
  352|         self.treeFormatterClass = clas
  353|     
  354|     def _config_file_layout(self, layout):
  355|         if self.verbose > 1: print "Using file layout",layout
  356|         self.files_class = import_object(layout)
  357| 
  358|     def _config_base_dir(self, dir):
  359|         if self.verbose > 1: print "Using base dir",dir
  360|         self.base_dir = str(dir)
  361| 
  362|     def _config_start_dir(self, dir):
  363|         if self.verbose > 1: print "Using start dir",dir
  364|         self.start_dir = str(dir)
  365| 
  366|     def _config_structs_as_classes(self, yesno):
  367|         if self.verbose > 1: print "Using structs as classes:",yesno
  368|         self.structs_as_classes = yesno
  369| 
  370|     def _config_exclude_globs(self, globs):
  371|         if self.verbose > 1: print "Using exclude globs:",globs
  372|         self.exclude_globs = map(compile_glob, globs)
  373| 
  374|     def _config_page_format(self, page_format):
  375|         if self.verbose > 1: print "Using page format class:",page_format
  376|         self.page_format = page_format
  377|     
  378|     def set_contents_page(self, page):
  379|         """Call this method to set the contents page. First come first served
  380|         -- whatever module the user puts first in the list that sets this is
  381|         it. This is the frame in the top-left if you use the default frameset."""
  382|         if not self.page_contents: self.page_contents = page
  383|     
  384|     def set_index_page(self, page):
  385|         """Call this method to set the index page. First come first served
  386|         -- whatever module the user puts first in the list that sets this is
  387|         it. This is the frame on the left if you use the default frameset."""
  388|         if not self.page_index: self.page_index = page
  389|     
  390|     def set_main_page(self, page):
  391|         """Call this method to set the main index.html page. First come first served
  392|         -- whatever module the user puts first in the list that sets this is
  393|         it."""
  394|         if not self.page_main: self.page_main = page
  395| 
  396|     def set_using_module_index(self):
  397|         """Sets the using_module_index flag. This will cause the an
  398|         intermediate level of links intended to go in the left frame."""
  399|         self.using_module_index = 1
  400| 
  401| # Create a globally accessible Config. After this point the HTML modules may
  402| # import it into their namespace for ease of use
  403| config = Config()
  404| 
  405| # HTML modules
  406| import CommentFormatter, FileLayout, ScopeSorter, Page
  407| from Tags import *
  408| 
  409| def sort(list):
  410|     "Utility func to sort and return the given list"
  411|     list.sort()
  412|     return list
  413| 
  414| def old_reference(name, scope, label=None, **keys):
  415|     """Utility method to insert a reference to a name.
  416|     @see ASTFormatter.BaseFormatter.reference()
  417|     """
  418|     if not label: label = anglebrackets(Util.ccolonName(name, scope))
  419|     entry = config.toc[name]
  420|     if entry: return apply(href, (entry.link, label), keys)
  421|     return label or ''
  422| 
  423| def compile_glob(globstr):
  424|     """Returns a compiled regular expression for the given glob string. A
  425|     glob string is something like "*.?pp" which gets translated into
  426|     "^.*\..pp$"."""
  427|     glob = string.replace(globstr, '.', '\.')
  428|     glob = string.replace(glob, '?', '.')
  429|     glob = string.replace(glob, '*', '.*')
  430|     glob = re.compile('^%s$'%glob)
  431|     return glob
  432| 
  433| 
  434| class Struct:
  435|     "Dummy class. Initialise with keyword args."
  436|     def __init__(self, **keys):
  437|         for name, value in keys.items(): setattr(self, name, value)
  438| 
  439| class DeclStyle:
  440|     """This class just maintains a mapping from declaration to display style.
  441|     The style is an enumeration, possible values being: SUMMARY (only display
  442|     a summary for this declaration), DETAIL (summary and detailed info),
  443|     INLINE (summary and detailed info, where detailed info is an inline
  444|     version of the declaration even if it's a class, etc.)
  445| 
  446|     Upon initiation, an instance of this class installs itself in the config
  447|     object as "decl_style".
  448|     """
  449|     SUMMARY = 0
  450|     DETAIL = 1
  451|     INLINE = 2
  452|     
  453|     def __init__(self):
  454|         self.__dict = {}
  455|     def style_of(self, decl):
  456|         """Returns the style of the given decl"""
  457|         SUMMARY = self.SUMMARY
  458|         DETAIL = self.DETAIL
  459|         key = id(decl)
  460|         if self.__dict.has_key(key): return self.__dict[key]
  461|         if len(decl.comments()) == 0:
  462|             # Set to summary, as this will mean no detailed section
  463|             style = SUMMARY
  464|         else:
  465|             comment = decl.comments()[0]
  466|             # Calculate the style. The default is detail
  467|             if not comment.text():
  468|                # No comment, don't show detail
  469|                style = SUMMARY
  470|             elif comment.summary() != comment.text():
  471|                # There is more to the comment than the summary, show detail
  472|                style = DETAIL
  473|          else:
  474|                # Summary == Comment, don't show detail
  475|                style = SUMMARY
  476|             # Always show tags
  477|             if comment.tags():
  478|                style = DETAIL
  479|             # Always show enums
  480|             if isinstance(decl, AST.Enum):
  481|                style = DETAIL
  482|             # Show functions if they have exceptions
  483|             if isinstance(decl, AST.Function) and len(decl.exceptions()):
  484|                style = DETAIL
  485|             # Don't show detail for scopes (they have their own pages)
  486|             if isinstance(decl, AST.Scope):
  487|                style = SUMMARY
  488|         self.__dict[key] = style
  489|         return style
  490|     __getitem__ = style_of
  491| 
  492| class PageManager:
  493|     """This class manages and coordinates the various pages. The user adds
  494|     pages by passing their class object to the addPage method. Pages should be
  495|     derived from Page.Page, and their constructors may want to call the
  496|     addRootPage method of the PageManager object to register a name and link
  497|     that is listed along with other root or top-level pages.
  498|     @see Page.Page
  499|     """
  500|     def __init__(self):
  501|         self.__pages = [] #all pages
  502|         self.__roots = [] #pages with roots, list of Structs
  503|         self.__global = None # The global scope
  504|         self.__files = {} # map from filename to (page,scope)
  505|         self.__page_objects = {} # map of pages by name
  506|         self._loadPages()
  507| 
  508|     def getPage(self, name):
  509|         """Returns the Page with the given name"""
  510|         return self.__page_objects[name]
  511|         
  512|     def globalScope(self):
  513|         "Return the global scope"
  514|         return self.__global
  515| 
  516|     def calculateStart(self, root, namespace=None):
  517|         "Calculates the start scope using the 'namespace' config var"
  518|         scope_names = string.split(namespace or config.namespace, "::")
  519|         start = root # The running result
  520|         config.sorter.set_scope(root)
  521|         scope = [] # The running name of the start
  522|         for scope_name in scope_names:
  523|             if not scope_name: break
  524|             scope.append(scope_name)
  525|         try:
  526|                child = config.sorter.child(tuple(scope))
  527|                if isinstance(child, AST.Scope):
  528|                  start = child
  529|                    config.sorter.set_scope(start)
  530|         else:
  531|                    raise TypeError, 'calculateStart: Not a Scope'
  532|            except:
  533|                # Don't continue if scope traversal failed!
  534|                import traceback
  535|                traceback.print_exc()
  536|                print "Fatal: Couldn't find child scope",scope
  537|                print "Children:",map(lambda x:x.name(), config.sorter.children())
  538|                sys.exit(3)
  539|         return start
  540| 
  541|     def addPage(self, pageClass):
  542|         """Add a page of the given class. An instance is created and stored,
  543|         and its root() method is called and the name,link tuple stored if None
  544|         isn't returned."""
  545|         page = pageClass(self)
  546|         self.__pages.append(page)
  547|         page.register()
  548|         return page
  549|     
  550|     def addRootPage(self, file, label, target, visibility):
  551|         """Adds a named link to the list of root pages. Called from the
  552|         constructors of Page objects. The root pages are displayed at the top
  553|         of every page, depending on their visibility (higher = more visible).
  554|         @param file        the filename, to be used when generating the 
  555|         @param label       the label of the p
  556|         @param target       target frame
  557|         @param visibility   should be a number such as 1 or 2. Visibility 2 is
  558|                           shown on all pages, 1 only on pages with lots of
  559|                           room. For example, pages for the top-left frame
  560|                           only show visibility 2 pages."""
  561|         self.__roots.append(Struct(file=file, label=label, target=target, visibility=visibility))
  562| 
  563|     def formatHeader(self, origin, visibility=1):
  564|         """Formats the list of root pages to HTML. The origin specifies the
  565|         generated page itself (which shouldn't be linked), such that the relative
  566|         links can be generated. Only root pages of 'visibility' or
  567|         above are included."""
  568|         # If not using frames, show all headings on all pages!
  569|         if not config.using_frames: visibility=1
  570|         #filter out roots that are visible
  571|         roots = filter(lambda x,v=visibility: x.visibility >= v, self.__roots)
  572|         #a function generating a link
  573|         other = lambda x, o=origin, span=span: span('root-other', href(rel(o, x.file), x.label, target=x.target))
  574|         #a function simply printing label
  575|         current = lambda x, span=span: span('root-current', x.label)
  576|         # generate the header
  577|         roots = map(lambda x, o=origin, other=other, current=current: x.file==o and current(x) or other(x), roots)
  578|         return string.join(roots, ' | \n')+'\n<hr>\n'
  579| 
  580|     def process(self, root):
  581|         """Create all pages from the start Scope, derived from the root Scope"""
  582|         self.__global = root
  583|         start = self.calculateStart(root)
  584|         if config.verbose: print "Registering filenames...",
  585|         for page in self.__pages:
  586|             page.register_filenames(start)
  587|         if config.verbose: print "Done."
  588|         for page in self.__pages:
  589|             if config.verbose:
  590|                print "Time for %s:"%page.__class__.__name__,
  591|                sys.stdout.flush()
  592|                start_time = time.time()
  593|             page.process(start)
  594|             if config.verbose:
  595|                print "%f"%(time.time() - start_time)
  596| 
  597|     def _loadPages(self):
  598|         """Loads the page objects from the config.pages list. Each element is
  599|         either a string or a tuple of two strings. One string means load the named
  600|         module and look for a 'htmlPageClass' attribute in it. A tuple of two
  601|         strings means load the module from the first string, and look for an
  602|         attribute using the second string."""
  603|         defaultAttr = 'htmlPageClass'
  604|         basePackage = 'Synopsis.Formatter.HTML.'
  605|         for page in list(config.pages):
  606|             obj = self.addPage(import_object(page, defaultAttr, basePackage))
  607|             self.__page_objects[page] = obj
  608|     
  609|     def register_filename(self, filename, page, scope):
  610|         """Registers a file for later production. The first page to register
  611|         the filename gets to keep it."""
  612|         filename = str(filename)
  613|         if not self.__files.has_key(filename):
  614|             self.__files[filename] = (page, scope)
  615| 
  616|     def filename_info(self, filename):
  617|         """Returns information about a registered file, as a (page,scope)
  618|         pair. Will return None if the filename isn't registered."""
  619|         filename = str(filename)
  620|         if not self.__files.has_key(filename): return None
  621|         return self.__files[filename]
  622| 
  623| def usage():
  624|     """Print usage to stdout"""
  625|     print \
  626| """
  627|   -o <dir>                             Output directory, created if it doesn't exist.
  628|   -s <filename>                        Filename of stylesheet in output directory
  629|   -S <filename>                        Filename of stylesheet to copy
  630|                                        If this is newer than the one in the output directory then it
  631|                                       is copied over it.
  632|   -n <namespace>                       Namespace to output
  633|   -c <formatter>                       add external Comment formatter
  634|   -C <formatter>                       add std Comment formatter (part of the HTML module) to use
  635|                                        - default Nothing
  636|                                       - ssd     Filters for and strips //. comments
  637|                                       - javadoc @tag style comments
  638|                                        - section test section breaks.
  639|                                       You may use multiple -c options
  640|   -t <filename>                        Generate a table of content and write it to <filename>
  641|   -r <filename> ['|'<url>|<directory>] merge in table of content from <filename>, possibly prefixing entries with the
  642|                                        given url or directory, to resolve external symbols"""
  643| 
  644| def __parseArgs(args, config_obj):
  645|     global verbose, commentParser, toc_out, toc_in
  646|     commentFormatters = CommentFormatter.commentFormatters
  647|     # Defaults
  648| 
  649|     # Convert the arguments to a list with custom getopt
  650|     try:
  651|         opts,remainder = Util.getopt_spec(args, "hvo:s:n:c:C:S:t:r:d:")
  652|     except Util.getopt.error, e:
  653|         sys.stderr.write("Error in arguments: " + str(e) + "\n")
  654|         sys.exit(1)
  655| 
  656|     # Check for verbose first so config loading can use it
  657|     for o, a in opts: 
  658|         if o == '-v': 
  659|             config.verbose = verbose = 1
  660| 
  661|     # Use config object if present
  662|     if config_obj: config.use_config(config_obj)
  663|     elif config.verbose: print "No config object given"
  664| 
  665|     # Parse the list of arguments
  666|     for opt in opts:
  667|         o,a = opt
  668|         if o == "-o":
  669|             config.basename = a #open(a, "w")
  670|         elif o == "-d":
  671|             config.datadir = a
  672|         elif o == "-s":
  673|             config.stylesheet = a
  674|         elif o == "-S":
  675|             config.stylesheet_file = a
  676|         elif o == "-n":
  677|             config.namespace = a
  678|         elif o == "-c": config.commentFormatterList.append(Util._import(a))
  679|         elif o == "-C":
  680|             if commentFormatters.has_key(a):
  681|                config.commentFormatterList.append(commentFormatters[a]())
  682|          else:
  683|                print "Error: Unknown formatter. Available comment formatters are:",string.join(commentFormatters.keys(), ', ')
  684|                sys.exit(1)
  685|         elif o == "-t":
  686|             config.toc_out = a
  687|         elif o == "-r":
  688|             config.toc_in.append(a)
  689|         elif o == "-h":
  690|            usage()
  691|             sys.exit(1)
  692|         elif o == "-v":
  693|             verbose=1
  694|             config.verbose = 1
  695| 
  696|     # Fill in any unspecified defaults
  697|     config.fillDefaults()
  698| 
  699| def format(args, ast, config_obj):
  700|     global toc_out, toc_in, manager
  701|     __parseArgs(args, config_obj)
  702|     config.ast = ast
  703|     config.types = ast.types()
  704|     declarations = ast.declarations()
  705| 
  706|     # Instantiate the files object
  707|     config.files = config.files_class()
  708| 
  709|     # Create the declaration styler and the comment formatter
  710|     config.decl_style = DeclStyle()
  711|     config.comments = CommentFormatter.CommentFormatter()
  712| 
  713|     # Create the Class Tree (TODO: only if needed...)
  714|     config.classTree = ClassTree.ClassTree()
  715| 
  716|     # Create the File Tree (TODO: only if needed...)
  717|     config.fileTree = FileTree()
  718|     config.fileTree.set_ast(ast)
  719| 
  720|     # Build class tree
  721|     for d in declarations:
  722|         d.accept(config.classTree)
  723| 
  724|     # Create the page manager, which loads the pages
  725|     manager = PageManager()
  726|     root = AST.Module(None,-1,"C++","Global",())
  727|     root.declarations()[:] = declarations
  728| 
  729|     # Create table of contents index
  730|     start = manager.calculateStart(root)
  731|     config.toc = manager.getPage(config.default_toc).get_toc(start)
  732|     if verbose: print "HTML Formatter: Initialising TOC"
  733| 
  734|     # Add all declarations to the namespace tree
  735|     #for d in declarations:
  736|     #   d.accept(config.t
  737|         
  738|     if verbose: print "TOC size:",config.toc.size()
  739|     if len(config.toc_out): config.toc.store(config.toc_out)
  740|     
  741|     # load external references from toc files, if any
  742|     for t in config.toc_in: config.toc.load(t)
  743|    
  744|     if verbose: print "HTML Formatter: Writing Pages..."
  745|     # Create the pages
  746|     manager.process(root)
  747| 
  748| def configure_for_gui(ast, config_obj):
  749|     global manager
  750| 
  751|     if config.ast is ast: return
  752| 
  753|     __parseArgs(["-o","/tmp"], config_obj)
  754|     config.ast = ast
  755|     config.types = ast.types()
  756|     declarations = ast.declarations()
  757|     config.files = config.files_class()
  758|     config.decl_style = DeclStyle()
  759|     config.comments = CommentFormatter.CommentFormatter()
  760|     config.classTree = ClassTree.ClassTree()
  761|     FileTree()
  762| 
  763|     # Build class and file trees
  764|     for d in declarations:
  765|         d.accept(config.classTree)
  766| 
  767|     config.fileTree.buildTree()
  768| 
  769|     # Create table of contents index
  770|     config.toc = TOC.TableOfContents(config.files)
  771| 
  772|     # Add all declarations to the namespace tree
  773|     for d in declarations:
  774|         d.accept(config.toc)
  775|     #if len(config.toc_out): config.toc.store(config.toc_out)
  776|     
  777|     # load external references from toc files, if any
  778|     for t in config.toc_in: config.toc.load(t)
  779|     
  780|     # Create the pages
  781|     # Create a dummy Module for the global namespace to simplify things
  782|     #root = AST.Module('',-1,"C++","Global",())
  783|     #root.declarations()[:] = declarations
  784|     manager = PageManager()
  785|     #manager.process(root)
  786| 
  787|