- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
544 lines
15 KiB
Python
544 lines
15 KiB
Python
"""
|
|
Java java.util.prefs standard library APIs implemented in Python.
|
|
java.util.prefs package - Preferences, PreferencesFactory, etc.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .._core import Stub
|
|
from ..java.lang import RuntimeException, Exception as _JavaException
|
|
import typing as _typing
|
|
import json as _json
|
|
import os as _os
|
|
import threading as _threading
|
|
|
|
__all__ = [
|
|
"Preferences", "PreferencesFactory", "AbstractPreferences",
|
|
"NodeChangeEvent", "NodeChangeListener",
|
|
"PreferenceChangeEvent", "PreferenceChangeListener",
|
|
"BackingStoreException", "InvalidPreferencesFormatException",
|
|
]
|
|
|
|
# ============================================================
|
|
# Exceptions
|
|
# ============================================================
|
|
|
|
class BackingStoreException(_JavaException):
|
|
"""java.util.prefs.BackingStoreException"""
|
|
def __init__(self, message):
|
|
super().__init__(message)
|
|
|
|
|
|
class InvalidPreferencesFormatException(_JavaException):
|
|
"""java.util.prefs.InvalidPreferencesFormatException"""
|
|
def __init__(self, message, cause=None):
|
|
super().__init__(message)
|
|
self._cause = cause
|
|
|
|
def getCause(self):
|
|
return self._cause
|
|
|
|
|
|
# ============================================================
|
|
# Event Classes
|
|
# ============================================================
|
|
|
|
class NodeChangeEvent(Stub):
|
|
"""java.util.prefs.NodeChangeEvent"""
|
|
def __init__(self, parent, child):
|
|
super().__init__()
|
|
self._parent = parent
|
|
self._child = child
|
|
|
|
def getParent(self):
|
|
return self._parent
|
|
|
|
def getChild(self):
|
|
return self._child
|
|
|
|
|
|
class PreferenceChangeEvent(Stub):
|
|
"""java.util.prefs.PreferenceChangeEvent"""
|
|
def __init__(self, node, key, new_value):
|
|
super().__init__()
|
|
self._node = node
|
|
self._key = key
|
|
self._new_value = new_value
|
|
|
|
def getNode(self):
|
|
return self._node
|
|
|
|
def getKey(self):
|
|
return self._key
|
|
|
|
def getNewValue(self):
|
|
return self._new_value
|
|
|
|
|
|
class NodeChangeListener(Stub):
|
|
"""java.util.prefs.NodeChangeListener"""
|
|
def childAdded(self, evt):
|
|
pass
|
|
|
|
def childRemoved(self, evt):
|
|
pass
|
|
|
|
|
|
class PreferenceChangeListener(Stub):
|
|
"""java.util.prefs.PreferenceChangeListener"""
|
|
def preferenceChange(self, evt):
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# Preferences
|
|
# ============================================================
|
|
|
|
class Preferences(Stub):
|
|
"""java.util.prefs.Preferences"""
|
|
MAX_KEY_LENGTH = 80
|
|
MAX_VALUE_LENGTH = 8192
|
|
MAX_NAME_LENGTH = 80
|
|
|
|
def __init__(self, name="", parent=None):
|
|
super().__init__()
|
|
self._name = name
|
|
self._parent = parent
|
|
self._children = {}
|
|
self._prefs = {}
|
|
self._node_listeners = []
|
|
self._pref_listeners = []
|
|
self._absolute_path = self._build_path()
|
|
|
|
def _build_path(self):
|
|
if self._parent is None:
|
|
return "/" + self._name if self._name else "/"
|
|
parent_path = self._parent._absolute_path
|
|
if parent_path == "/":
|
|
return "/" + self._name
|
|
return parent_path + "/" + self._name
|
|
|
|
def put(self, key, value):
|
|
if len(key) > Preferences.MAX_KEY_LENGTH:
|
|
raise IllegalArgumentException(f"Key too long: {len(key)}")
|
|
if len(value) > Preferences.MAX_VALUE_LENGTH:
|
|
raise IllegalArgumentException(f"Value too long: {len(value)}")
|
|
self._prefs[key] = value
|
|
self._fire_preference_change(key, value)
|
|
|
|
def get(self, key, default_value):
|
|
return self._prefs.get(key, default_value)
|
|
|
|
def remove(self, key):
|
|
if key in self._prefs:
|
|
old = self._prefs.pop(key)
|
|
self._fire_preference_change(key, None)
|
|
|
|
def clear(self):
|
|
self._prefs.clear()
|
|
|
|
def putInt(self, key, value):
|
|
self.put(key, str(value))
|
|
|
|
def getInt(self, key, default_value):
|
|
val = self.get(key, None)
|
|
if val is None:
|
|
return default_value
|
|
try:
|
|
return int(val)
|
|
except ValueError:
|
|
return default_value
|
|
|
|
def putLong(self, key, value):
|
|
self.put(key, str(value))
|
|
|
|
def getLong(self, key, default_value):
|
|
val = self.get(key, None)
|
|
if val is None:
|
|
return default_value
|
|
try:
|
|
return int(val)
|
|
except ValueError:
|
|
return default_value
|
|
|
|
def putFloat(self, key, value):
|
|
self.put(key, str(value))
|
|
|
|
def getFloat(self, key, default_value):
|
|
val = self.get(key, None)
|
|
if val is None:
|
|
return default_value
|
|
try:
|
|
return float(val)
|
|
except ValueError:
|
|
return default_value
|
|
|
|
def putDouble(self, key, value):
|
|
self.put(key, str(value))
|
|
|
|
def getDouble(self, key, default_value):
|
|
val = self.get(key, None)
|
|
if val is None:
|
|
return default_value
|
|
try:
|
|
return float(val)
|
|
except ValueError:
|
|
return default_value
|
|
|
|
def putBoolean(self, key, value):
|
|
self.put(key, "true" if value else "false")
|
|
|
|
def getBoolean(self, key, default_value):
|
|
val = self.get(key, None)
|
|
if val is None:
|
|
return default_value
|
|
return val.lower() == "true"
|
|
|
|
def putByteArray(self, key, value):
|
|
import base64
|
|
self.put(key, base64.b64encode(value).decode())
|
|
|
|
def getByteArray(self, key, default_value):
|
|
import base64
|
|
val = self.get(key, None)
|
|
if val is None:
|
|
return default_value
|
|
try:
|
|
return base64.b64decode(val)
|
|
except Exception:
|
|
return default_value
|
|
|
|
def keys(self):
|
|
return list(self._prefs.keys())
|
|
|
|
def childrenNames(self):
|
|
return list(self._children.keys())
|
|
|
|
def parent(self):
|
|
return self._parent
|
|
|
|
def node(self, path_name):
|
|
if path_name.startswith("/"):
|
|
return self._root()._node_path(path_name[1:])
|
|
return self._node_path(path_name)
|
|
|
|
def nodeExists(self, path_name):
|
|
try:
|
|
self.node(path_name)
|
|
return True
|
|
except BackingStoreException:
|
|
return False
|
|
|
|
def removeNode(self):
|
|
if self._parent is not None:
|
|
self._parent._remove_child(self._name)
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def absolutePath(self):
|
|
return self._absolute_path
|
|
|
|
def isUserNode(self):
|
|
return False
|
|
|
|
def toString(self):
|
|
return self._absolute_path
|
|
|
|
def flush(self):
|
|
pass
|
|
|
|
def sync(self):
|
|
pass
|
|
|
|
def addNodeChangeListener(self, listener):
|
|
self._node_listeners.append(listener)
|
|
|
|
def removeNodeChangeListener(self, listener):
|
|
if listener in self._node_listeners:
|
|
self._node_listeners.remove(listener)
|
|
|
|
def addPreferenceChangeListener(self, listener):
|
|
self._pref_listeners.append(listener)
|
|
|
|
def removePreferenceChangeListener(self, listener):
|
|
if listener in self._pref_listeners:
|
|
self._pref_listeners.remove(listener)
|
|
|
|
def exportNode(self, output_stream):
|
|
data = self._to_dict()
|
|
output_stream.write(_json.dumps(data, indent=2).encode())
|
|
|
|
def exportSubtree(self, output_stream):
|
|
data = self._to_dict(recursive=True)
|
|
output_stream.write(_json.dumps(data, indent=2).encode())
|
|
|
|
def _to_dict(self, recursive=False):
|
|
result = {"prefs": dict(self._prefs)}
|
|
if recursive:
|
|
result["children"] = {
|
|
name: child._to_dict(recursive=True)
|
|
for name, child in self._children.items()
|
|
}
|
|
return result
|
|
|
|
def _root(self):
|
|
if self._parent is None:
|
|
return self
|
|
return self._parent._root()
|
|
|
|
def _node_path(self, path):
|
|
if not path:
|
|
return self
|
|
parts = path.split("/")
|
|
current = self
|
|
for part in parts:
|
|
if not part:
|
|
continue
|
|
if part == "..":
|
|
if current._parent is not None:
|
|
current = current._parent
|
|
continue
|
|
if part not in current._children:
|
|
current._children[part] = Preferences(part, current)
|
|
current = current._children[part]
|
|
return current
|
|
|
|
def _remove_child(self, name):
|
|
if name in self._children:
|
|
child = self._children.pop(name)
|
|
evt = NodeChangeEvent(self, child)
|
|
for listener in self._node_listeners:
|
|
listener.childRemoved(evt)
|
|
|
|
def _fire_preference_change(self, key, value):
|
|
evt = PreferenceChangeEvent(self, key, value)
|
|
for listener in self._pref_listeners:
|
|
listener.preferenceChange(evt)
|
|
|
|
@staticmethod
|
|
def userRoot():
|
|
if Preferences._user_root is None:
|
|
Preferences._user_root = Preferences("")
|
|
return Preferences._user_root
|
|
|
|
@staticmethod
|
|
def systemRoot():
|
|
if Preferences._system_root is None:
|
|
Preferences._system_root = Preferences("")
|
|
return Preferences._system_root
|
|
|
|
@staticmethod
|
|
def importPreferences(input_stream):
|
|
data = _json.loads(input_stream.read())
|
|
root = Preferences("")
|
|
root._prefs = data.get("prefs", {})
|
|
return root
|
|
|
|
_user_root = None
|
|
_system_root = None
|
|
|
|
|
|
# ============================================================
|
|
# AbstractPreferences
|
|
# ============================================================
|
|
|
|
class AbstractPreferences(Preferences):
|
|
"""java.util.prefs.AbstractPreferences"""
|
|
def __init__(self, parent, name):
|
|
super().__init__(name, parent)
|
|
self._is_removed = False
|
|
self._lock = _threading.RLock()
|
|
|
|
def put(self, key, value):
|
|
with self._lock:
|
|
if self._is_removed:
|
|
raise BackingStoreException("Node has been removed")
|
|
super().put(key, value)
|
|
|
|
def get(self, key, default_value):
|
|
with self._lock:
|
|
if self._is_removed:
|
|
raise BackingStoreException("Node has been removed")
|
|
return super().get(key, default_value)
|
|
|
|
def remove(self, key):
|
|
with self._lock:
|
|
super().remove(key)
|
|
|
|
def keys(self):
|
|
with self._lock:
|
|
return super().keys()
|
|
|
|
def childrenNames(self):
|
|
with self._lock:
|
|
return super().childrenNames()
|
|
|
|
def node(self, path_name):
|
|
with self._lock:
|
|
return super().node(path_name)
|
|
|
|
def removeNode(self):
|
|
with self._lock:
|
|
super().removeNode()
|
|
self._is_removed = True
|
|
|
|
def flush(self):
|
|
pass
|
|
|
|
def sync(self):
|
|
pass
|
|
|
|
def isUserNode(self):
|
|
return False
|
|
|
|
|
|
# ============================================================
|
|
# PreferencesFactory
|
|
# ============================================================
|
|
|
|
class PreferencesFactory(Stub):
|
|
"""java.util.prefs.PreferencesFactory"""
|
|
def systemRoot(self):
|
|
return Preferences.systemRoot()
|
|
|
|
def userRoot(self):
|
|
return Preferences.userRoot()
|
|
|
|
|
|
# ============================================================
|
|
# IllegalArgumentException
|
|
# ============================================================
|
|
|
|
class IllegalArgumentException(RuntimeException):
|
|
"""java.lang.IllegalArgumentException"""
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# Preferences utility helpers
|
|
# ============================================================
|
|
|
|
class _PreferencesService(Stub):
|
|
"""Helper to manage Preferences with file-backed storage"""
|
|
def __init__(self, root_path=None):
|
|
super().__init__()
|
|
if root_path is None:
|
|
import os
|
|
root_path = os.path.join(os.path.expanduser("~"), ".java", "prefs")
|
|
self._root_path = root_path
|
|
self._cache = {}
|
|
|
|
def loadPreferences(self, node_path):
|
|
import os
|
|
import json
|
|
file_path = os.path.join(self._root_path, node_path.replace("/", "_") + ".json")
|
|
prefs = Preferences(node_path)
|
|
if os.path.exists(file_path):
|
|
try:
|
|
with open(file_path, "r") as f:
|
|
data = json.load(f)
|
|
for key, val in data.items():
|
|
prefs.put(key, val)
|
|
except Exception:
|
|
pass
|
|
self._cache[node_path] = prefs
|
|
return prefs
|
|
|
|
def savePreferences(self, prefs):
|
|
import os
|
|
import json
|
|
file_path = os.path.join(self._root_path,
|
|
prefs.absolutePath().replace("/", "_") + ".json")
|
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
data = {}
|
|
for key in prefs.keys():
|
|
data[key] = prefs.get(key, None)
|
|
with open(file_path, "w") as f:
|
|
json.dump(data, f, indent=2)
|
|
|
|
def clearCache(self):
|
|
self._cache.clear()
|
|
|
|
|
|
class _EventDispatchThread(Stub):
|
|
"""Helper for dispatching preference change events"""
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._listeners = []
|
|
import threading
|
|
self._lock = threading.Lock()
|
|
|
|
def addListener(self, listener):
|
|
with self._lock:
|
|
self._listeners.append(listener)
|
|
|
|
def removeListener(self, listener):
|
|
with self._lock:
|
|
if listener in self._listeners:
|
|
self._listeners.remove(listener)
|
|
|
|
def dispatchEvent(self, event):
|
|
with self._lock:
|
|
for listener in self._listeners:
|
|
try:
|
|
if hasattr(listener, 'preferenceChange'):
|
|
listener.preferenceChange(event)
|
|
elif callable(listener):
|
|
listener(event)
|
|
except Exception:
|
|
pass
|
|
|
|
def getListeners(self):
|
|
with self._lock:
|
|
return list(self._listeners)
|
|
|
|
|
|
# ============================================================
|
|
# FileSystemPreferences - persistent Preferences backed by file
|
|
# ============================================================
|
|
|
|
class FileSystemPreferences(AbstractPreferences):
|
|
"""java.util.prefs.FileSystemPreferences - persistent file-backed preferences"""
|
|
def __init__(self, parent, name, storage_path=None):
|
|
super().__init__(parent, name)
|
|
import os
|
|
if storage_path is None:
|
|
storage_path = os.path.join(os.path.expanduser("~"), ".java", "user_prefs")
|
|
self._storage_path = os.path.join(
|
|
storage_path, self.absolutePath().replace("/", "_") + ".xml"
|
|
)
|
|
self._loaded = False
|
|
self._service = _PreferencesService(storage_path)
|
|
|
|
def _ensureLoaded(self):
|
|
if not self._loaded:
|
|
try:
|
|
import os
|
|
if os.path.exists(self._storage_path):
|
|
node = self._service.loadPreferences(self.absolutePath())
|
|
for key in node.keys():
|
|
self._prefs[key] = node.get(key, "")
|
|
self._loaded = True
|
|
except Exception:
|
|
self._loaded = True
|
|
|
|
def put(self, key, value):
|
|
self._ensureLoaded()
|
|
super().put(key, value)
|
|
|
|
def get(self, key, default_value):
|
|
self._ensureLoaded()
|
|
return super().get(key, default_value)
|
|
|
|
def keys(self):
|
|
self._ensureLoaded()
|
|
return super().keys()
|
|
|
|
def flush(self):
|
|
self._service.savePreferences(self)
|
|
super().flush()
|
|
|
|
def sync(self):
|
|
self._ensureLoaded()
|
|
self._service.savePreferences(self) |