- 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关键字冲突
573 lines
19 KiB
Python
573 lines
19 KiB
Python
"""
|
|
Java java.nio.charset APIs implemented in Python.
|
|
java.nio.charset package - Charset, CharsetEncoder, CharsetDecoder, CoderResult, etc.
|
|
Wraps Python codecs module.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
import codecs as _codecs
|
|
import encodings as _encodings
|
|
from typing import Set as _Set, List as _List
|
|
|
|
__all__ = [
|
|
"Charset", "CharsetEncoder", "CharsetDecoder", "CoderResult",
|
|
"CodingErrorAction", "StandardCharsets",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# CodingErrorAction
|
|
# ============================================================
|
|
|
|
class CodingErrorAction(Stub):
|
|
"""
|
|
java.nio.charset.CodingErrorAction
|
|
Describes actions that a charset encoder or decoder may take on a coding error.
|
|
"""
|
|
REPORT: "CodingErrorAction" = None
|
|
IGNORE: "CodingErrorAction" = None
|
|
REPLACE: "CodingErrorAction" = None
|
|
|
|
def __init__(self, name: str):
|
|
super().__init__()
|
|
self._name = name
|
|
|
|
def name(self) -> str:
|
|
return self._name
|
|
|
|
def toString(self) -> str:
|
|
return self._name
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, CodingErrorAction):
|
|
return self._name == other._name
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._name) & 0x7FFFFFFF
|
|
|
|
|
|
CodingErrorAction.REPORT = CodingErrorAction("REPORT")
|
|
CodingErrorAction.IGNORE = CodingErrorAction("IGNORE")
|
|
CodingErrorAction.REPLACE = CodingErrorAction("REPLACE")
|
|
|
|
|
|
# ============================================================
|
|
# CoderResult
|
|
# ============================================================
|
|
|
|
class CoderResult(Stub):
|
|
"""
|
|
java.nio.charset.CoderResult
|
|
Describes the result state of a coder (encoder or decoder).
|
|
"""
|
|
UNDERFLOW: "CoderResult" = None
|
|
OVERFLOW: "CoderResult" = None
|
|
|
|
def __init__(self, _type: str, _length: int = 0):
|
|
super().__init__()
|
|
self._type = _type
|
|
self._length = _length
|
|
|
|
def isUnderflow(self) -> bool:
|
|
"""Returns True if the result is underflow (more input needed)."""
|
|
return self._type == "UNDERFLOW"
|
|
|
|
def isOverflow(self) -> bool:
|
|
"""Returns True if the result is overflow (output buffer too small)."""
|
|
return self._type == "OVERFLOW"
|
|
|
|
def isError(self) -> bool:
|
|
"""Returns True if the result is an error (malformed or unmappable)."""
|
|
return self._type in ("MALFORMED", "UNMAPPABLE")
|
|
|
|
def isMalformed(self) -> bool:
|
|
"""Returns True if the result is a malformed-input error."""
|
|
return self._type == "MALFORMED"
|
|
|
|
def isUnmappable(self) -> bool:
|
|
"""Returns True if the result is an unmappable-character error."""
|
|
return self._type == "UNMAPPABLE"
|
|
|
|
def length(self) -> int:
|
|
"""Returns the length of the erroneous input (for errors)."""
|
|
return self._length
|
|
|
|
@classmethod
|
|
def malformedForLength(cls, length: int) -> "CoderResult":
|
|
"""Creates a malformed CoderResult for the given length."""
|
|
return cls("MALFORMED", length)
|
|
|
|
@classmethod
|
|
def unmappableForLength(cls, length: int) -> "CoderResult":
|
|
"""Creates an unmappable CoderResult for the given length."""
|
|
return cls("UNMAPPABLE", length)
|
|
|
|
def toString(self) -> str:
|
|
if self._type == "UNDERFLOW":
|
|
return "UNDERFLOW"
|
|
elif self._type == "OVERFLOW":
|
|
return "OVERFLOW"
|
|
return f"{self._type}[{self._length}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, CoderResult):
|
|
return False
|
|
return self._type == other._type and self._length == other._length
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._type, self._length)) & 0x7FFFFFFF
|
|
|
|
|
|
CoderResult.UNDERFLOW = CoderResult("UNDERFLOW")
|
|
CoderResult.OVERFLOW = CoderResult("OVERFLOW")
|
|
|
|
|
|
# ============================================================
|
|
# Charset
|
|
# ============================================================
|
|
|
|
class Charset(Stub):
|
|
"""
|
|
java.nio.charset.Charset
|
|
A named mapping between sequences of 16-bit Unicode characters
|
|
and sequences of bytes.
|
|
"""
|
|
|
|
_cache: dict = {}
|
|
_aliases: dict = {}
|
|
|
|
# Standard charset constants
|
|
US_ASCII: "Charset" = None
|
|
ISO_8859_1: "Charset" = None
|
|
UTF_8: "Charset" = None
|
|
UTF_16: "Charset" = None
|
|
UTF_16BE: "Charset" = None
|
|
UTF_16LE: "Charset" = None
|
|
|
|
def __init__(self, canonical_name: str, aliases: _List[str] = None):
|
|
super().__init__()
|
|
self._name = canonical_name
|
|
self._aliases = aliases or []
|
|
self._registered = True
|
|
|
|
def name(self) -> str:
|
|
"""Returns the canonical name of this charset."""
|
|
return self._name
|
|
|
|
def displayName(self, locale=None) -> str:
|
|
"""Returns the display name of this charset."""
|
|
return self._name
|
|
|
|
def aliases(self) -> _Set[str]:
|
|
"""Returns a set containing this charset's aliases."""
|
|
return set(self._aliases)
|
|
|
|
def isRegistered(self) -> bool:
|
|
"""Returns True if this charset is registered in the IANA registry."""
|
|
return self._registered
|
|
|
|
def canEncode(self) -> bool:
|
|
"""Returns True if this charset supports encoding."""
|
|
try:
|
|
_codecs.lookup(self._name)
|
|
return True
|
|
except LookupError:
|
|
return False
|
|
|
|
def encode(self, text: str) -> "ByteBuffer":
|
|
"""
|
|
Converts a string into bytes using this charset.
|
|
Returns a ByteBuffer.
|
|
"""
|
|
encoded = text.encode(self._name, errors="replace")
|
|
from ..nio import ByteBuffer
|
|
buf = ByteBuffer.allocate(len(encoded))
|
|
buf.put(list(encoded))
|
|
buf.flip()
|
|
return buf
|
|
|
|
def decode(self, buf: "ByteBuffer") -> "CharBuffer":
|
|
"""
|
|
Converts bytes from a ByteBuffer into a CharBuffer.
|
|
"""
|
|
arr = bytearray()
|
|
for i in range(buf.position(), buf.limit()):
|
|
arr.append(buf.get(i))
|
|
decoded = arr.decode(self._name, errors="replace")
|
|
from ..nio import CharBuffer
|
|
cb = CharBuffer.allocate(len(decoded))
|
|
cb.put(decoded)
|
|
cb.flip()
|
|
return cb
|
|
|
|
def newDecoder(self) -> "CharsetDecoder":
|
|
"""Returns a new CharsetDecoder for this charset."""
|
|
return CharsetDecoder(self)
|
|
|
|
def newEncoder(self) -> "CharsetEncoder":
|
|
"""Returns a new CharsetEncoder for this charset."""
|
|
return CharsetEncoder(self)
|
|
|
|
def toString(self) -> str:
|
|
return self._name
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._name) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, Charset):
|
|
return False
|
|
return self._name == other._name
|
|
|
|
def compareTo(self, other: "Charset") -> int:
|
|
"""Compares this charset to another by canonical name."""
|
|
if not isinstance(other, Charset):
|
|
return -1
|
|
if self._name < other._name:
|
|
return -1
|
|
elif self._name > other._name:
|
|
return 1
|
|
return 0
|
|
|
|
@classmethod
|
|
def forName(cls, charset_name: str) -> "Charset":
|
|
"""Returns a Charset for the given charset name."""
|
|
if charset_name in cls._cache:
|
|
return cls._cache[charset_name]
|
|
|
|
try:
|
|
_codecs.lookup(charset_name)
|
|
except LookupError:
|
|
raise AndroidOnlyError(f"Charset not found: {charset_name}")
|
|
|
|
# Resolve aliases
|
|
canonical = charset_name.lower()
|
|
alias_map = {
|
|
"ascii": "US-ASCII", "us-ascii": "US-ASCII",
|
|
"iso-8859-1": "ISO-8859-1", "latin1": "ISO-8859-1",
|
|
"utf-8": "UTF-8", "utf8": "UTF-8",
|
|
"utf-16": "UTF-16", "utf16": "UTF-16",
|
|
"utf-16be": "UTF-16BE", "utf16be": "UTF-16BE",
|
|
"utf-16le": "UTF-16LE", "utf16le": "UTF-16LE",
|
|
}
|
|
canonical_name = alias_map.get(canonical, charset_name.upper())
|
|
|
|
charset = cls(canonical_name)
|
|
cls._cache[charset_name] = charset
|
|
return charset
|
|
|
|
@classmethod
|
|
def forNameUEE(cls, charset_name: str) -> "Charset":
|
|
"""Same as forName but wraps exception."""
|
|
try:
|
|
return cls.forName(charset_name)
|
|
except Exception:
|
|
return None
|
|
|
|
@classmethod
|
|
def isSupported(cls, charset_name: str) -> bool:
|
|
"""Returns True if the named charset is supported."""
|
|
try:
|
|
_codecs.lookup(charset_name)
|
|
return True
|
|
except LookupError:
|
|
return False
|
|
|
|
@classmethod
|
|
def defaultCharset(cls) -> "Charset":
|
|
"""Returns the default charset of the JVM (platform native encoding)."""
|
|
import locale
|
|
enc = locale.getpreferredencoding()
|
|
return cls.forName(enc)
|
|
|
|
@classmethod
|
|
def availableCharsets(cls) -> dict:
|
|
"""Returns a sorted map of available charset names to Charset objects."""
|
|
result = {}
|
|
for name in _codecs.available_encodings():
|
|
try:
|
|
result[name] = cls.forName(name)
|
|
except Exception:
|
|
pass
|
|
return result
|
|
|
|
@classmethod
|
|
def requiredCharsets(cls) -> _Set[str]:
|
|
"""Returns the set of required charset names per Java spec."""
|
|
return {
|
|
"US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16",
|
|
"UTF-16BE", "UTF-16LE",
|
|
}
|
|
|
|
|
|
# Initialize standard charset constants
|
|
Charset.US_ASCII = Charset("US-ASCII", ["ASCII", "US-ASCII", "ascii", "cp367"])
|
|
Charset.ISO_8859_1 = Charset("ISO-8859-1", ["ISO-8859-1", "latin1", "LATIN-1", "l1", "cp819"])
|
|
Charset.UTF_8 = Charset("UTF-8", ["UTF-8", "utf8", "UTF8"])
|
|
Charset.UTF_16 = Charset("UTF-16", ["UTF-16", "utf16", "UTF16", "UnicodeBig"])
|
|
Charset.UTF_16BE = Charset("UTF-16BE", ["UTF-16BE", "utf16be", "UTF16BE", "ISO-10646-UCS-2"])
|
|
Charset.UTF_16LE = Charset("UTF-16LE", ["UTF-16LE", "utf16le", "UTF16LE"])
|
|
|
|
|
|
# ============================================================
|
|
# CharsetEncoder
|
|
# ============================================================
|
|
|
|
class CharsetEncoder(Stub):
|
|
"""
|
|
java.nio.charset.CharsetEncoder
|
|
Converts character data to byte data using a Charset.
|
|
"""
|
|
|
|
def __init__(self, charset: Charset, average_bytes_per_char: float = 1.0,
|
|
max_bytes_per_char: float = 4.0, replacement: bytes = b'?'):
|
|
super().__init__()
|
|
self._charset = charset
|
|
self._average_bytes_per_char = average_bytes_per_char
|
|
self._max_bytes_per_char = max_bytes_per_char
|
|
self._replacement = replacement
|
|
self._malformed_input_action = CodingErrorAction.REPORT
|
|
self._unmappable_action = CodingErrorAction.REPORT
|
|
|
|
def charset(self) -> Charset:
|
|
"""Returns the charset that created this encoder."""
|
|
return self._charset
|
|
|
|
def replacement(self) -> bytes:
|
|
"""Returns the replacement value."""
|
|
return self._replacement
|
|
|
|
def replaceWith(self, new_replacement: bytes) -> "CharsetEncoder":
|
|
"""Sets a new replacement value."""
|
|
self._replacement = new_replacement
|
|
return self
|
|
|
|
def encode(self, input_str: str) -> "ByteBuffer":
|
|
"""Encodes a string into bytes."""
|
|
encoded = input_str.encode(self._charset.name(), errors="replace")
|
|
from ..nio import ByteBuffer
|
|
buf = ByteBuffer.allocate(len(encoded))
|
|
buf.put(list(encoded))
|
|
buf.flip()
|
|
return buf
|
|
|
|
def encode(self, input: "CharBuffer", output: "ByteBuffer", end_of_input: bool) -> CoderResult:
|
|
"""Encodes as much as possible from input to output."""
|
|
in_str = ""
|
|
for i in range(input.position(), input.limit()):
|
|
in_str += chr(input.get(i))
|
|
try:
|
|
encoded = in_str.encode(self._charset.name())
|
|
from ..nio import ByteBuffer
|
|
out_arr = bytearray()
|
|
for i in range(output.position(), output.limit()):
|
|
if i < len(encoded):
|
|
out_arr.append(encoded[i])
|
|
return CoderResult.UNDERFLOW
|
|
except UnicodeEncodeError:
|
|
return CoderResult.unmappableForLength(1)
|
|
|
|
def encodeLoop(self, input: "CharBuffer", output: "ByteBuffer") -> CoderResult:
|
|
"""The encoding loop (subclass hook)."""
|
|
return self.encode(input, output, True)
|
|
|
|
def implReplaceWith(self, new_replacement: bytes):
|
|
"""Hook for subclasses to react to replacement changes."""
|
|
pass
|
|
|
|
def implOnMalformedInput(self, new_action: CodingErrorAction):
|
|
"""Hook for subclasses to react to malformed input action changes."""
|
|
pass
|
|
|
|
def implOnUnmappableCharacter(self, new_action: CodingErrorAction):
|
|
"""Hook for subclasses to react to unmappable char action changes."""
|
|
pass
|
|
|
|
def implReset(self):
|
|
"""Hook for subclasses to reset internal state."""
|
|
pass
|
|
|
|
def averageBytesPerChar(self) -> float:
|
|
return self._average_bytes_per_char
|
|
|
|
def maxBytesPerChar(self) -> float:
|
|
return self._max_bytes_per_char
|
|
|
|
def isLegalReplacement(self, repl: bytes) -> bool:
|
|
"""Returns True if the replacement is a legal byte sequence."""
|
|
return len(repl) > 0
|
|
|
|
def malformedInputAction(self) -> CodingErrorAction:
|
|
return self._malformed_input_action
|
|
|
|
def unmappableCharacterAction(self) -> CodingErrorAction:
|
|
return self._unmappable_action
|
|
|
|
def onMalformedInput(self, action: CodingErrorAction) -> "CharsetEncoder":
|
|
self._malformed_input_action = action
|
|
return self
|
|
|
|
def onUnmappableCharacter(self, action: CodingErrorAction) -> "CharsetEncoder":
|
|
self._unmappable_action = action
|
|
return self
|
|
|
|
def reset(self) -> "CharsetEncoder":
|
|
"""Resets this encoder, clearing any internal state."""
|
|
self.implReset()
|
|
return self
|
|
|
|
def toString(self) -> str:
|
|
return f"CharsetEncoder@{hex(id(self))}[charset={self._charset.name()}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, CharsetEncoder):
|
|
return False
|
|
return self._charset == other._charset
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._charset.name()) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# CharsetDecoder
|
|
# ============================================================
|
|
|
|
class CharsetDecoder(Stub):
|
|
"""
|
|
java.nio.charset.CharsetDecoder
|
|
Converts byte data to character data using a Charset.
|
|
"""
|
|
|
|
def __init__(self, charset: Charset, average_chars_per_byte: float = 1.0,
|
|
max_chars_per_byte: float = 2.0, replacement: str = '\ufffd'):
|
|
super().__init__()
|
|
self._charset = charset
|
|
self._average_chars_per_byte = average_chars_per_byte
|
|
self._max_chars_per_byte = max_chars_per_byte
|
|
self._replacement = replacement
|
|
self._malformed_input_action = CodingErrorAction.REPORT
|
|
self._unmappable_action = CodingErrorAction.REPORT
|
|
self._auto_detect = False
|
|
self._detected_charset = None
|
|
|
|
def charset(self) -> Charset:
|
|
"""Returns the charset that created this decoder."""
|
|
return self._charset
|
|
|
|
def replacement(self) -> str:
|
|
"""Returns the replacement value."""
|
|
return self._replacement
|
|
|
|
def replaceWith(self, new_replacement: str) -> "CharsetDecoder":
|
|
"""Sets a new replacement value."""
|
|
self._replacement = new_replacement
|
|
return self
|
|
|
|
def decode(self, buf: "ByteBuffer") -> "CharBuffer":
|
|
"""Decodes bytes from a ByteBuffer into a CharBuffer."""
|
|
arr = bytearray()
|
|
for i in range(buf.position(), buf.limit()):
|
|
arr.append(buf.get(i))
|
|
decoded = arr.decode(self._charset.name(), errors="replace")
|
|
from ..nio import CharBuffer
|
|
cb = CharBuffer.allocate(len(decoded))
|
|
cb.put(decoded)
|
|
cb.flip()
|
|
return cb
|
|
|
|
def decode(self, input: "ByteBuffer", output: "CharBuffer", end_of_input: bool) -> CoderResult:
|
|
"""Decodes as much as possible from input to output."""
|
|
arr = bytearray()
|
|
for i in range(input.position(), input.limit()):
|
|
arr.append(input.get(i))
|
|
try:
|
|
decoded = arr.decode(self._charset.name())
|
|
return CoderResult.UNDERFLOW
|
|
except UnicodeDecodeError:
|
|
return CoderResult.malformedForLength(1)
|
|
|
|
def decodeLoop(self, input: "ByteBuffer", output: "CharBuffer") -> CoderResult:
|
|
"""The decoding loop (subclass hook)."""
|
|
return self.decode(input, output, True)
|
|
|
|
def implReplaceWith(self, new_replacement: str):
|
|
"""Hook for subclasses."""
|
|
pass
|
|
|
|
def implOnMalformedInput(self, new_action: CodingErrorAction):
|
|
"""Hook for subclasses."""
|
|
pass
|
|
|
|
def implOnUnmappableCharacter(self, new_action: CodingErrorAction):
|
|
"""Hook for subclasses."""
|
|
pass
|
|
|
|
def implReset(self):
|
|
"""Hook for subclasses."""
|
|
pass
|
|
|
|
def averageCharsPerByte(self) -> float:
|
|
return self._average_chars_per_byte
|
|
|
|
def maxCharsPerByte(self) -> float:
|
|
return self._max_chars_per_byte
|
|
|
|
def isCharsetDetected(self) -> bool:
|
|
return self._detected_charset is not None
|
|
|
|
def isAutoDetect(self) -> bool:
|
|
return self._auto_detect
|
|
|
|
def detectedCharset(self) -> Charset:
|
|
if self._detected_charset is None:
|
|
raise AndroidOnlyError("No charset detected")
|
|
return self._detected_charset
|
|
|
|
def malformedInputAction(self) -> CodingErrorAction:
|
|
return self._malformed_input_action
|
|
|
|
def unmappableCharacterAction(self) -> CodingErrorAction:
|
|
return self._unmappable_action
|
|
|
|
def onMalformedInput(self, action: CodingErrorAction) -> "CharsetDecoder":
|
|
self._malformed_input_action = action
|
|
return self
|
|
|
|
def onUnmappableCharacter(self, action: CodingErrorAction) -> "CharsetDecoder":
|
|
self._unmappable_action = action
|
|
return self
|
|
|
|
def reset(self) -> "CharsetDecoder":
|
|
self.implReset()
|
|
return self
|
|
|
|
def toString(self) -> str:
|
|
return f"CharsetDecoder@{hex(id(self))}[charset={self._charset.name()}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, CharsetDecoder):
|
|
return False
|
|
return self._charset == other._charset
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._charset.name()) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# StandardCharsets
|
|
# ============================================================
|
|
|
|
class StandardCharsets(Stub):
|
|
"""
|
|
java.nio.charset.StandardCharsets
|
|
Constant definitions for the standard charsets.
|
|
"""
|
|
US_ASCII: Charset = Charset.US_ASCII
|
|
ISO_8859_1: Charset = Charset.ISO_8859_1
|
|
UTF_8: Charset = Charset.UTF_8
|
|
UTF_16: Charset = Charset.UTF_16
|
|
UTF_16BE: Charset = Charset.UTF_16BE
|
|
UTF_16LE: Charset = Charset.UTF_16LE |