3693 lines
106 KiB
Python
3693 lines
106 KiB
Python
"""
|
|
Java java.util standard library APIs implemented in Python.
|
|
Android-pyApi: desktop Python translation of Android Java APIs.
|
|
"""
|
|
|
|
from .._core import Stub
|
|
from .lang import RuntimeException
|
|
import heapq as _heapq
|
|
import bisect as _bisect
|
|
import datetime as _datetime
|
|
import uuid as _uuid
|
|
import random as _py_random
|
|
import math as _py_math
|
|
import copy as _copy
|
|
import re as _re
|
|
import io as _io
|
|
import threading as _threading
|
|
import time as _time
|
|
from typing import Any, Dict, List, Callable, Tuple, Iterable as _IterableType
|
|
import typing as _typing
|
|
from collections.abc import Iterable as _IterableABC
|
|
|
|
__all__ = [
|
|
"ArrayList", "LinkedList", "HashMap", "LinkedHashMap", "TreeMap",
|
|
"HashSet", "TreeSet", "LinkedHashSet",
|
|
"Collections", "Arrays", "Date", "Calendar", "GregorianCalendar",
|
|
"UUID", "StringTokenizer", "Optional", "Random",
|
|
"Properties", "EnumSet", "EnumMap",
|
|
"Iterator", "ListIterator", "Enumeration",
|
|
"Objects", "Locale", "BitSet", "PriorityQueue",
|
|
"Vector", "Stack",
|
|
"ConcurrentModificationException", "NoSuchElementException",
|
|
"EmptyStackException", "TooManyListenersException",
|
|
"MissingResourceException", "IllegalFormatException",
|
|
"DuplicateFormatFlagsException", "FormatFlagsConversionMismatchException",
|
|
"FormatterClosedException",
|
|
]
|
|
|
|
# ============================================================
|
|
# Exceptions
|
|
# ============================================================
|
|
|
|
# Define exception classes in correct dependency order.
|
|
# IllegalArgumentException comes before IllegalFormatException.
|
|
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
|
|
ConcurrentModificationException = type(
|
|
"ConcurrentModificationException", (RuntimeException,), {}
|
|
)
|
|
NoSuchElementException = type("NoSuchElementException", (RuntimeException,), {})
|
|
EmptyStackException = type("EmptyStackException", (RuntimeException,), {})
|
|
TooManyListenersException = type("TooManyListenersException", (Exception,), {})
|
|
MissingResourceException = type("MissingResourceException", (RuntimeException,), {})
|
|
IllegalFormatException = type("IllegalFormatException", (IllegalArgumentException,), {})
|
|
DuplicateFormatFlagsException = type(
|
|
"DuplicateFormatFlagsException", (IllegalFormatException,), {}
|
|
)
|
|
FormatFlagsConversionMismatchException = type(
|
|
"FormatFlagsConversionMismatchException", (IllegalFormatException,), {}
|
|
)
|
|
|
|
# We need IllegalStateException for FormatterClosedException
|
|
from .lang import RuntimeException as _RuntimeException
|
|
IllegalStateException = type("IllegalStateException", (_RuntimeException,), {})
|
|
|
|
FormatterClosedException = type("FormatterClosedException", (IllegalStateException,), {})
|
|
UnsupportedOperationException = type("UnsupportedOperationException", (RuntimeException,), {})
|
|
|
|
# ============================================================
|
|
# Iterator / ListIterator / Enumeration
|
|
# ============================================================
|
|
|
|
class Iterator(Stub):
|
|
"""java.util.Iterator"""
|
|
def __init__(self, data):
|
|
super().__init__()
|
|
self._data = list(data)
|
|
self._index = 0
|
|
self._mod_count = 0
|
|
|
|
def hasNext(self) -> bool:
|
|
return self._index < len(self._data)
|
|
|
|
def next(self):
|
|
if not self.hasNext():
|
|
raise NoSuchElementException()
|
|
val = self._data[self._index]
|
|
self._index += 1
|
|
return val
|
|
|
|
def remove(self):
|
|
raise UnsupportedOperationException("remove")
|
|
|
|
def forEachRemaining(self, action):
|
|
while self.hasNext():
|
|
action(self.next())
|
|
|
|
|
|
class ListIterator(Iterator):
|
|
"""java.util.ListIterator"""
|
|
def __init__(self, data):
|
|
super().__init__(data)
|
|
|
|
def hasPrevious(self) -> bool:
|
|
return self._index > 0
|
|
|
|
def previous(self):
|
|
if not self.hasPrevious():
|
|
raise NoSuchElementException()
|
|
self._index -= 1
|
|
return self._data[self._index]
|
|
|
|
def nextIndex(self) -> int:
|
|
return self._index
|
|
|
|
def previousIndex(self) -> int:
|
|
return self._index - 1
|
|
|
|
def set(self, obj):
|
|
self._data[self._index - 1] = obj
|
|
|
|
def add(self, obj):
|
|
self._data.insert(self._index, obj)
|
|
self._index += 1
|
|
|
|
|
|
class Enumeration(Stub):
|
|
"""java.util.Enumeration"""
|
|
def __init__(self, data):
|
|
super().__init__()
|
|
self._it = iter(data)
|
|
|
|
def hasMoreElements(self) -> bool:
|
|
try:
|
|
self._next = next(self._it)
|
|
return True
|
|
except StopIteration:
|
|
return False
|
|
|
|
def nextElement(self):
|
|
if hasattr(self, '_next'):
|
|
val = self._next
|
|
del self._next
|
|
return val
|
|
return next(self._it)
|
|
|
|
def asIterator(self) -> Iterator:
|
|
return Iterator(self._it)
|
|
|
|
|
|
# ============================================================
|
|
# ArrayList
|
|
# ============================================================
|
|
|
|
class ArrayList(Stub):
|
|
"""java.util.ArrayList — backed by Python list"""
|
|
def __init__(self, initial_capacity: Any = None):
|
|
super().__init__()
|
|
if initial_capacity is None:
|
|
self._data = []
|
|
elif isinstance(initial_capacity, int):
|
|
self._data = []
|
|
elif isinstance(initial_capacity, _IterableABC):
|
|
self._data = list(initial_capacity)
|
|
else:
|
|
self._data = []
|
|
|
|
def add(self, obj) -> bool:
|
|
self._data.append(obj)
|
|
return True
|
|
|
|
def addAt(self, index: int, obj) -> None:
|
|
self._data.insert(index, obj)
|
|
|
|
def addAll(self, c) -> bool:
|
|
if isinstance(c, ArrayList):
|
|
self._data.extend(c._data)
|
|
else:
|
|
self._data.extend(c)
|
|
return True
|
|
|
|
def addAllAt(self, index: int, c) -> bool:
|
|
if isinstance(c, ArrayList):
|
|
self._data[index:index] = c._data
|
|
else:
|
|
self._data[index:index] = list(c)
|
|
return True
|
|
|
|
def clear(self) -> None:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'ArrayList':
|
|
return ArrayList(self._data)
|
|
|
|
def contains(self, obj) -> bool:
|
|
return obj in self._data
|
|
|
|
def containsAll(self, c) -> bool:
|
|
if isinstance(c, ArrayList):
|
|
return all(x in self._data for x in c._data)
|
|
return all(x in self._data for x in c)
|
|
|
|
def ensureCapacity(self, min_capacity: int) -> None:
|
|
pass # Python list handles resizing automatically
|
|
|
|
def get(self, index: int):
|
|
return self._data[index]
|
|
|
|
def indexOf(self, obj) -> int:
|
|
try:
|
|
return self._data.index(obj)
|
|
except ValueError:
|
|
return -1
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._data) == 0
|
|
|
|
def iterator(self) -> Iterator:
|
|
return Iterator(self._data)
|
|
|
|
def listIterator(self) -> ListIterator:
|
|
return ListIterator(self._data)
|
|
|
|
def lastIndexOf(self, obj) -> int:
|
|
try:
|
|
return len(self._data) - 1 - self._data[::-1].index(obj)
|
|
except ValueError:
|
|
return -1
|
|
|
|
def remove(self, obj) -> bool:
|
|
if obj in self._data:
|
|
self._data.remove(obj)
|
|
return True
|
|
return False
|
|
|
|
def removeAt(self, index: int):
|
|
return self._data.pop(index)
|
|
|
|
def removeAll(self, c) -> bool:
|
|
if isinstance(c, ArrayList):
|
|
items = c._data
|
|
else:
|
|
items = list(c)
|
|
old_len = len(self._data)
|
|
self._data = [x for x in self._data if x not in items]
|
|
return len(self._data) != old_len
|
|
|
|
def retainAll(self, c) -> bool:
|
|
if isinstance(c, ArrayList):
|
|
items = set(c._data)
|
|
else:
|
|
items = set(c)
|
|
old_len = len(self._data)
|
|
self._data = [x for x in self._data if x in items]
|
|
return len(self._data) != old_len
|
|
|
|
def set(self, index: int, obj):
|
|
old = self._data[index]
|
|
self._data[index] = obj
|
|
return old
|
|
|
|
def size(self) -> int:
|
|
return len(self._data)
|
|
|
|
def sort(self, key=None, reverse: bool = False) -> None:
|
|
self._data.sort(key=key, reverse=reverse)
|
|
|
|
def subList(self, from_index: int, to_index: int) -> 'ArrayList':
|
|
return ArrayList(self._data[from_index:to_index])
|
|
|
|
def toArray(self, a=None):
|
|
if a is None:
|
|
return list(self._data)
|
|
if len(a) < len(self._data):
|
|
return list(self._data)
|
|
for i, v in enumerate(self._data):
|
|
a[i] = v
|
|
if len(self._data) < len(a):
|
|
a[len(self._data)] = None
|
|
return a
|
|
|
|
def trimToSize(self) -> None:
|
|
pass
|
|
|
|
def forEach(self, action) -> None:
|
|
for item in self._data:
|
|
action(item)
|
|
|
|
def removeIf(self, predicate) -> bool:
|
|
old_len = len(self._data)
|
|
self._data = [x for x in self._data if not predicate(x)]
|
|
return len(self._data) != old_len
|
|
|
|
def replaceAll(self, operator) -> None:
|
|
self._data = [operator(x) for x in self._data]
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, ArrayList):
|
|
return self._data == other._data
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
h = 1
|
|
for v in self._data:
|
|
h = (31 * h + hash(v)) & 0x7FFFFFFF
|
|
return h
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(str(x) for x in self._data)
|
|
return f"[{items}]"
|
|
|
|
def __len__(self):
|
|
return len(self._data)
|
|
|
|
def __getitem__(self, idx):
|
|
return self._data[idx]
|
|
|
|
def __setitem__(self, idx, val):
|
|
self._data[idx] = val
|
|
|
|
def __delitem__(self, idx):
|
|
del self._data[idx]
|
|
|
|
def __iter__(self):
|
|
return iter(self._data)
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __contains__(self, obj):
|
|
return obj in self._data
|
|
|
|
def __add__(self, other):
|
|
if isinstance(other, ArrayList):
|
|
result = ArrayList(self._data)
|
|
result._data.extend(other._data)
|
|
return result
|
|
return NotImplemented
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# LinkedList
|
|
# ============================================================
|
|
|
|
class LinkedList(Stub):
|
|
"""java.util.LinkedList — backed by Python list"""
|
|
def __init__(self, c=None):
|
|
super().__init__()
|
|
if c is None:
|
|
self._data = []
|
|
elif isinstance(c, _IterableABC):
|
|
self._data = list(c)
|
|
else:
|
|
self._data = []
|
|
|
|
def add(self, obj) -> bool:
|
|
self._data.append(obj)
|
|
return True
|
|
|
|
def addFirst(self, obj) -> None:
|
|
self._data.insert(0, obj)
|
|
|
|
def addLast(self, obj) -> None:
|
|
self._data.append(obj)
|
|
|
|
def addAt(self, index: int, obj) -> None:
|
|
self._data.insert(index, obj)
|
|
|
|
def addAll(self, c) -> bool:
|
|
if isinstance(c, LinkedList):
|
|
self._data.extend(c._data)
|
|
else:
|
|
self._data.extend(c)
|
|
return True
|
|
|
|
def clear(self) -> None:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'LinkedList':
|
|
return LinkedList(self._data)
|
|
|
|
def contains(self, obj) -> bool:
|
|
return obj in self._data
|
|
|
|
def descendingIterator(self) -> Iterator:
|
|
return Iterator(reversed(self._data))
|
|
|
|
def element(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data[0]
|
|
|
|
def get(self, index: int):
|
|
return self._data[index]
|
|
|
|
def getFirst(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data[0]
|
|
|
|
def getLast(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data[-1]
|
|
|
|
def indexOf(self, obj) -> int:
|
|
try:
|
|
return self._data.index(obj)
|
|
except ValueError:
|
|
return -1
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._data) == 0
|
|
|
|
def iterator(self) -> Iterator:
|
|
return Iterator(self._data)
|
|
|
|
def lastIndexOf(self, obj) -> int:
|
|
try:
|
|
return len(self._data) - 1 - self._data[::-1].index(obj)
|
|
except ValueError:
|
|
return -1
|
|
|
|
def listIterator(self, index: int = 0) -> ListIterator:
|
|
li = ListIterator(self._data)
|
|
li._index = index
|
|
return li
|
|
|
|
def offer(self, obj) -> bool:
|
|
self._data.append(obj)
|
|
return True
|
|
|
|
def offerFirst(self, obj) -> bool:
|
|
self._data.insert(0, obj)
|
|
return True
|
|
|
|
def offerLast(self, obj) -> bool:
|
|
self._data.append(obj)
|
|
return True
|
|
|
|
def peek(self):
|
|
return self._data[0] if self._data else None
|
|
|
|
def peekFirst(self):
|
|
return self._data[0] if self._data else None
|
|
|
|
def peekLast(self):
|
|
return self._data[-1] if self._data else None
|
|
|
|
def poll(self):
|
|
return self._data.pop(0) if self._data else None
|
|
|
|
def pollFirst(self):
|
|
return self._data.pop(0) if self._data else None
|
|
|
|
def pollLast(self):
|
|
return self._data.pop() if self._data else None
|
|
|
|
def pop(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data.pop()
|
|
|
|
def push(self, obj) -> None:
|
|
self._data.insert(0, obj)
|
|
|
|
def remove(self, obj) -> bool:
|
|
if obj in self._data:
|
|
self._data.remove(obj)
|
|
return True
|
|
return False
|
|
|
|
def removeAt(self, index: int):
|
|
return self._data.pop(index)
|
|
|
|
def removeFirst(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data.pop(0)
|
|
|
|
def removeFirstOccurrence(self, obj) -> bool:
|
|
try:
|
|
idx = self._data.index(obj)
|
|
self._data.pop(idx)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
def removeLast(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data.pop()
|
|
|
|
def removeLastOccurrence(self, obj) -> bool:
|
|
try:
|
|
idx = len(self._data) - 1 - self._data[::-1].index(obj)
|
|
self._data.pop(idx)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
def set(self, index: int, obj):
|
|
old = self._data[index]
|
|
self._data[index] = obj
|
|
return old
|
|
|
|
def size(self) -> int:
|
|
return len(self._data)
|
|
|
|
def toArray(self, a=None):
|
|
if a is None:
|
|
return list(self._data)
|
|
if len(a) < len(self._data):
|
|
return list(self._data)
|
|
for i, v in enumerate(self._data):
|
|
a[i] = v
|
|
if len(self._data) < len(a):
|
|
a[len(self._data)] = None
|
|
return a
|
|
|
|
def forEach(self, action) -> None:
|
|
for item in self._data:
|
|
action(item)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, LinkedList):
|
|
return self._data == other._data
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
h = 1
|
|
for v in self._data:
|
|
h = (31 * h + hash(v)) & 0x7FFFFFFF
|
|
return h
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(str(x) for x in self._data)
|
|
return f"[{items}]"
|
|
|
|
def __len__(self):
|
|
return len(self._data)
|
|
|
|
def __getitem__(self, idx):
|
|
return self._data[idx]
|
|
|
|
def __iter__(self):
|
|
return iter(self._data)
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# HashMap
|
|
# ============================================================
|
|
|
|
class HashMap(Stub):
|
|
"""java.util.HashMap — backed by Python dict"""
|
|
def __init__(self, initial_capacity: Any = None, load_factor: float = 0.75):
|
|
super().__init__()
|
|
self._data: Dict = {}
|
|
if initial_capacity is not None and not isinstance(initial_capacity, (int, float)):
|
|
if isinstance(initial_capacity, HashMap):
|
|
self._data.update(initial_capacity._data)
|
|
elif isinstance(initial_capacity, dict):
|
|
self._data.update(initial_capacity)
|
|
elif isinstance(initial_capacity, _IterableABC):
|
|
for item in initial_capacity:
|
|
if hasattr(item, 'getKey') and hasattr(item, 'getValue'):
|
|
k, v = item.getKey(), item.getValue()
|
|
elif isinstance(item, tuple) and len(item) >= 2:
|
|
k, v = item[0], item[1]
|
|
else:
|
|
continue
|
|
self._data[k] = v
|
|
|
|
def clear(self) -> None:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'HashMap':
|
|
return HashMap(self._data)
|
|
|
|
def compute(self, key, remapping_function):
|
|
old = self._data.get(key)
|
|
new_val = remapping_function(key, old)
|
|
if new_val is not None:
|
|
self._data[key] = new_val
|
|
return new_val
|
|
self._data.pop(key, None)
|
|
return None
|
|
|
|
def computeIfAbsent(self, key, mapping_function):
|
|
if key not in self._data:
|
|
new_val = mapping_function(key)
|
|
if new_val is not None:
|
|
self._data[key] = new_val
|
|
return new_val
|
|
return self._data.get(key)
|
|
|
|
def computeIfPresent(self, key, remapping_function):
|
|
if key in self._data:
|
|
new_val = remapping_function(key, self._data[key])
|
|
if new_val is not None:
|
|
self._data[key] = new_val
|
|
return new_val
|
|
self._data.pop(key, None)
|
|
return None
|
|
|
|
def containsKey(self, key) -> bool:
|
|
return key in self._data
|
|
|
|
def containsValue(self, value) -> bool:
|
|
return value in self._data.values()
|
|
|
|
def entrySet(self) -> set:
|
|
return self._data.items()
|
|
|
|
def forEach(self, action) -> None:
|
|
for k, v in self._data.items():
|
|
action(k, v)
|
|
|
|
def get(self, key):
|
|
return self._data.get(key)
|
|
|
|
def getOrDefault(self, key, default):
|
|
return self._data.get(key, default)
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._data) == 0
|
|
|
|
def keySet(self) -> set:
|
|
return set(self._data.keys())
|
|
|
|
def merge(self, key, value, remapping_function):
|
|
old = self._data.get(key)
|
|
if old is None:
|
|
self._data[key] = value
|
|
return value
|
|
new_val = remapping_function(old, value)
|
|
if new_val is not None:
|
|
self._data[key] = new_val
|
|
return new_val
|
|
self._data.pop(key, None)
|
|
return None
|
|
|
|
def put(self, key, value):
|
|
old = self._data.get(key)
|
|
self._data[key] = value
|
|
return old
|
|
|
|
def putAll(self, m) -> None:
|
|
if isinstance(m, HashMap):
|
|
self._data.update(m._data)
|
|
else:
|
|
self._data.update(m)
|
|
|
|
def putIfAbsent(self, key, value):
|
|
if key not in self._data:
|
|
self._data[key] = value
|
|
return None
|
|
return self._data[key]
|
|
|
|
def remove(self, key, value=None) -> bool:
|
|
if value is None:
|
|
return self._data.pop(key, None) is not None or key in self._data
|
|
if key in self._data and self._data[key] == value:
|
|
del self._data[key]
|
|
return True
|
|
return False
|
|
|
|
def replace(self, key, old_value=None, new_value=None):
|
|
if old_value is None and new_value is None:
|
|
return None
|
|
if new_value is None:
|
|
# replace(key, value) form
|
|
if key in self._data:
|
|
old = self._data[key]
|
|
self._data[key] = old_value # old_value is actually new value here
|
|
return old
|
|
return None
|
|
# replace(key, oldValue, newValue) form
|
|
if key in self._data and self._data[key] == old_value:
|
|
self._data[key] = new_value
|
|
return True
|
|
return False
|
|
|
|
def replaceAll(self, function) -> None:
|
|
for k in list(self._data.keys()):
|
|
self._data[k] = function(k, self._data[k])
|
|
|
|
def size(self) -> int:
|
|
return len(self._data)
|
|
|
|
def values(self):
|
|
return list(self._data.values())
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, HashMap):
|
|
return self._data == other._data
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
h = 0
|
|
for k, v in self._data.items():
|
|
h = (h + (hash(k) ^ hash(v))) & 0x7FFFFFFF
|
|
return h
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(f"{k}={v}" for k, v in self._data.items())
|
|
return f"{{{items}}}"
|
|
|
|
def __len__(self):
|
|
return len(self._data)
|
|
|
|
def __getitem__(self, key):
|
|
return self._data[key]
|
|
|
|
def __setitem__(self, key, value):
|
|
self._data[key] = value
|
|
|
|
def __delitem__(self, key):
|
|
del self._data[key]
|
|
|
|
def __iter__(self):
|
|
return iter(self._data)
|
|
|
|
def __contains__(self, key):
|
|
return key in self._data
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# LinkedHashMap
|
|
# ============================================================
|
|
|
|
class LinkedHashMap(HashMap):
|
|
"""java.util.LinkedHashMap — backed by dict (preserves order in Python 3.7+)"""
|
|
def __init__(self, initial_capacity: Any = None, load_factor: float = 0.75,
|
|
access_order: bool = False):
|
|
super().__init__(initial_capacity, load_factor)
|
|
self._access_order = access_order
|
|
|
|
def get(self, key):
|
|
val = self._data.get(key)
|
|
if val is not None and self._access_order:
|
|
# Move to end (most recently accessed) by delete/reinsert
|
|
del self._data[key]
|
|
self._data[key] = val
|
|
return val
|
|
|
|
def getOrDefault(self, key, default):
|
|
val = self._data.get(key, default)
|
|
if key in self._data and self._access_order:
|
|
del self._data[key]
|
|
self._data[key] = val
|
|
return val
|
|
|
|
def put(self, key, value):
|
|
old = self._data.get(key)
|
|
if key in self._data:
|
|
del self._data[key]
|
|
self._data[key] = value
|
|
return old
|
|
|
|
def removeEldestEntry(self, eldest) -> bool:
|
|
return False
|
|
|
|
def eldest(self):
|
|
if not self._data:
|
|
return None
|
|
key = next(iter(self._data))
|
|
return (key, self._data[key])
|
|
|
|
|
|
# ============================================================
|
|
# TreeMap
|
|
# ============================================================
|
|
|
|
class TreeMap(Stub):
|
|
"""java.util.TreeMap — sorted map using Python's sorted()"""
|
|
def __init__(self, comparator=None, m=None):
|
|
super().__init__()
|
|
self._data: Dict = {}
|
|
self._comparator = comparator
|
|
if m is not None:
|
|
if isinstance(m, TreeMap):
|
|
self._data.update(m._data)
|
|
elif isinstance(m, dict):
|
|
self._data.update(m)
|
|
elif isinstance(m, _IterableABC):
|
|
for item in m:
|
|
if isinstance(item, tuple) and len(item) >= 2:
|
|
self._data[item[0]] = item[1]
|
|
|
|
def _sorted_keys(self):
|
|
key_fn = self._comparator if self._comparator else None
|
|
return sorted(self._data.keys(), key=key_fn)
|
|
|
|
def _get_entry_by_key(self, key):
|
|
if key in self._data:
|
|
return (key, self._data[key])
|
|
return None
|
|
|
|
def clear(self) -> None:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'TreeMap':
|
|
return TreeMap(self._comparator, self._data)
|
|
|
|
def comparator(self):
|
|
return self._comparator
|
|
|
|
def containsKey(self, key) -> bool:
|
|
return key in self._data
|
|
|
|
def containsValue(self, value) -> bool:
|
|
return value in self._data.values()
|
|
|
|
def ceilingKey(self, key):
|
|
keys = self._sorted_keys()
|
|
for k in keys:
|
|
if (self._comparator(k, key) >= 0 if self._comparator else k >= key):
|
|
return k
|
|
return None
|
|
|
|
def ceilingEntry(self, key):
|
|
k = self.ceilingKey(key)
|
|
return (k, self._data[k]) if k is not None else None
|
|
|
|
def floorKey(self, key):
|
|
keys = self._sorted_keys()
|
|
for k in reversed(keys):
|
|
if (self._comparator(k, key) <= 0 if self._comparator else k <= key):
|
|
return k
|
|
return None
|
|
|
|
def floorEntry(self, key):
|
|
k = self.floorKey(key)
|
|
return (k, self._data[k]) if k is not None else None
|
|
|
|
def higherKey(self, key):
|
|
keys = self._sorted_keys()
|
|
for k in keys:
|
|
if (self._comparator(k, key) > 0 if self._comparator else k > key):
|
|
return k
|
|
return None
|
|
|
|
def higherEntry(self, key):
|
|
k = self.higherKey(key)
|
|
return (k, self._data[k]) if k is not None else None
|
|
|
|
def lowerKey(self, key):
|
|
keys = self._sorted_keys()
|
|
for k in reversed(keys):
|
|
if (self._comparator(k, key) < 0 if self._comparator else k < key):
|
|
return k
|
|
return None
|
|
|
|
def lowerEntry(self, key):
|
|
k = self.lowerKey(key)
|
|
return (k, self._data[k]) if k is not None else None
|
|
|
|
def firstKey(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._sorted_keys()[0]
|
|
|
|
def firstEntry(self):
|
|
k = self.firstKey()
|
|
return (k, self._data[k])
|
|
|
|
def lastKey(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._sorted_keys()[-1]
|
|
|
|
def lastEntry(self):
|
|
k = self.lastKey()
|
|
return (k, self._data[k])
|
|
|
|
def pollFirstEntry(self):
|
|
if not self._data:
|
|
return None
|
|
k = self.firstKey()
|
|
v = self._data.pop(k)
|
|
return (k, v)
|
|
|
|
def pollLastEntry(self):
|
|
if not self._data:
|
|
return None
|
|
k = self.lastKey()
|
|
v = self._data.pop(k)
|
|
return (k, v)
|
|
|
|
def entrySet(self):
|
|
return [(k, self._data[k]) for k in self._sorted_keys()]
|
|
|
|
def forEach(self, action) -> None:
|
|
for k in self._sorted_keys():
|
|
action(k, self._data[k])
|
|
|
|
def get(self, key):
|
|
return self._data.get(key)
|
|
|
|
def getOrDefault(self, key, default):
|
|
return self._data.get(key, default)
|
|
|
|
def headMap(self, to_key, inclusive: bool = False) -> 'TreeMap':
|
|
result = TreeMap(self._comparator)
|
|
for k in self._sorted_keys():
|
|
cmp = self._comparator(k, to_key) if self._comparator else (k < to_key)
|
|
if cmp or (inclusive and (self._comparator(k, to_key) == 0 if self._comparator else k == to_key)):
|
|
result._data[k] = self._data[k]
|
|
elif not inclusive and (self._comparator(k, to_key) < 0 if self._comparator else k < to_key):
|
|
result._data[k] = self._data[k]
|
|
else:
|
|
break
|
|
return result
|
|
|
|
def tailMap(self, from_key, inclusive: bool = True) -> 'TreeMap':
|
|
result = TreeMap(self._comparator)
|
|
started = False
|
|
for k in self._sorted_keys():
|
|
cmp = self._comparator(k, from_key) if self._comparator else (k >= from_key)
|
|
if cmp or (inclusive and (self._comparator(k, from_key) == 0 if self._comparator else k == from_key)):
|
|
started = True
|
|
if started:
|
|
result._data[k] = self._data[k]
|
|
return result
|
|
|
|
def subMap(self, from_key, to_key, from_inclusive: bool = True, to_inclusive: bool = False) -> 'TreeMap':
|
|
result = TreeMap(self._comparator)
|
|
for k in self._sorted_keys():
|
|
cmp_from = self._comparator(k, from_key) if self._comparator else (k >= from_key if from_inclusive else k > from_key)
|
|
cmp_to = self._comparator(k, to_key) if self._comparator else (k <= to_key if to_inclusive else k < to_key)
|
|
if cmp_from and cmp_to:
|
|
result._data[k] = self._data[k]
|
|
return result
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._data) == 0
|
|
|
|
def keySet(self):
|
|
return self._sorted_keys()
|
|
|
|
def put(self, key, value):
|
|
old = self._data.get(key)
|
|
self._data[key] = value
|
|
return old
|
|
|
|
def putAll(self, m) -> None:
|
|
if isinstance(m, TreeMap):
|
|
self._data.update(m._data)
|
|
else:
|
|
self._data.update(m)
|
|
|
|
def putIfAbsent(self, key, value):
|
|
if key not in self._data:
|
|
self._data[key] = value
|
|
return None
|
|
return self._data[key]
|
|
|
|
def remove(self, key):
|
|
return self._data.pop(key, None)
|
|
|
|
def replace(self, key, old_value=None, new_value=None):
|
|
if new_value is None:
|
|
if key in self._data:
|
|
old = self._data[key]
|
|
self._data[key] = old_value
|
|
return old
|
|
return None
|
|
if key in self._data and self._data[key] == old_value:
|
|
self._data[key] = new_value
|
|
return True
|
|
return False
|
|
|
|
def size(self) -> int:
|
|
return len(self._data)
|
|
|
|
def values(self):
|
|
return [self._data[k] for k in self._sorted_keys()]
|
|
|
|
def navigableKeySet(self):
|
|
return self._sorted_keys()
|
|
|
|
def descendingKeySet(self):
|
|
return list(reversed(self._sorted_keys()))
|
|
|
|
def descendingMap(self) -> 'TreeMap':
|
|
result = TreeMap(self._comparator)
|
|
for k in reversed(self._sorted_keys()):
|
|
result._data[k] = self._data[k]
|
|
return result
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, TreeMap):
|
|
return self._data == other._data
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
h = 0
|
|
for k, v in self._data.items():
|
|
h = (h + (hash(k) ^ hash(v))) & 0x7FFFFFFF
|
|
return h
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(f"{k}={v}" for k in self._data.items())
|
|
return f"{{{items}}}"
|
|
|
|
def __len__(self):
|
|
return len(self._data)
|
|
|
|
def __getitem__(self, key):
|
|
return self._data[key]
|
|
|
|
def __setitem__(self, key, value):
|
|
self._data[key] = value
|
|
|
|
def __delitem__(self, key):
|
|
del self._data[key]
|
|
|
|
def __iter__(self):
|
|
return iter(self._sorted_keys())
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# HashSet
|
|
# ============================================================
|
|
|
|
class HashSet(Stub):
|
|
"""java.util.HashSet — backed by Python set"""
|
|
def __init__(self, c=None):
|
|
super().__init__()
|
|
if c is None:
|
|
self._data: set = set()
|
|
elif isinstance(c, _IterableABC):
|
|
self._data = set(c)
|
|
else:
|
|
self._data = set()
|
|
|
|
def add(self, obj) -> bool:
|
|
if obj in self._data:
|
|
return False
|
|
self._data.add(obj)
|
|
return True
|
|
|
|
def addAll(self, c) -> bool:
|
|
old_size = len(self._data)
|
|
if isinstance(c, HashSet):
|
|
self._data.update(c._data)
|
|
elif isinstance(c, set):
|
|
self._data.update(c)
|
|
else:
|
|
self._data.update(c)
|
|
return len(self._data) != old_size
|
|
|
|
def clear(self) -> None:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'HashSet':
|
|
return HashSet(self._data)
|
|
|
|
def contains(self, obj) -> bool:
|
|
return obj in self._data
|
|
|
|
def containsAll(self, c) -> bool:
|
|
if isinstance(c, HashSet):
|
|
return all(x in self._data for x in c._data)
|
|
return all(x in self._data for x in c)
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._data) == 0
|
|
|
|
def iterator(self) -> Iterator:
|
|
return Iterator(list(self._data))
|
|
|
|
def remove(self, obj) -> bool:
|
|
if obj in self._data:
|
|
self._data.remove(obj)
|
|
return True
|
|
return False
|
|
|
|
def removeAll(self, c) -> bool:
|
|
old_size = len(self._data)
|
|
if isinstance(c, HashSet):
|
|
self._data.difference_update(c._data)
|
|
else:
|
|
self._data.difference_update(c)
|
|
return len(self._data) != old_size
|
|
|
|
def retainAll(self, c) -> bool:
|
|
old_size = len(self._data)
|
|
if isinstance(c, HashSet):
|
|
self._data.intersection_update(c._data)
|
|
else:
|
|
self._data.intersection_update(c)
|
|
return len(self._data) != old_size
|
|
|
|
def size(self) -> int:
|
|
return len(self._data)
|
|
|
|
def toArray(self, a=None):
|
|
items = list(self._data)
|
|
if a is None:
|
|
return items
|
|
if len(a) < len(items):
|
|
return items
|
|
for i, v in enumerate(items):
|
|
a[i] = v
|
|
if len(items) < len(a):
|
|
a[len(items)] = None
|
|
return a
|
|
|
|
def forEach(self, action) -> None:
|
|
for item in list(self._data):
|
|
action(item)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, HashSet):
|
|
return self._data == other._data
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
h = 0
|
|
for v in self._data:
|
|
h = (h + hash(v)) & 0x7FFFFFFF
|
|
return h
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(str(x) for x in self._data)
|
|
return f"[{items}]"
|
|
|
|
def __len__(self):
|
|
return len(self._data)
|
|
|
|
def __iter__(self):
|
|
return iter(self._data)
|
|
|
|
def __contains__(self, obj):
|
|
return obj in self._data
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# LinkedHashSet
|
|
# ============================================================
|
|
|
|
class LinkedHashSet(HashSet):
|
|
"""java.util.LinkedHashSet — ordered set (preserves insertion order)"""
|
|
def __init__(self, c=None):
|
|
# dict keys preserve insertion order in Python 3.7+
|
|
self._order: Dict[Any, None] = {}
|
|
if c is not None:
|
|
if isinstance(c, _IterableABC):
|
|
for item in c:
|
|
self._order[item] = None
|
|
self._data = set(self._order.keys())
|
|
|
|
def add(self, obj) -> bool:
|
|
if obj in self._order:
|
|
return False
|
|
self._order[obj] = None
|
|
self._data.add(obj)
|
|
return True
|
|
|
|
def remove(self, obj) -> bool:
|
|
if obj in self._order:
|
|
del self._order[obj]
|
|
self._data.discard(obj)
|
|
return True
|
|
return False
|
|
|
|
def clear(self) -> None:
|
|
self._order.clear()
|
|
self._data.clear()
|
|
|
|
def iterator(self) -> Iterator:
|
|
return Iterator(list(self._order.keys()))
|
|
|
|
def toArray(self, a=None):
|
|
items = list(self._order.keys())
|
|
if a is None:
|
|
return items
|
|
if len(a) < len(items):
|
|
return items
|
|
for i, v in enumerate(items):
|
|
a[i] = v
|
|
if len(items) < len(a):
|
|
a[len(items)] = None
|
|
return a
|
|
|
|
def forEach(self, action) -> None:
|
|
for item in list(self._order.keys()):
|
|
action(item)
|
|
|
|
def __iter__(self):
|
|
return iter(self._order.keys())
|
|
|
|
|
|
# ============================================================
|
|
# TreeSet
|
|
# ============================================================
|
|
|
|
class TreeSet(Stub):
|
|
"""java.util.TreeSet — sorted set"""
|
|
def __init__(self, comparator=None, c=None):
|
|
super().__init__()
|
|
self._data: set = set()
|
|
self._comparator = comparator
|
|
if c is not None:
|
|
if isinstance(c, _IterableABC):
|
|
self._data = set(c)
|
|
|
|
def _sorted(self):
|
|
key_fn = self._comparator if self._comparator else None
|
|
return sorted(self._data, key=key_fn)
|
|
|
|
def add(self, obj) -> bool:
|
|
if obj in self._data:
|
|
return False
|
|
self._data.add(obj)
|
|
return True
|
|
|
|
def addAll(self, c) -> bool:
|
|
old_size = len(self._data)
|
|
if isinstance(c, TreeSet):
|
|
self._data.update(c._data)
|
|
else:
|
|
self._data.update(c)
|
|
return len(self._data) != old_size
|
|
|
|
def ceiling(self, obj):
|
|
sorted_items = self._sorted()
|
|
for item in sorted_items:
|
|
if (self._comparator(item, obj) >= 0 if self._comparator else item >= obj):
|
|
return item
|
|
return None
|
|
|
|
def floor(self, obj):
|
|
sorted_items = self._sorted()
|
|
for item in reversed(sorted_items):
|
|
if (self._comparator(item, obj) <= 0 if self._comparator else item <= obj):
|
|
return item
|
|
return None
|
|
|
|
def higher(self, obj):
|
|
sorted_items = self._sorted()
|
|
for item in sorted_items:
|
|
if (self._comparator(item, obj) > 0 if self._comparator else item > obj):
|
|
return item
|
|
return None
|
|
|
|
def lower(self, obj):
|
|
sorted_items = self._sorted()
|
|
for item in reversed(sorted_items):
|
|
if (self._comparator(item, obj) < 0 if self._comparator else item < obj):
|
|
return item
|
|
return None
|
|
|
|
def clear(self) -> None:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'TreeSet':
|
|
return TreeSet(self._comparator, self._data)
|
|
|
|
def comparator(self):
|
|
return self._comparator
|
|
|
|
def contains(self, obj) -> bool:
|
|
return obj in self._data
|
|
|
|
def containsAll(self, c) -> bool:
|
|
if isinstance(c, TreeSet):
|
|
return all(x in self._data for x in c._data)
|
|
return all(x in self._data for x in c)
|
|
|
|
def first(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._sorted()[0]
|
|
|
|
def last(self):
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._sorted()[-1]
|
|
|
|
def pollFirst(self):
|
|
if not self._data:
|
|
return None
|
|
val = self._sorted()[0]
|
|
self._data.discard(val)
|
|
return val
|
|
|
|
def pollLast(self):
|
|
if not self._data:
|
|
return None
|
|
val = self._sorted()[-1]
|
|
self._data.discard(val)
|
|
return val
|
|
|
|
def headSet(self, to_element, inclusive: bool = False) -> 'TreeSet':
|
|
result = TreeSet(self._comparator)
|
|
for item in self._data:
|
|
cmp = self._comparator(item, to_element) if self._comparator else (item < to_element)
|
|
if cmp or (inclusive and item == to_element):
|
|
result._data.add(item)
|
|
return result
|
|
|
|
def tailSet(self, from_element, inclusive: bool = True) -> 'TreeSet':
|
|
result = TreeSet(self._comparator)
|
|
for item in self._data:
|
|
cmp = self._comparator(item, from_element) if self._comparator else (item > from_element)
|
|
if cmp or (inclusive and item == from_element):
|
|
result._data.add(item)
|
|
return result
|
|
|
|
def subSet(self, from_element, to_element,
|
|
from_inclusive: bool = True, to_inclusive: bool = False) -> 'TreeSet':
|
|
result = TreeSet(self._comparator)
|
|
for item in self._data:
|
|
cmp_from = self._comparator(item, from_element) if self._comparator else (
|
|
item >= from_element if from_inclusive else item > from_element)
|
|
cmp_to = self._comparator(item, to_element) if self._comparator else (
|
|
item <= to_element if to_inclusive else item < to_element)
|
|
if cmp_from and cmp_to:
|
|
result._data.add(item)
|
|
return result
|
|
|
|
def descendingIterator(self) -> Iterator:
|
|
return Iterator(reversed(self._sorted()))
|
|
|
|
def descendingSet(self) -> 'TreeSet':
|
|
result = TreeSet(self._comparator)
|
|
result._data = set(self._data)
|
|
return result
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._data) == 0
|
|
|
|
def iterator(self) -> Iterator:
|
|
return Iterator(self._sorted())
|
|
|
|
def remove(self, obj) -> bool:
|
|
if obj in self._data:
|
|
self._data.discard(obj)
|
|
return True
|
|
return False
|
|
|
|
def removeAll(self, c) -> bool:
|
|
old_size = len(self._data)
|
|
if isinstance(c, TreeSet):
|
|
self._data.difference_update(c._data)
|
|
else:
|
|
self._data.difference_update(c)
|
|
return len(self._data) != old_size
|
|
|
|
def retainAll(self, c) -> bool:
|
|
old_size = len(self._data)
|
|
if isinstance(c, TreeSet):
|
|
self._data.intersection_update(c._data)
|
|
else:
|
|
self._data.intersection_update(c)
|
|
return len(self._data) != old_size
|
|
|
|
def size(self) -> int:
|
|
return len(self._data)
|
|
|
|
def toArray(self, a=None):
|
|
items = self._sorted()
|
|
if a is None:
|
|
return items
|
|
if len(a) < len(items):
|
|
return items
|
|
for i, v in enumerate(items):
|
|
a[i] = v
|
|
if len(items) < len(a):
|
|
a[len(items)] = None
|
|
return a
|
|
|
|
def forEach(self, action) -> None:
|
|
for item in self._sorted():
|
|
action(item)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, TreeSet):
|
|
return self._data == other._data
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
h = 0
|
|
for v in self._data:
|
|
h = (h + hash(v)) & 0x7FFFFFFF
|
|
return h
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(str(x) for x in self._sorted())
|
|
return f"[{items}]"
|
|
|
|
def __len__(self):
|
|
return len(self._data)
|
|
|
|
def __iter__(self):
|
|
return iter(self._sorted())
|
|
|
|
def __contains__(self, obj):
|
|
return obj in self._data
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# Collections
|
|
# ============================================================
|
|
|
|
class Collections:
|
|
"""java.util.Collections — static utility methods"""
|
|
EMPTY_LIST = []
|
|
EMPTY_SET = set()
|
|
EMPTY_MAP = {}
|
|
|
|
@staticmethod
|
|
def sort(data, key=None, reverse: bool = False) -> None:
|
|
if key is not None:
|
|
if isinstance(data, ArrayList):
|
|
data._data.sort(key=key, reverse=reverse)
|
|
elif isinstance(data, Vector):
|
|
data._data.sort(key=key, reverse=reverse)
|
|
else:
|
|
data.sort(key=key, reverse=reverse)
|
|
else:
|
|
data.sort(reverse=reverse)
|
|
|
|
@staticmethod
|
|
def reverse(data) -> None:
|
|
if isinstance(data, ArrayList):
|
|
data._data.reverse()
|
|
elif isinstance(data, Vector):
|
|
data._data.reverse()
|
|
else:
|
|
data.reverse()
|
|
|
|
@staticmethod
|
|
def shuffle(data, rnd: _typing.Optional['Random'] = None) -> None:
|
|
if isinstance(data, ArrayList):
|
|
lst = data._data
|
|
elif isinstance(data, Vector):
|
|
lst = data._data
|
|
else:
|
|
lst = data
|
|
if rnd:
|
|
_py_random.shuffle(lst)
|
|
else:
|
|
_py_random.shuffle(lst)
|
|
|
|
@staticmethod
|
|
def swap(data, i: int, j: int) -> None:
|
|
if isinstance(data, ArrayList):
|
|
data._data[i], data._data[j] = data._data[j], data._data[i]
|
|
elif isinstance(data, Vector):
|
|
data._data[i], data._data[j] = data._data[j], data._data[i]
|
|
else:
|
|
data[i], data[j] = data[j], data[i]
|
|
|
|
@staticmethod
|
|
def fill(data, obj) -> None:
|
|
if isinstance(data, ArrayList):
|
|
for i in range(len(data._data)):
|
|
data._data[i] = obj
|
|
elif isinstance(data, Vector):
|
|
for i in range(len(data._data)):
|
|
data._data[i] = obj
|
|
else:
|
|
for i in range(len(data)):
|
|
data[i] = obj
|
|
|
|
@staticmethod
|
|
def copy(dest, src) -> None:
|
|
if isinstance(dest, ArrayList) and isinstance(src, ArrayList):
|
|
dest._data[:len(src._data)] = src._data[:]
|
|
elif isinstance(dest, Vector) and isinstance(src, Vector):
|
|
dest._data[:len(src._data)] = src._data[:]
|
|
else:
|
|
dest[:len(src)] = src[:]
|
|
|
|
@staticmethod
|
|
def min(coll, comp=None):
|
|
if comp:
|
|
return min(coll, key=comp)
|
|
return min(coll)
|
|
|
|
@staticmethod
|
|
def max(coll, comp=None):
|
|
if comp:
|
|
return max(coll, key=comp)
|
|
return max(coll)
|
|
|
|
@staticmethod
|
|
def rotate(data, distance: int) -> None:
|
|
if isinstance(data, ArrayList):
|
|
lst = data._data
|
|
elif isinstance(data, Vector):
|
|
lst = data._data
|
|
else:
|
|
lst = data
|
|
n = len(lst)
|
|
if n == 0:
|
|
return
|
|
d = distance % n
|
|
lst[:] = lst[-d:] + lst[:-d]
|
|
|
|
@staticmethod
|
|
def indexOfSubList(source, target) -> int:
|
|
if isinstance(source, ArrayList):
|
|
src = source._data
|
|
else:
|
|
src = list(source)
|
|
if isinstance(target, ArrayList):
|
|
tgt = target._data
|
|
else:
|
|
tgt = list(target)
|
|
for i in range(len(src) - len(tgt) + 1):
|
|
if src[i:i + len(tgt)] == tgt:
|
|
return i
|
|
return -1
|
|
|
|
@staticmethod
|
|
def lastIndexOfSubList(source, target) -> int:
|
|
if isinstance(source, ArrayList):
|
|
src = source._data
|
|
else:
|
|
src = list(source)
|
|
if isinstance(target, ArrayList):
|
|
tgt = target._data
|
|
else:
|
|
tgt = list(target)
|
|
for i in range(len(src) - len(tgt), -1, -1):
|
|
if src[i:i + len(tgt)] == tgt:
|
|
return i
|
|
return -1
|
|
|
|
@staticmethod
|
|
def replaceAll(data, old_val, new_val) -> bool:
|
|
if isinstance(data, ArrayList):
|
|
lst = data._data
|
|
elif isinstance(data, Vector):
|
|
lst = data._data
|
|
else:
|
|
lst = data
|
|
found = False
|
|
for i, v in enumerate(lst):
|
|
if v == old_val:
|
|
lst[i] = new_val
|
|
found = True
|
|
return found
|
|
|
|
@staticmethod
|
|
def binarySearch(data, key):
|
|
if isinstance(data, ArrayList):
|
|
lst = data._data
|
|
else:
|
|
lst = list(data)
|
|
idx = _bisect.bisect_left(lst, key)
|
|
if idx < len(lst) and lst[idx] == key:
|
|
return idx
|
|
return -1
|
|
|
|
@staticmethod
|
|
def disjoint(c1, c2) -> bool:
|
|
set1 = set(c1)
|
|
set2 = set(c2)
|
|
return set1.isdisjoint(set2)
|
|
|
|
@staticmethod
|
|
def frequency(c, obj) -> int:
|
|
count = 0
|
|
for item in c:
|
|
if item == obj:
|
|
count += 1
|
|
return count
|
|
|
|
@staticmethod
|
|
def nCopies(n: int, obj) -> list:
|
|
return [obj] * n
|
|
|
|
@staticmethod
|
|
def singleton(obj) -> set:
|
|
return {obj}
|
|
|
|
@staticmethod
|
|
def singletonList(obj) -> list:
|
|
return [obj]
|
|
|
|
@staticmethod
|
|
def singletonMap(key, value) -> dict:
|
|
return {key: value}
|
|
|
|
@staticmethod
|
|
def unmodifiableList(data) -> list:
|
|
if isinstance(data, ArrayList):
|
|
return tuple(data._data)
|
|
return tuple(data)
|
|
|
|
@staticmethod
|
|
def unmodifiableSet(data) -> frozenset:
|
|
if isinstance(data, HashSet):
|
|
return frozenset(data._data)
|
|
return frozenset(data)
|
|
|
|
@staticmethod
|
|
def unmodifiableMap(data) -> dict:
|
|
if isinstance(data, HashMap):
|
|
return dict(data._data)
|
|
return dict(data)
|
|
|
|
@staticmethod
|
|
def synchronizedList(data):
|
|
lock = _threading.Lock()
|
|
|
|
class _SyncList:
|
|
def __init__(self, lst):
|
|
self._lock = lock
|
|
self._lst = lst
|
|
|
|
def __getattr__(self, name):
|
|
with self._lock:
|
|
return getattr(self._lst, name)
|
|
|
|
def add(self, obj):
|
|
with self._lock:
|
|
return self._lst.add(obj)
|
|
|
|
def get(self, idx):
|
|
with self._lock:
|
|
return self._lst[idx]
|
|
|
|
def set(self, idx, val):
|
|
with self._lock:
|
|
old = self._lst[idx]
|
|
self._lst[idx] = val
|
|
return old
|
|
|
|
def remove(self, obj):
|
|
with self._lock:
|
|
return self._lst.remove(obj)
|
|
|
|
def size(self):
|
|
with self._lock:
|
|
return len(self._lst)
|
|
|
|
def __len__(self):
|
|
with self._lock:
|
|
return len(self._lst)
|
|
|
|
def __getitem__(self, idx):
|
|
with self._lock:
|
|
return self._lst[idx]
|
|
|
|
def __setitem__(self, idx, val):
|
|
with self._lock:
|
|
self._lst[idx] = val
|
|
|
|
def __iter__(self):
|
|
with self._lock:
|
|
return iter(list(self._lst))
|
|
|
|
return _SyncList(data)
|
|
|
|
@staticmethod
|
|
def synchronizedSet(data):
|
|
lock = _threading.Lock()
|
|
|
|
class _SyncSet:
|
|
def __init__(self, s):
|
|
self._lock = lock
|
|
self._s = s
|
|
|
|
def add(self, obj):
|
|
with self._lock:
|
|
return self._s.add(obj)
|
|
|
|
def remove(self, obj):
|
|
with self._lock:
|
|
return self._s.remove(obj)
|
|
|
|
def __contains__(self, obj):
|
|
with self._lock:
|
|
return obj in self._s
|
|
|
|
def __iter__(self):
|
|
with self._lock:
|
|
return iter(list(self._s))
|
|
|
|
def __len__(self):
|
|
with self._lock:
|
|
return len(self._s)
|
|
|
|
return _SyncSet(data)
|
|
|
|
@staticmethod
|
|
def synchronizedMap(data):
|
|
lock = _threading.Lock()
|
|
|
|
class _SyncMap:
|
|
def __init__(self, m):
|
|
self._lock = lock
|
|
self._m = m
|
|
|
|
def put(self, k, v):
|
|
with self._lock:
|
|
return self._m.put(k, v)
|
|
|
|
def get(self, k):
|
|
with self._lock:
|
|
return self._m.get(k)
|
|
|
|
def remove(self, k):
|
|
with self._lock:
|
|
return self._m.remove(k)
|
|
|
|
def __getitem__(self, k):
|
|
with self._lock:
|
|
return self._m[k]
|
|
|
|
def __setitem__(self, k, v):
|
|
with self._lock:
|
|
self._m[k] = v
|
|
|
|
def __contains__(self, k):
|
|
with self._lock:
|
|
return k in self._m
|
|
|
|
def __len__(self):
|
|
with self._lock:
|
|
return len(self._m)
|
|
|
|
def keys(self):
|
|
with self._lock:
|
|
return list(self._m.keys())
|
|
|
|
return _SyncMap(data)
|
|
|
|
@staticmethod
|
|
def enumaration(c) -> Enumeration:
|
|
return Enumeration(c)
|
|
|
|
@staticmethod
|
|
def list(enum: Enumeration) -> ArrayList:
|
|
items = []
|
|
while enum.hasMoreElements():
|
|
items.append(enum.nextElement())
|
|
return ArrayList(items)
|
|
|
|
@staticmethod
|
|
def newSetFromMap(m) -> HashSet:
|
|
class _SetFromMap(HashSet):
|
|
def __init__(self, backing_map):
|
|
super().__init__()
|
|
self._backing = backing_map
|
|
|
|
def add(self, obj):
|
|
return self._backing.put(obj, True) is None
|
|
|
|
def iterator(self):
|
|
return Iterator(self._backing.keySet())
|
|
|
|
def size(self):
|
|
return self._backing.size()
|
|
|
|
def remove(self, obj):
|
|
return self._backing.remove(obj) is not None
|
|
|
|
def contains(self, obj):
|
|
return self._backing.containsKey(obj)
|
|
|
|
def clear(self):
|
|
self._backing.clear()
|
|
|
|
return _SetFromMap(m)
|
|
|
|
@staticmethod
|
|
def asLifoQueue(deque) -> 'Collections._LifoQueue':
|
|
class _LifoQueue:
|
|
def __init__(self, dq):
|
|
self._dq = dq
|
|
|
|
def add(self, e):
|
|
self._dq.addFirst(e)
|
|
return True
|
|
|
|
def offer(self, e):
|
|
return self.add(e)
|
|
|
|
def remove(self):
|
|
if len(self._dq) == 0:
|
|
raise NoSuchElementException()
|
|
return self._dq.removeFirst()
|
|
|
|
def poll(self):
|
|
return self._dq.pollFirst()
|
|
|
|
def element(self):
|
|
if len(self._dq) == 0:
|
|
raise NoSuchElementException()
|
|
return self._dq.getFirst()
|
|
|
|
def peek(self):
|
|
return self._dq.peekFirst()
|
|
|
|
def size(self):
|
|
return self._dq.size()
|
|
|
|
def isEmpty(self):
|
|
return self._dq.isEmpty()
|
|
|
|
return _LifoQueue(deque)
|
|
|
|
|
|
# ============================================================
|
|
# Arrays
|
|
# ============================================================
|
|
|
|
class Arrays:
|
|
"""java.util.Arrays — static utility methods"""
|
|
@staticmethod
|
|
def asList(*args) -> ArrayList:
|
|
if len(args) == 1 and isinstance(args[0], (list, tuple)):
|
|
return ArrayList(args[0])
|
|
return ArrayList(list(args))
|
|
|
|
@staticmethod
|
|
def sort(a, from_index: int = 0, to_index: int = None) -> None:
|
|
if to_index is None:
|
|
a.sort()
|
|
else:
|
|
a[from_index:to_index] = sorted(a[from_index:to_index])
|
|
|
|
@staticmethod
|
|
def parallelSort(a, from_index: int = 0, to_index: int = None) -> None:
|
|
Arrays.sort(a, from_index, to_index)
|
|
|
|
@staticmethod
|
|
def binarySearch(a, key, from_index: int = 0, to_index: int = None) -> int:
|
|
if to_index is None:
|
|
lst = a
|
|
else:
|
|
lst = a[from_index:to_index]
|
|
idx = _bisect.bisect_left(lst, key)
|
|
if idx < len(lst) and lst[idx] == key:
|
|
return idx + from_index
|
|
return -(idx + from_index + 1)
|
|
|
|
@staticmethod
|
|
def fill(a, val, from_index: int = 0, to_index: int = None) -> None:
|
|
if to_index is None:
|
|
for i in range(len(a)):
|
|
a[i] = val
|
|
else:
|
|
for i in range(from_index, to_index):
|
|
a[i] = val
|
|
|
|
@staticmethod
|
|
def copyOf(original, new_length: int) -> list:
|
|
return list(original[:new_length]) + [None] * max(0, new_length - len(original))
|
|
|
|
@staticmethod
|
|
def copyOfRange(original, from_index: int, to_index: int) -> list:
|
|
return list(original[from_index:to_index])
|
|
|
|
@staticmethod
|
|
def equals(a, b) -> bool:
|
|
if len(a) != len(b):
|
|
return False
|
|
return all(x == y for x, y in zip(a, b))
|
|
|
|
@staticmethod
|
|
def deepEquals(a, b) -> bool:
|
|
if len(a) != len(b):
|
|
return False
|
|
return all(_deep_eq(x, y) for x, y in zip(a, b))
|
|
|
|
@staticmethod
|
|
def hashCode(a) -> int:
|
|
if a is None:
|
|
return 0
|
|
h = 1
|
|
for v in a:
|
|
h = (31 * h + hash(v)) & 0x7FFFFFFF
|
|
return h
|
|
|
|
@staticmethod
|
|
def deepHashCode(a) -> int:
|
|
if a is None:
|
|
return 0
|
|
h = 1
|
|
for v in a:
|
|
if isinstance(v, (list, tuple)):
|
|
h = (31 * h + Arrays.deepHashCode(v)) & 0x7FFFFFFF
|
|
else:
|
|
h = (31 * h + hash(v)) & 0x7FFFFFFF
|
|
return h
|
|
|
|
@staticmethod
|
|
def toString(a) -> str:
|
|
if a is None:
|
|
return "null"
|
|
return "[" + ", ".join(str(x) for x in a) + "]"
|
|
|
|
@staticmethod
|
|
def deepToString(a) -> str:
|
|
if a is None:
|
|
return "null"
|
|
return "[" + ", ".join(_deep_str(x) for x in a) + "]"
|
|
|
|
@staticmethod
|
|
def stream(a) -> 'Arrays._ArrayStream':
|
|
class _ArrayStream:
|
|
def __init__(self, arr):
|
|
self._arr = arr
|
|
self._idx = 0
|
|
|
|
def filter(self, pred):
|
|
return _ArrayStream([x for x in self._arr if pred(x)])
|
|
|
|
def map(self, func):
|
|
return _ArrayStream([func(x) for x in self._arr])
|
|
|
|
def forEach(self, action):
|
|
for x in self._arr:
|
|
action(x)
|
|
|
|
def toArray(self):
|
|
return list(self._arr)
|
|
|
|
def count(self):
|
|
return len(self._arr)
|
|
|
|
return _ArrayStream(a)
|
|
|
|
@staticmethod
|
|
def setAll(a, generator) -> None:
|
|
for i in range(len(a)):
|
|
a[i] = generator(i)
|
|
|
|
@staticmethod
|
|
def parallelSetAll(a, generator) -> None:
|
|
Arrays.setAll(a, generator)
|
|
|
|
@staticmethod
|
|
def spliterator(a) -> Iterator:
|
|
return Iterator(a)
|
|
|
|
|
|
def _deep_eq(x, y) -> bool:
|
|
if isinstance(x, (list, tuple)) and isinstance(y, (list, tuple)):
|
|
return Arrays.deepEquals(x, y)
|
|
return x == y
|
|
|
|
|
|
def _deep_str(x) -> str:
|
|
if isinstance(x, (list, tuple)):
|
|
return Arrays.deepToString(x)
|
|
return str(x)
|
|
|
|
|
|
# ============================================================
|
|
# Date
|
|
# ============================================================
|
|
|
|
class Date(Stub):
|
|
"""java.util.Date — wraps Python datetime"""
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
if len(args) == 0:
|
|
self._dt = _datetime.datetime.now()
|
|
elif len(args) == 1:
|
|
if isinstance(args[0], (int, float)):
|
|
self._dt = _datetime.datetime.fromtimestamp(args[0] / 1000.0)
|
|
elif isinstance(args[0], str):
|
|
self._dt = _datetime.datetime.fromisoformat(args[0])
|
|
elif isinstance(args[0], _datetime.datetime):
|
|
self._dt = args[0]
|
|
elif isinstance(args[0], Date):
|
|
self._dt = args[0]._dt
|
|
else:
|
|
self._dt = _datetime.datetime.now()
|
|
elif len(args) >= 6:
|
|
year = args[0] - 1900
|
|
month = args[1]
|
|
day = args[2]
|
|
hour = args[3] if len(args) > 3 else 0
|
|
minute = args[4] if len(args) > 4 else 0
|
|
second = args[5] if len(args) > 5 else 0
|
|
self._dt = _datetime.datetime(year + 1900, month + 1, day, hour, minute, second)
|
|
else:
|
|
self._dt = _datetime.datetime.now()
|
|
|
|
def getTime(self) -> int:
|
|
return int(self._dt.timestamp() * 1000)
|
|
|
|
def setTime(self, time_millis: int) -> None:
|
|
self._dt = _datetime.datetime.fromtimestamp(time_millis / 1000.0)
|
|
|
|
def before(self, when: 'Date') -> bool:
|
|
return self._dt < when._dt
|
|
|
|
def after(self, when: 'Date') -> bool:
|
|
return self._dt > when._dt
|
|
|
|
def compareTo(self, another: 'Date') -> int:
|
|
return (self._dt > another._dt) - (self._dt < another._dt)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, Date):
|
|
return self._dt == other._dt
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._dt) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return self._dt.strftime("%a %b %d %H:%M:%S %z %Y")
|
|
|
|
def clone(self) -> 'Date':
|
|
return Date(self._dt)
|
|
|
|
def getYear(self) -> int:
|
|
return self._dt.year - 1900
|
|
|
|
def getMonth(self) -> int:
|
|
return self._dt.month - 1
|
|
|
|
def getDay(self) -> int:
|
|
return self._dt.weekday()
|
|
|
|
def getHours(self) -> int:
|
|
return self._dt.hour
|
|
|
|
def getMinutes(self) -> int:
|
|
return self._dt.minute
|
|
|
|
def getSeconds(self) -> int:
|
|
return self._dt.second
|
|
|
|
def getDate(self) -> int:
|
|
return self._dt.day
|
|
|
|
def setYear(self, year: int) -> None:
|
|
self._dt = self._dt.replace(year=year + 1900)
|
|
|
|
def setMonth(self, month: int) -> None:
|
|
self._dt = self._dt.replace(month=month + 1)
|
|
|
|
def setDate(self, date: int) -> None:
|
|
self._dt = self._dt.replace(day=date)
|
|
|
|
def setHours(self, hours: int) -> None:
|
|
self._dt = self._dt.replace(hour=hours)
|
|
|
|
def setMinutes(self, minutes: int) -> None:
|
|
self._dt = self._dt.replace(minute=minutes)
|
|
|
|
def setSeconds(self, seconds: int) -> None:
|
|
self._dt = self._dt.replace(second=seconds)
|
|
|
|
def toGMTString(self) -> str:
|
|
return self._dt.strftime("%d %b %Y %H:%M:%S GMT")
|
|
|
|
def toLocaleString(self) -> str:
|
|
return self._dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
@staticmethod
|
|
def parse(s: str) -> 'Date':
|
|
try:
|
|
return Date(_datetime.datetime.fromisoformat(s))
|
|
except ValueError:
|
|
return Date()
|
|
|
|
@staticmethod
|
|
def fromInstant(instant) -> 'Date':
|
|
return Date(instant.toEpochMilli())
|
|
|
|
def toInstant(self):
|
|
return self._dt
|
|
|
|
def __str__(self):
|
|
return self.toString()
|
|
|
|
def __repr__(self):
|
|
return f"Date({self.getTime()})"
|
|
|
|
|
|
# ============================================================
|
|
# Calendar / GregorianCalendar
|
|
# ============================================================
|
|
|
|
class Calendar(Stub):
|
|
"""java.util.Calendar — wraps Python datetime with Java field system"""
|
|
# Field constants
|
|
YEAR = 1
|
|
MONTH = 2
|
|
DAY_OF_MONTH = 5
|
|
DAY_OF_YEAR = 6
|
|
DAY_OF_WEEK = 7
|
|
DAY_OF_WEEK_IN_MONTH = 8
|
|
AM_PM = 9
|
|
HOUR = 10
|
|
HOUR_OF_DAY = 11
|
|
MINUTE = 12
|
|
SECOND = 13
|
|
MILLISECOND = 14
|
|
WEEK_OF_MONTH = 15
|
|
WEEK_OF_YEAR = 16
|
|
ERA = 17
|
|
ZONE_OFFSET = 18
|
|
DST_OFFSET = 19
|
|
FIELD_COUNT = 20
|
|
|
|
# Day of week constants
|
|
SUNDAY = 1
|
|
MONDAY = 2
|
|
TUESDAY = 3
|
|
WEDNESDAY = 4
|
|
THURSDAY = 5
|
|
FRIDAY = 6
|
|
SATURDAY = 7
|
|
|
|
# Month constants
|
|
JANUARY = 0
|
|
FEBRUARY = 1
|
|
MARCH = 2
|
|
APRIL = 3
|
|
MAY = 4
|
|
JUNE = 5
|
|
JULY = 6
|
|
AUGUST = 7
|
|
SEPTEMBER = 8
|
|
OCTOBER = 9
|
|
NOVEMBER = 10
|
|
DECEMBER = 11
|
|
UNDECIMBER = 12
|
|
|
|
# AM/PM
|
|
AM = 0
|
|
PM = 1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._dt = _datetime.datetime.now()
|
|
self._fields: Dict[int, int] = {}
|
|
self._is_set: Dict[int, bool] = {}
|
|
self._lenient = True
|
|
self._first_day_of_week = Calendar.SUNDAY
|
|
self._minimal_days_in_first_week = 1
|
|
self._timezone = _datetime.timezone.utc
|
|
self._set_fields_from_dt()
|
|
|
|
def _set_fields_from_dt(self) -> None:
|
|
self._fields[Calendar.YEAR] = self._dt.year
|
|
self._fields[Calendar.MONTH] = self._dt.month - 1
|
|
self._fields[Calendar.DAY_OF_MONTH] = self._dt.day
|
|
self._fields[Calendar.DAY_OF_YEAR] = self._dt.timetuple().tm_yday
|
|
self._fields[Calendar.DAY_OF_WEEK] = self._dt.isoweekday() % 7 + 1
|
|
self._fields[Calendar.HOUR] = self._dt.hour % 12
|
|
self._fields[Calendar.HOUR_OF_DAY] = self._dt.hour
|
|
self._fields[Calendar.MINUTE] = self._dt.minute
|
|
self._fields[Calendar.SECOND] = self._dt.second
|
|
self._fields[Calendar.MILLISECOND] = self._dt.microsecond // 1000
|
|
self._fields[Calendar.AM_PM] = Calendar.AM if self._dt.hour < 12 else Calendar.PM
|
|
self._fields[Calendar.WEEK_OF_YEAR] = self._dt.isocalendar()[1]
|
|
self._fields[Calendar.ERA] = 1 # AD = 1
|
|
|
|
def get(self, field: int) -> int:
|
|
return self._fields.get(field, 0)
|
|
|
|
def set(self, field: int, value: int) -> None:
|
|
self._fields[field] = value
|
|
self._is_set[field] = True
|
|
|
|
def setTime(self, date: 'Date') -> None:
|
|
self._dt = date._dt
|
|
self._set_fields_from_dt()
|
|
|
|
def getTime(self) -> Date:
|
|
return Date(self._dt)
|
|
|
|
def getTimeInMillis(self) -> int:
|
|
return int(self._dt.timestamp() * 1000)
|
|
|
|
def setTimeInMillis(self, millis: int) -> None:
|
|
self._dt = _datetime.datetime.fromtimestamp(millis / 1000.0)
|
|
self._set_fields_from_dt()
|
|
|
|
def add(self, field: int, amount: int) -> None:
|
|
if field == Calendar.YEAR:
|
|
self._dt = self._dt.replace(year=self._dt.year + amount)
|
|
elif field == Calendar.MONTH:
|
|
new_month = self._dt.month - 1 + amount
|
|
new_year = self._dt.year + new_month // 12
|
|
new_month = new_month % 12
|
|
try:
|
|
self._dt = self._dt.replace(year=new_year, month=new_month + 1)
|
|
except ValueError:
|
|
self._dt = self._dt.replace(year=new_year, month=new_month + 1, day=28)
|
|
elif field == Calendar.DAY_OF_MONTH:
|
|
self._dt += _datetime.timedelta(days=amount)
|
|
elif field == Calendar.HOUR_OF_DAY or field == Calendar.HOUR:
|
|
self._dt += _datetime.timedelta(hours=amount)
|
|
elif field == Calendar.MINUTE:
|
|
self._dt += _datetime.timedelta(minutes=amount)
|
|
elif field == Calendar.SECOND:
|
|
self._dt += _datetime.timedelta(seconds=amount)
|
|
elif field == Calendar.MILLISECOND:
|
|
self._dt += _datetime.timedelta(milliseconds=amount)
|
|
elif field == Calendar.WEEK_OF_YEAR or field == Calendar.WEEK_OF_MONTH:
|
|
self._dt += _datetime.timedelta(weeks=amount)
|
|
self._set_fields_from_dt()
|
|
|
|
def roll(self, field: int, amount: int) -> None:
|
|
if field == Calendar.MONTH:
|
|
old_month = self._dt.month
|
|
new_month = ((old_month - 1 + amount) % 12) + 1
|
|
try:
|
|
self._dt = self._dt.replace(month=new_month)
|
|
except ValueError:
|
|
self._dt = self._dt.replace(month=new_month, day=28)
|
|
elif field == Calendar.DAY_OF_MONTH:
|
|
import calendar as _cal
|
|
max_day = _cal.monthrange(self._dt.year, self._dt.month)[1]
|
|
new_day = ((self._dt.day - 1 + amount) % max_day) + 1
|
|
self._dt = self._dt.replace(day=new_day)
|
|
else:
|
|
self.add(field, amount)
|
|
self._set_fields_from_dt()
|
|
|
|
def before(self, when: 'Calendar') -> bool:
|
|
return self._dt < when._dt
|
|
|
|
def after(self, when: 'Calendar') -> bool:
|
|
return self._dt > when._dt
|
|
|
|
def compareTo(self, another: 'Calendar') -> int:
|
|
return (self._dt > another._dt) - (self._dt < another._dt)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, Calendar):
|
|
return self._dt == other._dt
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._dt) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return str(self._dt)
|
|
|
|
def isLenient(self) -> bool:
|
|
return self._lenient
|
|
|
|
def setLenient(self, lenient: bool) -> None:
|
|
self._lenient = lenient
|
|
|
|
def getFirstDayOfWeek(self) -> int:
|
|
return self._first_day_of_week
|
|
|
|
def setFirstDayOfWeek(self, value: int) -> None:
|
|
self._first_day_of_week = value
|
|
|
|
def getMinimalDaysInFirstWeek(self) -> int:
|
|
return self._minimal_days_in_first_week
|
|
|
|
def setMinimalDaysInFirstWeek(self, value: int) -> None:
|
|
self._minimal_days_in_first_week = value
|
|
|
|
def getActualMaximum(self, field: int) -> int:
|
|
import calendar as _cal
|
|
if field == Calendar.DAY_OF_MONTH:
|
|
return _cal.monthrange(self._dt.year, self._dt.month)[1]
|
|
if field == Calendar.DAY_OF_YEAR:
|
|
return 366 if self._dt.year % 4 == 0 and (self._dt.year % 100 != 0 or self._dt.year % 400 == 0) else 365
|
|
if field == Calendar.MONTH:
|
|
return 11
|
|
if field == Calendar.HOUR:
|
|
return 11
|
|
if field == Calendar.HOUR_OF_DAY:
|
|
return 23
|
|
if field == Calendar.MINUTE:
|
|
return 59
|
|
if field == Calendar.SECOND:
|
|
return 59
|
|
if field == Calendar.MILLISECOND:
|
|
return 999
|
|
return 0
|
|
|
|
def getActualMinimum(self, field: int) -> int:
|
|
return 0
|
|
|
|
def getDisplayName(self, field: int, style: int, locale: 'Locale') -> str:
|
|
return str(self.get(field))
|
|
|
|
@staticmethod
|
|
def getInstance() -> 'GregorianCalendar':
|
|
return GregorianCalendar()
|
|
|
|
|
|
class GregorianCalendar(Calendar):
|
|
"""java.util.GregorianCalendar"""
|
|
BC = 0
|
|
AD = 1
|
|
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
if len(args) == 0:
|
|
self._dt = _datetime.datetime.now()
|
|
elif len(args) == 3:
|
|
year, month, day = args
|
|
self._dt = _datetime.datetime(year, month + 1, day)
|
|
elif len(args) >= 5:
|
|
year, month, day, hour, minute = args[:5]
|
|
second = args[5] if len(args) > 5 else 0
|
|
self._dt = _datetime.datetime(year, month + 1, day, hour, minute, second)
|
|
else:
|
|
self._dt = _datetime.datetime.now()
|
|
self._set_fields_from_dt()
|
|
|
|
def isLeapYear(self, year: int) -> bool:
|
|
return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0
|
|
|
|
def getGreatestMinimum(self, field: int) -> int:
|
|
return self.getActualMinimum(field)
|
|
|
|
def getLeastMaximum(self, field: int) -> int:
|
|
return self.getActualMaximum(field)
|
|
|
|
def getMaximum(self, field: int) -> int:
|
|
if field == Calendar.DAY_OF_MONTH:
|
|
return 31
|
|
if field == Calendar.DAY_OF_YEAR:
|
|
return 366
|
|
if field == Calendar.MONTH:
|
|
return 11
|
|
return self.getActualMaximum(field)
|
|
|
|
def getMinimum(self, field: int) -> int:
|
|
return 0
|
|
|
|
|
|
# ============================================================
|
|
# UUID
|
|
# ============================================================
|
|
|
|
class UUID(Stub):
|
|
"""java.util.UUID — wraps Python uuid module"""
|
|
def __init__(self, most_sig_bits: int, least_sig_bits: int):
|
|
super().__init__()
|
|
self._most_sig_bits = most_sig_bits & 0xFFFFFFFFFFFFFFFF
|
|
self._least_sig_bits = least_sig_bits & 0xFFFFFFFFFFFFFFFF
|
|
# Rebuild a Python UUID from the bits for string representation
|
|
hex_str = f"{self._most_sig_bits:016x}{self._least_sig_bits:016x}"
|
|
self._uuid = _uuid.UUID(hex_str)
|
|
|
|
@staticmethod
|
|
def randomUUID() -> 'UUID':
|
|
u = _uuid.uuid4()
|
|
most = u.int >> 64
|
|
least = u.int & 0xFFFFFFFFFFFFFFFF
|
|
return UUID(most, least)
|
|
|
|
@staticmethod
|
|
def nameUUIDFromBytes(name: bytes) -> 'UUID':
|
|
u = _uuid.uuid3(_uuid.NAMESPACE_DNS, name.decode('utf-8', errors='replace'))
|
|
most = u.int >> 64
|
|
least = u.int & 0xFFFFFFFFFFFFFFFF
|
|
return UUID(most, least)
|
|
|
|
@staticmethod
|
|
def fromString(name: str) -> 'UUID':
|
|
u = _uuid.UUID(name)
|
|
most = u.int >> 64
|
|
least = u.int & 0xFFFFFFFFFFFFFFFF
|
|
return UUID(most, least)
|
|
|
|
def getMostSignificantBits(self) -> int:
|
|
return self._most_sig_bits
|
|
|
|
def getLeastSignificantBits(self) -> int:
|
|
return self._least_sig_bits
|
|
|
|
def version(self) -> int:
|
|
return self._uuid.version
|
|
|
|
def variant(self) -> int:
|
|
return self._uuid.variant
|
|
|
|
def timestamp(self) -> int:
|
|
if self.version() != 1:
|
|
raise UnsupportedOperationException("Not a time-based UUID")
|
|
return self._uuid.time
|
|
|
|
def clockSequence(self) -> int:
|
|
if self.version() != 1:
|
|
raise UnsupportedOperationException("Not a time-based UUID")
|
|
return self._uuid.clock_seq
|
|
|
|
def node(self) -> int:
|
|
if self.version() != 1:
|
|
raise UnsupportedOperationException("Not a time-based UUID")
|
|
return self._uuid.node
|
|
|
|
def compareTo(self, other: 'UUID') -> int:
|
|
return (self._uuid.int > other._uuid.int) - (self._uuid.int < other._uuid.int)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, UUID):
|
|
return self._uuid == other._uuid
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._uuid) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return str(self._uuid)
|
|
|
|
def __str__(self):
|
|
return str(self._uuid)
|
|
|
|
def __repr__(self):
|
|
return f"UUID({str(self._uuid)})"
|
|
|
|
|
|
# ============================================================
|
|
# StringTokenizer
|
|
# ============================================================
|
|
|
|
class StringTokenizer(Stub):
|
|
"""java.util.StringTokenizer — simple tokenizer"""
|
|
def __init__(self, s: str, delim: str = " \t\n\r\f", return_delims: bool = False):
|
|
super().__init__()
|
|
self._str = str(s)
|
|
self._delim = delim
|
|
self._return_delims = return_delims
|
|
self._tokens = self._tokenize()
|
|
self._index = 0
|
|
|
|
def _tokenize(self):
|
|
if self._return_delims:
|
|
tokens = []
|
|
buf = ""
|
|
for ch in self._str:
|
|
if ch in self._delim:
|
|
if buf:
|
|
tokens.append(buf)
|
|
buf = ""
|
|
tokens.append(ch)
|
|
else:
|
|
buf += ch
|
|
if buf:
|
|
tokens.append(buf)
|
|
return tokens
|
|
import re as _re
|
|
pattern = f"[{_re.escape(self._delim)}]+"
|
|
return [t for t in _re.split(pattern, self._str) if t]
|
|
|
|
def countTokens(self) -> int:
|
|
return len(self._tokens)
|
|
|
|
def hasMoreTokens(self) -> bool:
|
|
return self._index < len(self._tokens)
|
|
|
|
def nextToken(self, delim: str = None) -> str:
|
|
if delim is not None:
|
|
self._delim = delim
|
|
remaining = self._str[self._index:]
|
|
st = StringTokenizer(remaining, delim, self._return_delims)
|
|
self._tokens = st._tokens
|
|
self._index = 0
|
|
if not self.hasMoreTokens():
|
|
raise NoSuchElementException()
|
|
tok = self._tokens[self._index]
|
|
self._index += 1
|
|
return tok
|
|
|
|
def hasMoreElements(self) -> bool:
|
|
return self.hasMoreTokens()
|
|
|
|
def nextElement(self):
|
|
return self.nextToken()
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
if not self.hasMoreTokens():
|
|
raise StopIteration
|
|
return self.nextToken()
|
|
|
|
|
|
# ============================================================
|
|
# Optional
|
|
# ============================================================
|
|
|
|
class Optional(Stub):
|
|
"""java.util.Optional — wraps value or None"""
|
|
def __init__(self, value=None):
|
|
super().__init__()
|
|
self._value = value
|
|
|
|
@staticmethod
|
|
def empty() -> 'Optional':
|
|
return Optional(None)
|
|
|
|
@staticmethod
|
|
def of(value) -> 'Optional':
|
|
if value is None:
|
|
raise TypeError("value is null")
|
|
return Optional(value)
|
|
|
|
@staticmethod
|
|
def ofNullable(value) -> 'Optional':
|
|
return Optional(value)
|
|
|
|
def get(self):
|
|
if self._value is None:
|
|
raise NoSuchElementException("No value present")
|
|
return self._value
|
|
|
|
def isPresent(self) -> bool:
|
|
return self._value is not None
|
|
|
|
def isEmpty(self) -> bool:
|
|
return self._value is None
|
|
|
|
def ifPresent(self, consumer) -> None:
|
|
if self._value is not None:
|
|
consumer(self._value)
|
|
|
|
def ifPresentOrElse(self, action, empty_action) -> None:
|
|
if self._value is not None:
|
|
action(self._value)
|
|
else:
|
|
empty_action()
|
|
|
|
def filter(self, predicate) -> 'Optional':
|
|
if self._value is None:
|
|
return self
|
|
return Optional.ofNullable(self._value) if predicate(self._value) else Optional.empty()
|
|
|
|
def map(self, mapper) -> 'Optional':
|
|
if self._value is None:
|
|
return Optional.empty()
|
|
return Optional.ofNullable(mapper(self._value))
|
|
|
|
def flatMap(self, mapper) -> 'Optional':
|
|
if self._value is None:
|
|
return Optional.empty()
|
|
return mapper(self._value)
|
|
|
|
def orElse(self, other):
|
|
return self._value if self._value is not None else other
|
|
|
|
def orElseGet(self, supplier):
|
|
if self._value is not None:
|
|
return self._value
|
|
return supplier()
|
|
|
|
def orElseThrow(self, exception_supplier=None):
|
|
if self._value is not None:
|
|
return self._value
|
|
if exception_supplier:
|
|
raise exception_supplier()
|
|
raise NoSuchElementException("No value present")
|
|
|
|
def stream(self):
|
|
if self._value is not None:
|
|
return [self._value]
|
|
return []
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, Optional):
|
|
return self._value == other._value
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._value) & 0x7FFFFFFF if self._value is not None else 0
|
|
|
|
def toString(self) -> str:
|
|
if self._value is None:
|
|
return "Optional.empty"
|
|
return f"Optional[{self._value}]"
|
|
|
|
def __str__(self):
|
|
return self.toString()
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __bool__(self):
|
|
return self._value is not None
|
|
|
|
|
|
# ============================================================
|
|
# Random
|
|
# ============================================================
|
|
|
|
class Random(Stub):
|
|
"""java.util.Random — wraps Python random module"""
|
|
def __init__(self, seed: _typing.Optional[int] = None):
|
|
super().__init__()
|
|
self._rng = _py_random.Random(seed)
|
|
|
|
def setSeed(self, seed: int) -> None:
|
|
self._rng.seed(seed)
|
|
|
|
def nextInt(self, bound: _typing.Optional[int] = None) -> int:
|
|
if bound is None:
|
|
return self._rng.randint(-2147483648, 2147483647)
|
|
if bound <= 0:
|
|
raise IllegalArgumentException("bound must be positive")
|
|
return self._rng.randrange(0, bound)
|
|
|
|
def nextLong(self) -> int:
|
|
return (self._rng.randint(-2147483648, 2147483647) << 32) | self._rng.randint(0, 0xFFFFFFFF)
|
|
|
|
def nextBoolean(self) -> bool:
|
|
return self._rng.random() < 0.5
|
|
|
|
def nextFloat(self) -> float:
|
|
return self._rng.random()
|
|
|
|
def nextDouble(self) -> float:
|
|
return self._rng.random()
|
|
|
|
def nextGaussian(self) -> float:
|
|
return self._rng.gauss(0.0, 1.0)
|
|
|
|
def nextBytes(self, bytes_arr: bytearray) -> None:
|
|
for i in range(len(bytes_arr)):
|
|
bytes_arr[i] = self._rng.randint(0, 255)
|
|
|
|
def next(self, bits: int) -> int:
|
|
return self._rng.getrandbits(bits) & ((1 << bits) - 1)
|
|
|
|
@staticmethod
|
|
def create() -> 'Random':
|
|
return Random()
|
|
|
|
|
|
# ============================================================
|
|
# Properties
|
|
# ============================================================
|
|
|
|
class Properties(HashMap):
|
|
"""java.util.Properties — extends Hashtable-like dict with load/store"""
|
|
def __init__(self, defaults: 'Properties' = None):
|
|
super().__init__()
|
|
self._defaults = defaults
|
|
|
|
def setProperty(self, key: str, value: str) -> None:
|
|
self.put(str(key), str(value))
|
|
|
|
def getProperty(self, key: str, default_value: str = None) -> _typing.Optional[str]:
|
|
val = self._data.get(str(key))
|
|
if val is not None:
|
|
return str(val)
|
|
if self._defaults is not None:
|
|
return self._defaults.getProperty(key, default_value)
|
|
return default_value
|
|
|
|
def load(self, in_stream) -> None:
|
|
if isinstance(in_stream, (str, bytes)):
|
|
lines = in_stream.splitlines() if isinstance(in_stream, str) else in_stream.decode('utf-8').splitlines()
|
|
else:
|
|
lines = in_stream.read().splitlines() if hasattr(in_stream, 'read') else in_stream.splitlines()
|
|
for line in lines:
|
|
line = line.strip()
|
|
if not line or line.startswith('#') or line.startswith('!'):
|
|
continue
|
|
if '=' in line:
|
|
key, val = line.split('=', 1)
|
|
elif ':' in line:
|
|
key, val = line.split(':', 1)
|
|
else:
|
|
key = line
|
|
val = ""
|
|
key = key.strip()
|
|
val = val.strip()
|
|
self._data[key] = val
|
|
|
|
def store(self, out_stream, comments: str = None) -> None:
|
|
lines = []
|
|
if comments:
|
|
lines.append(f"#{comments}")
|
|
lines.append(f"# {_datetime.datetime.now().isoformat()}")
|
|
for k, v in self._data.items():
|
|
escaped_val = str(v).replace("\\", "\\\\").replace(" ", "\\ ")
|
|
lines.append(f"{k}={escaped_val}")
|
|
content = "\n".join(lines) + "\n"
|
|
if isinstance(out_stream, _io.IOBase):
|
|
out_stream.write(content)
|
|
else:
|
|
out_stream.write(content) # type: ignore
|
|
|
|
def list(self, out_stream) -> None:
|
|
for k, v in self._data.items():
|
|
out_stream.write(f"{k}={v}\n")
|
|
|
|
def propertyNames(self):
|
|
return Iterator(self._data.keys())
|
|
|
|
def stringPropertyNames(self) -> set:
|
|
names = set()
|
|
for k in self._data.keys():
|
|
if isinstance(k, str):
|
|
names.add(k)
|
|
if self._defaults:
|
|
names.update(self._defaults.stringPropertyNames())
|
|
return names
|
|
|
|
def loadFromXML(self, in_stream) -> None:
|
|
import xml.etree.ElementTree as _ET
|
|
tree = _ET.parse(in_stream)
|
|
root = tree.getroot()
|
|
for entry in root.findall('entry'):
|
|
key = entry.get('key', '')
|
|
self._data[key] = entry.text or ''
|
|
|
|
def storeToXML(self, out_stream, comment: str = None) -> None:
|
|
import xml.etree.ElementTree as _ET
|
|
root = _ET.Element('properties')
|
|
if comment:
|
|
c = _ET.SubElement(root, 'comment')
|
|
c.text = comment
|
|
for k, v in self._data.items():
|
|
entry = _ET.SubElement(root, 'entry')
|
|
entry.set('key', str(k))
|
|
entry.text = str(v)
|
|
tree = _ET.ElementTree(root)
|
|
tree.write(out_stream, xml_declaration=True, encoding='UTF-8')
|
|
|
|
def get(self, key):
|
|
val = self._data.get(key)
|
|
if val is not None:
|
|
return val
|
|
return self._defaults.get(key) if self._defaults else None
|
|
|
|
def put(self, key, value):
|
|
return super().put(str(key), str(value))
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(f"{k}={v}" for k, v in self._data.items())
|
|
return f"{{{items}}}"
|
|
|
|
|
|
# ============================================================
|
|
# EnumSet
|
|
# ============================================================
|
|
|
|
class EnumSet(HashSet):
|
|
"""java.util.EnumSet — simple implementation using set"""
|
|
def __init__(self, c=None):
|
|
super().__init__()
|
|
self._element_type = None
|
|
if c is not None:
|
|
if isinstance(c, EnumSet):
|
|
self._data = set(c._data)
|
|
self._element_type = c._element_type
|
|
elif isinstance(c, type):
|
|
self._element_type = c
|
|
elif isinstance(c, _IterableABC):
|
|
for item in c:
|
|
self._data.add(item)
|
|
if self._element_type is None:
|
|
self._element_type = item.__class__
|
|
|
|
@staticmethod
|
|
def allOf(element_type) -> 'EnumSet':
|
|
es = EnumSet()
|
|
es._element_type = element_type
|
|
if hasattr(element_type, '_values'):
|
|
for v in element_type._values():
|
|
es._data.add(v)
|
|
elif hasattr(element_type, '__members__'):
|
|
for name, member in element_type.__members__.items():
|
|
es._data.add(member)
|
|
return es
|
|
|
|
@staticmethod
|
|
def noneOf(element_type) -> 'EnumSet':
|
|
es = EnumSet()
|
|
es._element_type = element_type
|
|
return es
|
|
|
|
@staticmethod
|
|
def range(element_type, from_val, to_val) -> 'EnumSet':
|
|
es = EnumSet()
|
|
es._element_type = element_type
|
|
started = False
|
|
if hasattr(element_type, '_values'):
|
|
for v in element_type._values():
|
|
if v == from_val:
|
|
started = True
|
|
if started:
|
|
es._data.add(v)
|
|
if v == to_val:
|
|
break
|
|
elif hasattr(element_type, '__members__'):
|
|
for name, member in element_type.__members__.items():
|
|
if member == from_val:
|
|
started = True
|
|
if started:
|
|
es._data.add(member)
|
|
if member == to_val:
|
|
break
|
|
return es
|
|
|
|
@staticmethod
|
|
def copyOf(es) -> 'EnumSet':
|
|
return EnumSet(es)
|
|
|
|
def complementOf(self, other: 'EnumSet') -> 'EnumSet':
|
|
result = EnumSet.noneOf(self._element_type)
|
|
if self._element_type and hasattr(self._element_type, '_values'):
|
|
for v in self._element_type._values():
|
|
if v not in other._data:
|
|
result._data.add(v)
|
|
return result
|
|
|
|
|
|
# ============================================================
|
|
# EnumMap
|
|
# ============================================================
|
|
|
|
class EnumMap(HashMap):
|
|
"""java.util.EnumMap — simple implementation using dict"""
|
|
def __init__(self, key_type=None, m=None):
|
|
super().__init__()
|
|
self._key_type = key_type
|
|
if m is not None:
|
|
if isinstance(m, EnumMap):
|
|
self._data.update(m._data)
|
|
self._key_type = m._key_type
|
|
elif isinstance(m, dict):
|
|
self._data.update(m)
|
|
else:
|
|
for k, v in m.items():
|
|
self._data[k] = v
|
|
|
|
def put(self, key, value):
|
|
if self._key_type is None:
|
|
self._key_type = key.__class__
|
|
return super().put(key, value)
|
|
|
|
def keySet(self):
|
|
return set(self._data.keys())
|
|
|
|
|
|
# ============================================================
|
|
# Objects
|
|
# ============================================================
|
|
|
|
class Objects:
|
|
"""java.util.Objects — static helper methods"""
|
|
@staticmethod
|
|
def equals(a, b) -> bool:
|
|
return a == b
|
|
|
|
@staticmethod
|
|
def deepEquals(a, b) -> bool:
|
|
return _deep_eq(a, b)
|
|
|
|
@staticmethod
|
|
def hashCode(o) -> int:
|
|
return 0 if o is None else (hash(o) & 0x7FFFFFFF)
|
|
|
|
@staticmethod
|
|
def hash(*values) -> int:
|
|
return Arrays.hashCode(values)
|
|
|
|
@staticmethod
|
|
def toString(o, null_default: str = "null") -> str:
|
|
if o is None:
|
|
return null_default
|
|
return str(o)
|
|
|
|
@staticmethod
|
|
def compare(a, b, cmp) -> int:
|
|
if cmp:
|
|
return cmp(a, b)
|
|
return (a > b) - (a < b)
|
|
|
|
@staticmethod
|
|
def requireNonNull(obj, message: Any = None):
|
|
if obj is None:
|
|
if message is None:
|
|
raise TypeError("null")
|
|
raise TypeError(str(message) if not callable(message) else message.get())
|
|
return obj
|
|
|
|
@staticmethod
|
|
def nonNull(obj) -> bool:
|
|
return obj is not None
|
|
|
|
@staticmethod
|
|
def isNull(obj) -> bool:
|
|
return obj is None
|
|
|
|
@staticmethod
|
|
def requireNonNullElse(obj, default):
|
|
return obj if obj is not None else default
|
|
|
|
@staticmethod
|
|
def requireNonNullElseGet(obj, supplier):
|
|
return obj if obj is not None else supplier()
|
|
|
|
@staticmethod
|
|
def checkIndex(index: int, length: int) -> int:
|
|
if index < 0 or index >= length:
|
|
raise IndexError(f"Index {index} out of bounds [0, {length})")
|
|
return index
|
|
|
|
@staticmethod
|
|
def checkFromToIndex(from_index: int, to_index: int, length: int) -> int:
|
|
if from_index < 0 or from_index > to_index or to_index > length:
|
|
raise IndexError(f"Range [{from_index}, {to_index}) out of bounds [0, {length})")
|
|
return from_index
|
|
|
|
@staticmethod
|
|
def checkFromIndexSize(from_index: int, size: int, length: int) -> int:
|
|
if from_index < 0 or size < 0 or from_index + size > length:
|
|
raise IndexError(f"Range [{from_index}, {from_index + size}) out of bounds [0, {length})")
|
|
return from_index
|
|
|
|
|
|
# ============================================================
|
|
# Locale
|
|
# ============================================================
|
|
|
|
class Locale(Stub):
|
|
"""java.util.Locale — basic locale representation"""
|
|
ENGLISH = None
|
|
FRENCH = None
|
|
GERMAN = None
|
|
ITALIAN = None
|
|
JAPANESE = None
|
|
KOREAN = None
|
|
CHINESE = None
|
|
SIMPLIFIED_CHINESE = None
|
|
TRADITIONAL_CHINESE = None
|
|
FRANCE = None
|
|
GERMANY = None
|
|
ITALY = None
|
|
JAPAN = None
|
|
KOREA = None
|
|
CHINA = None
|
|
UK = None
|
|
US = None
|
|
CANADA = None
|
|
CANADA_FRENCH = None
|
|
ROOT = None
|
|
|
|
def __init__(self, language: str = "", country: str = "", variant: str = ""):
|
|
super().__init__()
|
|
self._language = language
|
|
self._country = country
|
|
self._variant = variant
|
|
|
|
@staticmethod
|
|
def getDefault() -> 'Locale':
|
|
return Locale("en", "US")
|
|
|
|
@staticmethod
|
|
def setDefault(locale: 'Locale') -> None:
|
|
pass
|
|
|
|
def getLanguage(self) -> str:
|
|
return self._language
|
|
|
|
def getCountry(self) -> str:
|
|
return self._country
|
|
|
|
def getVariant(self) -> str:
|
|
return self._variant
|
|
|
|
def getDisplayLanguage(self, locale: 'Locale' = None) -> str:
|
|
return self._language
|
|
|
|
def getDisplayCountry(self, locale: 'Locale' = None) -> str:
|
|
return self._country
|
|
|
|
def getDisplayName(self, locale: 'Locale' = None) -> str:
|
|
parts = [self._language]
|
|
if self._country:
|
|
parts.append(self._country)
|
|
if self._variant:
|
|
parts.append(self._variant)
|
|
return "_".join(parts)
|
|
|
|
def toString(self) -> str:
|
|
return self.getDisplayName()
|
|
|
|
def toLanguageTag(self) -> str:
|
|
tag = self._language
|
|
if self._country:
|
|
tag += "-" + self._country
|
|
if self._variant:
|
|
tag += "-" + self._variant
|
|
return tag
|
|
|
|
@staticmethod
|
|
def forLanguageTag(tag: str) -> 'Locale':
|
|
parts = tag.replace("-", "_").split("_")
|
|
lang = parts[0] if len(parts) > 0 else ""
|
|
country = parts[1] if len(parts) > 1 else ""
|
|
variant = parts[2] if len(parts) > 2 else ""
|
|
return Locale(lang, country, variant)
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, Locale):
|
|
return (self._language == other._language and
|
|
self._country == other._country and
|
|
self._variant == other._variant)
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._language, self._country, self._variant)) & 0x7FFFFFFF
|
|
|
|
|
|
# Initialize Locale constants
|
|
Locale.ENGLISH = Locale("en")
|
|
Locale.FRENCH = Locale("fr")
|
|
Locale.GERMAN = Locale("de")
|
|
Locale.ITALIAN = Locale("it")
|
|
Locale.JAPANESE = Locale("ja")
|
|
Locale.KOREAN = Locale("ko")
|
|
Locale.CHINESE = Locale("zh")
|
|
Locale.SIMPLIFIED_CHINESE = Locale("zh", "CN")
|
|
Locale.TRADITIONAL_CHINESE = Locale("zh", "TW")
|
|
Locale.FRANCE = Locale("fr", "FR")
|
|
Locale.GERMANY = Locale("de", "DE")
|
|
Locale.ITALY = Locale("it", "IT")
|
|
Locale.JAPAN = Locale("ja", "JP")
|
|
Locale.KOREA = Locale("ko", "KR")
|
|
Locale.CHINA = Locale("zh", "CN")
|
|
Locale.UK = Locale("en", "GB")
|
|
Locale.US = Locale("en", "US")
|
|
Locale.CANADA = Locale("en", "CA")
|
|
Locale.CANADA_FRENCH = Locale("fr", "CA")
|
|
Locale.ROOT = Locale("", "")
|
|
|
|
|
|
# ============================================================
|
|
# BitSet
|
|
# ============================================================
|
|
|
|
class BitSet(Stub):
|
|
"""java.util.BitSet — bit manipulation"""
|
|
def __init__(self, nbits: int = 0):
|
|
super().__init__()
|
|
if isinstance(nbits, int):
|
|
self._bits = 0
|
|
elif isinstance(nbits, BitSet):
|
|
self._bits = nbits._bits
|
|
else:
|
|
self._bits = 0
|
|
|
|
def _check(self, bit_index: int) -> None:
|
|
if bit_index < 0:
|
|
raise IndexError(f"bitIndex < 0: {bit_index}")
|
|
|
|
def get(self, bit_index: int) -> bool:
|
|
self._check(bit_index)
|
|
return bool((self._bits >> bit_index) & 1)
|
|
|
|
def getRange(self, from_index: int, to_index: int) -> 'BitSet':
|
|
self._check(from_index)
|
|
mask = ((1 << (to_index - from_index)) - 1) << from_index
|
|
result = BitSet()
|
|
result._bits = (self._bits & mask) >> from_index
|
|
return result
|
|
|
|
def set(self, bit_index: int, value: bool = True) -> None:
|
|
self._check(bit_index)
|
|
if value:
|
|
self._bits |= (1 << bit_index)
|
|
else:
|
|
self._bits &= ~(1 << bit_index)
|
|
|
|
def setRange(self, from_index: int, to_index: int, value: bool = True) -> None:
|
|
self._check(from_index)
|
|
mask = ((1 << (to_index - from_index)) - 1) << from_index
|
|
if value:
|
|
self._bits |= mask
|
|
else:
|
|
self._bits &= ~mask
|
|
|
|
def clear(self, bit_index: int = None) -> None:
|
|
if bit_index is None:
|
|
self._bits = 0
|
|
else:
|
|
self._check(bit_index)
|
|
self._bits &= ~(1 << bit_index)
|
|
|
|
def clearRange(self, from_index: int, to_index: int) -> None:
|
|
self.setRange(from_index, to_index, False)
|
|
|
|
def flip(self, bit_index: int) -> None:
|
|
self._check(bit_index)
|
|
self._bits ^= (1 << bit_index)
|
|
|
|
def flipRange(self, from_index: int, to_index: int) -> None:
|
|
self._check(from_index)
|
|
mask = ((1 << (to_index - from_index)) - 1) << from_index
|
|
self._bits ^= mask
|
|
|
|
def cardinality(self) -> int:
|
|
return self._bits.bit_count()
|
|
|
|
def intersects(self, bs: 'BitSet') -> bool:
|
|
return (self._bits & bs._bits) != 0
|
|
|
|
def and_(self, bs: 'BitSet') -> None:
|
|
self._bits &= bs._bits
|
|
|
|
def or_(self, bs: 'BitSet') -> None:
|
|
self._bits |= bs._bits
|
|
|
|
def xor(self, bs: 'BitSet') -> None:
|
|
self._bits ^= bs._bits
|
|
|
|
def andNot(self, bs: 'BitSet') -> None:
|
|
self._bits &= ~bs._bits
|
|
|
|
def size(self) -> int:
|
|
return self._bits.bit_length() + (8 - (self._bits.bit_length() % 8)) % 8
|
|
|
|
def length(self) -> int:
|
|
return self._bits.bit_length()
|
|
|
|
def isEmpty(self) -> bool:
|
|
return self._bits == 0
|
|
|
|
def nextSetBit(self, from_index: int) -> int:
|
|
self._check(from_index)
|
|
bits = self._bits >> from_index
|
|
if bits == 0:
|
|
return -1
|
|
return (bits & -bits).bit_length() - 1 + from_index
|
|
|
|
def nextClearBit(self, from_index: int) -> int:
|
|
self._check(from_index)
|
|
bits = ~(self._bits >> from_index)
|
|
if bits == 0:
|
|
return -1
|
|
return (bits & -bits).bit_length() - 1 + from_index
|
|
|
|
def previousSetBit(self, from_index: int) -> int:
|
|
if from_index < 0:
|
|
return -1
|
|
mask = (1 << (from_index + 1)) - 1
|
|
bits = self._bits & mask
|
|
if bits == 0:
|
|
return -1
|
|
return bits.bit_length() - 1
|
|
|
|
def previousClearBit(self, from_index: int) -> int:
|
|
if from_index < 0:
|
|
return -1
|
|
mask = (1 << (from_index + 1)) - 1
|
|
bits = (~self._bits) & mask
|
|
if bits == 0:
|
|
return -1
|
|
return bits.bit_length() - 1
|
|
|
|
def toByteArray(self) -> bytearray:
|
|
if self._bits == 0:
|
|
return bytearray()
|
|
n_bytes = (self._bits.bit_length() + 7) // 8
|
|
return bytearray(self._bits.to_bytes(n_bytes, 'little'))
|
|
|
|
def toLongArray(self) -> list:
|
|
if self._bits == 0:
|
|
return []
|
|
n_longs = (self._bits.bit_length() + 63) // 64
|
|
result = []
|
|
for i in range(n_longs):
|
|
result.append((self._bits >> (i * 64)) & 0xFFFFFFFFFFFFFFFF)
|
|
return result
|
|
|
|
@staticmethod
|
|
def valueOf(bytes_or_longs) -> 'BitSet':
|
|
bs = BitSet()
|
|
if isinstance(bytes_or_longs, (bytes, bytearray)):
|
|
bs._bits = int.from_bytes(bytes_or_longs, 'little')
|
|
elif isinstance(bytes_or_longs, list):
|
|
for i, v in enumerate(bytes_or_longs):
|
|
bs._bits |= (v & 0xFFFFFFFFFFFFFFFF) << (i * 64)
|
|
return bs
|
|
|
|
def stream(self):
|
|
bits = []
|
|
i = 0
|
|
while True:
|
|
idx = self.nextSetBit(i)
|
|
if idx < 0:
|
|
break
|
|
bits.append(idx)
|
|
i = idx + 1
|
|
return bits
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, BitSet):
|
|
return self._bits == other._bits
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._bits) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
bits = []
|
|
i = 0
|
|
while True:
|
|
idx = self.nextSetBit(i)
|
|
if idx < 0:
|
|
break
|
|
bits.append(str(idx))
|
|
i = idx + 1
|
|
return "{" + ", ".join(bits) + "}"
|
|
|
|
def clone(self) -> 'BitSet':
|
|
return BitSet(self)
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __eq__(self, other):
|
|
return self.equals(other)
|
|
|
|
|
|
# ============================================================
|
|
# PriorityQueue
|
|
# ============================================================
|
|
|
|
class PriorityQueue(Stub):
|
|
"""java.util.PriorityQueue — using heapq"""
|
|
def __init__(self, initial_capacity: Any = None, comparator=None):
|
|
super().__init__()
|
|
self._heap: list = []
|
|
self._comparator = comparator
|
|
if initial_capacity is not None and not isinstance(initial_capacity, (int, float)):
|
|
if isinstance(initial_capacity, _IterableABC):
|
|
for item in initial_capacity:
|
|
self._heap.append(item)
|
|
if comparator:
|
|
self._heap.sort(key=comparator)
|
|
else:
|
|
self._heap.sort()
|
|
|
|
def _wrap_key(self, obj):
|
|
if self._comparator:
|
|
return (self._comparator(obj), obj)
|
|
return obj
|
|
|
|
def add(self, obj) -> bool:
|
|
_heapq.heappush(self._heap, self._wrap_key(obj))
|
|
return True
|
|
|
|
def offer(self, obj) -> bool:
|
|
return self.add(obj)
|
|
|
|
def peek(self):
|
|
return self._heap[0][1] if self._heap and self._comparator else (self._heap[0] if self._heap else None)
|
|
|
|
def element(self):
|
|
val = self.peek()
|
|
if val is None:
|
|
raise NoSuchElementException()
|
|
return val
|
|
|
|
def poll(self):
|
|
if not self._heap:
|
|
return None
|
|
val = _heapq.heappop(self._heap)
|
|
return val[1] if self._comparator else val
|
|
|
|
def remove(self, obj) -> bool:
|
|
if self._comparator:
|
|
wrapped = self._wrap_key(obj)
|
|
for i, v in enumerate(self._heap):
|
|
if v == wrapped:
|
|
self._heap[i] = self._heap[-1]
|
|
self._heap.pop()
|
|
_heapq.heapify(self._heap)
|
|
return True
|
|
else:
|
|
for i, v in enumerate(self._heap):
|
|
if v == obj:
|
|
self._heap[i] = self._heap[-1]
|
|
self._heap.pop()
|
|
_heapq.heapify(self._heap)
|
|
return True
|
|
return False
|
|
|
|
def size(self) -> int:
|
|
return len(self._heap)
|
|
|
|
def isEmpty(self) -> bool:
|
|
return len(self._heap) == 0
|
|
|
|
def clear(self) -> None:
|
|
self._heap.clear()
|
|
|
|
def comparator(self):
|
|
return self._comparator
|
|
|
|
def iterator(self) -> Iterator:
|
|
return Iterator(list(self._heap))
|
|
|
|
def toArray(self, a=None):
|
|
if self._comparator:
|
|
items = sorted([v[1] for v in self._heap], key=self._comparator)
|
|
else:
|
|
items = sorted(self._heap)
|
|
if a is None:
|
|
return items
|
|
return items[:len(a)]
|
|
|
|
def forEach(self, action) -> None:
|
|
for item in list(self._heap):
|
|
action(item)
|
|
|
|
def toString(self) -> str:
|
|
items = ", ".join(str(x) for x in self._heap)
|
|
return f"[{items}]"
|
|
|
|
def __len__(self):
|
|
return len(self._heap)
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
|
|
# ============================================================
|
|
# Vector
|
|
# ============================================================
|
|
|
|
class Vector(Stub):
|
|
"""java.util.Vector — synchronized list"""
|
|
def __init__(self, initial_capacity: Any = None, capacity_increment: int = 0):
|
|
super().__init__()
|
|
if initial_capacity is None:
|
|
self._data = []
|
|
elif isinstance(initial_capacity, int):
|
|
self._data = []
|
|
elif isinstance(initial_capacity, _IterableABC):
|
|
self._data = list(initial_capacity)
|
|
else:
|
|
self._data = []
|
|
self._capacity_increment = capacity_increment
|
|
self._lock = _threading.Lock()
|
|
|
|
def _synchronized(self, func, *args, **kwargs):
|
|
with self._lock:
|
|
return func(*args, **kwargs)
|
|
|
|
def add(self, obj) -> bool:
|
|
with self._lock:
|
|
self._data.append(obj)
|
|
return True
|
|
|
|
def addAt(self, index: int, obj) -> None:
|
|
with self._lock:
|
|
self._data.insert(index, obj)
|
|
|
|
def addAll(self, c) -> bool:
|
|
with self._lock:
|
|
if isinstance(c, Vector):
|
|
self._data.extend(c._data)
|
|
else:
|
|
self._data.extend(c)
|
|
return True
|
|
|
|
def addAllAt(self, index: int, c) -> bool:
|
|
with self._lock:
|
|
if isinstance(c, Vector):
|
|
self._data[index:index] = c._data
|
|
else:
|
|
self._data[index:index] = list(c)
|
|
return True
|
|
|
|
def addElement(self, obj) -> None:
|
|
self.add(obj)
|
|
|
|
def capacity(self) -> int:
|
|
with self._lock:
|
|
return len(self._data)
|
|
|
|
def clear(self) -> None:
|
|
with self._lock:
|
|
self._data.clear()
|
|
|
|
def clone(self) -> 'Vector':
|
|
with self._lock:
|
|
return Vector(self._data)
|
|
|
|
def contains(self, obj) -> bool:
|
|
with self._lock:
|
|
return obj in self._data
|
|
|
|
def containsAll(self, c) -> bool:
|
|
with self._lock:
|
|
if isinstance(c, Vector):
|
|
return all(x in self._data for x in c._data)
|
|
return all(x in self._data for x in c)
|
|
|
|
def copyInto(self, an_array: list) -> None:
|
|
with self._lock:
|
|
for i, v in enumerate(self._data):
|
|
an_array[i] = v
|
|
|
|
def elementAt(self, index: int):
|
|
with self._lock:
|
|
return self._data[index]
|
|
|
|
def elements(self) -> Enumeration:
|
|
with self._lock:
|
|
return Enumeration(list(self._data))
|
|
|
|
def ensureCapacity(self, min_capacity: int) -> None:
|
|
pass
|
|
|
|
def firstElement(self):
|
|
with self._lock:
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data[0]
|
|
|
|
def forEach(self, action) -> None:
|
|
with self._lock:
|
|
for item in list(self._data):
|
|
action(item)
|
|
|
|
def get(self, index: int):
|
|
with self._lock:
|
|
return self._data[index]
|
|
|
|
def indexOf(self, obj, index: int = 0) -> int:
|
|
with self._lock:
|
|
try:
|
|
return self._data.index(obj, index)
|
|
except ValueError:
|
|
return -1
|
|
|
|
def insertElementAt(self, obj, index: int) -> None:
|
|
with self._lock:
|
|
self._data.insert(index, obj)
|
|
|
|
def isEmpty(self) -> bool:
|
|
with self._lock:
|
|
return len(self._data) == 0
|
|
|
|
def iterator(self) -> Iterator:
|
|
with self._lock:
|
|
return Iterator(list(self._data))
|
|
|
|
def lastElement(self):
|
|
with self._lock:
|
|
if not self._data:
|
|
raise NoSuchElementException()
|
|
return self._data[-1]
|
|
|
|
def lastIndexOf(self, obj, index: int = None) -> int:
|
|
with self._lock:
|
|
if index is None:
|
|
index = len(self._data) - 1
|
|
for i in range(index, -1, -1):
|
|
if self._data[i] == obj:
|
|
return i
|
|
return -1
|
|
|
|
def listIterator(self, index: int = 0) -> ListIterator:
|
|
with self._lock:
|
|
li = ListIterator(list(self._data))
|
|
li._index = index
|
|
return li
|
|
|
|
def remove(self, obj) -> bool:
|
|
with self._lock:
|
|
if obj in self._data:
|
|
self._data.remove(obj)
|
|
return True
|
|
return False
|
|
|
|
def removeAt(self, index: int):
|
|
with self._lock:
|
|
return self._data.pop(index)
|
|
|
|
def removeAll(self, c) -> bool:
|
|
with self._lock:
|
|
if isinstance(c, Vector):
|
|
items = c._data
|
|
else:
|
|
items = list(c)
|
|
old_len = len(self._data)
|
|
self._data = [x for x in self._data if x not in items]
|
|
return len(self._data) != old_len
|
|
|
|
def removeAllElements(self) -> None:
|
|
with self._lock:
|
|
self._data.clear()
|
|
|
|
def removeElement(self, obj) -> bool:
|
|
return self.remove(obj)
|
|
|
|
def removeElementAt(self, index: int) -> None:
|
|
with self._lock:
|
|
self._data.pop(index)
|
|
|
|
def removeIf(self, predicate) -> bool:
|
|
with self._lock:
|
|
old_len = len(self._data)
|
|
self._data = [x for x in self._data if not predicate(x)]
|
|
return len(self._data) != old_len
|
|
|
|
def replaceAll(self, operator) -> None:
|
|
with self._lock:
|
|
self._data = [operator(x) for x in self._data]
|
|
|
|
def retainAll(self, c) -> bool:
|
|
with self._lock:
|
|
if isinstance(c, Vector):
|
|
items = set(c._data)
|
|
else:
|
|
items = set(c)
|
|
old_len = len(self._data)
|
|
self._data = [x for x in self._data if x in items]
|
|
return len(self._data) != old_len
|
|
|
|
def set(self, index: int, obj):
|
|
with self._lock:
|
|
old = self._data[index]
|
|
self._data[index] = obj
|
|
return old
|
|
|
|
def setElementAt(self, obj, index: int) -> None:
|
|
with self._lock:
|
|
self._data[index] = obj
|
|
|
|
def setSize(self, new_size: int) -> None:
|
|
with self._lock:
|
|
while len(self._data) < new_size:
|
|
self._data.append(None)
|
|
while len(self._data) > new_size:
|
|
self._data.pop()
|
|
|
|
def size(self) -> int:
|
|
with self._lock:
|
|
return len(self._data)
|
|
|
|
def sort(self, key=None, reverse: bool = False) -> None:
|
|
with self._lock:
|
|
self._data.sort(key=key, reverse=reverse)
|
|
|
|
def subList(self, from_index: int, to_index: int) -> 'Vector':
|
|
with self._lock:
|
|
return Vector(self._data[from_index:to_index])
|
|
|
|
def toArray(self, a=None):
|
|
with self._lock:
|
|
if a is None:
|
|
return list(self._data)
|
|
if len(a) < len(self._data):
|
|
return list(self._data)
|
|
for i, v in enumerate(self._data):
|
|
a[i] = v
|
|
if len(self._data) < len(a):
|
|
a[len(self._data)] = None
|
|
return a
|
|
|
|
def toString(self) -> str:
|
|
with self._lock:
|
|
items = ", ".join(str(x) for x in self._data)
|
|
return f"[{items}]"
|
|
|
|
def trimToSize(self) -> None:
|
|
pass
|
|
|
|
def __len__(self):
|
|
with self._lock:
|
|
return len(self._data)
|
|
|
|
def __getitem__(self, idx):
|
|
with self._lock:
|
|
return self._data[idx]
|
|
|
|
def __setitem__(self, idx, val):
|
|
with self._lock:
|
|
self._data[idx] = val
|
|
|
|
def __delitem__(self, idx):
|
|
with self._lock:
|
|
del self._data[idx]
|
|
|
|
def __iter__(self):
|
|
with self._lock:
|
|
return iter(list(self._data))
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
def __contains__(self, obj):
|
|
with self._lock:
|
|
return obj in self._data
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, Vector):
|
|
with self._lock:
|
|
return self._data == other._data
|
|
return False
|
|
|
|
|
|
# ============================================================
|
|
# Stack
|
|
# ============================================================
|
|
|
|
class Stack(Vector):
|
|
"""java.util.Stack — extends Vector"""
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def push(self, item):
|
|
with self._lock:
|
|
self._data.append(item)
|
|
return item
|
|
|
|
def pop(self):
|
|
with self._lock:
|
|
if not self._data:
|
|
raise EmptyStackException()
|
|
return self._data.pop()
|
|
|
|
def peek(self):
|
|
with self._lock:
|
|
if not self._data:
|
|
raise EmptyStackException()
|
|
return self._data[-1]
|
|
|
|
def empty(self) -> bool:
|
|
with self._lock:
|
|
return len(self._data) == 0
|
|
|
|
def search(self, obj) -> int:
|
|
with self._lock:
|
|
try:
|
|
idx = len(self._data) - 1 - self._data[::-1].index(obj)
|
|
return len(self._data) - idx
|
|
except ValueError:
|
|
return -1
|
|
|
|
def add(self, obj) -> bool:
|
|
self.push(obj)
|
|
return True
|
|
|
|
def remove(self, obj) -> bool:
|
|
with self._lock:
|
|
if obj in self._data:
|
|
self._data.remove(obj)
|
|
return True
|
|
return False |