File: Synopsis/Formatters/HTML/Views/InheritanceGraph.py
  1#
  2# Copyright (C) 2000 Stephen Davies
  3# Copyright (C) 2000 Stefan Seefeld
  4# All rights reserved.
  5# Licensed to the public under the terms of the GNU LGPL (>= 2),
  6# see the file COPYING for details.
  7#
  8
  9from Synopsis.Processor import *
 10from Synopsis import IR, ASG
 11from Synopsis.Formatters.HTML.View import View
 12from Synopsis.Formatters.HTML.Tags import *
 13
 14import os
 15
 16class DeclarationFinder(ASG.Visitor):
 17   def __init__(self, types, verbose):
 18
 19      self.types = types
 20      self.verbose = verbose
 21
 22   def __call__(self, name):
 23      try:
 24         typeobj = self.types[name]
 25      except KeyError:
 26         # Eg: Unknown parent which has been NameMapped
 27         if self.verbose: print "Warning: %s not found in type dict."%(name,)
 28         return None
 29      self.__decl = None
 30      typeobj.accept(self)
 31      #if self.__decl is None:
 32      #    return None
 33      return self.__decl
 34
 35   def visit_buitin_type_id(self, type): return
 36   def visit_unknown_type_id(self, type): return
 37   def visit_declared_type_id(self, type): self.__decl = type.declaration
 38   def visit_modifier_type_id(self, type): type.alias.accept(self)
 39   def visit_array_type_id(self, type): type.alias.accept(self)
 40   def visit_template_id(self, type): self.__decl = type.declaration
 41   def visit_parametrized_type_id(self, type): type.template.accept(self)
 42   def visit_function_type_id(self, type): return
 43
 44def find_common_name(graph):
 45   common_name = list(graph[0])
 46   for decl_name in graph[1:]:
 47      if len(common_name) > len(decl_name):
 48         common_name = common_name[:len(decl_name)]
 49      for i in range(min(len(decl_name), len(common_name))):
 50         if decl_name[i] != common_name[i]:
 51            common_name = common_name[:i]
 52            break
 53   return '::'.join(common_name)
 54
 55class InheritanceGraph(View):
 56
 57   min_size = Parameter(2, 'minimum number of nodes for a graph to be displayed')
 58   min_group_size = Parameter(5, 'how many nodes to put into a group')
 59   direction = Parameter('vertical', 'layout of the graph')
 60
 61   def register(self, frame):
 62
 63      super(InheritanceGraph, self).register(frame)
 64      self.decl_finder = DeclarationFinder(self.processor.ir.asg.types,
 65                                           self.processor.verbose)
 66
 67   def filename(self):
 68
 69      if self.main:
 70         return self.directory_layout.index()
 71      else:
 72         return self.directory_layout.special('InheritanceGraph')
 73
 74   def title(self):
 75
 76      return 'Inheritance Graph'
 77
 78   def root(self):
 79
 80      return self.filename(), self.title()
 81
 82   def consolidate(self, graphs):
 83      """Consolidates small graphs into larger ones"""
 84
 85      # Weed out the small graphs and group by common base name
 86      common = {}
 87      for graph in graphs:
 88         len_graph = len(graph)
 89         if len_graph < self.min_size:
 90            # Ignore the graph
 91            continue
 92         common.setdefault(find_common_name(graph), []).append(graph)
 93      # Consolidate each group
 94      for name, graphs in common.items():
 95         conned = []
 96         pending = []
 97         for graph in graphs:
 98            # Try adding to an already pending graph
 99            for pend in pending:
100               if len_graph + len(pend) <= self.min_group_size:
101                  pend.extend(graph)
102                  graph = None
103                  if len(pend) == self.min_group_size:
104                     conned.append(pend)
105                     pending.remove(pend)
106                  break
107            if graph:
108               if len_graph >= self.min_group_size:
109                  # Add to final list
110                  conned.append(graph)
111               else:
112                  # Add to pending list
113                  pending.append(graph)
114         graphs[:] = conned + pending
115      return common
116
117   def process(self):
118      """Creates a file with the inheritance graph"""
119
120      filename = self.filename()
121      self.start_file()
122      self.write_navigation_bar()
123      self.write(element('h1', "Inheritance Graph"))
124
125      from Synopsis.Formatters import Dot
126      # Create a toc file for Dot to use
127      toc_file = filename + "-dot.toc"
128      self.processor.toc.store(toc_file)
129      graphs = self.processor.class_tree.graphs()
130      count = 0
131      # Consolidate the graphs, and sort to make the largest appear first
132      lensorter = lambda a, b: cmp(len(b),len(a))
133      common_graphs = self.consolidate(graphs)
134      names = common_graphs.keys()
135      names.sort()
136      for name in names:
137         graphs = common_graphs[name]
138         graphs.sort(lensorter)
139         if name:
140            self.write('<div class="inheritance-group">')
141            type_str = ''
142            types = self.processor.ir.asg.types
143            type = types.get(name, None)
144            if isinstance(type, ASG.DeclaredTypeId):
145               type_str = type.declaration.type + ' '
146            self.write('Graphs in '+type_str+name+':<br/>')
147         for graph in graphs:
148            if self.processor.verbose: print "Creating graph #%s - %s classes"%(count,len(graph))
149            # Find declarations
150            declarations = map(self.decl_finder, graph)
151            declarations = filter(lambda x: x is not None, declarations)
152            #output = os.path.join(self.processor.output, 'InheritanceGraph', str(count))
153            output = os.path.join(self.processor.output,
154                                  os.path.splitext(self.filename())[0]) + '-%s'%count
155            dot = Dot.Formatter(bgcolor=self.processor.graph_color)
156            ir = IR.IR(files={}, asg=ASG.ASG(declarations, self.processor.ir.asg.types))
157            try:
158               dot.process(ir,
159                           output=output,
160                           format='html',
161                           toc_in=[toc_file],
162                           base_url=self.filename(),
163                           title='Synopsis %s'%count,
164                           layout=self.direction)
165               dot_file = open(output + '.html', 'r')
166               self.write(dot_file.read())
167               dot_file.close()
168               os.remove(output + ".html")
169            except InvalidCommand, e:
170               print 'Warning : %s'%str(e)
171            count = count + 1
172         if name:
173            self.write('</div>')
174
175      os.remove(toc_file)
176
177      self.end_file()
178