Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Formatter/HTML/InheritanceGraph.py
    1| # $Id: InheritanceGraph.py,v 1.24 2003/02/01 05:35:45 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: InheritanceGraph.py,v $
   23| # Revision 1.24  2003/02/01 05:35:45  chalky
   24| # Remove warning
   25| #
   26| # Revision 1.23  2002/12/09 04:00:59  chalky
   27| # Added multiple file support to parsers, changed AST datastructure to handle
   28| # new information, added a demo to demo/C++. AST Declarations now have a
   29| # reference to a SourceFile (which includes a filename) instead of a filename.
   30| #
   31| # Revision 1.22  2002/10/28 16:27:22  chalky
   32| # Support horizontal inheritance graphs
   33| #
   34| # Revision 1.21  2002/10/28 11:44:57  chalky
   35| # Split graphs into groups based on common prefixes, and display each separately
   36| #
   37| # Revision 1.20  2002/10/28 06:13:07  chalky
   38| # Fix typo
   39| #
   40| # Revision 1.19  2002/10/27 12:03:49  chalky
   41| # Rename min_size option to min_group_size, add new min_size option
   42| #
   43| # Revision 1.18  2002/07/19 14:26:32  chalky
   44| # Revert prefix in FileLayout but keep relative referencing elsewhere.
   45| #
   46| # Revision 1.17  2002/07/11 02:09:33  chalky
   47| # Patch from Patrick Mauritz: Use png support in latest graphviz. If dot not
   48| # present, don't subvert what the user asked for but instead tell them.
   49| #
   50| # Revision 1.16  2002/07/04 06:43:18  chalky
   51| # Improved support for absolute references - pages known their full path.
   52| #
   53| # Revision 1.15  2001/07/19 04:03:05  chalky
   54| # New .syn file format.
   55| #
   56| # Revision 1.14  2001/07/11 01:45:03  stefan
   57| # fix Dot and HTML formatters to adjust the references depending on the filename of the output
   58| #
   59| # Revision 1.13  2001/07/05 05:39:58  stefan
   60| # advanced a lot in the refactoring of the HTML module.
   61| # Page now is a truely polymorphic (abstract) class. Some derived classes
   62| # implement the 'filename()' method as a constant, some return a variable
   63| # dependent on what the current scope is...
   64| #
   65| # Revision 1.12  2001/07/05 02:08:35  uid20151
   66| # Changed the registration of pages to be part of a two-phase construction
   67| #
   68| # Revision 1.11  2001/06/28 07:22:18  stefan
   69| # more refactoring/cleanup in the HTML formatter
   70| #
   71| # Revision 1.10  2001/06/26 04:32:16  stefan
   72| # A whole slew of changes mostly to fix the HTML formatter's output generation,
   73| # i.e. to make the output more robust towards changes in the layout of files.
   74| #
   75| # the rpm script now works, i.e. it generates source and binary packages.
   76| #
   77| # Revision 1.9  2001/06/06 04:44:11  uid20151
   78| # Only create TOC once instead of for every graph
   79| #
   80| # Revision 1.8  2001/04/06 06:26:39  chalky
   81| # No more warning on bad types
   82| #
   83| # Revision 1.7  2001/03/29 14:12:42  chalky
   84| # Consolidate small graphs to speed up processing time (graphviz is slow)
   85| #
   86| # Revision 1.6  2001/02/13 06:55:23  chalky
   87| # Made synopsis -l work again
   88| #
   89| # Revision 1.5  2001/02/06 16:54:15  chalky
   90| # Added -n to Dot which stops those nested classes
   91| #
   92| # Revision 1.4  2001/02/06 16:02:23  chalky
   93| # Fixes
   94| #
   95| # Revision 1.3  2001/02/06 05:13:05  chalky
   96| # Fixes
   97| #
   98| # Revision 1.2  2001/02/05 05:26:24  chalky
   99| # Graphs are separated. Misc changes
  100| #
  101| # Revision 1.1  2001/02/01 20:09:18  chalky
  102| # Initial commit.
  103| #
  104| # Revision 1.3  2001/02/01 18:36:55  chalky
  105| # Moved TOC out to Formatter/TOC.py
  106| #
  107| # Revision 1.2  2001/02/01 15:23:24  chalky
  108| # Copywritten brown paper bag edition.
  109| #
  110| #
  111| 
  112| import os
  113| 
  114| from Synopsis.Core import AST, Type, Util
  115| 
  116| import core, Page
  117| from core import config
  118| from Tags import *
  119| 
  120| class ToDecl (Type.Visitor):
  121|     def __call__(self, name):
  122|         try:
  123|             typeobj = config.types[name]
  124|         except KeyError:
  125|             # Eg: Unknown parent which has been NameMapped
  126|             #if config.verbose: print "Warning: %s not found in types dict."%(name,)
  127|             return None
  128|         self.__decl = None
  129|         typeobj.accept(self)
  130|         #if self.__decl is None:
  131|         #    return None
  132|         return self.__decl
  133|         
  134|     def visitBaseType(self, type): return
  135|     def visitUnknown(self, type): return
  136|     def visitDeclared(self, type): self.__decl = type.declaration()
  137|     def visitModifier(self, type): type.alias().accept(self)
  138|     def visitArray(self, type): type.alias().accept(self)
  139|     def visitTemplate(self, type): self.__decl = type.declaration()
  140|     def visitParametrized(self, type): type.template().accept(self)
  141|     def visitFunctionType(self, type): return
  142|         
  143| def find_common_name(graph):
  144|     common_name = list(graph[0])
  145|     for decl_name in graph[1:]:
  146|         if len(common_name) > len(decl_name):
  147|             common_name = common_name[:len(decl_name)]
  148|         for i in range(min(len(decl_name), len(common_name))):
  149|             if decl_name[i] != common_name[i]:
  150|                common_name = common_name[:i]
  151|         break
  152|     return string.join(common_name, '::')
  153|         
  154| 
  155| class InheritanceGraph(Page.Page):
  156|     def __init__(self, manager):
  157|         Page.Page.__init__(self, manager)
  158|         self.__todecl = ToDecl()
  159|         self.__direction = 'vertical'
  160|         if hasattr(config.obj,'InheritanceGraph'):
  161|             if hasattr(config.obj.InheritanceGraph,'direction'):
  162|                 if config.obj.InheritanceGraph.direction == 'horizontal':
  163|                     self.__direction = 'horizontal'
  164| 
  165|     def filename(self): return config.files.nameOfSpecial('InheritanceGraph')
  166|     def title(self): return 'Synopsis - Class Hierarchy'
  167| 
  168|     def register(self):
  169|         """Registers this page with the manager"""
  170|         self.manager.addRootPage(self.filename(), 'Inheritance Graph', 'main', 1)
  171|  
  172|     def consolidate(self, graphs):
  173|         """Consolidates small graphs into larger ones"""
  174|         try:
  175|             min_size = config.obj.InheritanceGraph.min_size
  176|         except:
  177|             if config.verbose:
  178|                print "Error getting InheritanceGraph.min_size value. Using 1."
  179|             min_size = 1
  180|         try:
  181|             min_group_size = config.obj.InheritanceGraph.min_group_size
  182|         except:
  183|             if config.verbose:
  184|                print "Error getting InheritanceGraph.min_group_size value. Using 5."
  185|             min_group_size = 5
  186|         # Weed out the small graphs and group by common base name
  187|         common = {}
  188|         for graph in graphs:
  189|             len_graph = len(graph)
  190|             if len_graph < min_size:
  191|                # Ignore the graph
  192|                continue
  193|             common.setdefault(find_common_name(graph), []).append(graph)
  194|         # Consolidate each group
  195|         for name, graphs in common.items():
  196|             conned = []
  197|             pending = []
  198|             for graph in graphs:
  199|                # Try adding to an already pending graph
  200|                for pend in pending:
  201|                    if len_graph + len(pend) <= min_group_size:
  202|                       pend.extend(graph)
  203|                graph = None
  204|                       if len(pend) == min_group_size:
  205|                        conned.append(pend)
  206|                         pending.remove(pend)
  207|         break
  208|                if graph:
  209|                    if len_graph >= min_group_size:
  210|                       # Add to final list
  211|                       conned.append(graph)
  212|                else:
  213|                       # Add to pending list
  214|                       pending.append(graph)
  215|             graphs[:] = conned + pending
  216|         return common
  217| 
  218|     def process(self, start):
  219|         """Creates a file with the inheritance graph"""
  220|         filename = self.filename()
  221|         self.start_file()
  222|         self.write(self.manager.formatHeader(filename))
  223|         self.write(entity('h1', "Inheritance Graph"))
  224| 
  225|         try:
  226|             from Synopsis.Formatter import Dot
  227|         except:
  228|             print "InheritanceGraph: Can't load the Dot formatter"
  229|             self.end_file()
  230|           return
  231|         # Create a toc file for Dot to use
  232|         toc_file = filename + "-dot.toc"
  233|         config.toc.store(toc_file)
  234|         graphs = config.classTree.graphs()
  235|         count = 0
  236|         # Consolidate the graphs, and sort to make the largest appear first
  237|         lensorter = lambda a, b: cmp(len(b),len(a))
  238|         common_graphs = self.consolidate(graphs)
  239|         names = common_graphs.keys()
  240|         names.sort()
  241|         for name in names:
  242|             graphs = common_graphs[name]
  243|             graphs.sort(lensorter)
  244|             if name:
  245|                self.write('<div class="inheritance-group">')
  246|                scoped_name = string.split(name,'::')
  247|                type_str = ''
  248|                if core.config.types.has_key(scoped_name):
  249|                    type = core.config.types[scoped_name]
  250|                    if isinstance(type, Type.Declared):
  251|                       type_str = type.declaration().type() + ' '
  252|                self.write('Graphs in '+type_str+name+':<br>')
  253|             for graph in graphs:
  254|         try:
  255|                    if core.verbose: print "Creating graph #%s - %s classes"%(count,len(graph))
  256|                    # Find declarations
  257|                    declarations = map(self.__todecl, graph)
  258|                    declarations = filter(lambda x: x is not None, declarations)
  259|                    # Call Dot formatter
  260|                    output = os.path.join(config.basename, os.path.splitext(self.filename())[0]) + '-%s'%count
  261|                args = (
  262|                       '-i','-f','html','-o',output,'-r',toc_file,
  263|                       '-R',self.filename(),'-t','Synopsis %s'%count,'-n', 
  264|                       '-p',name,'-d',self.__direction)
  265|                    temp_ast = AST.AST({}, declarations, config.types)
  266|                    Dot.format(args, temp_ast, None)
  267|                    dot_file = open(output + '.html', 'r')
  268|                    self.write(dot_file.read())
  269|                    dot_file.close()
  270|                    os.remove(output + ".html")
  271|         except:
  272|                    import traceback
  273|                    traceback.print_exc()
  274|                    print "Graph:",graph
  275|                    print "Declarations:",declarations
  276|                count = count + 1
  277|             if name:
  278|                self.write('</div>')
  279| 
  280|         os.remove(toc_file)
  281| 
  282|         self.end_file() 
  283| 
  284| 
  285| htmlPageClass = InheritanceGraph