Synopsis - Cross-Reference
File: Synopsis/Formatters/HTML/View.py1# 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 9""" 10View base class, contains base functionality and common interface for all Views. 11""" 12 13from Synopsis.Processor import Parametrized, Parameter 14from Synopsis.Formatters import open_file 15from Tags import * 16 17import re, os 18 19class Format(Parametrized): 20 """Default and base class for formatting a view layout. The Format 21 class basically defines the HTML used at the start and end of the view. 22 The default creates an XHTML compliant header and footer with a proper 23 title, and link to the stylesheet.""" 24 25 def init(self, processor, prefix): 26 27 self.prefix = prefix 28 29 def view_header(self, os, title, body, headextra, view): 30 """Called to output the view header to the given output stream. 31 @param os a file-like object (use os.write()) 32 @param title the title of this view 33 @param body the body tag, which may contain extra parameters such as 34 onLoad scripts, and may also be empty eg: for the frames index 35 @param headextra extra html to put in the head section, such as 36 scripts 37 """ 38 39 os.write('<?xml version="1.0" encoding="iso-8859-1"?>\n') 40 os.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n') 41 os.write(' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n') 42 os.write('<html xmlns="http://www.w3.org/1999/xhtml" lang="en">\n') 43 os.write('<!-- ' + view.filename() + ' -->\n') 44 os.write('<!-- this view was generated by ' + view.__class__.__name__ + ' -->\n') 45 os.write("<head>\n") 46 os.write('<meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type"/>\n') 47 os.write(element('title','Synopsis - '+ title) + '\n') 48 css = self.prefix + 'style.css' 49 os.write(element('link', type='text/css', rel='stylesheet', href=css) + '\n') 50 os.write(headextra) 51 os.write("</head>\n%s\n"%body) 52 53 def view_footer(self, os, body): 54 """Called to output the view footer to the given output stream. 55 @param os a file-like object (use os.write()) 56 @param body the close body tag, which may be empty eg: for the frames 57 index 58 """ 59 60 os.write("\n%s\n</html>\n"%body) 61 62class Template(Format): 63 """Format subclass that uses a template file to define the HTML header 64 and footer for each view.""" 65 66 template = Parameter('', 'the html template file') 67 copy_files = Parameter([], 'a list of files to be copied into the output dir') 68 69 def init(self, processor, prefix): 70 71 Format.init(self, processor, prefix) 72 self.__re_body = re.compile('<body(?P<params>([ \t\n]+[-a-zA-Z0-9]+=("[^"]*"|\'[^\']*\'|[^ \t\n>]*))*)>', re.I) 73 self.__re_closebody = re.compile('</body>', re.I) 74 self.__re_closehead = re.compile('</head>', re.I) 75 self.__title_tag = '@TITLE@' 76 self.__content_tag = '@CONTENT@' 77 for file in self.copy_files: 78 processor.file_layout.copy_file(file, file) 79 self.load_file() 80 81 def load_file(self): 82 """Loads and parses the template file""" 83 84 f = open(self.template, 'rt') 85 text = f.read(1024*64) # arbitrary max limit of 64kb 86 f.close() 87 # Find the content tag 88 content_index = text.find(self.__content_tag) 89 if content_index == -1: 90 print "Fatal: content tag '%s' not found in template file!"%self.__content_tag 91 raise SystemError, "Content tag not found" 92 header = text[:content_index] 93 # Find the title (doesn't matter if not found) 94 self.__title_index = text.find(self.__title_tag) 95 if self.__title_index != -1: 96 # Remove the title tag 97 header = header[:self.__title_index] +header[self.__title_index+len(self.__title_tag):] 98 # Find the close head tag 99 mo = self.__re_closehead.search(header) 100 if mo: self.__headextra_index = mo.start() 101 else: self.__headextra_index = -1 102 # Find the body tag 103 mo = self.__re_body.search(header) 104 if not mo: 105 print "Fatal: body tag not found in template file!" 106 print "(if you are sure there is one, this may be a bug in Synopsis)" 107 raise SystemError, "Body tag not found" 108 if mo.group('params'): self.__body_params = mo.group('params') 109 else: self.__body_params = '' 110 self.__body_index = mo.start() 111 header = header[:mo.start()] + header[mo.end():] 112 # Store the header 113 self.__header = header 114 footer = text[content_index+len(self.__content_tag):] 115 # Find the close body tag 116 mo = self.__re_closebody.search(footer) 117 if not mo: 118 print "Fatal: close body tag not found in template file" 119 raise SystemError, "Close body tag not found" 120 self.__closebody_index = mo.start() 121 footer = footer[:mo.start()] + footer[mo.end():] 122 self.__footer = footer 123 124 def write(self, os, text): 125 """Writes the text to the output stream, replaceing @PREFIX@ with the 126 prefix for this file""" 127 128 sections = text.split('@PREFIX@') 129 os.write(self.prefix.join(sections)) 130 131 def view_header(self, os, title, body, headextra, view): 132 """Formats the header using the template file""" 133 134 if not body: return Format.view_header(self, os, title, body, headextra) 135 header = self.__header 136 index = 0 137 if self.__title_index != -1: 138 self.write(os, header[:self.__title_index]) 139 self.write(os, title) 140 index = self.__title_index 141 if self.__headextra_index != -1: 142 self.write(os, header[index:self.__headextra_index]) 143 self.write(os, headextra) 144 index = self.__headextra_index 145 self.write(os, header[index:self.__body_index]) 146 if body: 147 if body[-1] == '>': 148 self.write(os, body[:-1]+self.__body_params+body[-1]) 149 else: 150 # Hmmmm... Should not happen, perhaps use regex? 151 self.write(os, body) 152 self.write(os, header[self.__body_index:]) 153 154 def view_footer(self, os, body): 155 """Formats the footer using the template file""" 156 157 if not body: return Format.view_footer(self, os, body) 158 footer = self.__footer 159 self.write(os, footer[:self.__closebody_index]) 160 self.write(os, body) 161 self.write(os, footer[self.__closebody_index:]) 162 163class View(Parametrized): 164 """Base class for Views. The base class provides a common interface, and 165 also handles common operations such as opening the file, and delegating 166 the view formatting to a strategy class.""" 167 168 template = Parameter(Format(), 'the object that provides the html template for the view') 169 170 def __init__(self, **kwds): 171 172 super(View, self).__init__(**kwds) 173 self.main = False 174 175 def register(self, frame): 176 """Registers this View class with its frame.""" 177 178 self.frame = frame 179 self.directory_layout = self.frame.processor.directory_layout 180 self.processor = frame.processor 181 self.__os = None 182 183 def filename(self): 184 """Return the filename (currently) associated with the view.""" 185 186 return '' 187 188 def title(self): 189 """Return the title (currently) associated with the view.""" 190 191 return '' 192 193 def root(self): 194 """Return a pair of (url, label) to link to the entry point of this view.""" 195 196 return None, None 197 198 def write_navigation_bar(self): 199 """Generate a navigation bar for this view.""" 200 201 self.write(self.frame.navigation_bar(self) + '\n') 202 203 def os(self): 204 "Returns the output stream opened with start_file" 205 206 return self.__os 207 208 def write(self, str): 209 """Writes the given string to the currently opened file""" 210 211 self.__os.write(str) 212 213 def register_filenames(self): 214 """Register filenames for each file this View will generate.""" 215 216 pass 217 218 def toc(self): 219 """Retrieves the TOC for this view. This method assumes that the view 220 generates info for the the whole ASG, which could be the Scope, 221 the Source (source code) or the XRef (cross reference info). 222 The default implementation returns None.""" 223 224 pass 225 226 def process(self): 227 """Process the ASG, creating view-specific html pages.""" 228 229 pass 230 231 def open_file(self): 232 """Returns a new output stream. This template method is for internal 233 use only, but may be overriden in derived classes. 234 The default joins output dir and self.filename()""" 235 236 path = os.path.join(self.processor.output, self.filename()) 237 return open_file(path) 238 239 def close_file(self): 240 """Closes the internal output stream. This template method is for 241 internal use only, but may be overriden in derived classes.""" 242 243 self.__os.close() 244 self.__os = None 245 246 def start_file(self, body='', headextra=''): 247 """Start a new file with given filename, title and body. This method 248 opens a file for writing, and writes the html header crap at the top. 249 You must specify a title, which is prepended with the project name. 250 The body argument is optional, and it is preferred to use stylesheets 251 for that sort of stuff. You may want to put an onLoad handler in it 252 though in which case that's the place to do it. The opened file is 253 stored and can be accessed using the os() method.""" 254 255 self.__os = self.open_file() 256 prefix = rel(self.filename(), '') 257 self.template.init(self.processor, prefix) 258 if not body: 259 body = '<body class="%s">'%self.__class__.__name__ 260 self.template.view_header(self.__os, self.title(), body, headextra, self) 261 262 def end_file(self, body='</body>'): 263 """Close the file using given close body tag. The default is 264 just a close body tag, but if you specify '' then nothing will be 265 written (useful for a frames view)""" 266 267 self.template.view_footer(self.__os, body) 268 self.close_file() 269 270 def reference(self, name, scope, label=None, **keys): 271 """Returns a reference to the given name. The name is a scoped name, 272 and the optional label is an alternative name to use as the link text. 273 The name is looked up in the TOC so the link may not be local. The 274 optional keys are appended as attributes to the A tag.""" 275 276 if not label: label = escape(str(scope.prune(name))) 277 entry = self.processor.toc[name] 278 if entry: return href(rel(self.filename(), entry.link), label, **keys) 279 return label or '' 280
Generated on Tue May 13 02:39:37 2008 by
synopsis (version 0.10)
synopsis (version 0.10)