File: View.py

package info (click to toggle)
synopsis 0.8.0-5
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 10,112 kB
  • ctags: 12,996
  • sloc: cpp: 34,254; ansic: 33,620; python: 10,975; sh: 7,261; xml: 6,369; makefile: 773; asm: 445
file content (292 lines) | stat: -rw-r--r-- 10,934 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#
# Copyright (C) 2000 Stephen Davies
# Copyright (C) 2000 Stefan Seefeld
# All rights reserved.
# Licensed to the public under the terms of the GNU LGPL (>= 2),
# see the file COPYING for details.
#

"""
View base class, contains base functionality and common interface for all Views.
"""

from Synopsis.Processor import Parametrized, Parameter
from Synopsis import Util
from Tags import *

import os.path, cStringIO

class Format(Parametrized):
   """Default and base class for formatting a view layout. The Format
   class basically defines the HTML used at the start and end of the view.
   The default creates an XHTML compliant header and footer with a proper
   title, and link to the stylesheet."""

   def init(self, processor, prefix):

      self.prefix = prefix

   def view_header(self, os, title, body, headextra, view):
      """Called to output the view header to the given output stream.
      @param os a file-like object (use os.write())
      @param title the title of this view
      @param body the body tag, which may contain extra parameters such as
      onLoad scripts, and may also be empty eg: for the frames index
      @param headextra extra html to put in the head section, such as
      scripts
      """

      os.write('<?xml version="1.0" encoding="iso-8859-1"?>\n')
      os.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n')
      os.write('    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n')
      os.write('<html xmlns="http://www.w3.org/1999/xhtml" lang="en">\n')
      os.write('<!-- ' + view.filename() + ' -->\n')
      os.write('<!-- this view was generated by ' + view.__class__.__name__ + ' -->\n')
      os.write("<head>\n")
      os.write('<meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type"/>')
      os.write(entity('title','Synopsis - '+ title) + '\n')
      css = self.prefix + 'style.css'
      os.write(solotag('link', type='text/css', rel='stylesheet', href=css) + '\n')
      os.write(headextra)
      os.write("</head>\n%s\n"%body)

   def view_footer(self, os, body):
      """Called to output the view footer to the given output stream.
      @param os a file-like object (use os.write())
      @param body the close body tag, which may be empty eg: for the frames
      index
      """

      os.write("\n%s\n</html>\n"%body)

class Template(Format):
   """Format subclass that uses a template file to define the HTML header
   and footer for each view."""

   template = Parameter('', 'the html template file')
   copy_files = Parameter([], 'a list of files to be copied into the output dir')
   
   def init(self, processor, prefix):
      
      Format.init(self, processor, prefix)
      self.__re_body = re.compile('<body(?P<params>([ \t\n]+[-a-zA-Z0-9]+=("[^"]*"|\'[^\']*\'|[^ \t\n>]*))*)>', re.I)
      self.__re_closebody = re.compile('</body>', re.I)
      self.__re_closehead = re.compile('</head>', re.I)
      self.__title_tag = '@TITLE@'
      self.__content_tag = '@CONTENT@'
      for file in self.copy_files:
         processor.file_layout.copy_file(file, file)
      self.load_file()

   def load_file(self):
      """Loads and parses the template file"""

      f = open(self.template, 'rt')
      text = f.read(1024*64) # arbitrary max limit of 64kb
      f.close()
      # Find the content tag
      content_index = text.find(self.__content_tag)
      if content_index == -1:
         print "Fatal: content tag '%s' not found in template file!"%self.__content_tag
         raise SystemError, "Content tag not found"
      header = text[:content_index]
      # Find the title (doesn't matter if not found)
      self.__title_index = text.find(self.__title_tag)
      if self.__title_index != -1:
         # Remove the title tag
         header = header[:self.__title_index] + \
                  header[self.__title_index+len(self.__title_tag):]
      # Find the close head tag
      mo = self.__re_closehead.search(header)
      if mo: self.__headextra_index = mo.start()
      else: self.__headextra_index = -1
      # Find the body tag
      mo = self.__re_body.search(header)
      if not mo:
         print "Fatal: body tag not found in template file!"
         print "(if you are sure there is one, this may be a bug in Synopsis)"
         raise SystemError, "Body tag not found"
      if mo.group('params'): self.__body_params = mo.group('params')
      else: self.__body_params = ''
      self.__body_index = mo.start()
      header = header[:mo.start()] + header[mo.end():]
      # Store the header
      self.__header = header
      footer = text[content_index+len(self.__content_tag):]
      # Find the close body tag
      mo = self.__re_closebody.search(footer)
      if not mo:
         print "Fatal: close body tag not found in template file"
         raise SystemError, "Close body tag not found"
      self.__closebody_index = mo.start()
      footer = footer[:mo.start()] + footer[mo.end():]
      self.__footer = footer

   def write(self, os, text):
      """Writes the text to the output stream, replaceing @PREFIX@ with the
      prefix for this file"""

      sections = string.split(text, '@PREFIX@')
      os.write(string.join(sections, self.prefix))

   def view_header(self, os, title, body, headextra, view):
      """Formats the header using the template file"""

      if not body: return Format.view_header(self, os, title, body, headextra)
      header = self.__header
      index = 0
      if self.__title_index != -1:
         self.write(os, header[:self.__title_index])
         self.write(os, title)
         index = self.__title_index
      if self.__headextra_index != -1:
         self.write(os, header[index:self.__headextra_index])
         self.write(os, headextra)
         index = self.__headextra_index
      self.write(os, header[index:self.__body_index])
      if body:
         if body[-1] == '>':
            self.write(os, body[:-1]+self.__body_params+body[-1])
         else:
            # Hmmmm... Should not happen, perhaps use regex?
            self.write(os, body)
      self.write(os, header[self.__body_index:])

   def view_footer(self, os, body):
      """Formats the footer using the template file"""

      if not body: return Format.view_footer(self, os, body)
      footer = self.__footer
      self.write(os, footer[:self.__closebody_index])
      self.write(os, body)
      self.write(os, footer[self.__closebody_index:])

