Initial packaging for Tizen
[profile/ivi/gobject-introspection.git] / giscanner / mallardwriter.py
1 #!/usr/bin/env python
2 # -*- Mode: Python -*-
3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2010 Zach Goldberg
5 # Copyright (C) 2011 Johan Dahlin
6 # Copyright (C) 2011 Shaun McCance
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 # 02110-1301, USA.
22 #
23
24 import os
25 import re
26 import tempfile
27
28 from xml.sax import saxutils
29 from mako.template import Template
30 from mako.runtime import supports_caller
31
32 from . import ast
33 from .utils import to_underscores
34
35 def _space(num):
36     return " " * num
37
38 class MallardFormatter(object):
39     def __init__(self, transformer):
40         self._transformer = transformer
41
42     def escape(self, text):
43         return saxutils.escape(text.encode('utf-8')).decode('utf-8')
44
45     def format(self, doc):
46         if doc is None:
47             return ''
48
49         result = ''
50         for para in doc.split('\n\n'):
51             result += '<p>'
52             result += self.format_inline(para)
53             result += '</p>'
54         return result
55
56     def format_inline(self, para):
57         result = ''
58
59         poss = []
60         poss.append((para.find('#'), '#'))
61         poss = [pos for pos in poss if pos[0] >= 0]
62         poss.sort(cmp=lambda x, y: cmp(x[0], y[0]))
63         if len(poss) == 0:
64             result += self.escape(para)
65         elif poss[0][1] == '#':
66             pos = poss[0][0]
67             result += self.escape(para[:pos])
68             rest = para[pos + 1:]
69             link = re.split('[^a-zA-Z_:-]', rest, maxsplit=1)[0]
70             if link.endswith(':'):
71                 link = link[:-1]
72             namespace = self._transformer.namespace
73             if '::' in link:
74                 type_name, signal_name = link.split('::')
75                 if type_name in namespace.ctypes:
76                     type_ = namespace.get_by_ctype(type_name)
77                     xref = '%s.%s-%s' % (namespace.name, type_.name, signal_name)
78                     xref_name = '%s.%s::%s' % (namespace.name, type_.name, signal_name)
79                 else:
80                     xref = link
81                     xref_name = link
82             elif ':' in link:
83                 type_name, property_name = link.split(':')
84                 if type_name in namespace.ctypes:
85                     type_ = namespace.get_by_ctype(type_name)
86                     xref = '%s.%s-%s' % (namespace.name, type_.name, property_name)
87                     xref_name = '%s.%s:%s' % (namespace.name, type_.name, property_name)
88                 else:
89                     xref = link
90                     xref_name = link
91             elif link in namespace.ctypes:
92                 type_ = namespace.get_by_ctype(link)
93                 xref = '%s.%s' % (namespace.name, type_.name)
94                 xref_name = xref
95             else:
96                 xref = link
97                 xref_name = link
98             result += '<link xref="%s">%s</link>' % (xref, xref_name)
99             if len(link) < len(rest):
100                 result += self.format_inline(rest[len(link):])
101
102         return result
103
104     def format_type(self, type_):
105         raise NotImplementedError
106
107     def format_property_flags(self, property_):
108         flags = []
109         if property_.readable:
110             flags.append("Read")
111         if property_.writable:
112             flags.append("Write")
113         if property_.construct:
114             flags.append("Construct")
115         if property_.construct_only:
116             flags.append("Construct Only")
117
118         return " / ".join(flags)
119
120     def to_underscores(self, string):
121         return to_underscores(string)
122
123     def get_class_hierarchy(self, node):
124         parent_chain = [node]
125
126         while node.parent:
127             node = self._transformer.lookup_giname(str(node.parent))
128             parent_chain.append(node)
129
130         parent_chain.reverse()
131         return parent_chain
132
133 class MallardFormatterC(MallardFormatter):
134
135     def format_type(self, type_):
136         if isinstance(type_, ast.Array):
137             try:
138                 return self.format_type(type_.element_type) + '*'
139             except:
140                 return type_.target_fundamental
141         elif type_.ctype is not None:
142             return type_.ctype
143         else:
144             return type_.target_fundamental
145
146 class MallardFormatterPython(MallardFormatter):
147
148     def format_type(self, type_):
149         if isinstance(type_, ast.Array):
150             return '[' + self.format_type(type_.element_type) + ']'
151         elif isinstance(type_, ast.Map):
152             return '{%s: %s}' % (self.format_type(type_.key_type),
153                                  self.format_type(type_.value_type))
154         elif type_.target_giname is not None:
155             return type_.target_giname
156         else:
157             return type_.target_fundamental
158
159     def format(self, doc):
160         doc = MallardFormatter.format(self, doc)
161         doc = doc.replace('%NULL', 'None')
162         doc = doc.replace('%TRUE', 'True')
163         doc = doc.replace('%FALSE', 'False')
164         return doc
165
166 class MallardWriter(object):
167     def __init__(self, transformer, language):
168         self._transformer = transformer
169         self._language = language
170
171         if self._language == 'C':
172             self._formatter = MallardFormatterC(self._transformer)
173         elif self._language == 'Python':
174             self._formatter = MallardFormatterPython(self._transformer)
175         else:
176             raise SystemExit("Unsupported language: %s" % language)
177
178     def write(self, output):
179         nodes = [self._transformer.namespace]
180         for node in self._transformer.namespace.itervalues():
181             if isinstance(node, ast.Function) and node.moved_to is not None:
182                 continue
183             if getattr(node, 'disguised', False):
184                 continue
185             if isinstance(node, ast.Record) and \
186                self._language == 'Python' and \
187                node.is_gtype_struct_for is not None:
188                 continue
189             nodes.append(node)
190             if isinstance(node, (ast.Class, ast.Interface, ast.Record)):
191                 nodes += getattr(node, 'methods', [])
192                 nodes += getattr(node, 'static_methods', [])
193                 nodes += getattr(node, 'virtual_methods', [])
194                 nodes += getattr(node, 'properties', [])
195                 nodes += getattr(node, 'signals', [])
196                 if self._language == 'C':
197                     nodes += getattr(node, 'constructors', [])
198         for node in nodes:
199             self._render_node(node, output)
200
201     def _render_node(self, node, output):
202         namespace = self._transformer.namespace
203         if isinstance(node, ast.Namespace):
204             template_name = 'mallard-%s-namespace.tmpl' % self._language
205             page_id = 'index'
206         elif isinstance(node, (ast.Class, ast.Interface)):
207             template_name = 'mallard-%s-class.tmpl' % self._language
208             page_id = '%s.%s' % (namespace.name, node.name)
209         elif isinstance(node, ast.Record):
210             template_name = 'mallard-%s-record.tmpl' % self._language
211             page_id = '%s.%s' % (namespace.name, node.name)
212         elif isinstance(node, ast.Function):
213             template_name = 'mallard-%s-function.tmpl' % self._language
214             if node.parent is not None:
215                 page_id = '%s.%s.%s' % (namespace.name, node.parent.name, node.name)
216             else:
217                 page_id = '%s.%s' % (namespace.name, node.name)
218         elif isinstance(node, ast.Enum):
219             template_name = 'mallard-%s-enum.tmpl' % self._language
220             page_id = '%s.%s' % (namespace.name, node.name)
221         elif isinstance(node, ast.Property) and node.parent is not None:
222             template_name = 'mallard-%s-property.tmpl' % self._language
223             page_id = '%s.%s-%s' % (namespace.name, node.parent.name, node.name)
224         elif isinstance(node, ast.Signal) and node.parent is not None:
225             template_name = 'mallard-%s-signal.tmpl' % self._language
226             page_id = '%s.%s-%s' % (namespace.name, node.parent.name, node.name)
227         elif isinstance(node, ast.VFunction) and node.parent is not None:
228             template_name = 'mallard-%s-vfunc.tmpl' % self._language
229             page_id = '%s.%s-%s' % (namespace.name, node.parent.name, node.name)
230         else:
231             template_name = 'mallard-%s-default.tmpl' % self._language
232             page_id = '%s.%s' % (namespace.name, node.name)
233
234         if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
235             top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR']
236             template_dir = os.path.join(top_srcdir, 'giscanner')
237         else:
238             template_dir = os.path.dirname(__file__)
239
240         file_name = os.path.join(template_dir, template_name)
241         file_name = os.path.abspath(file_name)
242         template = Template(filename=file_name, output_encoding='utf-8',
243                             module_directory=tempfile.gettempdir())
244         result = template.render(namespace=namespace,
245                                  node=node,
246                                  page_id=page_id,
247                                  formatter=self._formatter)
248
249         output_file_name = os.path.join(os.path.abspath(output),
250                                         page_id + '.page')
251         fp = open(output_file_name, 'w')
252         fp.write(result)
253         fp.close()