Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Formatter/HTML/FormatStrategy.py
    1| # $Id: FormatStrategy.py,v 1.30 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| # Copyright (C) 2000, 2001 Stefan Seefeld
    6| #
    7| # Synopsis is free software; you can redistribute it and/or modify it
    8| # under the terms of the GNU General Public License as published by
    9| # the Free Software Foundation; either version 2 of the License, or
   10| # (at your option) any later version.
   11| #
   12| # This program is distributed in the hope that it will be useful,
   13| # but WITHOUT ANY WARRANTY; without even the implied warranty of
   14| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   15| # General Public License for more details.
   16| #
   17| # You should have received a copy of the GNU General Public License
   18| # along with this program; if not, write to the Free Software
   19| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   20| # 02111-1307, USA.
   21| #
   22| # $Log: FormatStrategy.py,v $
   23| # Revision 1.30  2003/01/20 06:43:02  chalky
   24| # Refactored comment processing. Added AST.CommentTag. Linker now determines
   25| # comment summary and extracts tags. Increased AST version number.
   26| #
   27| # Revision 1.29  2002/12/30 13:48:17  chalky
   28| # Use stylesheets for ModuleListing and classes' headings
   29| #
   30| # Revision 1.28  2002/12/23 13:49:23  chalky
   31| # Fix namespace display
   32| #
   33| # Revision 1.27  2002/12/23 12:15:12  chalky
   34| # Show separate "namespace foo::bar" on class pages, remove the namespace part
   35| # from the class name to make it cleaner. Template decl is same font as class
   36| # name.
   37| #
   38| # Revision 1.26  2002/12/19 05:31:03  chalky
   39| # In class pages, show the template before the class
   40| #
   41| # Revision 1.25  2002/12/19 05:07:13  chalky
   42| # Quote the names of classes
   43| #
   44| # Revision 1.24  2002/12/09 04:00:59  chalky
   45| # Added multiple file support to parsers, changed AST datastructure to handle
   46| # new information, added a demo to demo/C++. AST Declarations now have a
   47| # reference to a SourceFile (which includes a filename) instead of a filename.
   48| #
   49| # Revision 1.23  2002/11/02 06:37:37  chalky
   50| # Allow non-frames output, some refactoring of page layout, new modules.
   51| #
   52| # Revision 1.22  2002/11/01 07:21:15  chalky
   53| # More HTML formatting fixes eg: ampersands and stuff
   54| #
   55| # Revision 1.21  2002/11/01 04:26:19  chalky
   56| # Quote ampersand as modifier
   57| #
   58| # Revision 1.20  2002/11/01 03:39:21  chalky
   59| # Cleaning up HTML after using 'htmltidy'
   60| #
   61| # Revision 1.19  2002/10/28 17:39:34  chalky
   62| # Cross referencing support
   63| #
   64| # Revision 1.18  2002/10/28 08:16:53  chalky
   65| # Undo previous table change. Put non-breaking spaces in first column instead
   66| #
   67| # Revision 1.17  2002/10/28 06:33:31  chalky
   68| # Add heading for namespaces
   69| #
   70| # Revision 1.16  2002/10/28 06:13:49  chalky
   71| # Fix summary display: templates use special div, use nested table to fix
   72| # formatting of first column
   73| #
   74| # Revision 1.15  2002/10/27 12:05:17  chalky
   75| # Support putting the identifier in the right place in funcptr parameters.
   76| #
   77| # Revision 1.14  2002/10/26 04:17:58  chalky
   78| # Show templates on previous line. Hide constructors in base class. Commas
   79| # between inherited members
   80| #
   81| # Revision 1.13  2002/10/25 05:13:36  chalky
   82| # Show method names on new line after template definition
   83| #
   84| # Revision 1.12  2002/10/20 15:38:08  chalky
   85| # Much improved template support, including Function Templates.
   86| #
   87| # Revision 1.11  2002/07/19 14:26:32  chalky
   88| # Revert prefix in FileLayout but keep relative referencing elsewhere.
   89| #
   90| # Revision 1.10  2002/07/11 02:09:33  chalky
   91| # Patch from Patrick Mauritz: Use png support in latest graphviz. If dot not
   92| # present, don't subvert what the user asked for but instead tell them.
   93| #
   94| # Revision 1.9  2002/07/04 06:43:18  chalky
   95| # Improved support for absolute references - pages known their full path.
   96| #
   97| # Revision 1.8  2002/04/26 01:21:14  chalky
   98| # Bugs and cleanups
   99| #
  100| # Revision 1.7  2002/03/14 00:19:47  chalky
  101| # Added demo of template specializations, and fixed HTML formatter to deal with
  102| # angle brackets in class names :)
  103| #
  104| # Revision 1.6  2001/07/19 04:03:05  chalky
  105| # New .syn file format.
  106| #
  107| # Revision 1.5  2001/07/15 08:28:43  chalky
  108| # Added 'Inheritance' page Part
  109| #
  110| # Revision 1.4  2001/07/15 06:41:57  chalky
  111| # Factored summarizer and detailer into 'Parts', and added a separate one for
  112| # the top of the page (Heading)
  113| #
  114| # Revision 1.3  2001/07/11 01:45:03  stefan
  115| # fix Dot and HTML formatters to adjust the references depending on the filename of the output
  116| #
  117| # Revision 1.2  2001/07/10 14:41:08  chalky
  118| # Fix inlined inheritance graph filenames
  119| #
  120| # Revision 1.1  2001/07/10 05:08:28  chalky
  121| # Refactored ASTFormatters into FormatStrategies, and simplified names all round
  122| #
  123| #
  124| """AST Formatting Strategies.
  125| 
  126| This module contains all the builtin formatting strategies used to make the
  127| main scope pages. These strategies are used by Strategy.Summary/Detail to
  128| create the entries for each declaration - each has a list of strategies that
  129| it calls in turn to generate a HTML fragment for each declaration. So for
  130| example, a link to source code is added just by adding a Strategy (eg:
  131| FilePages) that outputs the link to the source. This can be done without
  132| changing the strategies that generate the declaration name, type or
  133| comments.
  134| """
  135| # System modules
  136| import types, os, string
  137| 
  138| # Synopsis modules
  139| from Synopsis.Core import AST, Type, Util
  140| 
  141| # HTML modules
  142| import Tags, core
  143| from core import config
  144| from Tags import *
  145| 
  146| class Strategy:
  147|     """Generates HTML fragment for a declaration. Multiple strategies are
  148|     combined to generate the output for a single declaration, allowing the
  149|     user to customise the output by choosing a set of strategies. This follows
  150|     the Strategy design pattern.
  151| 
  152|     The key concept of this class is the format* methods. Any
  153|     class derived from Strategy that overrides one of the format methods
  154|     will have that method called by the Summary and Detail formatters when
  155|     they visit that AST type. Summary and Detail maintain a list of
  156|     Strategies, and a list for each AST type.
  157|     
  158|     For example, when Strategy.Summary visits a Function object, it calls
  159|     the formatFunction method on all Strategys registed with
  160|     SummaryFormatter that implemented that method. Each of these format
  161|     methods returns a string, which may contain a TD tag to create a new
  162|     column.
  163| 
  164|     An important point to note is that only Strategies which override a
  165|     particular format method are called - if that format method is not
  166|     overridden then it is not called for that declaration type.
  167|     """
  168|     def __init__(self, formatter):
  169|         """Store formatter as self.formatter. The formatter is either a
  170|         SummaryFormatter or DetailFormatter, and is used for things like
  171|         reference() and label() calls. Local references to the formatter's
  172|         reference and label methods are stored in self for more efficient use
  173|         of them."""
  174|         self.formatter = formatter
  175|         self.label = formatter.label
  176|         self.reference = formatter.reference
  177|         self.formatType = formatter.formatType
  178|         self.page = formatter.page()
  179| 
  180|     #
  181|     # Utility methods
  182|     #
  183|     def formatModifiers(self, modifiers):
  184|         """Returns a HTML string from the given list of string modifiers. The
  185|         modifiers are enclosed in 'keyword' spans."""
  186|         def keyword(m):
  187|             if m == '&': return span('keyword', '&')
  188|             return span('keyword', m)
  189|         return string.join(map(keyword, modifiers))
  190| 
  191| 
  192|     #
  193|     # AST Formatters
  194|     #
  195|     def formatDeclaration(self, decl):  pa
  196|     def formatForward(self, decl):      
  197|     def formatGroup(self, decl):        
  198|     def formatScope(self, decl):        
  199|     def formatModule(self, decl):       
  200|     def formatMetaModule(self, decl):   p
  201|     def formatClass(self, decl):        
  202|     def formatTypedef(self, decl):      
  203|     def formatEnum(self, decl):        
  204|     def formatVariable(self, decl):     
  205|     def formatConst(self, decl):        
  206|     def formatFunction(self, decl):     
  207|     def formatOperation(self, decl):    
  208| 
  209| class BaseAST(Strategy):
  210|     """Base class for SummaryAST and DetailAST.
  211|     
  212|     The two classes SummaryAST and DetailAST are actually
  213|     very similar in operation, and so most of their methods are defined here.
  214|     Both of them print out the definition of the declarations, including type,
  215|     parameters, etc. Some things such as exception specifications are only
  216|     printed out in the detailed version.
  217|     """
  218|     col_sep = '<td class="summ-info">'
  219|     row_sep = '</tr><tr><td class="summ-info">'
  220|     whole_row = '<td class="summ-start" colspan="2">'
  221|     def formatParameters(self, parameters):
  222|         "Returns formatted string for the given parameter list"
  223|         return string.join(map(self.formatParameter, parameters), ", ")
  224| 
  225|     def formatDeclaration(self, decl):
  226|         """The default is to return no type and just the declarations name for
  227|         the name"""
  228|         return self.col_sep + self.label(decl.name())
  229| 
  230|     def formatForward(self, decl): return self.formatDeclaration(decl)
  231|     def formatGroup(self, decl):
  232|         return self.col_sep + ''
  233|     def formatScope(self, decl):
  234|         """Scopes have their own pages, so return a reference to it"""
  235|         name = decl.name()
  236|         link = rel(self.formatter.filename(), config.files.nameOfScope(name))
  237|         return self.col_sep + href(link, anglebrackets(name[-1]))
  238|     def formatModule(self, decl): return self.formatScope(decl)
  239|     def formatMetaModule(self, decl): return self.formatModule(decl)
  240|     def formatClass(self, decl): return self.formatScope(decl)
  241| 
  242|     def formatTypedef(self, decl):
  243|         "(typedef type, typedef name)"
  244|         type = self.formatType(decl.alias())
  245|         return type + self.col_sep +  self.label(decl.name())
  246| 
  247|     def formatEnumerator(self, decl):
  248|         """This is only called by formatEnum"""
  249|         self.formatDeclaration(decl)
  250| 
  251|     def formatEnum(self, decl):
  252|         "(enum name, list of enumerator names)"
  253|         type = self.label(decl.name())
  254|         name = map(lambda enumor:enumor.name()[-1], decl.enumerators())
  255|         name = string.join(name, ', ')
  256|         return type + self.col_sep + name
  257| 
  258|     def formatVariable(self, decl):
  259|         # TODO: deal with sizes
  260|         type = self.formatType(decl.vtype())
  261|         return type + self.col_sep + self.label(decl.name())
  262| 
  263|     def formatConst(self, decl):
  264|         "(const type, const name = const value)"
  265|         type = self.formatType(decl.ctype())
  266|         name = self.label(decl.name()) + " = " + decl.value()
  267|         return type + self.col_sep + name
  268| 
  269|     def formatFunction(self, decl):
  270|         "(return type, func + params + formatFunctionExceptions)"
  271|         premod = self.formatModifiers(decl.premodifier())
  272|         type = self.formatType(decl.returnType())
  273|         name = self.label(decl.name(), decl.realname())
  274|         # Special C++ functions  TODO: maybe move to a separate AST formatter...
  275|         if decl.language() == 'C++' and len(decl.realname())>1:
  276|             if decl.realname()[-1] == decl.realname()[-2]: type = '<i>constructor</i>'
  277|             elif decl.realname()[-1] == "~"+decl.realname()[-2]: type = '<i>destructor</i>'
  278|             elif decl.realname()[-1] == "(conversion)":
  279|                name = "(%s)"%type
  280|         params = self.formatParameters(decl.parameters())
  281|         postmod = self.formatModifiers(decl.postmodifier())
  282|         raises = self.formatOperationExceptions(decl)
  283|         type = '%s %s'%(premod,type)
  284|         # Prevent linebreaks on shorter lines
  285|         if len(type) < 60:
  286|             type = replace_spaces(type)
  287|         if decl.type() == "attribute": name = '%s %s %s'%(name, postmod, raises)
  288|         else: name = '%s(%s) %s %s'%(name, params, postmod, raises)
  289|         if decl.template():
  290|             templ = 'template &lt;%s&gt;'%(self.formatParameters(decl.template().parameters()),)
  291|             templ = div('template', templ)
  292|             return self.whole_row + templ + self.row_sep + type + self.col_sep + name
  293|         return type + self.col_sep + name
  294| 
  295|     # Default operation is same as function, and quickest way is to assign:
  296|     def formatOperation(self, decl): return self.formatFunction(decl)
  297| 
  298|     def formatParameter(self, parameter):
  299|         """Returns one string for the given parameter"""
  300|         str = []
  301|         keyword = lambda m,span=span: span("keyword", m)
  302|         # Premodifiers
  303|         str.extend(map(keyword, parameter.premodifier()))
  304|         # Param Type
  305|         id_holder = [parameter.identifier()]
  306|         typestr = self.formatType(parameter.type(), id_holder)
  307|         if typestr: str.append(typestr)
  308|         # Postmodifiers
  309|         str.extend(map(keyword, parameter.postmodifier()))
  310|         # Param identifier
  311|         if id_holder and len(parameter.identifier()) != 0:
  312|             str.append(span("variable", parameter.identifier()))
  313|         # Param value
  314|         if len(parameter.value()) != 0:
  315|             str.append(" = " + span("value", parameter.value()))
  316|         return string.join(str)
  317| 
  318| class SummaryAST (BaseAST):
  319|     """Derives from BaseStrategy to provide summary-specific methods.
  320|     Currently the only one is formatOperationExceptions"""
  321|     def formatOperationExceptions(self, oper):
  322|         """Returns a reference to the detail if there are any exceptions."""
  323|         if len(oper.exceptions()):
  324|             return self.reference(oper.name(), " raises")
  325|         return ''
  326| 
  327| class Default (Strategy):
  328|     """A base AST strategy that calls formatDeclaration for all types"""
  329|     # All these use the same method:
  330|     def formatForward(self, decl): return self.formatDeclaration(decl)
  331|     def formatGroup(self, decl): return self.formatDeclaration(decl)
  332|     def formatScope(self, decl): return self.formatDeclaration(decl)
  333|     def formatModule(self, decl): return self.formatDeclaration(decl)
  334|     def formatMetaModule(self, decl): return self.formatDeclaration(decl)
  335|     def formatClass(self, decl): return self.formatDeclaration(decl)
  336|     def formatTypedef(self, decl): return self.formatDeclaration(decl)
  337|     def formatEnum(self, decl): return self.formatDeclaration(decl)
  338|     def formatVariable(self, decl): return self.formatDeclaration(decl)
  339|     def formatConst(self, decl): return self.formatDeclaration(decl)
  340|     def formatFunction(self, decl): return self.formatDeclaration(decl)
  341|     def formatOperation(self, decl): return self.formatDeclaration(decl)
  342|  
  343| class SummaryCommenter (Default):
  344|     """Adds summary comments to all declarations"""
  345|     def formatDeclaration(self, decl):
  346|         summary = config.comments.format_summary(self.page, decl)
  347|         if summary:
  348|             return '<br>'+span('summary', summary)
  349|         return ''
  350|     def formatGroup(self, decl):
  351|         """Override for group to use the div version of commenting, and no
  352|         <br> before"""
  353|         summary = config.comments.format(self.page, decl)
  354|         if summary:
  355|             return desc(summary)
  356|         return ''
  357|     
  358| class SourceLinker (Default):
  359|     """Adds a link to the decl on the file page to all declarations"""
  360|     def formatDeclaration(self, decl):
  361|         if not decl.file(): return ''
  362|         filename = config.files.nameOfFileSource(decl.file().filename())
  363|         line = decl.line()
  364|         link = filename + "#%d" % line
  365|         return href(rel(self.formatter.filename(), link), "[Source]")
  366| 
  367| class XRefLinker (Default):
  368|     """Adds an xref link to all declarations"""
  369|     def __init__(self, formatter):
  370|         Default.__init__(self, formatter)
  371|         self.xref = core.config.xref
  372|     def formatDeclaration(self, decl):
  373|         info = self.xref.get_info(decl.name())
  374|         if not info:
  375|             return ''
  376|         page = self.xref.get_page_for(decl.name())
  377|         filename = config.files.nameOfSpecial('xref%d'%page)
  378|         filename = filename + "#" + Util.quote(string.join(decl.name(), '::'))
  379|         return href(rel(self.formatter.filename(), filename), "[xref]")
  380|         
  381| class Heading (Strategy):
  382|     """Formats the top of a page - it is passed only the Declaration that the
  383|     page is for (a Module or Class)."""
  384| 
  385|     def formatName(self, scoped_name):
  386|         """Formats a reference to each parent scope"""
  387|         scope, text = [], []
  388|         for name in scoped_name[:-1]:
  389|             scope.append(name)
  390|             text.append(self.reference(scope))
  391|         text.append(anglebrackets(scoped_name[-1]))
  392|         return string.join(text, "::\n") + '\n'
  393| 
  394|     def formatNameInNamespace(self, scoped_name):
  395|         """Formats a reference to each parent scope, starting at the first
  396|         non-module scope"""
  397|         scope, text = [], []
  398|         for name in scoped_name[:-1]:
  399|             scope.append(name)
  400|             if config.types.has_key(scope):
  401|                ns_type = config.types[scope]
  402|                if isinstance(ns_type, Type.Declared):
  403|                    decl = ns_type.declaration()
  404|                    if isinstance(decl, AST.Module):
  405|                       # Skip modules (including namespaces)
  406|                continue
  407|             text.append(self.reference(scope))
  408|         text.append(anglebrackets(scoped_name[-1]))
  409|         return string.join(text, "::\n") + '\n'
  410| 
  411|     def formatNamespaceOfName(self, scoped_name):
  412|         "Formats a reference to each parent scope and this one"
  413|         scope, text = [], []
  414|         last_decl = None
  415|         for name in scoped_name:
  416|             scope.append(name)
  417|             if config.types.has_key(scope):
  418|                ns_type = config.types[scope]
  419|                if isinstance(ns_type, Type.Declared):
  420|                    decl = ns_type.declaration()
  421|                    if isinstance(decl, AST.Module):
  422|                       # Only do modules and namespaces
  423|                       text.append(self.reference(scope))
  424|                       last_decl = decl
  425|                continue
  426|          break
  427|         return last_decl, string.join(text, "::") + '\n'
  428| 
  429|     def formatModule(self, module):
  430|         """Formats the module by linking to each parent scope in the name"""
  431|         # Module details are only printed at the top of their page
  432|         if not module.name():
  433|             type, name = "Global", "Namespace"
  434|         else:
  435|             type = string.capitalize(module.type())
  436|             name = self.formatName(module.name())
  437|         name = entity('h1', "%s %s"%(type, name))
  438|         return name
  439| 
  440|     def formatMetaModule(self, module):
  441|         """Calls formatModule"""
  442|         return self.formatModule(module)
  443| 
  444|     def formatClass(self, clas):
  445|         """Formats the class by linking to each parent scope in the name"""
  446|         # Calculate the namespace string
  447|         decl, namespace = self.formatNamespaceOfName(clas.name())
  448|         if decl:
  449|             namespace = '%s %s'%(decl.type(), namespace)
  450|             namespace = div('class-namespace', namespace)
  451|         else:
  452|             namespace = ''
  453| 
  454|         # Calculate template string
  455|         templ = clas.template()
  456|         if templ:
  457|             params = templ.parameters()
  458|             params = string.join(map(self.formatParameter, params), ', ')
  459|             templ = div('class-template', "template &lt;%s&gt;"%params)
  460|         else:
  461|             templ = ''
  462| 
  463|         # Calculate class name string
  464|         type = clas.type()
  465|         name = self.formatNameInNamespace(clas.name())
  466|         name = div('class-name', "%s %s"%(type, name))
  467| 
  468|         # Calculate file-related string
  469|         file_name = rel(config.base_dir, clas.file().filename())
  470|         # Try the file index page first
  471|         file_link = config.files.nameOfFileIndex(clas.file().filename())
  472|         if core.manager.filename_info(file_link):
  473|             file_ref = href(rel(self.formatter.filename(), file_link), file_name, target="index")
  474|         else:
  475|             # Try source file next
  476|             file_link = config.files.nameOfFileSource(clas.file().filename())
  477|             if core.manager.filename_info(file_link):
  478|                 file_ref = href(rel(self.formatter.filename(), file_link), file_name)
  479|             else:
  480|                 file_ref = file_name
  481|         files = "Files: "+file_ref + "<br>"
  482| 
  483|         return '%s%s%s%s'%(namespace, templ, name, files)
  484| 
  485|     def formatParameter(self, parameter):
  486|         """Returns one string for the given parameter"""
  487|         str = []
  488|         keyword = lambda m,span=span: span("keyword", m)
  489|         # Premodifiers
  490|         str.extend(map(keyword, parameter.premodifier()))
  491|         # Param Type
  492|         typestr = self.formatType(parameter.type())
  493|         if typestr: str.append(typestr)
  494|         # Postmodifiers
  495|         str.extend(map(keyword, parameter.postmodifier()))
  496|         # Param identifier
  497|         if len(parameter.identifier()) != 0:
  498|             str.append(span("variable", parameter.identifier()))
  499|         # Param value
  500|         if len(parameter.value()) != 0:
  501|             str.append(" = " + span("value", parameter.value()))
  502|         return string.join(str)
  503| 
  504| class DetailAST (BaseAST):
  505|     """Derives BaseAST to provide detail-specific AST formatting."""
  506| 
  507|     col_sep = ' '
  508|     row_sep = '<br>'
  509|     whole_row = ''
  510| 
  511|     def formatOperationExceptions(self, oper):
  512|         """Prints out the full exception spec"""
  513|         if len(oper.exceptions()):
  514|             raises = span("keyword", "raises")
  515|             exceptions = []
  516|             for exception in oper.exceptions():
  517|                 exceptions.append(self.reference(exception.name()))
  518|             exceptions = span("raises", string.join(exceptions, ", "))
  519|             return '%s (%s)'%(raises, exceptions)
  520|         return ''
  521| 
  522|     def formatEnum(self, enum):
  523|         name = span("keyword", "enum ") + self.label(enum.name())
  524|         start = '<div class="enum">'
  525|         enumors = string.join(map(self.formatEnumerator, enum.enumerators()))
  526|         end = "</div>"
  527|         return '%s%s%s%s'%(name, start, enumors, end)
  528| 
  529|     def formatEnumerator(self, enumerator):
  530|         text = self.label(enumerator.name())
  531|         if len(enumerator.value()):
  532|             value = " = " + span("value", enumerator.value())
  533|         else: value = ''
  534|         comments = config.comments.format(self.page, enumerator)
  535|         return '<div class="enumerator">%s%s%s</div>'%(text,value,comments)
  536| 
  537| class DetailCommenter (Default):
  538|     """Adds summary comments to all declarations"""
  539|     def formatDeclaration(self, decl):
  540|         text = config.comments.format(self.page, decl)
  541|         if text:
  542|             return desc(text)
  543|         return ''
  544| 
  545| class ClassHierarchySimple (Strategy):
  546|     "Prints a simple text hierarchy for classes"
  547|     def formatInheritance(self, inheritance):
  548|         return '%s %s'%( self.formatModifiers(inheritance.attributes()),
  549|             self.formatType(inheritance.parent()))
  550| 
  551|     def formatClass(self, clas):
  552|         # Print out a list of the parents
  553|         super = sub = ''
  554|         if clas.parents():
  555|             parents = map(self.formatInheritance, clas.parents())
  556|             super = string.join(parents, ", ")
  557|             super = div('superclasses', "Superclasses: "+super)
  558| 
  559|         # Print subclasses
  560|         subs = config.classTree.subclasses(clas.name())
  561|         if subs:
  562|             refs = map(self.reference, subs)
  563|             sub = string.join(refs, ", ")
  564|             sub = div('subclasses', "Known subclasses: "+sub) 
  565|         
  566|         return super + sub
  567| 
  568| class ClassHierarchyGraph (ClassHierarchySimple):
  569|     """Prints a graphical hierarchy for classes, using the Dot formatter.
  570| 
  571|     @see Formatter.Dot
  572|     """
  573|     def formatClass(self, clas):
  574|         try:
  575|             import tempfile
  576|             from Synopsis.Formatter import Dot
  577|         except:
  578|             print "HierarchyGraph: Dot not found"
  579|             return ""
  580|         if 1:
  581|             super = config.classTree.superclasses(clas.name())
  582|             sub = config.classTree.subclasses(clas.name())
  583|             if len(super) == 0 and len(sub) == 0:
  584|                # Skip classes with a boring graph
  585|                return ''
  586|         #label = config.files.nameOfScopedSpecial('inheritance', clas.name())
  587|         label = self.formatter.filename()[:-5] + '-inheritance.html'
  588|         tmp = os.path.join(config.basename, label)
  589|         dot_args = ['-o', tmp, '-f', 'html', '-R', self.formatter.filename(), '-s', '-t', label]
  590|         #if core.verbose: args.append("-v")
  591|         Dot.toc = config.toc
  592|         Dot.nodes = {}
  593|         ast = AST.AST({}, [clas], config.types)
  594|         Dot.format(dot_args, ast, None)
  595|         text = ''
  596|         input = open(tmp, "r+")
  597|         line = input.readline()
  598|         while line:
  599|             text = text + line
  600|             line = input.readline()
  601|         input.close()
  602|         os.unlink(tmp)
  603|         return text
  604| 
  605| class Inheritance (Default):
  606|     """Prints just the name of each declaration, with a link to its doc"""
  607|     def formatDeclaration(self, decl, label=None):
  608|         if not label: label = decl.name()[-1]
  609|         fullname = Util.ccolonName(decl.name(), self.formatter.scope())
  610|         title = decl.type() + " " + anglebrackets(fullname)
  611|         return self.reference(decl.name(), label=label, title=title) + ' '
  612| 
  613|     def formatFunction(self, decl):
  614|         return self.formatDeclaration(decl, label=decl.realname()[-1])
  615| 
  616|     def formatOperation(self, decl): return self.formatFunction(decl)