""" Java I/O standard library APIs implemented in Python. java.io package - File, streams, readers, writers, and exceptions. """ from .._core import Stub, AndroidOnlyError import io as _pyio import os as _os import sys as _sys import errno as _errno import stat as _stat import threading as _threading from datetime import datetime as _datetime from collections import OrderedDict as _OrderedDict from pathlib import Path as _PathLibPath __all__ = [ "Serializable", "Externalizable", "ObjectStreamField", "ObjectStreamConstants", "FileFilter", "FilenameFilter", "File", "FileDescriptor", "FileInputStream", "FileOutputStream", "BufferedInputStream", "BufferedOutputStream", "BufferedReader", "BufferedWriter", "InputStreamReader", "OutputStreamWriter", "FileReader", "FileWriter", "PrintWriter", "ByteArrayInputStream", "ByteArrayOutputStream", "StringReader", "StringWriter", "DataInputStream", "DataOutputStream", "ObjectInputStream", "ObjectOutputStream", "FilterInputStream", "FilterOutputStream", "PipedInputStream", "PipedOutputStream", "PushbackInputStream", "SequenceInputStream", "RandomAccessFile", "Closeable", "Flushable", "IOError", "IOException", "FileNotFoundException", "EOFException", "CharConversionException", "ObjectStreamException", "SyncFailedException", "UTFDataFormatException", "WriteAbortedException", "UnsupportedEncodingException", ] # ============================================================ # Marker / mixin interfaces # ============================================================ class Closeable(Stub): def close(self): raise NotImplementedError class Flushable(Stub): def flush(self): raise NotImplementedError class Serializable(Stub): pass class Externalizable(Stub): pass class ObjectStreamConstants: STREAM_MAGIC = 0xACED STREAM_VERSION = 5 TC_BASE = 0x70 TC_NULL = 0x70 TC_REFERENCE = 0x71 TC_CLASS = 0x72 TC_OBJECT = 0x73 TC_STRING = 0x74 TC_ARRAY = 0x75 TC_ENUM = 0x7E TC_CLASSDESC = 0x72 TC_PROXYCLASSDESC = 0x7D TC_BLOCKDATA = 0x77 TC_ENDBLOCKDATA = 0x78 TC_RESET = 0x79 TC_EXCEPTION = 0x7A TC_LONGSTRING = 0x7C baseWireHandle = 0x7E0000 class ObjectStreamField(Stub): def __init__(self, name, typ): self._name = name self._type = typ def getName(self): return self._name def getType(self): return self._type def toString(self): return f"{self._type} {self._name}" # ============================================================ # FileFilter / FilenameFilter # ============================================================ class FileFilter(Stub): def accept(self, pathname): raise NotImplementedError class FilenameFilter(Stub): def accept(self, dir, name): raise NotImplementedError # ============================================================ # FileDescriptor # ============================================================ class FileDescriptor(Stub): _valid_fd = -1 def __init__(self, fd=None): super().__init__() if fd is not None: self._valid_fd = fd def valid(self): return self._valid_fd >= 0 def sync(self): if self._valid_fd >= 0: try: _os.fsync(self._valid_fd) except OSError: pass def hashCode(self): return self._valid_fd @staticmethod def _from_fileno(fd): return FileDescriptor(fd) # ============================================================ # Exception classes # ============================================================ class IOException(Exception): def __init__(self, message=None, cause=None): super().__init__(message) self._cause = cause def getCause(self): return self._cause class FileNotFoundException(IOException): pass class EOFException(IOException): pass class CharConversionException(IOException): pass class ObjectStreamException(IOException): pass class SyncFailedException(IOException): pass class UTFDataFormatException(IOException): pass class WriteAbortedException(ObjectStreamException): def __init__(self, message=None, cause=None): super().__init__(message) self.detail = cause class UnsupportedEncodingException(IOException): pass class IOError(Exception): def __init__(self, message=None, cause=None): super().__init__(message) self._cause = cause def getCause(self): return self._cause # ============================================================ # File # ============================================================ class File(Stub): separator = _os.sep separatorChar = _os.sep pathSeparator = _os.pathsep pathSeparatorChar = _os.pathsep def __init__(self, *args): super().__init__() if len(args) == 1: path = args[0] if isinstance(path, File): self._path = path._path else: self._path = str(path) elif len(args) == 2: parent, child = args if parent is None: self._path = str(child) else: parent_str = parent._path if isinstance(parent, File) else str(parent) child_str = str(child) if child_str.startswith(self.separator): self._path = child_str else: self._path = _os.path.join(parent_str, child_str) else: self._path = "" self._path = self._path or "" def _resolve(self): return _os.path.realpath(self._path) if self._path else "" def exists(self): return _os.path.exists(self._path) def isFile(self): return _os.path.isfile(self._path) def isDirectory(self): return _os.path.isdir(self._path) def getName(self): return _os.path.basename(self._path) def getPath(self): return self._path def getAbsolutePath(self): return _os.path.abspath(self._path) def getCanonicalPath(self): return _os.path.realpath(self._path) def getParent(self): p = _os.path.dirname(self._path) return p if p else None def getParentFile(self): p = self.getParent() return File(p) if p else None def length(self): try: return _os.path.getsize(self._path) except OSError: return 0 def lastModified(self): try: return int(_os.path.getmtime(self._path) * 1000) except OSError: return 0 def createNewFile(self): try: f = _os.open(self._path, _os.O_CREAT | _os.O_EXCL, 0o644) _os.close(f) return True except OSError: return False def delete(self): try: if self.isDirectory(): _os.rmdir(self._path) else: _os.remove(self._path) return True except OSError: return False def mkdir(self): try: _os.mkdir(self._path) return True except OSError: return False def mkdirs(self): try: _os.makedirs(self._path, exist_ok=True) return True except OSError: return False def renameTo(self, dest): try: dest_path = dest._path if isinstance(dest, File) else str(dest) _os.rename(self._path, dest_path) return True except OSError: return False def list(self): try: return _os.listdir(self._path) except OSError: return None def listFiles(self, filter=None): try: names = _os.listdir(self._path) files = [File(self._path, n) for n in names] if filter: return [f for f in files if filter.accept(f)] return files except OSError: return None @staticmethod def listRoots(): if _sys.platform == "win32": import string return [File(d + ":\\") for d in string.ascii_uppercase if _os.path.exists(d + ":\\")] return [File("/")] def canRead(self): return _os.access(self._path, _os.R_OK) def canWrite(self): return _os.access(self._path, _os.W_OK) def canExecute(self): return _os.access(self._path, _os.X_OK) def setReadable(self, readable, ownerOnly=True): try: if readable: if ownerOnly: _os.chmod(self._path, _stat.S_IRUSR | (_stat.S_IRGRP if _os.stat(self._path).st_gid else 0)) else: _os.chmod(self._path, _stat.S_IRUSR | _stat.S_IRGRP | _stat.S_IROTH) else: mode = _os.stat(self._path).st_mode if ownerOnly: mode &= ~_stat.S_IRUSR else: mode &= ~(_stat.S_IRUSR | _stat.S_IRGRP | _stat.S_IROTH) _os.chmod(self._path, mode) return True except OSError: return False def setWritable(self, writable, ownerOnly=True): try: if writable: if ownerOnly: _os.chmod(self._path, _stat.S_IWUSR) else: _os.chmod(self._path, _stat.S_IWUSR | _stat.S_IWGRP | _stat.S_IWOTH) else: mode = _os.stat(self._path).st_mode if ownerOnly: mode &= ~_stat.S_IWUSR else: mode &= ~(_stat.S_IWUSR | _stat.S_IWGRP | _stat.S_IWOTH) _os.chmod(self._path, mode) return True except OSError: return False def setExecutable(self, executable, ownerOnly=True): try: if executable: if ownerOnly: _os.chmod(self._path, _stat.S_IXUSR) else: _os.chmod(self._path, _stat.S_IXUSR | _stat.S_IXGRP | _stat.S_IXOTH) else: mode = _os.stat(self._path).st_mode if ownerOnly: mode &= ~_stat.S_IXUSR else: mode &= ~(_stat.S_IXUSR | _stat.S_IXGRP | _stat.S_IXOTH) _os.chmod(self._path, mode) return True except OSError: return False def isHidden(self): return _os.path.basename(self._path).startswith(".") def toURL(self): from urllib.parse import quote return f"file:{quote(self.getAbsolutePath())}" def toURI(self): return self.toURL() def getTotalSpace(self): try: st = _os.statvfs(self._path) return st.f_frsize * st.f_blocks except OSError: return 0 def getFreeSpace(self): try: st = _os.statvfs(self._path) return st.f_frsize * st.f_bfree except OSError: return 0 def getUsableSpace(self): try: st = _os.statvfs(self._path) return st.f_frsize * st.f_bavail except OSError: return 0 def compareTo(self, other): if isinstance(other, File): return self._path.__cmp__(other._path) return -1 def equals(self, other): if isinstance(other, File): return self._path == other._path return False def hashCode(self): return hash(self._path) & 0x7FFFFFFF def toString(self): return self._path def toPath(self): return _PathLibPath(self._path) def __str__(self): return self._path def __repr__(self): return f"File({self._path!r})" # ============================================================ # FileInputStream # ============================================================ class FileInputStream(Closeable): def __init__(self, file): super().__init__() if isinstance(file, File): path = file.getPath() else: path = str(file) self._path = path self._fd = None self._channel = None self._closed = False try: self._fd = _os.open(path, _os.O_RDONLY) except OSError as e: raise FileNotFoundException(str(e)) def read(self, b=None, off=None, length=None): if self._closed: raise IOException("Stream closed") if b is None: data = _os.read(self._fd, 1) return data[0] if data else -1 if isinstance(b, (bytes, bytearray)): if off is None: off = 0 if length is None: length = len(b) - off data = _os.read(self._fd, length) if not data: return -1 n = len(data) if isinstance(b, bytearray): b[off:off+n] = data else: ba = bytearray(b) ba[off:off+n] = data b[:] = bytes(ba) return n return -1 def skip(self, n): try: pos = _os.lseek(self._fd, 0, _os.SEEK_CUR) end = _os.lseek(self._fd, 0, _os.SEEK_END) _os.lseek(self._fd, pos, _os.SEEK_SET) actual = min(n, end - pos) _os.lseek(self._fd, actual, _os.SEEK_CUR) return actual except OSError: return 0 def available(self): if self._closed: return 0 try: pos = _os.lseek(self._fd, 0, _os.SEEK_CUR) end = _os.lseek(self._fd, 0, _os.SEEK_END) _os.lseek(self._fd, pos, _os.SEEK_SET) return end - pos except OSError: return 0 def close(self): if not self._closed: self._closed = True if self._fd is not None: _os.close(self._fd) self._fd = None def getFD(self): if self._closed: raise IOException("Stream closed") return FileDescriptor(self._fd) def getChannel(self): if self._closed: raise IOException("Stream closed") if self._channel is None: import io as _pyio_mod self._channel = _pyio_mod.FileIO(self._fd, closefd=False) return self._channel def __del__(self): self.close() # ============================================================ # FileOutputStream # ============================================================ class FileOutputStream(Closeable, Flushable): def __init__(self, file, append=False): super().__init__() if isinstance(file, File): path = file.getPath() elif isinstance(file, FileDescriptor): self._fd = file._valid_fd self._path = None self._closed = False self._channel = None return else: path = str(file) self._path = path self._closed = False self._channel = None try: flags = _os.O_WRONLY | _os.O_CREAT if append: flags |= _os.O_APPEND else: flags |= _os.O_TRUNC self._fd = _os.open(path, flags, 0o644) except OSError as e: raise FileNotFoundException(str(e)) def write(self, b=None, off=None, length=None): if self._closed: raise IOException("Stream closed") if b is None: return if isinstance(b, int): _os.write(self._fd, bytes([b])) elif isinstance(b, (bytes, bytearray)): if off is None: data = bytes(b) else: data = bytes(b[off:off+length if length else (len(b)-off)]) _os.write(self._fd, data) else: if off is None: data = bytes(b) else: data = bytes(b[off:off+length if length else (len(b)-off)]) _os.write(self._fd, data) def close(self): if not self._closed: self._closed = True if self._fd is not None: _os.close(self._fd) self._fd = None def getFD(self): if self._closed: raise IOException("Stream closed") return FileDescriptor(self._fd) def getChannel(self): if self._closed: raise IOException("Stream closed") if self._channel is None: import io as _pyio_mod self._channel = _pyio_mod.FileIO(self._fd, closefd=False, mode='ab') return self._channel def flush(self): if self._fd is not None: try: _os.fsync(self._fd) except OSError: pass def __del__(self): self.close() # ============================================================ # FilterInputStream / FilterOutputStream (abstract wrappers) # ============================================================ class FilterInputStream(Closeable): def __init__(self, inp): super().__init__() self._in = inp def read(self, *args): return self._in.read(*args) def skip(self, n): return self._in.skip(n) def available(self): return self._in.available() def close(self): self._in.close() def mark(self, readlimit): pass def reset(self): raise IOException("mark/reset not supported") def markSupported(self): return False class FilterOutputStream(Closeable, Flushable): def __init__(self, out): super().__init__() self._out = out def write(self, *args): self._out.write(*args) def close(self): self._out.close() def flush(self): self._out.flush() # ============================================================ # BufferedInputStream # ============================================================ class BufferedInputStream(FilterInputStream): def __init__(self, inp, size=8192): super().__init__(inp) self._buffer = bytearray() self._buf_size = size self._pos = 0 self._count = 0 def read(self, b=None, off=None, length=None): if b is None: if self._pos >= self._count: self._fill() if self._pos >= self._count: return -1 val = self._buffer[self._pos] self._pos += 1 return val if off is None: off = 0 if length is None: length = len(b) total = 0 while total < length: if self._pos >= self._count: if not self._fill(): break chunk = min(length - total, self._count - self._pos) if isinstance(b, bytearray): b[off+total:off+total+chunk] = self._buffer[self._pos:self._pos+chunk] else: ba = bytearray(b) ba[off+total:off+total+chunk] = self._buffer[self._pos:self._pos+chunk] b[:] = bytes(ba) self._pos += chunk total += chunk return total if total > 0 else -1 def _fill(self): data = self._in.read(bytearray(self._buf_size), 0, self._buf_size) if data is None or data == -1: return False n = data if isinstance(data, int) else len(data) self._buffer = bytearray(self._in._buffer if hasattr(self._in, '_buffer') else data) if n > 0 else bytearray() self._pos = 0 self._count = n return n > 0 # ============================================================ # BufferedOutputStream # ============================================================ class BufferedOutputStream(FilterOutputStream): def __init__(self, out, size=8192): super().__init__(out) self._buf = bytearray() self._buf_size = size def write(self, b=None, off=None, length=None): if b is None: return if isinstance(b, int): self._buf.append(b & 0xFF) else: if off is not None and length is not None: data = b[off:off+length] else: data = b self._buf.extend(data) if len(self._buf) >= self._buf_size: self.flush() def flush(self): if self._buf: self._out.write(bytes(self._buf)) self._buf.clear() self._out.flush() # ============================================================ # InputStreamReader # ============================================================ class InputStreamReader(Closeable): def __init__(self, inp, charset=None): super().__init__() self._in = inp self._charset = charset or "utf-8" self._buffer = None def read(self, cbuf=None, off=None, length=None): if cbuf is None: data = self._in.read() if data is None or data == -1: return -1 return data & 0xFF if off is None: off = 0 if length is None: length = len(cbuf) raw = self._in.read(bytearray(length), 0, length) if raw is None or raw == -1: return -1 n = len(raw) if isinstance(raw, (bytes, bytearray)) else raw if isinstance(raw, int): return raw decoded = raw.decode(self._charset, errors='replace') for i, ch in enumerate(decoded): if off + i < len(cbuf): cbuf[off + i] = ch return len(decoded) def getEncoding(self): return self._charset def close(self): self._in.close() # ============================================================ # OutputStreamWriter # ============================================================ class OutputStreamWriter(Closeable, Flushable): def __init__(self, out, charset=None): super().__init__() self._out = out self._charset = charset or "utf-8" def write(self, s, off=None, length=None): if isinstance(s, str): if off is not None and length is not None: s = s[off:off+length] self._out.write(s.encode(self._charset)) elif isinstance(s, (list, tuple)): chars = "".join(s) if off is not None and length is not None: chars = chars[off:off+length] self._out.write(chars.encode(self._charset)) def flush(self): self._out.flush() def close(self): self._out.close() def getEncoding(self): return self._charset # ============================================================ # FileReader # ============================================================ class FileReader(InputStreamReader): def __init__(self, file): if isinstance(file, File): path = file.getPath() else: path = str(file) inp = FileInputStream(path) super().__init__(inp, "utf-8") self._file_path = path # ============================================================ # FileWriter # ============================================================ class FileWriter(OutputStreamWriter): def __init__(self, file, append=False): if isinstance(file, File): path = file.getPath() append = append else: path = str(file) out = FileOutputStream(path, append) super().__init__(out, "utf-8") self._file_path = path # ============================================================ # BufferedReader # ============================================================ class BufferedReader(Closeable): def __init__(self, reader, size=8192): super().__init__() self._reader = reader self._buf_size = size self._buffer = "" self._pos = 0 self._closed = False def read(self, cbuf=None, off=None, length=None): if self._closed: raise IOException("Stream closed") if cbuf is None: if self._pos >= len(self._buffer): self._fill() if self._pos >= len(self._buffer): return -1 ch = ord(self._buffer[self._pos]) self._pos += 1 return ch if off is None: off = 0 if length is None: length = len(cbuf) total = 0 while total < length: if self._pos >= len(self._buffer): if not self._fill(): break chunk = min(length - total, len(self._buffer) - self._pos) for i in range(chunk): cbuf[off + total + i] = self._buffer[self._pos + i] self._pos += chunk total += chunk return total if total > 0 else -1 def _fill(self): if hasattr(self._reader, '_in'): raw = self._reader._in.read(bytearray(self._buf_size), 0, self._buf_size) if raw is None or raw == -1: return False data = bytes(raw) if isinstance(raw, int) else bytes(raw) self._buffer = data.decode(self._reader.getEncoding() if hasattr(self._reader, 'getEncoding') else "utf-8", errors='replace') else: raw = [] for _ in range(self._buf_size): ch = self._reader.read() if ch == -1: break raw.append(chr(ch)) self._buffer = "".join(raw) self._pos = 0 return len(self._buffer) > 0 def readLine(self): if self._closed: raise IOException("Stream closed") result = [] while True: if self._pos >= len(self._buffer): if not self._fill(): break while self._pos < len(self._buffer): ch = self._buffer[self._pos] self._pos += 1 if ch == "\n": return "".join(result) if ch == "\r": if self._pos < len(self._buffer) and self._buffer[self._pos] == "\n": self._pos += 1 return "".join(result) result.append(ch) return "".join(result) if result else None def ready(self): return self._pos < len(self._buffer) def mark(self, readAheadLimit): self._mark_pos = self._pos self._mark_buffer = self._buffer def reset(self): if hasattr(self, '_mark_pos'): self._pos = self._mark_pos self._buffer = self._mark_buffer else: raise IOException("Mark not set") def markSupported(self): return True def skip(self, n): skipped = 0 while skipped < n: if self._pos >= len(self._buffer): if not self._fill(): break chunk = min(n - skipped, len(self._buffer) - self._pos) self._pos += chunk skipped += chunk return skipped def close(self): self._closed = True self._reader.close() def lines(self): line = self.readLine() while line is not None: yield line line = self.readLine() # ============================================================ # BufferedWriter # ============================================================ class BufferedWriter(Closeable, Flushable): def __init__(self, writer, size=8192): super().__init__() self._writer = writer self._buf_size = size self._buffer = [] def write(self, s, off=None, length=None): if isinstance(s, str): if off is not None and length is not None: s = s[off:off+length] self._buffer.append(s) elif isinstance(s, (list, tuple)): chars = "".join(s) if off is not None and length is not None: chars = chars[off:off+length] self._buffer.append(chars) elif isinstance(s, int): self._buffer.append(chr(s)) if sum(len(x) for x in self._buffer) >= self._buf_size: self.flush() def newLine(self): self._buffer.append("\n") def flush(self): if self._buffer: data = "".join(self._buffer) self._writer.write(data) self._buffer.clear() self._writer.flush() def close(self): self.flush() self._writer.close() # ============================================================ # PrintWriter # ============================================================ class PrintWriter(Closeable, Flushable): def __init__(self, out, autoFlush=False): super().__init__() if isinstance(out, _pyio.TextIOBase): self._out = out else: self._out = out self._auto_flush = autoFlush self._error = False def _write_str(self, s): s = str(s) if hasattr(self._out, 'write'): self._out.write(s) else: self._out.write(s.encode('utf-8')) def print_bool(self, b): self._write_str(str(b)) if self._auto_flush: self.flush() def print_char(self, c): self._write_str(str(c)) if self._auto_flush: self.flush() def print_char_array(self, c): self._write_str("".join(c)) if self._auto_flush: self.flush() def print_double(self, d): self._write_str(str(d)) if self._auto_flush: self.flush() def print_float(self, f): self._write_str(str(f)) if self._auto_flush: self.flush() def print_int(self, i): self._write_str(str(i)) if self._auto_flush: self.flush() def print_long(self, l): self._write_str(str(l)) if self._auto_flush: self.flush() def print_obj(self, obj): self._write_str(str(obj)) if self._auto_flush: self.flush() def print_str(self, s): self._write_str(str(s) if s is not None else "null") if self._auto_flush: self.flush() print = print_str print_boolean = print_bool def println(self, s=None): if s is None: self._write_str("\n") else: self._write_str(str(s) + "\n") if self._auto_flush: self.flush() def println_bool(self, b): self._write_str(str(b) + "\n") if self._auto_flush: self.flush() def println_char(self, c): self._write_str(str(c) + "\n") if self._auto_flush: self.flush() def println_char_array(self, c): self._write_str("".join(c) + "\n") if self._auto_flush: self.flush() def println_double(self, d): self._write_str(str(d) + "\n") if self._auto_flush: self.flush() def println_float(self, f): self._write_str(str(f) + "\n") if self._auto_flush: self.flush() def println_int(self, i): self._write_str(str(i) + "\n") if self._auto_flush: self.flush() def println_long(self, l): self._write_str(str(l) + "\n") if self._auto_flush: self.flush() def println_obj(self, obj): self._write_str(str(obj) + "\n") if self._auto_flush: self.flush() def write(self, s, off=None, length=None): if isinstance(s, str): if off is not None and length is not None: s = s[off:off+length] self._write_str(s) else: self._write_str(str(s)) def format(self, fmt, *args): s = str(fmt) % args if args else str(fmt) self._write_str(s) return self def printf(self, fmt, *args): return self.format(fmt, *args) def flush(self): if hasattr(self._out, 'flush'): self._out.flush() def close(self): self.flush() if hasattr(self._out, 'close'): self._out.close() def checkError(self): return self._error def setError(self): self._error = True # ============================================================ # ByteArrayInputStream # ============================================================ class ByteArrayInputStream(Closeable): def __init__(self, buf): super().__init__() self._buf = bytes(buf) self._pos = 0 self._count = len(self._buf) self._mark = 0 self._closed = False def read(self, b=None, off=None, length=None): if self._closed: raise IOException("Stream closed") if b is None: if self._pos >= self._count: return -1 val = self._buf[self._pos] & 0xFF self._pos += 1 return val if off is None: off = 0 if length is None: length = len(b) avail = self._count - self._pos if avail <= 0: return -1 n = min(length, avail) if isinstance(b, bytearray): b[off:off+n] = self._buf[self._pos:self._pos+n] else: ba = bytearray(b) ba[off:off+n] = self._buf[self._pos:self._pos+n] b[:] = bytes(ba) self._pos += n return n def skip(self, n): if self._closed: return 0 avail = self._count - self._pos actual = min(n, avail) self._pos += actual return actual def available(self): if self._closed: return 0 return self._count - self._pos def close(self): self._closed = True def mark(self, readAheadLimit): self._mark = self._pos def reset(self): self._pos = self._mark # ============================================================ # ByteArrayOutputStream # ============================================================ class ByteArrayOutputStream(Closeable, Flushable): def __init__(self, size=32): super().__init__() self._buf = bytearray() self._closed = False def write(self, b=None, off=None, length=None): if self._closed: raise IOException("Stream closed") if b is None: return if isinstance(b, int): self._buf.append(b & 0xFF) elif isinstance(b, (bytes, bytearray)): if off is not None and length is not None: self._buf.extend(b[off:off+length]) else: self._buf.extend(b) def toByteArray(self): return bytes(self._buf) def size(self): return len(self._buf) def toString(self, charset=None): if charset: return bytes(self._buf).decode(charset, errors='replace') return bytes(self._buf).decode('utf-8', errors='replace') def reset(self): self._buf.clear() def close(self): self._closed = True def flush(self): pass # ============================================================ # StringReader # ============================================================ class StringReader(Closeable): def __init__(self, s): super().__init__() self._str = s self._pos = 0 self._length = len(s) self._mark = 0 self._closed = False def read(self, cbuf=None, off=None, length=None): if self._closed: raise IOException("Stream closed") if cbuf is None: if self._pos >= self._length: return -1 val = ord(self._str[self._pos]) self._pos += 1 return val if off is None: off = 0 if length is None: length = len(cbuf) avail = self._length - self._pos if avail <= 0: return -1 n = min(length, avail) for i in range(n): cbuf[off + i] = self._str[self._pos + i] self._pos += n return n def skip(self, n): avail = self._length - self._pos actual = min(n, avail) self._pos += actual return max(actual, 0) def ready(self): return self._pos < self._length def mark(self, readAheadLimit): self._mark = self._pos def reset(self): self._pos = self._mark def markSupported(self): return True def close(self): self._closed = True # ============================================================ # StringWriter # ============================================================ class StringWriter(Closeable, Flushable): def __init__(self): super().__init__() self._buffer = [] self._closed = False def write(self, s, off=None, length=None): if self._closed: raise IOException("Stream closed") if isinstance(s, str): if off is not None and length is not None: s = s[off:off+length] self._buffer.append(s) elif isinstance(s, int): self._buffer.append(chr(s)) elif isinstance(s, (list, tuple)): chars = "".join(s) if off is not None and length is not None: chars = chars[off:off+length] self._buffer.append(chars) def flush(self): pass def close(self): self._closed = True def toString(self): return "".join(self._buffer) def getBuffer(self): return "".join(self._buffer) # ============================================================ # DataInputStream # ============================================================ class DataInputStream(Closeable): def __init__(self, inp): super().__init__() self._in = inp def read(self, b=None): if b is None: return self._in.read() return self._in.read(b) def readBoolean(self): v = self._in.read() if v == -1: raise EOFException() return bool(v) def readByte(self): v = self._in.read() if v == -1: raise EOFException() return (v & 0xFF) if v <= 127 else (v - 256) def readChar(self): hi = self._in.read() lo = self._in.read() if hi == -1 or lo == -1: raise EOFException() return chr((hi << 8) | lo) def readDouble(self): return _struct.unpack(">d", self._read_bytes(8))[0] def readFloat(self): return _struct.unpack(">f", self._read_bytes(4))[0] def readFully(self, b, off=0, length=None): if length is None: length = len(b) total = 0 while total < length: n = self._in.read(b, off + total, length - total) if n == -1: raise EOFException() total += n def readInt(self): return _struct.unpack(">i", self._read_bytes(4))[0] def readLong(self): return _struct.unpack(">q", self._read_bytes(8))[0] def readShort(self): v = _struct.unpack(">h", self._read_bytes(2))[0] return v def readUTF(self): length = self.readUnsignedShort() data = self._read_bytes(length) return data.decode("utf-8") def readUnsignedByte(self): v = self._in.read() if v == -1: raise EOFException() return v & 0xFF def readUnsignedShort(self): hi = self._in.read() lo = self._in.read() if hi == -1 or lo == -1: raise EOFException() return ((hi & 0xFF) << 8) | (lo & 0xFF) def skipBytes(self, n): return self._in.skip(n) def _read_bytes(self, n): buf = bytearray(n) total = 0 while total < n: r = self._in.read(buf, total, n - total) if r == -1: raise EOFException() total += r return bytes(buf) def close(self): self._in.close() # ============================================================ # DataOutputStream # ============================================================ import struct as _struct class DataOutputStream(Closeable, Flushable): def __init__(self, out): super().__init__() self._out = out self._written = 0 def write(self, b=None, off=None, length=None): if b is not None: self._out.write(b, off, length) self._written += length if length else (len(b) if isinstance(b, (bytes, bytearray)) else 1) def writeBoolean(self, v): self._out.write(int(v)) def writeByte(self, v): self._out.write(v & 0xFF) def writeBytes(self, s): for ch in str(s): self._out.write(ord(ch) & 0xFF) def writeChar(self, v): self._out.write((v >> 8) & 0xFF) self._out.write(v & 0xFF) def writeChars(self, s): for ch in str(s): self.writeChar(ord(ch)) def writeDouble(self, v): self._write_struct(">d", v) def writeFloat(self, v): self._write_struct(">f", v) def writeInt(self, v): self._write_struct(">i", v) def writeLong(self, v): self._write_struct(">q", v) def writeShort(self, v): self._write_struct(">h", v) def writeUTF(self, s): encoded = str(s).encode("utf-8") self.writeShort(len(encoded)) for b in encoded: self._out.write(b) def size(self): return self._written def _write_struct(self, fmt, v): data = _struct.pack(fmt, v) for b in data: self._out.write(b) def flush(self): self._out.flush() def close(self): self._out.close() # ============================================================ # ObjectInputStream / ObjectOutputStream (stub) # ============================================================ class ObjectInputStream(Closeable): def __init__(self, inp): super().__init__() self._in = inp def readObject(self): raise AndroidOnlyError("ObjectInputStream.readObject()") def readInt(self): return DataInputStream(self._in).readInt() def close(self): self._in.close() class ObjectOutputStream(Closeable, Flushable): def __init__(self, out): super().__init__() self._out = out def writeObject(self, obj): raise AndroidOnlyError("ObjectOutputStream.writeObject()") def writeInt(self, v): DataOutputStream(self._out).writeInt(v) def flush(self): self._out.flush() def close(self): self._out.close() # ============================================================ # PipedInputStream / PipedOutputStream # ============================================================ class PipedInputStream(Closeable): def __init__(self, src=None, pipe_size=1024): super().__init__() self._buffer = bytearray() self._pipe_size = pipe_size self._closed = False self._lock = _threading.RLock() self._data_available = _threading.Condition(self._lock) if src: src.connect(self) def connect(self, src): src._sink = self def receive(self, b): with self._lock: self._buffer.append(b & 0xFF) self._data_available.notify() def read(self, b=None, off=None, length=None): with self._lock: while len(self._buffer) == 0 and not self._closed: self._data_available.wait(timeout=1) if self._closed and len(self._buffer) == 0: return -1 if b is None: if len(self._buffer) == 0: return -1 return self._buffer.pop(0) if off is None: off = 0 if length is None: length = len(b) n = min(length, len(self._buffer)) for i in range(n): if isinstance(b, bytearray): b[off + i] = self._buffer.pop(0) else: b[off + i] = self._buffer.pop(0) return n def close(self): with self._lock: self._closed = True self._data_available.notify_all() class PipedOutputStream(Closeable): def __init__(self, sink=None): super().__init__() self._sink = sink def connect(self, sink): self._sink = sink def write(self, b, off=None, length=None): if self._sink is None: raise IOException("Pipe not connected") if isinstance(b, int): self._sink.receive(b) else: if off is not None and length is not None: for i in range(off, off + length): self._sink.receive(b[i]) else: for byte in b: self._sink.receive(byte) def flush(self): pass def close(self): if self._sink: self._sink.close() # ============================================================ # PushbackInputStream # ============================================================ class PushbackInputStream(FilterInputStream): def __init__(self, inp, size=1): super().__init__(inp) self._pushback_buf = bytearray() self._max_size = size def read(self, b=None, off=None, length=None): if b is None: if self._pushback_buf: return self._pushback_buf.pop(0) return self._in.read() if off is None: off = 0 if length is None: length = len(b) total = 0 while self._pushback_buf and total < length: if isinstance(b, bytearray): b[off + total] = self._pushback_buf.pop(0) else: ba = bytearray(b) ba[off + total] = self._pushback_buf.pop(0) b[:] = bytes(ba) total += 1 if total < length: n = self._in.read(b, off + total, length - total) if n > 0: total += n return total if total > 0 else -1 def unread(self, b): if isinstance(b, int): if len(self._pushback_buf) >= self._max_size: raise IOException("Pushback buffer overflow") self._pushback_buf.append(b & 0xFF) else: for byte_val in b: self.unread(byte_val) def close(self): self._pushback_buf.clear() self._in.close() # ============================================================ # SequenceInputStream # ============================================================ class SequenceInputStream(Closeable): def __init__(self, streams): super().__init__() if hasattr(streams, 'hasMoreElements'): self._streams = [] while streams.hasMoreElements(): self._streams.append(streams.nextElement()) else: self._streams = list(streams) self._current_idx = 0 def _current(self): if self._current_idx < len(self._streams): return self._streams[self._current_idx] return None def read(self, b=None, off=None, length=None): while self._current(): inp = self._current() if b is None: result = inp.read() else: result = inp.read(b, off, length) if result != -1: return result self._current_idx += 1 return -1 def close(self): for s in self._streams: s.close() # ============================================================ # RandomAccessFile # ============================================================ class RandomAccessFile(Closeable, Flushable): def __init__(self, file, mode="r"): super().__init__() if isinstance(file, File): path = file.getPath() else: path = str(file) self._path = path self._mode = mode self._closed = False py_modes = {"r": "rb", "rw": "r+b", "rws": "r+b", "rwd": "r+b"} py_mode = py_modes.get(mode, "r+b") try: if "w" in mode and mode != "r": self._file = open(path, py_mode) else: self._file = open(path, py_mode) except FileNotFoundError as e: if "w" in mode: self._file = open(path, "w+b") else: raise FileNotFoundException(str(e)) def seek(self, pos): self._file.seek(pos) def getFilePointer(self): return self._file.tell() def length(self): pos = self._file.tell() self._file.seek(0, 2) end = self._file.tell() self._file.seek(pos) return end def read(self, b=None, off=None, length=None): if b is None: data = self._file.read(1) return data[0] if data else -1 if off is None: off = 0 if length is None: length = len(b) data = self._file.read(length) if not data: return -1 n = len(data) if isinstance(b, bytearray): b[off:off+n] = data else: ba = bytearray(b) ba[off:off+n] = data b[:] = bytes(ba) return n def readBoolean(self): v = self.read() if v == -1: raise EOFException() return bool(v) def readByte(self): v = self.read() if v == -1: raise EOFException() return (v & 0xFF) if v <= 127 else (v - 256) def readChar(self): hi = self.read() lo = self.read() if hi == -1 or lo == -1: raise EOFException() return chr((hi << 8) | lo) def readDouble(self): data = self._read_bytes(8) return _struct.unpack(">d", data)[0] def readFloat(self): data = self._read_bytes(4) return _struct.unpack(">f", data)[0] def readInt(self): data = self._read_bytes(4) return _struct.unpack(">i", data)[0] def readLine(self): result = [] while True: ch = self.read() if ch == -1: break if ch == 0x0A: break if ch == 0x0D: next_ch = self.read() if next_ch != 0x0A: self.seek(self.getFilePointer() - 1) break result.append(chr(ch)) return "".join(result) if result else None def readLong(self): data = self._read_bytes(8) return _struct.unpack(">q", data)[0] def readShort(self): data = self._read_bytes(2) return _struct.unpack(">h", data)[0] def readUnsignedByte(self): v = self.read() if v == -1: raise EOFException() return v & 0xFF def readUnsignedShort(self): hi = self.read() lo = self.read() if hi == -1 or lo == -1: raise EOFException() return ((hi & 0xFF) << 8) | (lo & 0xFF) def readUTF(self): length = self.readUnsignedShort() data = self._read_bytes(length) return data.decode("utf-8") def write(self, b=None, off=None, length=None): if b is None: return if isinstance(b, int): self._file.write(bytes([b & 0xFF])) else: if off is not None and length is not None: self._file.write(bytes(b[off:off+length])) else: self._file.write(bytes(b)) def writeBoolean(self, v): self.write(int(v)) def writeByte(self, v): self.write(v & 0xFF) def writeBytes(self, s): for ch in str(s): self.write(ord(ch) & 0xFF) def writeChar(self, v): self.write((v >> 8) & 0xFF) self.write(v & 0xFF) def writeChars(self, s): for ch in str(s): self.writeChar(ord(ch)) def writeDouble(self, v): data = _struct.pack(">d", v) self._file.write(data) def writeFloat(self, v): data = _struct.pack(">f", v) self._file.write(data) def writeInt(self, v): data = _struct.pack(">i", v) self._file.write(data) def writeLong(self, v): data = _struct.pack(">q", v) self._file.write(data) def writeShort(self, v): data = _struct.pack(">h", v) self._file.write(data) def writeUTF(self, s): encoded = str(s).encode("utf-8") self.writeShort(len(encoded)) self._file.write(encoded) def _read_bytes(self, n): data = self._file.read(n) if len(data) < n: raise EOFException() return data def setLength(self, new_length): pos = self.getFilePointer() self._file.truncate(new_length) if pos > new_length: self.seek(new_length) def close(self): if not self._closed: self._closed = True self._file.close() def getChannel(self): return self._file def flush(self): self._file.flush() def __del__(self): self.close()