167 lines
5.6 KiB
Python
167 lines
5.6 KiB
Python
#!/usr/bin/env python3.9
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
JFinal Prop - Properties File Handler
|
|
"""
|
|
|
|
import os
|
|
from typing import Optional
|
|
|
|
class Prop:
|
|
"""Properties file handler"""
|
|
|
|
DEFAULT_ENCODING = "UTF-8"
|
|
|
|
def __init__(self, file_name_or_content=None, encoding: str = DEFAULT_ENCODING, is_file: bool = True):
|
|
"""
|
|
Initialize Prop
|
|
|
|
Args:
|
|
file_name_or_content: file name (if is_file=True) or content string
|
|
encoding: encoding for reading properties file
|
|
is_file: True if first parameter is file name, False if it's content string
|
|
"""
|
|
self._properties = {}
|
|
|
|
if file_name_or_content is None:
|
|
return
|
|
|
|
if is_file:
|
|
self._load_from_file(file_name_or_content, encoding)
|
|
else:
|
|
self._load_from_content(file_name_or_content, encoding)
|
|
|
|
def _load_from_file(self, file_name: str, encoding: str):
|
|
"""Load properties from file"""
|
|
try:
|
|
# Try to find file in classpath first
|
|
import importlib.resources
|
|
try:
|
|
with importlib.resources.open_text(file_name, encoding=encoding) as f:
|
|
self._parse_properties(f.read())
|
|
return
|
|
except (FileNotFoundError, TypeError):
|
|
pass
|
|
|
|
# Try as absolute file path
|
|
if os.path.isabs(file_name):
|
|
file_path = file_name
|
|
else:
|
|
file_path = os.path.join(os.getcwd(), file_name)
|
|
|
|
if not os.path.exists(file_path):
|
|
raise FileNotFoundError(f"Properties file not found: {file_name}")
|
|
|
|
with open(file_path, 'r', encoding=encoding) as f:
|
|
self._parse_properties(f.read())
|
|
|
|
except Exception as e:
|
|
raise RuntimeError(f"Error loading properties file: {e}")
|
|
|
|
def _load_from_content(self, content: str, encoding: str):
|
|
"""Load properties from content string"""
|
|
try:
|
|
self._parse_properties(content)
|
|
except Exception as e:
|
|
raise RuntimeError(f"Error parsing properties content: {e}")
|
|
|
|
def _parse_properties(self, content: str):
|
|
"""Parse properties content"""
|
|
for line in content.split('\n'):
|
|
line = line.strip()
|
|
|
|
# Skip empty lines and comments
|
|
if not line or line.startswith('#') or line.startswith('!'):
|
|
continue
|
|
|
|
# Handle line continuation
|
|
while line.endswith('\\'):
|
|
line = line[:-1].strip()
|
|
# This is a simplified implementation
|
|
|
|
# Find the first separator
|
|
separator_idx = -1
|
|
for sep in ['=', ':']:
|
|
idx = line.find(sep)
|
|
if idx != -1:
|
|
if separator_idx == -1 or idx < separator_idx:
|
|
separator_idx = idx
|
|
|
|
if separator_idx == -1:
|
|
# No separator, treat as key with empty value
|
|
key = line.strip()
|
|
value = ""
|
|
else:
|
|
key = line[:separator_idx].strip()
|
|
value = line[separator_idx + 1:].strip()
|
|
|
|
if key:
|
|
self._properties[key] = value
|
|
|
|
def get(self, key: str, default_value: str = None) -> Optional[str]:
|
|
"""Get property value"""
|
|
value = self._properties.get(key)
|
|
if value is not None and len(value) != 0:
|
|
return value.strip()
|
|
return default_value
|
|
|
|
def get_int(self, key: str, default_value: int = None) -> Optional[int]:
|
|
"""Get property as integer"""
|
|
value = self.get(key)
|
|
if value is not None:
|
|
return int(value)
|
|
return default_value
|
|
|
|
def get_long(self, key: str, default_value: int = None) -> Optional[int]:
|
|
"""Get property as long integer"""
|
|
value = self.get(key)
|
|
if value is not None:
|
|
return int(value)
|
|
return default_value
|
|
|
|
def get_double(self, key: str, default_value: float = None) -> Optional[float]:
|
|
"""Get property as double"""
|
|
value = self.get(key)
|
|
if value is not None:
|
|
return float(value)
|
|
return default_value
|
|
|
|
def get_boolean(self, key: str, default_value: bool = None) -> Optional[bool]:
|
|
"""Get property as boolean"""
|
|
value = self.get(key)
|
|
if value is not None:
|
|
value_lower = value.lower().strip()
|
|
if value_lower == "true":
|
|
return True
|
|
elif value_lower == "false":
|
|
return False
|
|
else:
|
|
raise ValueError(f"Cannot parse boolean value: {value}")
|
|
return default_value
|
|
|
|
def contains_key(self, key: str) -> bool:
|
|
"""Check if key exists"""
|
|
return key in self._properties
|
|
|
|
def is_empty(self) -> bool:
|
|
"""Check if properties is empty"""
|
|
return len(self._properties) == 0
|
|
|
|
def not_empty(self) -> bool:
|
|
"""Check if properties is not empty"""
|
|
return not self.is_empty()
|
|
|
|
def append(self, prop) -> 'Prop':
|
|
"""Append properties from another Prop"""
|
|
if prop is None:
|
|
raise ValueError("prop cannot be None")
|
|
|
|
for key, value in prop.get_properties().items():
|
|
self._properties[key] = value
|
|
|
|
return self
|
|
|
|
def get_properties(self) -> dict:
|
|
"""Get all properties"""
|
|
return self._properties.copy()
|