1# Copyright 2008-2015 Nokia Networks 2# Copyright 2016- Robot Framework Foundation 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16from .markuputils import attribute_escape, html_escape, xml_escape 17from .robottypes import is_string 18from .robotio import file_writer 19 20 21class _MarkupWriter(object): 22 23 def __init__(self, output, write_empty=True): 24 """ 25 :param output: Either an opened, file like object, or a path to the 26 desired output file. In the latter case, the file is created 27 and clients should use :py:meth:`close` method to close it. 28 :param write_empty: Whether to write empty elements and attributes. 29 """ 30 if is_string(output): 31 output = file_writer(output) 32 self.output = output 33 self._write_empty = write_empty 34 self._preamble() 35 36 def _preamble(self): 37 pass 38 39 def start(self, name, attrs=None, newline=True): 40 attrs = self._format_attrs(attrs) 41 self._start(name, attrs, newline) 42 43 def _start(self, name, attrs, newline): 44 self._write('<%s %s>' % (name, attrs) if attrs else '<%s>' % name, 45 newline) 46 47 def _format_attrs(self, attrs): 48 if not attrs: 49 return '' 50 attrs = [(k, attribute_escape(attrs[k] or '')) 51 for k in self._order_attrs(attrs)] 52 write_empty = self._write_empty 53 return ' '.join('%s="%s"' % a for a in attrs if write_empty or a[1]) 54 55 def _order_attrs(self, attrs): 56 return attrs 57 58 def content(self, content=None, escape=True, newline=False): 59 if content: 60 self._write(self._escape(content) if escape else content, newline) 61 62 def _escape(self, content): 63 raise NotImplementedError 64 65 def end(self, name, newline=True): 66 self._write('</%s>' % name, newline) 67 68 def element(self, name, content=None, attrs=None, escape=True, 69 newline=True, replace_newlines=False): 70 attrs = self._format_attrs(attrs) 71 if self._write_empty or content or attrs: 72 self._start(name, attrs, newline=False) 73 self.content(content, escape, replace_newlines) 74 self.end(name, newline) 75 76 def close(self): 77 """Closes the underlying output file.""" 78 self.output.close() 79 80 def _write(self, text, newline=False): 81 self.output.write(text) 82 if newline: 83 self.output.write('\n') 84 85 86class HtmlWriter(_MarkupWriter): 87 88 def _order_attrs(self, attrs): 89 return sorted(attrs) # eases testing 90 91 def _escape(self, content): 92 return html_escape(content) 93 94 95class XmlWriter(_MarkupWriter): 96 97 def _preamble(self): 98 self._write('<?xml version="1.0" encoding="UTF-8"?>', newline=True) 99 100 def _escape(self, text): 101 return xml_escape(text) 102 103 104class NullMarkupWriter(object): 105 """Null implementation of the _MarkupWriter interface.""" 106 107 __init__ = start = content = element = end = close = \ 108 lambda *args, **kwargs: None 109