xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/xml/sax/saxutils.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""\
2*cda5da8dSAndroid Build Coastguard WorkerA library of useful helper classes to the SAX classes, for the
3*cda5da8dSAndroid Build Coastguard Workerconvenience of application and driver writers.
4*cda5da8dSAndroid Build Coastguard Worker"""
5*cda5da8dSAndroid Build Coastguard Worker
6*cda5da8dSAndroid Build Coastguard Workerimport os, urllib.parse, urllib.request
7*cda5da8dSAndroid Build Coastguard Workerimport io
8*cda5da8dSAndroid Build Coastguard Workerimport codecs
9*cda5da8dSAndroid Build Coastguard Workerfrom . import handler
10*cda5da8dSAndroid Build Coastguard Workerfrom . import xmlreader
11*cda5da8dSAndroid Build Coastguard Worker
12*cda5da8dSAndroid Build Coastguard Workerdef __dict_replace(s, d):
13*cda5da8dSAndroid Build Coastguard Worker    """Replace substrings of a string using a dictionary."""
14*cda5da8dSAndroid Build Coastguard Worker    for key, value in d.items():
15*cda5da8dSAndroid Build Coastguard Worker        s = s.replace(key, value)
16*cda5da8dSAndroid Build Coastguard Worker    return s
17*cda5da8dSAndroid Build Coastguard Worker
18*cda5da8dSAndroid Build Coastguard Workerdef escape(data, entities={}):
19*cda5da8dSAndroid Build Coastguard Worker    """Escape &, <, and > in a string of data.
20*cda5da8dSAndroid Build Coastguard Worker
21*cda5da8dSAndroid Build Coastguard Worker    You can escape other strings of data by passing a dictionary as
22*cda5da8dSAndroid Build Coastguard Worker    the optional entities parameter.  The keys and values must all be
23*cda5da8dSAndroid Build Coastguard Worker    strings; each key will be replaced with its corresponding value.
24*cda5da8dSAndroid Build Coastguard Worker    """
25*cda5da8dSAndroid Build Coastguard Worker
26*cda5da8dSAndroid Build Coastguard Worker    # must do ampersand first
27*cda5da8dSAndroid Build Coastguard Worker    data = data.replace("&", "&amp;")
28*cda5da8dSAndroid Build Coastguard Worker    data = data.replace(">", "&gt;")
29*cda5da8dSAndroid Build Coastguard Worker    data = data.replace("<", "&lt;")
30*cda5da8dSAndroid Build Coastguard Worker    if entities:
31*cda5da8dSAndroid Build Coastguard Worker        data = __dict_replace(data, entities)
32*cda5da8dSAndroid Build Coastguard Worker    return data
33*cda5da8dSAndroid Build Coastguard Worker
34*cda5da8dSAndroid Build Coastguard Workerdef unescape(data, entities={}):
35*cda5da8dSAndroid Build Coastguard Worker    """Unescape &amp;, &lt;, and &gt; in a string of data.
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard Worker    You can unescape other strings of data by passing a dictionary as
38*cda5da8dSAndroid Build Coastguard Worker    the optional entities parameter.  The keys and values must all be
39*cda5da8dSAndroid Build Coastguard Worker    strings; each key will be replaced with its corresponding value.
40*cda5da8dSAndroid Build Coastguard Worker    """
41*cda5da8dSAndroid Build Coastguard Worker    data = data.replace("&lt;", "<")
42*cda5da8dSAndroid Build Coastguard Worker    data = data.replace("&gt;", ">")
43*cda5da8dSAndroid Build Coastguard Worker    if entities:
44*cda5da8dSAndroid Build Coastguard Worker        data = __dict_replace(data, entities)
45*cda5da8dSAndroid Build Coastguard Worker    # must do ampersand last
46*cda5da8dSAndroid Build Coastguard Worker    return data.replace("&amp;", "&")
47*cda5da8dSAndroid Build Coastguard Worker
48*cda5da8dSAndroid Build Coastguard Workerdef quoteattr(data, entities={}):
49*cda5da8dSAndroid Build Coastguard Worker    """Escape and quote an attribute value.
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Worker    Escape &, <, and > in a string of data, then quote it for use as
52*cda5da8dSAndroid Build Coastguard Worker    an attribute value.  The \" character will be escaped as well, if
53*cda5da8dSAndroid Build Coastguard Worker    necessary.
54*cda5da8dSAndroid Build Coastguard Worker
55*cda5da8dSAndroid Build Coastguard Worker    You can escape other strings of data by passing a dictionary as
56*cda5da8dSAndroid Build Coastguard Worker    the optional entities parameter.  The keys and values must all be
57*cda5da8dSAndroid Build Coastguard Worker    strings; each key will be replaced with its corresponding value.
58*cda5da8dSAndroid Build Coastguard Worker    """
59*cda5da8dSAndroid Build Coastguard Worker    entities = {**entities, '\n': '&#10;', '\r': '&#13;', '\t':'&#9;'}
60*cda5da8dSAndroid Build Coastguard Worker    data = escape(data, entities)
61*cda5da8dSAndroid Build Coastguard Worker    if '"' in data:
62*cda5da8dSAndroid Build Coastguard Worker        if "'" in data:
63*cda5da8dSAndroid Build Coastguard Worker            data = '"%s"' % data.replace('"', "&quot;")
64*cda5da8dSAndroid Build Coastguard Worker        else:
65*cda5da8dSAndroid Build Coastguard Worker            data = "'%s'" % data
66*cda5da8dSAndroid Build Coastguard Worker    else:
67*cda5da8dSAndroid Build Coastguard Worker        data = '"%s"' % data
68*cda5da8dSAndroid Build Coastguard Worker    return data
69*cda5da8dSAndroid Build Coastguard Worker
70*cda5da8dSAndroid Build Coastguard Worker
71*cda5da8dSAndroid Build Coastguard Workerdef _gettextwriter(out, encoding):
72*cda5da8dSAndroid Build Coastguard Worker    if out is None:
73*cda5da8dSAndroid Build Coastguard Worker        import sys
74*cda5da8dSAndroid Build Coastguard Worker        return sys.stdout
75*cda5da8dSAndroid Build Coastguard Worker
76*cda5da8dSAndroid Build Coastguard Worker    if isinstance(out, io.TextIOBase):
77*cda5da8dSAndroid Build Coastguard Worker        # use a text writer as is
78*cda5da8dSAndroid Build Coastguard Worker        return out
79*cda5da8dSAndroid Build Coastguard Worker
80*cda5da8dSAndroid Build Coastguard Worker    if isinstance(out, (codecs.StreamWriter, codecs.StreamReaderWriter)):
81*cda5da8dSAndroid Build Coastguard Worker        # use a codecs stream writer as is
82*cda5da8dSAndroid Build Coastguard Worker        return out
83*cda5da8dSAndroid Build Coastguard Worker
84*cda5da8dSAndroid Build Coastguard Worker    # wrap a binary writer with TextIOWrapper
85*cda5da8dSAndroid Build Coastguard Worker    if isinstance(out, io.RawIOBase):
86*cda5da8dSAndroid Build Coastguard Worker        # Keep the original file open when the TextIOWrapper is
87*cda5da8dSAndroid Build Coastguard Worker        # destroyed
88*cda5da8dSAndroid Build Coastguard Worker        class _wrapper:
89*cda5da8dSAndroid Build Coastguard Worker            __class__ = out.__class__
90*cda5da8dSAndroid Build Coastguard Worker            def __getattr__(self, name):
91*cda5da8dSAndroid Build Coastguard Worker                return getattr(out, name)
92*cda5da8dSAndroid Build Coastguard Worker        buffer = _wrapper()
93*cda5da8dSAndroid Build Coastguard Worker        buffer.close = lambda: None
94*cda5da8dSAndroid Build Coastguard Worker    else:
95*cda5da8dSAndroid Build Coastguard Worker        # This is to handle passed objects that aren't in the
96*cda5da8dSAndroid Build Coastguard Worker        # IOBase hierarchy, but just have a write method
97*cda5da8dSAndroid Build Coastguard Worker        buffer = io.BufferedIOBase()
98*cda5da8dSAndroid Build Coastguard Worker        buffer.writable = lambda: True
99*cda5da8dSAndroid Build Coastguard Worker        buffer.write = out.write
100*cda5da8dSAndroid Build Coastguard Worker        try:
101*cda5da8dSAndroid Build Coastguard Worker            # TextIOWrapper uses this methods to determine
102*cda5da8dSAndroid Build Coastguard Worker            # if BOM (for UTF-16, etc) should be added
103*cda5da8dSAndroid Build Coastguard Worker            buffer.seekable = out.seekable
104*cda5da8dSAndroid Build Coastguard Worker            buffer.tell = out.tell
105*cda5da8dSAndroid Build Coastguard Worker        except AttributeError:
106*cda5da8dSAndroid Build Coastguard Worker            pass
107*cda5da8dSAndroid Build Coastguard Worker    return io.TextIOWrapper(buffer, encoding=encoding,
108*cda5da8dSAndroid Build Coastguard Worker                            errors='xmlcharrefreplace',
109*cda5da8dSAndroid Build Coastguard Worker                            newline='\n',
110*cda5da8dSAndroid Build Coastguard Worker                            write_through=True)
111*cda5da8dSAndroid Build Coastguard Worker
112*cda5da8dSAndroid Build Coastguard Workerclass XMLGenerator(handler.ContentHandler):
113*cda5da8dSAndroid Build Coastguard Worker
114*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, out=None, encoding="iso-8859-1", short_empty_elements=False):
115*cda5da8dSAndroid Build Coastguard Worker        handler.ContentHandler.__init__(self)
116*cda5da8dSAndroid Build Coastguard Worker        out = _gettextwriter(out, encoding)
117*cda5da8dSAndroid Build Coastguard Worker        self._write = out.write
118*cda5da8dSAndroid Build Coastguard Worker        self._flush = out.flush
119*cda5da8dSAndroid Build Coastguard Worker        self._ns_contexts = [{}] # contains uri -> prefix dicts
120*cda5da8dSAndroid Build Coastguard Worker        self._current_context = self._ns_contexts[-1]
121*cda5da8dSAndroid Build Coastguard Worker        self._undeclared_ns_maps = []
122*cda5da8dSAndroid Build Coastguard Worker        self._encoding = encoding
123*cda5da8dSAndroid Build Coastguard Worker        self._short_empty_elements = short_empty_elements
124*cda5da8dSAndroid Build Coastguard Worker        self._pending_start_element = False
125*cda5da8dSAndroid Build Coastguard Worker
126*cda5da8dSAndroid Build Coastguard Worker    def _qname(self, name):
127*cda5da8dSAndroid Build Coastguard Worker        """Builds a qualified name from a (ns_url, localname) pair"""
128*cda5da8dSAndroid Build Coastguard Worker        if name[0]:
129*cda5da8dSAndroid Build Coastguard Worker            # Per http://www.w3.org/XML/1998/namespace, The 'xml' prefix is
130*cda5da8dSAndroid Build Coastguard Worker            # bound by definition to http://www.w3.org/XML/1998/namespace.  It
131*cda5da8dSAndroid Build Coastguard Worker            # does not need to be declared and will not usually be found in
132*cda5da8dSAndroid Build Coastguard Worker            # self._current_context.
133*cda5da8dSAndroid Build Coastguard Worker            if 'http://www.w3.org/XML/1998/namespace' == name[0]:
134*cda5da8dSAndroid Build Coastguard Worker                return 'xml:' + name[1]
135*cda5da8dSAndroid Build Coastguard Worker            # The name is in a non-empty namespace
136*cda5da8dSAndroid Build Coastguard Worker            prefix = self._current_context[name[0]]
137*cda5da8dSAndroid Build Coastguard Worker            if prefix:
138*cda5da8dSAndroid Build Coastguard Worker                # If it is not the default namespace, prepend the prefix
139*cda5da8dSAndroid Build Coastguard Worker                return prefix + ":" + name[1]
140*cda5da8dSAndroid Build Coastguard Worker        # Return the unqualified name
141*cda5da8dSAndroid Build Coastguard Worker        return name[1]
142*cda5da8dSAndroid Build Coastguard Worker
143*cda5da8dSAndroid Build Coastguard Worker    def _finish_pending_start_element(self,endElement=False):
144*cda5da8dSAndroid Build Coastguard Worker        if self._pending_start_element:
145*cda5da8dSAndroid Build Coastguard Worker            self._write('>')
146*cda5da8dSAndroid Build Coastguard Worker            self._pending_start_element = False
147*cda5da8dSAndroid Build Coastguard Worker
148*cda5da8dSAndroid Build Coastguard Worker    # ContentHandler methods
149*cda5da8dSAndroid Build Coastguard Worker
150*cda5da8dSAndroid Build Coastguard Worker    def startDocument(self):
151*cda5da8dSAndroid Build Coastguard Worker        self._write('<?xml version="1.0" encoding="%s"?>\n' %
152*cda5da8dSAndroid Build Coastguard Worker                        self._encoding)
153*cda5da8dSAndroid Build Coastguard Worker
154*cda5da8dSAndroid Build Coastguard Worker    def endDocument(self):
155*cda5da8dSAndroid Build Coastguard Worker        self._flush()
156*cda5da8dSAndroid Build Coastguard Worker
157*cda5da8dSAndroid Build Coastguard Worker    def startPrefixMapping(self, prefix, uri):
158*cda5da8dSAndroid Build Coastguard Worker        self._ns_contexts.append(self._current_context.copy())
159*cda5da8dSAndroid Build Coastguard Worker        self._current_context[uri] = prefix
160*cda5da8dSAndroid Build Coastguard Worker        self._undeclared_ns_maps.append((prefix, uri))
161*cda5da8dSAndroid Build Coastguard Worker
162*cda5da8dSAndroid Build Coastguard Worker    def endPrefixMapping(self, prefix):
163*cda5da8dSAndroid Build Coastguard Worker        self._current_context = self._ns_contexts[-1]
164*cda5da8dSAndroid Build Coastguard Worker        del self._ns_contexts[-1]
165*cda5da8dSAndroid Build Coastguard Worker
166*cda5da8dSAndroid Build Coastguard Worker    def startElement(self, name, attrs):
167*cda5da8dSAndroid Build Coastguard Worker        self._finish_pending_start_element()
168*cda5da8dSAndroid Build Coastguard Worker        self._write('<' + name)
169*cda5da8dSAndroid Build Coastguard Worker        for (name, value) in attrs.items():
170*cda5da8dSAndroid Build Coastguard Worker            self._write(' %s=%s' % (name, quoteattr(value)))
171*cda5da8dSAndroid Build Coastguard Worker        if self._short_empty_elements:
172*cda5da8dSAndroid Build Coastguard Worker            self._pending_start_element = True
173*cda5da8dSAndroid Build Coastguard Worker        else:
174*cda5da8dSAndroid Build Coastguard Worker            self._write(">")
175*cda5da8dSAndroid Build Coastguard Worker
176*cda5da8dSAndroid Build Coastguard Worker    def endElement(self, name):
177*cda5da8dSAndroid Build Coastguard Worker        if self._pending_start_element:
178*cda5da8dSAndroid Build Coastguard Worker            self._write('/>')
179*cda5da8dSAndroid Build Coastguard Worker            self._pending_start_element = False
180*cda5da8dSAndroid Build Coastguard Worker        else:
181*cda5da8dSAndroid Build Coastguard Worker            self._write('</%s>' % name)
182*cda5da8dSAndroid Build Coastguard Worker
183*cda5da8dSAndroid Build Coastguard Worker    def startElementNS(self, name, qname, attrs):
184*cda5da8dSAndroid Build Coastguard Worker        self._finish_pending_start_element()
185*cda5da8dSAndroid Build Coastguard Worker        self._write('<' + self._qname(name))
186*cda5da8dSAndroid Build Coastguard Worker
187*cda5da8dSAndroid Build Coastguard Worker        for prefix, uri in self._undeclared_ns_maps:
188*cda5da8dSAndroid Build Coastguard Worker            if prefix:
189*cda5da8dSAndroid Build Coastguard Worker                self._write(' xmlns:%s="%s"' % (prefix, uri))
190*cda5da8dSAndroid Build Coastguard Worker            else:
191*cda5da8dSAndroid Build Coastguard Worker                self._write(' xmlns="%s"' % uri)
192*cda5da8dSAndroid Build Coastguard Worker        self._undeclared_ns_maps = []
193*cda5da8dSAndroid Build Coastguard Worker
194*cda5da8dSAndroid Build Coastguard Worker        for (name, value) in attrs.items():
195*cda5da8dSAndroid Build Coastguard Worker            self._write(' %s=%s' % (self._qname(name), quoteattr(value)))
196*cda5da8dSAndroid Build Coastguard Worker        if self._short_empty_elements:
197*cda5da8dSAndroid Build Coastguard Worker            self._pending_start_element = True
198*cda5da8dSAndroid Build Coastguard Worker        else:
199*cda5da8dSAndroid Build Coastguard Worker            self._write(">")
200*cda5da8dSAndroid Build Coastguard Worker
201*cda5da8dSAndroid Build Coastguard Worker    def endElementNS(self, name, qname):
202*cda5da8dSAndroid Build Coastguard Worker        if self._pending_start_element:
203*cda5da8dSAndroid Build Coastguard Worker            self._write('/>')
204*cda5da8dSAndroid Build Coastguard Worker            self._pending_start_element = False
205*cda5da8dSAndroid Build Coastguard Worker        else:
206*cda5da8dSAndroid Build Coastguard Worker            self._write('</%s>' % self._qname(name))
207*cda5da8dSAndroid Build Coastguard Worker
208*cda5da8dSAndroid Build Coastguard Worker    def characters(self, content):
209*cda5da8dSAndroid Build Coastguard Worker        if content:
210*cda5da8dSAndroid Build Coastguard Worker            self._finish_pending_start_element()
211*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(content, str):
212*cda5da8dSAndroid Build Coastguard Worker                content = str(content, self._encoding)
213*cda5da8dSAndroid Build Coastguard Worker            self._write(escape(content))
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker    def ignorableWhitespace(self, content):
216*cda5da8dSAndroid Build Coastguard Worker        if content:
217*cda5da8dSAndroid Build Coastguard Worker            self._finish_pending_start_element()
218*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(content, str):
219*cda5da8dSAndroid Build Coastguard Worker                content = str(content, self._encoding)
220*cda5da8dSAndroid Build Coastguard Worker            self._write(content)
221*cda5da8dSAndroid Build Coastguard Worker
222*cda5da8dSAndroid Build Coastguard Worker    def processingInstruction(self, target, data):
223*cda5da8dSAndroid Build Coastguard Worker        self._finish_pending_start_element()
224*cda5da8dSAndroid Build Coastguard Worker        self._write('<?%s %s?>' % (target, data))
225*cda5da8dSAndroid Build Coastguard Worker
226*cda5da8dSAndroid Build Coastguard Worker
227*cda5da8dSAndroid Build Coastguard Workerclass XMLFilterBase(xmlreader.XMLReader):
228*cda5da8dSAndroid Build Coastguard Worker    """This class is designed to sit between an XMLReader and the
229*cda5da8dSAndroid Build Coastguard Worker    client application's event handlers.  By default, it does nothing
230*cda5da8dSAndroid Build Coastguard Worker    but pass requests up to the reader and events on to the handlers
231*cda5da8dSAndroid Build Coastguard Worker    unmodified, but subclasses can override specific methods to modify
232*cda5da8dSAndroid Build Coastguard Worker    the event stream or the configuration requests as they pass
233*cda5da8dSAndroid Build Coastguard Worker    through."""
234*cda5da8dSAndroid Build Coastguard Worker
235*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, parent = None):
236*cda5da8dSAndroid Build Coastguard Worker        xmlreader.XMLReader.__init__(self)
237*cda5da8dSAndroid Build Coastguard Worker        self._parent = parent
238*cda5da8dSAndroid Build Coastguard Worker
239*cda5da8dSAndroid Build Coastguard Worker    # ErrorHandler methods
240*cda5da8dSAndroid Build Coastguard Worker
241*cda5da8dSAndroid Build Coastguard Worker    def error(self, exception):
242*cda5da8dSAndroid Build Coastguard Worker        self._err_handler.error(exception)
243*cda5da8dSAndroid Build Coastguard Worker
244*cda5da8dSAndroid Build Coastguard Worker    def fatalError(self, exception):
245*cda5da8dSAndroid Build Coastguard Worker        self._err_handler.fatalError(exception)
246*cda5da8dSAndroid Build Coastguard Worker
247*cda5da8dSAndroid Build Coastguard Worker    def warning(self, exception):
248*cda5da8dSAndroid Build Coastguard Worker        self._err_handler.warning(exception)
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker    # ContentHandler methods
251*cda5da8dSAndroid Build Coastguard Worker
252*cda5da8dSAndroid Build Coastguard Worker    def setDocumentLocator(self, locator):
253*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.setDocumentLocator(locator)
254*cda5da8dSAndroid Build Coastguard Worker
255*cda5da8dSAndroid Build Coastguard Worker    def startDocument(self):
256*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.startDocument()
257*cda5da8dSAndroid Build Coastguard Worker
258*cda5da8dSAndroid Build Coastguard Worker    def endDocument(self):
259*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.endDocument()
260*cda5da8dSAndroid Build Coastguard Worker
261*cda5da8dSAndroid Build Coastguard Worker    def startPrefixMapping(self, prefix, uri):
262*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.startPrefixMapping(prefix, uri)
263*cda5da8dSAndroid Build Coastguard Worker
264*cda5da8dSAndroid Build Coastguard Worker    def endPrefixMapping(self, prefix):
265*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.endPrefixMapping(prefix)
266*cda5da8dSAndroid Build Coastguard Worker
267*cda5da8dSAndroid Build Coastguard Worker    def startElement(self, name, attrs):
268*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.startElement(name, attrs)
269*cda5da8dSAndroid Build Coastguard Worker
270*cda5da8dSAndroid Build Coastguard Worker    def endElement(self, name):
271*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.endElement(name)
272*cda5da8dSAndroid Build Coastguard Worker
273*cda5da8dSAndroid Build Coastguard Worker    def startElementNS(self, name, qname, attrs):
274*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.startElementNS(name, qname, attrs)
275*cda5da8dSAndroid Build Coastguard Worker
276*cda5da8dSAndroid Build Coastguard Worker    def endElementNS(self, name, qname):
277*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.endElementNS(name, qname)
278*cda5da8dSAndroid Build Coastguard Worker
279*cda5da8dSAndroid Build Coastguard Worker    def characters(self, content):
280*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.characters(content)
281*cda5da8dSAndroid Build Coastguard Worker
282*cda5da8dSAndroid Build Coastguard Worker    def ignorableWhitespace(self, chars):
283*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.ignorableWhitespace(chars)
284*cda5da8dSAndroid Build Coastguard Worker
285*cda5da8dSAndroid Build Coastguard Worker    def processingInstruction(self, target, data):
286*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.processingInstruction(target, data)
287*cda5da8dSAndroid Build Coastguard Worker
288*cda5da8dSAndroid Build Coastguard Worker    def skippedEntity(self, name):
289*cda5da8dSAndroid Build Coastguard Worker        self._cont_handler.skippedEntity(name)
290*cda5da8dSAndroid Build Coastguard Worker
291*cda5da8dSAndroid Build Coastguard Worker    # DTDHandler methods
292*cda5da8dSAndroid Build Coastguard Worker
293*cda5da8dSAndroid Build Coastguard Worker    def notationDecl(self, name, publicId, systemId):
294*cda5da8dSAndroid Build Coastguard Worker        self._dtd_handler.notationDecl(name, publicId, systemId)
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker    def unparsedEntityDecl(self, name, publicId, systemId, ndata):
297*cda5da8dSAndroid Build Coastguard Worker        self._dtd_handler.unparsedEntityDecl(name, publicId, systemId, ndata)
298*cda5da8dSAndroid Build Coastguard Worker
299*cda5da8dSAndroid Build Coastguard Worker    # EntityResolver methods
300*cda5da8dSAndroid Build Coastguard Worker
301*cda5da8dSAndroid Build Coastguard Worker    def resolveEntity(self, publicId, systemId):
302*cda5da8dSAndroid Build Coastguard Worker        return self._ent_handler.resolveEntity(publicId, systemId)
303*cda5da8dSAndroid Build Coastguard Worker
304*cda5da8dSAndroid Build Coastguard Worker    # XMLReader methods
305*cda5da8dSAndroid Build Coastguard Worker
306*cda5da8dSAndroid Build Coastguard Worker    def parse(self, source):
307*cda5da8dSAndroid Build Coastguard Worker        self._parent.setContentHandler(self)
308*cda5da8dSAndroid Build Coastguard Worker        self._parent.setErrorHandler(self)
309*cda5da8dSAndroid Build Coastguard Worker        self._parent.setEntityResolver(self)
310*cda5da8dSAndroid Build Coastguard Worker        self._parent.setDTDHandler(self)
311*cda5da8dSAndroid Build Coastguard Worker        self._parent.parse(source)
312*cda5da8dSAndroid Build Coastguard Worker
313*cda5da8dSAndroid Build Coastguard Worker    def setLocale(self, locale):
314*cda5da8dSAndroid Build Coastguard Worker        self._parent.setLocale(locale)
315*cda5da8dSAndroid Build Coastguard Worker
316*cda5da8dSAndroid Build Coastguard Worker    def getFeature(self, name):
317*cda5da8dSAndroid Build Coastguard Worker        return self._parent.getFeature(name)
318*cda5da8dSAndroid Build Coastguard Worker
319*cda5da8dSAndroid Build Coastguard Worker    def setFeature(self, name, state):
320*cda5da8dSAndroid Build Coastguard Worker        self._parent.setFeature(name, state)
321*cda5da8dSAndroid Build Coastguard Worker
322*cda5da8dSAndroid Build Coastguard Worker    def getProperty(self, name):
323*cda5da8dSAndroid Build Coastguard Worker        return self._parent.getProperty(name)
324*cda5da8dSAndroid Build Coastguard Worker
325*cda5da8dSAndroid Build Coastguard Worker    def setProperty(self, name, value):
326*cda5da8dSAndroid Build Coastguard Worker        self._parent.setProperty(name, value)
327*cda5da8dSAndroid Build Coastguard Worker
328*cda5da8dSAndroid Build Coastguard Worker    # XMLFilter methods
329*cda5da8dSAndroid Build Coastguard Worker
330*cda5da8dSAndroid Build Coastguard Worker    def getParent(self):
331*cda5da8dSAndroid Build Coastguard Worker        return self._parent
332*cda5da8dSAndroid Build Coastguard Worker
333*cda5da8dSAndroid Build Coastguard Worker    def setParent(self, parent):
334*cda5da8dSAndroid Build Coastguard Worker        self._parent = parent
335*cda5da8dSAndroid Build Coastguard Worker
336*cda5da8dSAndroid Build Coastguard Worker# --- Utility functions
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Workerdef prepare_input_source(source, base=""):
339*cda5da8dSAndroid Build Coastguard Worker    """This function takes an InputSource and an optional base URL and
340*cda5da8dSAndroid Build Coastguard Worker    returns a fully resolved InputSource object ready for reading."""
341*cda5da8dSAndroid Build Coastguard Worker
342*cda5da8dSAndroid Build Coastguard Worker    if isinstance(source, os.PathLike):
343*cda5da8dSAndroid Build Coastguard Worker        source = os.fspath(source)
344*cda5da8dSAndroid Build Coastguard Worker    if isinstance(source, str):
345*cda5da8dSAndroid Build Coastguard Worker        source = xmlreader.InputSource(source)
346*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(source, "read"):
347*cda5da8dSAndroid Build Coastguard Worker        f = source
348*cda5da8dSAndroid Build Coastguard Worker        source = xmlreader.InputSource()
349*cda5da8dSAndroid Build Coastguard Worker        if isinstance(f.read(0), str):
350*cda5da8dSAndroid Build Coastguard Worker            source.setCharacterStream(f)
351*cda5da8dSAndroid Build Coastguard Worker        else:
352*cda5da8dSAndroid Build Coastguard Worker            source.setByteStream(f)
353*cda5da8dSAndroid Build Coastguard Worker        if hasattr(f, "name") and isinstance(f.name, str):
354*cda5da8dSAndroid Build Coastguard Worker            source.setSystemId(f.name)
355*cda5da8dSAndroid Build Coastguard Worker
356*cda5da8dSAndroid Build Coastguard Worker    if source.getCharacterStream() is None and source.getByteStream() is None:
357*cda5da8dSAndroid Build Coastguard Worker        sysid = source.getSystemId()
358*cda5da8dSAndroid Build Coastguard Worker        basehead = os.path.dirname(os.path.normpath(base))
359*cda5da8dSAndroid Build Coastguard Worker        sysidfilename = os.path.join(basehead, sysid)
360*cda5da8dSAndroid Build Coastguard Worker        if os.path.isfile(sysidfilename):
361*cda5da8dSAndroid Build Coastguard Worker            source.setSystemId(sysidfilename)
362*cda5da8dSAndroid Build Coastguard Worker            f = open(sysidfilename, "rb")
363*cda5da8dSAndroid Build Coastguard Worker        else:
364*cda5da8dSAndroid Build Coastguard Worker            source.setSystemId(urllib.parse.urljoin(base, sysid))
365*cda5da8dSAndroid Build Coastguard Worker            f = urllib.request.urlopen(source.getSystemId())
366*cda5da8dSAndroid Build Coastguard Worker
367*cda5da8dSAndroid Build Coastguard Worker        source.setByteStream(f)
368*cda5da8dSAndroid Build Coastguard Worker
369*cda5da8dSAndroid Build Coastguard Worker    return source
370