[TYPEDEF] transformer.py - revert last bad fix
[gnome.gobject-introspection] / 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 from contextlib import contextmanager
24 from cStringIO import StringIO
25 from xml.sax.saxutils import quoteattr
26
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:
71     from giscanner._giscanner import collect_attributes
72
73
74 class XMLWriter(object):
75
76     def __init__(self):
77         self._data = StringIO()
78         self._data.write('<?xml version="1.0"?>\n')
79         self._tag_stack = []
80         self._indent = 0
81         self._indent_unit = 2
82         self._indent_char = ' '
83
84     # Private
85
86     def _open_tag(self, tag_name, attributes=None):
87         if attributes is None:
88             attributes = []
89         attrs = collect_attributes(
90             tag_name, attributes, self._indent,
91             self._indent_char,
92             len(tag_name) + 2)
93         self.write_line('<%s%s>' % (tag_name, attrs))
94
95     def _close_tag(self, tag_name):
96         self.write_line('</%s>' % (tag_name, ))
97
98     # Public API
99
100     def get_xml(self):
101         return self._data.getvalue()
102
103     def write_line(self, line=''):
104         self._data.write('%s%s\n' % (self._indent_char * self._indent, line))
105
106     def write_comment(self, text):
107         self.write_line('<!-- %s -->' % (text, ))
108
109     def write_tag(self, tag_name, attributes, data=None):
110         if attributes is None:
111             attributes = []
112         prefix = '<%s' % (tag_name, )
113         if data is not None:
114             suffix = '>%s</%s>' % (data, tag_name)
115         else:
116             suffix = '/>'
117         attrs = collect_attributes(
118             tag_name, attributes,
119             self._indent,
120             self._indent_char,
121             len(prefix) + len(suffix))
122         self.write_line(prefix + attrs + suffix)
123
124     def push_tag(self, tag_name, attributes=None):
125         if attributes is None:
126             attributes = []
127         self._open_tag(tag_name, attributes)
128         self._tag_stack.append(tag_name)
129         self._indent += self._indent_unit
130
131     def pop_tag(self):
132         self._indent -= self._indent_unit
133         tag_name = self._tag_stack.pop()
134         self._close_tag(tag_name)
135         return tag_name
136
137     @contextmanager
138     def tagcontext(self, tag_name, attributes=None):
139         self.push_tag(tag_name, attributes)
140         try:
141             yield
142         finally:
143             self.pop_tag()
144
145
146 def test():
147     w = XMLWriter()
148     w.push_tag('repository')
149     w.push_tag('namespace')
150     w.push_tag('enumeration')
151     w.push_tag('member',
152                [('name', 'west'),
153                 ('value', '7'),
154                 ('c:identifier', 'GTK_ANCHOR_WEST'),
155                 ('glib:nick', 'west')])
156
157     w.pop_tag()
158     w.pop_tag()
159     w.pop_tag()
160     x = w.get_xml()
161     lines = x.split('\n')
162     import pprint
163     pprint.pprint(lines)
164     assert len(lines[3]) < 80, len(lines[3])
165
166 if __name__ == '__main__':
167     test()