Files
Android-pyApi/android_pyapi/org/xml_pull.py
T
shuiyu1123 a51854455b feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包
- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等)
- java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等)
- javax/ 4个模块 (net/crypto/security/sql)
- org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http)
- dalvik/ 1个模块 (system)
- ~3053个类/接口,方法签名完全对齐Android SDK
- 桌面跨平台运行 (Windows/macOS/Linux)
- 修复所有导入错误和Python关键字冲突
2026-06-24 14:15:56 +00:00

589 lines
19 KiB
Python

"""
Java org.xmlpull.v1 APIs implemented in Python.
org.xmlpull.v1 package - XmlPullParser, XmlSerializer, XmlPullParserFactory.
Wraps Python xml.etree.ElementTree.
"""
from __future__ import annotations
from .._core import Stub, AndroidOnlyError
import xml.etree.ElementTree as _ET
from io import StringIO as _StringIO
from typing import List, Optional
__all__ = [
"XmlPullParser", "XmlSerializer", "XmlPullParserFactory", "Factory",
]
# ============================================================
# XmlPullParser
# ============================================================
class XmlPullParser(Stub):
"""
org.xmlpull.v1.XmlPullParser
An XML pull parser interface.
"""
# Event types
START_TAG: int = 2
END_TAG: int = 3
TEXT: int = 4
CDSECT: int = 5
ENTITY_REF: int = 6
IGNORABLE_WHITESPACE: int = 7
PROCESSING_INSTRUCTION: int = 8
COMMENT: int = 9
DOCDECL: int = 10
START_DOCUMENT: int = 0
END_DOCUMENT: int = 1
TYPES: List[str] = [
"START_DOCUMENT", "END_DOCUMENT", "START_TAG", "END_TAG",
"TEXT", "CDSECT", "ENTITY_REF", "IGNORABLE_WHITESPACE",
"PROCESSING_INSTRUCTION", "COMMENT", "DOCDECL",
]
NO_TYPES: List[str] = []
# Features
FEATURE_PROCESS_NAMESPACES: str = "http://xmlpull.org/v1/doc/features.html#process-ns"
FEATURE_REPORT_NAMESPACE_ATTRIBUTES: str = "http://xmlpull.org/v1/doc/features.html#report-ns-attributes"
FEATURE_PROCESS_DOCDECL: str = "http://xmlpull.org/v1/doc/features.html#process-docdecl"
FEATURE_VALIDATION: str = "http://xmlpull.org/v1/doc/features.html#validation"
class _Event:
def __init__(self, type_=0, name="", text="", namespace="", prefix="",
attributes=None, depth=0, line=0, column=0):
self.type = type_
self.name = name
self.text = text
self.namespace = namespace
self.prefix = prefix
self.attributes = attributes or []
self.depth = depth
self.line = line
self.column = column
def __init__(self):
super().__init__()
self._events = []
self._pos = -1
self._depth = 0
self._features = {
self.FEATURE_PROCESS_NAMESPACES: True,
self.FEATURE_REPORT_NAMESPACE_ATTRIBUTES: False,
self.FEATURE_PROCESS_DOCDECL: False,
self.FEATURE_VALIDATION: False,
}
self._properties = {}
self._input_encoding = "UTF-8"
self._namespace_stack = []
def setInput(self, source: Any, input_encoding: str = None):
"""Set the input source for parsing."""
if input_encoding:
self._input_encoding = input_encoding
if isinstance(source, str):
# Parse directly from XML string
self._parse_string(source)
elif hasattr(source, 'read'):
# File-like object
data = source.read()
if isinstance(data, bytes):
data = data.decode(input_encoding or "UTF-8")
self._parse_string(data)
else:
self._parse_string(str(source))
def _parse_string(self, xml_str: str):
"""Parse XML string into a list of events."""
self._events = []
self._pos = -1
try:
root = _ET.fromstring(xml_str)
except _ET.ParseError as e:
# Create minimal events if parsing fails
self._events.append(XmlPullParser._Event(XmlPullParser.START_DOCUMENT))
self._events.append(XmlPullParser._Event(XmlPullParser.END_DOCUMENT))
return
self._events.append(XmlPullParser._Event(XmlPullParser.START_DOCUMENT))
self._build_events(root, 0)
self._events.append(XmlPullParser._Event(XmlPullParser.END_DOCUMENT))
def _build_events(self, element: _ET.Element, depth: int):
"""Recursively build events from ElementTree."""
attrs = []
for k, v in element.attrib.items():
ns = ""
name = k
prefix = ""
if "}" in k:
ns = k[1:k.index("}")]
name = k[k.index("}") + 1:]
attrs.append({"namespace": ns, "name": name, "prefix": prefix, "value": v})
# START_TAG
tag = element.tag
ns = ""
prefix = ""
if "}" in tag:
ns = tag[1:tag.index("}")]
tag = tag[tag.index("}") + 1:]
self._events.append(XmlPullParser._Event(
XmlPullParser.START_TAG, tag, "", ns, prefix, attrs, depth))
# Text content
if element.text and element.text.strip():
self._events.append(XmlPullParser._Event(
XmlPullParser.TEXT, "", element.text, "", "", [], depth + 1))
# Child elements
for child in element:
self._build_events(child, depth + 1)
# END_TAG
self._events.append(XmlPullParser._Event(
XmlPullParser.END_TAG, tag, "", ns, prefix, [], depth))
# Tail text
if element.tail and element.tail.strip():
self._events.append(XmlPullParser._Event(
XmlPullParser.TEXT, "", element.tail, "", "", [], depth))
# --- Event navigation ---
def getEventType(self) -> int:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].type
return self.END_DOCUMENT
def next(self) -> int:
"""Move to next event."""
if self._pos < len(self._events) - 1:
self._pos += 1
return self.getEventType()
def nextToken(self) -> int:
"""Move to next token (same as next for this impl)."""
return self.next()
def nextText(self) -> str:
"""Returns the text content of the current element."""
if self.getEventType() != self.START_TAG:
return ""
self.next()
text = self.getText()
self.next() # Move to END_TAG
return text or ""
def nextTag(self) -> int:
"""Move to next tag event."""
self.next()
while self.getEventType() not in (self.START_TAG, self.END_TAG, self.END_DOCUMENT):
self.next()
return self.getEventType()
def require(self, type_: int, namespace: str, name: str):
"""Check that the current event matches expectations."""
if self.getEventType() != type_:
raise AndroidOnlyError(f"Expected event {type_}, got {self.getEventType()}")
if namespace is not None and self.getNamespace() != namespace:
raise AndroidOnlyError(f"Expected namespace '{namespace}', got '{self.getNamespace()}'")
if name is not None and self.getName() != name:
raise AndroidOnlyError(f"Expected name '{name}', got '{self.getName()}'")
# --- Position info ---
def getDepth(self) -> int:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].depth
return 0
def getLineNumber(self) -> int:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].line
return -1
def getColumnNumber(self) -> int:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].column
return -1
def getInputEncoding(self) -> str:
return self._input_encoding
# --- Current event data ---
def getName(self) -> str:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].name
return ""
def getNamespace(self) -> str:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].namespace
return ""
def getPrefix(self) -> str:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].prefix
return ""
def getText(self) -> Optional[str]:
if 0 <= self._pos < len(self._events):
return self._events[self._pos].text
return None
def getTextCharacters(self, holder_for_start: List[int]) -> List[str]:
"""Returns text characters and fills start/length in holder."""
text = self.getText() or ""
if len(holder_for_start) >= 2:
holder_for_start[0] = 0
holder_for_start[1] = len(text)
return list(text)
# --- Attributes ---
def getAttributeCount(self) -> int:
if 0 <= self._pos < len(self._events):
return len(self._events[self._pos].attributes)
return 0
def getAttributeName(self, index: int) -> str:
if 0 <= self._pos < len(self._events):
attrs = self._events[self._pos].attributes
if 0 <= index < len(attrs):
return attrs[index]["name"]
return ""
def getAttributeNamespace(self, index: int) -> str:
if 0 <= self._pos < len(self._events):
attrs = self._events[self._pos].attributes
if 0 <= index < len(attrs):
return attrs[index]["namespace"]
return ""
def getAttributePrefix(self, index: int) -> str:
if 0 <= self._pos < len(self._events):
attrs = self._events[self._pos].attributes
if 0 <= index < len(attrs):
return attrs[index]["prefix"]
return ""
def getAttributeType(self, index: int) -> str:
return "CDATA"
def getAttributeValue(self, index_or_name: Any, namespace: str = None) -> str:
if isinstance(index_or_name, int):
if 0 <= self._pos < len(self._events):
attrs = self._events[self._pos].attributes
if 0 <= index_or_name < len(attrs):
return attrs[index_or_name]["value"]
else:
# Lookup by name
if 0 <= self._pos < len(self._events):
attrs = self._events[self._pos].attributes
for attr in attrs:
if attr["name"] == index_or_name:
if namespace is None or attr["namespace"] == namespace:
return attr["value"]
return ""
def isAttributeDefault(self, index: int) -> bool:
return False
# --- Element state ---
def isEmptyElementTag(self) -> bool:
"""Returns True if the current event is an empty element tag."""
if self.getEventType() == self.START_TAG:
if self._pos + 1 < len(self._events):
next_event = self._events[self._pos + 1]
return next_event.type == self.END_TAG and next_event.name == self.getName()
return False
def isWhitespace(self) -> bool:
text = self.getText() or ""
return text.strip() == ""
# --- Namespace support ---
def getNamespaceCount(self, depth: int) -> int:
return len(self._namespace_stack)
def getNamespacePrefix(self, index: int) -> str:
if 0 <= index < len(self._namespace_stack):
return self._namespace_stack[index][0]
return ""
def getNamespaceUri(self, prefix: str) -> str:
for p, uri in self._namespace_stack:
if p == prefix:
return uri
return ""
# --- Features / Properties ---
def getFeature(self, name: str) -> bool:
return self._features.get(name, False)
def setFeature(self, name: str, value: bool):
if name in self._features:
self._features[name] = value
else:
raise AndroidOnlyError(f"Unknown feature: {name}")
def getProperty(self, name: str):
return self._properties.get(name)
def setProperty(self, name: str, value: Any):
self._properties[name] = value
def toString(self) -> str:
return f"XmlPullParser@{hex(id(self))}[event={self.getEventType()}]"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return hash(self) & 0x7FFFFFFF
# ============================================================
# XmlSerializer
# ============================================================
class XmlSerializer(Stub):
"""
org.xmlpull.v1.XmlSerializer
Interface for serializing XML documents.
"""
def __init__(self):
super().__init__()
self._output = []
self._depth = 0
self._features = {}
self._properties = {}
self._prefixes = {}
self._output_stream = None
self._encoding = "UTF-8"
def setOutput(self, output_stream, encoding: str = None):
"""Sets the output writer/stream."""
self._output_stream = output_stream
self._encoding = encoding or "UTF-8"
def getFeature(self, name: str) -> bool:
return self._features.get(name, False)
def setFeature(self, name: str, value: bool):
self._features[name] = value
def getProperty(self, name: str):
return self._properties.get(name)
def setProperty(self, name: str, value: Any):
self._properties[name] = value
def setPrefix(self, prefix: str, namespace_uri: str):
"""Sets the prefix for a namespace."""
self._prefixes[namespace_uri] = prefix
if prefix:
self._write(f' xmlns:{prefix}="{namespace_uri}"')
else:
self._write(f' xmlns="{namespace_uri}"')
def startTag(self, namespace: str, name: str) -> "XmlSerializer":
"""Writes a start tag."""
self._write(f"<{name}")
self._depth += 1
return self
def attribute(self, namespace: str, name: str, value: str) -> "XmlSerializer":
"""Writes an attribute."""
if value is not None:
escaped = value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace('"', "&quot;")
self._write(f' {name}="{escaped}"')
return self
def endTag(self, namespace: str, name: str) -> "XmlSerializer":
"""Writes an end tag."""
self._depth -= 1
self._write(f"</{name}>")
return self
def text(self, text_or_buf: Any, start: int = None, length: int = None) -> "XmlSerializer":
"""Writes text content."""
if isinstance(text_or_buf, str):
escaped = text_or_buf.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
self._write(escaped)
elif isinstance(text_or_buf, (list, tuple)) and start is not None and length is not None:
chars = text_or_buf[start:start + length]
self._write("".join(chars))
return self
def cdsect(self, text: str) -> "XmlSerializer":
"""Writes a CDATA section."""
self._write(f"<![CDATA[{text}]]>")
return self
def entityRef(self, name: str) -> "XmlSerializer":
"""Writes an entity reference."""
self._write(f"&{name};")
return self
def processingInstruction(self, text: str) -> "XmlSerializer":
"""Writes a processing instruction."""
self._write(f"<?{text}?>")
return self
def comment(self, text: str) -> "XmlSerializer":
"""Writes a comment."""
self._write(f"<!--{text}-->")
return self
def docdecl(self, text: str) -> "XmlSerializer":
"""Writes a document declaration."""
self._write(f"<!{text}>")
return self
def startDocument(self, encoding: str = None, standalone: bool = None):
"""Writes the XML declaration."""
encoding = encoding or self._encoding
decl = f'<?xml version="1.0" encoding="{encoding}"'
if standalone is not None:
decl += f' standalone="{"yes" if standalone else "no"}"'
decl += "?>"
self._write(decl)
def endDocument(self):
"""Finishes the document."""
self.flush()
def flush(self):
"""Flushes the output."""
content = "".join(self._output)
if self._output_stream:
if hasattr(self._output_stream, 'write'):
self._output_stream.write(content)
self._output = []
def getDepth(self) -> int:
return self._depth
def getName(self) -> str:
return ""
def getNamespace(self) -> str:
return ""
def getPrefix(self, namespace: str, prefix: bool = False) -> str:
return self._prefixes.get(namespace, "")
def _write(self, s: str):
self._output.append(s)
def verbatim(self, text: str) -> "XmlSerializer":
"""Writes raw text without escaping."""
self._write(text)
return self
def write(self, text: str) -> "XmlSerializer":
"""Writes text."""
return self.text(text)
def toString(self) -> str:
return f"XmlSerializer@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return hash(self) & 0x7FFFFFFF
# ============================================================
# XmlPullParserFactory
# ============================================================
class XmlPullParserFactory(Stub):
"""
org.xmlpull.v1.XmlPullParserFactory
Factory for creating XmlPullParser and XmlSerializer instances.
"""
def __init__(self):
super().__init__()
self._features = {}
self._properties = {}
@staticmethod
def newInstance(unused: Any = None, unused_cls: Any = None) -> "XmlPullParserFactory":
return XmlPullParserFactory()
def setFeature(self, name: str, value: bool):
self._features[name] = value
def getFeature(self, name: str) -> bool:
return self._features.get(name, False)
def setProperty(self, name: str, value: Any):
self._properties[name] = value
def getProperty(self, name: str):
return self._properties.get(name)
def newPullParser(self) -> XmlPullParser:
"""Creates a new XmlPullParser."""
return XmlPullParser()
def newSerializer(self) -> XmlSerializer:
"""Creates a new XmlSerializer."""
return XmlSerializer()
def isNamespaceAware(self) -> bool:
return self.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES)
def isValidating(self) -> bool:
return self.getFeature(XmlPullParser.FEATURE_VALIDATION)
def toString(self) -> str:
return f"XmlPullParserFactory@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return hash(self) & 0x7FFFFFFF
# ============================================================
# Factory (alias)
# ============================================================
class Factory(XmlPullParserFactory):
"""
Factory alias for XmlPullParserFactory.
"""
def __init__(self):
super().__init__()
@staticmethod
def newInstance(unused: Any = None, unused_cls: Any = None) -> "Factory":
return Factory()
@staticmethod
def getInstance() -> "Factory":
return Factory()
def toString(self) -> str:
return f"Factory@{hex(id(self))}"
def equals(self, other) -> bool:
return isinstance(other, Factory)
def hashCode(self) -> int:
return hash("Factory") & 0x7FFFFFFF