Files
Android-pyApi/android_pyapi/java/nio_charset.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

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