TINF-96: add gobject-introspection; dep for connman-test
[profile/ivi/gobject-introspection.git] / giscanner / xmlwriter.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 from __future__ import with_statement
22
23 import os
24 from contextlib import contextmanager
25 from cStringIO import StringIO
26 from xml.sax.saxutils import escape, quoteattr
27 from .libtoolimporter import LibtoolImporter
28
29
30 def _calc_attrs_length(attributes, indent, self_indent):
31     if indent == -1:
32         return -1
33     attr_length = 0
34     for attr, value in attributes:
35         # FIXME: actually, if we have attributes with None as value this
36         # should be considered a bug and raise an error. We are just
37         # ignoring them here while we fix GIRParser to create the right
38         # ast with the correct attributes.
39         if value is None:
40             continue
41         attr_length += 2 + len(attr) + len(quoteattr(value))
42     return attr_length + indent + self_indent
43
44
45 def collect_attributes(tag_name, attributes, self_indent,
46                        self_indent_char, indent=-1):
47     if not attributes:
48         return ''
49     if _calc_attrs_length(attributes, indent, self_indent) > 79:
50         indent_len = self_indent + len(tag_name) + 1
51     else:
52         indent_len = 0
53     first = True
54     attr_value = ''
55     for attr, value in attributes:
56         # FIXME: actually, if we have attributes with None as value this
57         # should be considered a bug and raise an error. We are just
58         # ignoring them here while we fix GIRParser to create the right
59         # ast with the correct attributes.
60         if value is None:
61             continue
62         if indent_len and not first:
63             attr_value += '\n%s' % (self_indent_char * indent_len)
64         attr_value += ' %s=%s' % (attr, quoteattr(value))
65         if first:
66             first = False
67     return attr_value
68
69
70 with LibtoolImporter(None, None):
71     if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
72         from _giscanner import collect_attributes
73     else:
74         from giscanner._giscanner import collect_attributes
75
76
77 class XMLWriter(object):
78
79     def __init__(self):
80         self._data = StringIO()
81         self._data.write('<?xml version="1.0"?>\n')
82         self._tag_stack = []
83         self._indent = 0
84         self._indent_unit = 2
85         self.enable_whitespace()
86
87     # Private
88
89     def _open_tag(self, tag_name, attributes=None):
90         if attributes is None:
91             attributes = []
92         attrs = collect_attributes(
93             tag_name, attributes, self._indent,
94             self._indent_char,
95             len(tag_name) + 2)
96         self.write_line(u'<%s%s>' % (tag_name, attrs))
97
98     def _close_tag(self, tag_name):
99         self.write_line(u'</%s>' % (tag_name, ))
100
101     # Public API
102
103     def enable_whitespace(self):
104         self._indent_char = ' '
105         self._newline_char = '\n'
106
107     def disable_whitespace(self):
108         self._indent_char = ''
109         self._newline_char = ''
110
111     def get_xml(self):
112         return self._data.getvalue()
113
114     def write_line(self, line=u'', indent=True, do_escape=False):
115         if isinstance(line, str):
116             line = line.decode('utf-8')
117         assert isinstance(line, unicode)
118         if do_escape:
119             line = escape(line.encode('utf-8')).decode('utf-8')
120         if indent:
121             self._data.write('%s%s%s' % (
122                     self._indent_char * self._indent,
123                     line.encode('utf-8'),
124                     self._newline_char))
125         else:
126             self._data.write('%s%s' % (line.encode('utf-8'), self._newline_char))
127
128     def write_comment(self, text):
129         self.write_line('<!-- %s -->' % (text, ))
130
131     def write_tag(self, tag_name, attributes, data=None):
132         if attributes is None:
133             attributes = []
134         prefix = u'<%s' % (tag_name, )
135         if data is not None:
136             if isinstance(data, str):
137                 data = data.decode('UTF-8')
138             suffix = u'>%s</%s>' % (escape(data), tag_name)
139         else:
140             suffix = u'/>'
141         attrs = collect_attributes(
142             tag_name, attributes,
143             self._indent,
144             self._indent_char,
145             len(prefix) + len(suffix))
146         self.write_line(prefix + attrs + suffix)
147
148     def push_tag(self, tag_name, attributes=None):
149         if attributes is None:
150             attributes = []
151         self._open_tag(tag_name, attributes)
152         self._tag_stack.append(tag_name)
153         self._indent += self._indent_unit
154
155     def pop_tag(self):
156         self._indent -= self._indent_unit
157         tag_name = self._tag_stack.pop()
158         self._close_tag(tag_name)
159         return tag_name
160
161     @contextmanager
162     def tagcontext(self, tag_name, attributes=None):
163         self.push_tag(tag_name, attributes)
164         try:
165             yield
166         finally:
167             self.pop_tag()
168
169
170 def test():
171     w = XMLWriter()
172     w.push_tag('repository')
173     w.push_tag('namespace')
174     w.push_tag('enumeration')
175     w.push_tag('member',
176                [('name', 'west'),
177                 ('value', '7'),
178                 ('c:identifier', 'GTK_ANCHOR_WEST'),
179                 ('glib:nick', 'west')])
180
181     w.pop_tag()
182     w.pop_tag()
183     w.pop_tag()
184     x = w.get_xml()
185     lines = x.split('\n')
186     import pprint
187     pprint.pprint(lines)
188     assert len(lines[3]) < 80, len(lines[3])
189
190 if __name__ == '__main__':
191     test()