class View(Parametrized):
   """Base class for a Views. The base class provides a common interface, and
   also handles common operations such as opening the file, and delegating
   the view formatting to a strategy class.
   @see Format"""

   template = Parameter(Format(), 'the object that provides the html template for the view')
   
   def register(self, processor):
      """Registers this View class with the processor."""

      self.processor = processor
      self.__os = None

   def filename(self):
      "Polymorphic method returning the filename associated with the view"

      return ''

   def title(self):
      "Polymorphic method returning the title associated with the view"

      return ''

   def os(self):
      "Returns the output stream opened with start_file"

      return self.__os

   def write(self, str):
      """Writes the given string to the currently opened file"""

      self.__os.write(str)

   def register_filenames(self, start):
      """Registers filenames for each file this View will generate, given
      the starting Scope."""

      pass

   def get_toc(self, start):
      """Retrieves the TOC for this view. This method assumes that the view
      generates info for the the whole AST, which could be the Scope,
      the Source (source code) or the XRef (cross reference info).
      The default implementation returns None. Start is the declaration to
      start processing from, which could be the global namespace."""

      pass
       
   def process(self, start):
      """Process the given Scope recursively. This is the method which is
      called to actually create the files, so you probably want to override
      it ;)"""

      pass

   def open_file(self):
      """Returns a new output stream. This template method is for internal
      use only, but may be overriden in derived classes.
      The default joins output dir and self.filename()
      and uses Util.open()"""

      return Util.open(os.path.join(self.processor.output, self.filename()))

   def close_file(self):
      """Closes the internal output stream. This template method is for
      internal use only, but may be overriden in derived classes."""

      self.__os.close()
      self.__os = None
	
   def start_file(self, body='<body>', headextra=''):
      """Start a new file with given filename, title and body. This method
      opens a file for writing, and writes the html header crap at the top.
      You must specify a title, which is prepended with the project name.
      The body argument is optional, and it is preferred to use stylesheets
      for that sort of stuff. You may want to put an onLoad handler in it
      though in which case that's the place to do it. The opened file is
      stored and can be accessed using the os() method."""

      self.__os = self.open_file()
      prefix = rel(self.filename(), '')
      self.template.init(self.processor, prefix)
      self.template.view_header(self.__os, self.title(), body, headextra, self)
    
   def end_file(self, body='</body>'):
      """Close the file using given close body tag. The default is
      just a close body tag, but if you specify '' then nothing will be
      written (useful for a frames view)"""

      self.template.view_footer(self.__os, body)
      self.close_file()

   def reference(self, name, scope, label=None, **keys):
      """Returns a reference to the given name. The name is a scoped name,
      and the optional label is an alternative name to use as the link text.
      The name is looked up in the TOC so the link may not be local. The
      optional keys are appended as attributes to the A tag."""

      if not label: label = escape(Util.ccolonName(name, scope))
      entry = self.processor.toc[name]
      if entry: return apply(href, (rel(self.filename(), entry.link), label), keys)
      return label or ''

class BufferView(View):
   """A view that writes to a string buffer."""
   def _take_control(self):

      self.open_file = lambda s=self: BufferView.open_file(s)
      self.close_file = lambda s=self: BufferView.close_file(s)
      self.get_buffer = lambda s=self: BufferView.get_buffer(s)
	
   def open_file(self):
      "Returns a new StringIO"

      return cStringIO.StringIO()

   def close_file(self):
      "Does nothing."

      pass

   def get_buffer(self):
      """Returns the view as a string, then deletes the internal buffer"""

      view = self.os().getvalue()
      # NOW we do the close
      View.close_file(self)
      return view