session: segment handle in accept/connect notifications
[vpp.git] / doxygen / siphon / process.py
1 # Copyright (c) 2016 Comcast Cable Communications Management, LLC.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Generation template class
16
17 import logging, os,sys, cgi, json, jinja2, HTMLParser
18
19 # Classes register themselves in this dictionary
20 """Mapping of known processors to their classes"""
21 siphons = {}
22
23 """Mapping of known output formats to their classes"""
24 formats = {}
25
26
27 """Generate rendered output for siphoned data."""
28 class Siphon(object):
29
30     # Set by subclasses
31     """Our siphon name"""
32     name = None
33
34     # Set by subclasses
35     """Name of an identifier used by this siphon"""
36     identifier = None
37
38     # Set by subclasses
39     """The pyparsing object to use to parse with"""
40     _parser = None
41
42     """The input data"""
43     _cmds = None
44
45     """Group key to (directory,file) mapping"""
46     _group = None
47
48     """Logging handler"""
49     log = None
50
51     """Directory to look for siphon rendering templates"""
52     template_directory = None
53
54     """Template environment, if we're using templates"""
55     _tplenv = None
56
57     def __init__(self, template_directory, format):
58         super(Siphon, self).__init__()
59         self.log = logging.getLogger("siphon.process.%s" % self.name)
60
61         # Get our output format details
62         fmt_klass = formats[format]
63         fmt = fmt_klass()
64         self._format = fmt
65
66         # Sort out the template search path
67         def _tpldir(name):
68             return os.sep.join((template_directory, fmt.name, name))
69
70         self.template_directory = template_directory
71         searchpath = [
72             _tpldir(self.name),
73             _tpldir("default"),
74         ]
75         loader = jinja2.FileSystemLoader(searchpath=searchpath)
76         self._tplenv = jinja2.Environment(
77             loader=loader,
78             trim_blocks=True,
79             keep_trailing_newline=True)
80
81         # Convenience, get a reference to the internal escape and
82         # unescape methods in cgi and HTMLParser. These then become
83         # available to templates to use, if needed.
84         self._h = HTMLParser.HTMLParser()
85         self.escape = cgi.escape
86         self.unescape = self._h.unescape
87
88
89     # Output renderers
90
91     """Returns an object to be used as the sorting key in the item index."""
92     def index_sort_key(self, group):
93         return group
94
95     """Returns a string to use as the header at the top of the item index."""
96     def index_header(self):
97         return self.template("index_header")
98
99     """Returns the string fragment to use for each section in the item
100     index."""
101     def index_section(self, group):
102         return self.template("index_section", group=group)
103
104     """Returns the string fragment to use for each entry in the item index."""
105     def index_entry(self, meta, item):
106         return self.template("index_entry", meta=meta, item=item)
107
108     """Returns an object, typically a string, to be used as the sorting key
109     for items within a section."""
110     def item_sort_key(self, item):
111         return item['name']
112
113     """Returns a key for grouping items together."""
114     def group_key(self, directory, file, macro, name):
115         _global = self._cmds['_global']
116
117         if file in _global and 'group_label' in _global[file]:
118             self._group[file] = (directory, file)
119             return file
120
121         self._group[directory] = (directory, None)
122         return directory
123
124     """Returns a key for identifying items within a grouping."""
125     def item_key(self, directory, file, macro, name):
126         return name
127
128     """Returns a string to use as the header when rendering the item."""
129     def item_header(self, group):
130         return self.template("item_header", group=group)
131
132     """Returns a string to use as the body when rendering the item."""
133     def item_format(self, meta, item):
134         return self.template("item_format", meta=meta, item=item)
135
136     """Returns a string to use as the label for the page reference."""
137     def page_label(self, group):
138         return "_".join((
139             self.name,
140             self.sanitize_label(group)
141         ))
142
143     """Returns a title to use for a page."""
144     def page_title(self, group):
145         _global = self._cmds['_global']
146         (directory, file) = self._group[group]
147
148         if file and file in _global and 'group_label' in _global[file]:
149             return _global[file]['group_label']
150
151         if directory in _global and 'group_label' in _global[directory]:
152             return _global[directory]['group_label']
153
154         return directory
155
156     """Returns a string to use as the label for the section reference."""
157     def item_label(self, group, item):
158         return "__".join((
159             self.name,
160             item
161         ))
162
163     """Label sanitizer; for creating Doxygen references"""
164     def sanitize_label(self, value):
165         return value.replace(" ", "_") \
166                     .replace("/", "_") \
167                     .replace(".", "_")
168
169     """Template processor"""
170     def template(self, name, **kwargs):
171       tpl = self._tplenv.get_template(name + self._format.extension)
172       return tpl.render(
173             this=self,
174             **kwargs)
175
176
177     # Processing methods
178
179     """Parse the input file into a more usable dictionary structure."""
180     def load_json(self, files):
181         self._cmds = {}
182         self._group = {}
183
184         line_num = 0
185         line_start = 0
186         for filename in files:
187             filename = os.path.relpath(filename)
188             self.log.info("Parsing items in file \"%s\"." % filename)
189             data = None
190             with open(filename, "r") as fd:
191                 data = json.load(fd)
192
193             self._cmds['_global'] = data['global']
194
195             # iterate the items loaded and regroup it
196             for item in data["items"]:
197                 try:
198                     o = self._parser.parse(item['block'])
199                 except:
200                     self.log.error("Exception parsing item: %s\n%s" \
201                             % (json.dumps(item, separators=(',', ': '),
202                                 indent=4),
203                                 item['block']))
204                     raise
205
206                 # Augment the item with metadata
207                 o["meta"] = {}
208                 for key in item:
209                     if key == 'block':
210                         continue
211                     o['meta'][key] = item[key]
212
213                 # Load some interesting fields
214                 directory = item['directory']
215                 file = item['file']
216                 macro = o["macro"]
217                 name = o["name"]
218
219                 # Generate keys to group items by
220                 group_key = self.group_key(directory, file, macro, name)
221                 item_key = self.item_key(directory, file, macro, name)
222
223                 if group_key not in self._cmds:
224                     self._cmds[group_key] = {}
225
226                 self._cmds[group_key][item_key] = o
227
228     """Iterate over the input data, calling render methods to generate the
229     output."""
230     def process(self, out=None):
231
232         if out is None:
233             out = sys.stdout
234
235         # Accumulated body contents
236         contents = ""
237
238         # Write the header for this siphon type
239         out.write(self.index_header())
240
241         # Sort key helper for the index
242         def group_sort_key(group):
243             return self.index_sort_key(group)
244
245         # Iterate the dictionary and process it
246         for group in sorted(self._cmds.keys(), key=group_sort_key):
247             if group.startswith('_'):
248                 continue
249
250             self.log.info("Processing items in group \"%s\" (%s)." % \
251                 (group, group_sort_key(group)))
252
253             # Generate the section index entry (write it now)
254             out.write(self.index_section(group))
255
256             # Generate the item header (save for later)
257             contents += self.item_header(group)
258
259             def item_sort_key(key):
260                 return self.item_sort_key(self._cmds[group][key])
261
262             for key in sorted(self._cmds[group].keys(), key=item_sort_key):
263                 self.log.debug("--- Processing key \"%s\" (%s)." % \
264                     (key, item_sort_key(key)))
265
266                 o = self._cmds[group][key]
267                 meta = {
268                     "directory": o['meta']['directory'],
269                     "file": o['meta']['file'],
270                     "macro": o['macro'],
271                     "name": o['name'],
272                     "key": key,
273                     "label": self.item_label(group, key),
274                 }
275
276                 # Generate the index entry for the item (write it now)
277                 out.write(self.index_entry(meta, o))
278
279                 # Generate the item itself (save for later)
280                 contents += self.item_format(meta, o)
281
282         # Deliver the accumulated body output
283         out.write(contents)
284
285
286 """Output format class"""
287 class Format(object):
288
289     """Name of this output format"""
290     name = None
291
292     """Expected file extension of templates that build this format"""
293     extension = None
294
295
296 """Markdown output format"""
297 class FormatMarkdown(Format):
298     name = "markdown"
299     extension = ".md"
300
301 # Register 'markdown'
302 formats["markdown"] = FormatMarkdown
303
304
305 """Itemlist output format"""
306 class FormatItemlist(Format):
307     name = "itemlist"
308     extension = ".itemlist"
309
310 # Register 'itemlist'
311 formats["itemlist"] = FormatItemlist