Imported Upstream version 1.47.1
[platform/upstream/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 from __future__ import absolute_import
23 from __future__ import division
24 from __future__ import print_function
25 from __future__ import unicode_literals
26
27 import os
28 import sys
29
30 from contextlib import contextmanager
31 from xml.sax.saxutils import escape
32
33 from .libtoolimporter import LibtoolImporter
34
35 if sys.version_info.major < 3:
36     from StringIO import StringIO
37 else:
38     from io import StringIO
39     unicode = str
40
41
42 with LibtoolImporter(None, None):
43     if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
44         from _giscanner import collect_attributes
45     else:
46         from giscanner._giscanner import collect_attributes
47
48
49 def build_xml_tag(tag_name, attributes=None, data=None, self_indent=0,
50                   self_indent_char=' '):
51     if attributes is None:
52         attributes = []
53     prefix = '<%s' % (tag_name, )
54     if data is not None:
55         if isinstance(data, bytes):
56             data = data.decode('UTF-8')
57         suffix = '>%s</%s>' % (escape(data), tag_name)
58     else:
59         suffix = '/>'
60     attrs = collect_attributes(
61         tag_name, attributes,
62         self_indent,
63         self_indent_char,
64         len(prefix) + len(suffix))
65     return prefix + attrs + suffix
66
67
68 class XMLWriter(object):
69
70     def __init__(self):
71         # Build up the XML buffer as unicode strings. When writing to disk,
72         # we can assume the lack of a Byte Order Mark (BOM) and lack
73         # of an "encoding" xml property means utf-8.
74         # See: http://www.opentag.com/xfaq_enc.htm#enc_default
75         self._data = StringIO()
76         self._data.write('<?xml version="1.0"?>\n')
77         self._tag_stack = []
78         self._indent = 0
79         self._indent_unit = 2
80         self.enable_whitespace()
81
82     # Private
83
84     def _open_tag(self, tag_name, attributes=None):
85         if attributes is None:
86             attributes = []
87         attrs = collect_attributes(tag_name, attributes,
88                                    self._indent, self._indent_char, len(tag_name) + 2)
89         self.write_line('<%s%s>' % (tag_name, attrs))
90
91     def _close_tag(self, tag_name):
92         self.write_line('</%s>' % (tag_name, ))
93
94     # Public API
95
96     def enable_whitespace(self):
97         self._indent_char = ' '
98         self._newline_char = '\n'
99
100     def disable_whitespace(self):
101         self._indent_char = ''
102         self._newline_char = ''
103
104     def get_xml(self):
105         """Returns a unicode string containing the XML."""
106         return self._data.getvalue()
107
108     def get_encoded_xml(self):
109         """Returns a utf-8 encoded bytes object containing the XML."""
110         return self._data.getvalue().encode('utf-8')
111
112     def write_line(self, line='', indent=True, do_escape=False):
113         if isinstance(line, bytes):
114             line = line.decode('utf-8')
115         assert isinstance(line, unicode)
116         if do_escape:
117             line = escape(line)
118         if indent:
119             self._data.write('%s%s%s' % (self._indent_char * self._indent,
120                                          line,
121                                          self._newline_char))
122         else:
123             self._data.write('%s%s' % (line, self._newline_char))
124
125     def write_comment(self, text):
126         self.write_line('<!-- %s -->' % (text, ))
127
128     def write_tag(self, tag_name, attributes, data=None):
129         self.write_line(build_xml_tag(tag_name, attributes, data,
130                                       self._indent, self._indent_char))
131
132     def push_tag(self, tag_name, attributes=None):
133         if attributes is None:
134             attributes = []
135         self._open_tag(tag_name, attributes)
136         self._tag_stack.append(tag_name)
137         self._indent += self._indent_unit
138
139     def pop_tag(self):
140         self._indent -= self._indent_unit
141         tag_name = self._tag_stack.pop()
142         self._close_tag(tag_name)
143         return tag_name
144
145     @contextmanager
146     def tagcontext(self, tag_name, attributes=None):
147         self.push_tag(tag_name, attributes)
148         try:
149             yield
150         finally:
151             self.pop_tag()
152
153
154 def test():
155     w = XMLWriter()
156     w.push_tag('repository')
157     w.push_tag('namespace')
158     w.push_tag('enumeration')
159     w.push_tag('member',
160                [('name', 'west'),
161                 ('value', '7'),
162                 ('c:identifier', 'GTK_ANCHOR_WEST'),
163                 ('glib:nick', 'west')])
164
165     w.pop_tag()
166     w.pop_tag()
167     w.pop_tag()
168     x = w.get_xml()
169     lines = x.split('\n')
170     import pprint
171     pprint.pprint(lines)
172     assert len(lines[3]) < 80, len(lines[3])
173
174 if __name__ == '__main__':
175     test()