267 lines
9.5 KiB
Python
267 lines
9.5 KiB
Python
#!/usr/bin/env python3.9
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
JFinal EngineConfig - Template Engine Configuration
|
|
"""
|
|
|
|
from typing import Dict, Optional, List, Set, Type, Any
|
|
from decimal import Decimal
|
|
from .Directive import Directive
|
|
from .stat.Parser import Parser
|
|
from .stat.ast.Define import Define
|
|
from .stat.OutputDirectiveFactory import OutputDirectiveFactory
|
|
from .io.WriterBuffer import WriterBuffer
|
|
from .source.ISourceFactory import ISourceFactory
|
|
from .source.FileSourceFactory import FileSourceFactory
|
|
from .expr.ast.SharedMethodKit import SharedMethodKit
|
|
from .ext.directive.RenderDirective import RenderDirective
|
|
from .ext.directive.DateDirective import DateDirective
|
|
from .ext.directive.EscapeDirective import EscapeDirective
|
|
from .ext.directive.RandomDirective import RandomDirective
|
|
from .ext.directive.NumberDirective import NumberDirective
|
|
from .ext.directive.CallDirective import CallDirective
|
|
from .ext.directive.StringDirective import StringDirective
|
|
from .ext.sharedmethod.SharedMethodLib import SharedMethodLib
|
|
|
|
class EngineConfig:
|
|
"""Template engine configuration"""
|
|
|
|
DEFAULT_ENCODING = "UTF-8"
|
|
|
|
def __init__(self):
|
|
"""Initialize engine configuration"""
|
|
self._writer_buffer = WriterBuffer()
|
|
self._compressor = None
|
|
self._shared_function_map: Dict[str, Define] = {}
|
|
self._shared_function_source_list: List[object] = []
|
|
self._shared_object_map: Optional[Dict[str, Any]] = None
|
|
self._output_directive_factory = OutputDirectiveFactory()
|
|
self._source_factory = FileSourceFactory()
|
|
self._directive_map: Dict[str, Type[Directive]] = {}
|
|
self._shared_method_kit = SharedMethodKit()
|
|
self._keep_line_blank_directives: Set[str] = set()
|
|
|
|
self._dev_mode = False
|
|
self._reload_modified_shared_function_in_dev_mode = True
|
|
self._base_template_path: Optional[str] = None
|
|
self._encoding = EngineConfig.DEFAULT_ENCODING
|
|
self._date_pattern = "yyyy-MM-dd HH:mm"
|
|
self._rounding_mode = "HALF_UP" # Default rounding mode
|
|
self._support_static_method_expression = False
|
|
self._support_static_field_expression = False
|
|
|
|
# Initialize built-in directives
|
|
self._init_builtin_directives()
|
|
|
|
def _init_builtin_directives(self):
|
|
"""Initialize built-in directives"""
|
|
self._keep_line_blank_directives.add("output")
|
|
self._keep_line_blank_directives.add("include")
|
|
|
|
self.add_directive("render", RenderDirective, True)
|
|
self.add_directive("date", DateDirective, True)
|
|
self.add_directive("escape", EscapeDirective, True)
|
|
self.add_directive("random", RandomDirective, True)
|
|
self.add_directive("number", NumberDirective, True)
|
|
self.add_directive("call", CallDirective, False)
|
|
self.add_directive("string", StringDirective, False)
|
|
|
|
# Add built-in shared methods
|
|
self.add_shared_method(SharedMethodLib())
|
|
|
|
def add_shared_function(self, file_name: str):
|
|
"""Add shared function from file"""
|
|
file_name = file_name.replace("\\", "/")
|
|
source = self._source_factory.get_source(self._base_template_path, file_name, self._encoding)
|
|
self._do_add_shared_function(source, file_name)
|
|
|
|
def _do_add_shared_function(self, source, file_name: str):
|
|
"""Internal method to add shared function"""
|
|
# Create a minimal env for parsing
|
|
env = Env(self)
|
|
parser = Parser(env, source.get_content(), file_name)
|
|
parser.parse()
|
|
|
|
# Add to shared function map
|
|
func_map = env.get_function_map()
|
|
for func_name, func in func_map.items():
|
|
if func_name in self._shared_function_map:
|
|
raise ValueError(f"Template function already exists: {func_name}")
|
|
|
|
self._shared_function_map[func_name] = func
|
|
|
|
# Add source for dev mode
|
|
if self._dev_mode:
|
|
self._shared_function_source_list.append(source)
|
|
env.add_source(source)
|
|
|
|
def add_shared_function_by_content(self, content: str):
|
|
"""Add shared function from content string"""
|
|
from .source.StringSource import StringSource
|
|
string_source = StringSource(content, False)
|
|
self._do_add_shared_function(string_source, None)
|
|
|
|
def add_shared_object(self, name: str, obj: Any):
|
|
"""Add shared object"""
|
|
if self._shared_object_map is None:
|
|
self._shared_object_map = {}
|
|
|
|
if name in self._shared_object_map:
|
|
raise ValueError(f"Shared object already exists: {name}")
|
|
|
|
self._shared_object_map[name] = obj
|
|
|
|
def get_shared_object(self, name: str) -> Optional[Any]:
|
|
"""Get shared object"""
|
|
if self._shared_object_map:
|
|
return self._shared_object_map.get(name)
|
|
return None
|
|
|
|
def remove_shared_object(self, name: str):
|
|
"""Remove shared object"""
|
|
if self._shared_object_map:
|
|
self._shared_object_map.pop(name, None)
|
|
|
|
def add_directive(self, directive_name: str, directive_class: Type[Directive], keep_line_blank: bool = False):
|
|
"""Add custom directive"""
|
|
if not directive_name or not directive_name.strip():
|
|
raise ValueError("directive name cannot be blank")
|
|
|
|
if directive_class is None:
|
|
raise ValueError("directiveClass cannot be null")
|
|
|
|
if directive_name in self._directive_map:
|
|
raise ValueError(f"directive already exists: {directive_name}")
|
|
|
|
self._directive_map[directive_name] = directive_class
|
|
|
|
if keep_line_blank:
|
|
self._keep_line_blank_directives.add(directive_name)
|
|
|
|
def get_directive(self, directive_name: str) -> Optional[Type[Directive]]:
|
|
"""Get directive class"""
|
|
return self._directive_map.get(directive_name)
|
|
|
|
def remove_directive(self, directive_name: str):
|
|
"""Remove directive"""
|
|
self._directive_map.pop(directive_name, None)
|
|
self._keep_line_blank_directives.discard(directive_name)
|
|
|
|
def add_shared_method(self, obj: Any):
|
|
"""Add shared method from object"""
|
|
self._shared_method_kit.add_shared_method(obj)
|
|
|
|
def add_shared_method_from_class(self, clazz: Type):
|
|
"""Add shared method from class"""
|
|
self._shared_method_kit.add_shared_method(clazz)
|
|
|
|
def add_shared_static_method(self, clazz: Type):
|
|
"""Add shared static method from class"""
|
|
self._shared_method_kit.add_shared_static_method(clazz)
|
|
|
|
def remove_shared_method(self, method_name: str):
|
|
"""Remove shared method by name"""
|
|
self._shared_method_kit.remove_shared_method(method_name)
|
|
|
|
def get_shared_method_kit(self) -> SharedMethodKit:
|
|
"""Get shared method kit"""
|
|
return self._shared_method_kit
|
|
|
|
# Property accessors
|
|
@property
|
|
def dev_mode(self) -> bool:
|
|
"""Get dev mode setting"""
|
|
return self._dev_mode
|
|
|
|
@dev_mode.setter
|
|
def dev_mode(self, value: bool):
|
|
"""Set dev mode"""
|
|
self._dev_mode = value
|
|
|
|
@property
|
|
def base_template_path(self) -> Optional[str]:
|
|
"""Get base template path"""
|
|
return self._base_template_path
|
|
|
|
@base_template_path.setter
|
|
def base_template_path(self, value: str):
|
|
"""Set base template path"""
|
|
if value is None:
|
|
self._base_template_path = None
|
|
return
|
|
|
|
value = value.strip().replace("\\", "/")
|
|
if not value:
|
|
raise ValueError("baseTemplatePath cannot be blank")
|
|
|
|
if len(value) > 1 and value.endswith("/"):
|
|
value = value[:-1]
|
|
|
|
self._base_template_path = value
|
|
|
|
@property
|
|
def encoding(self) -> str:
|
|
"""Get encoding"""
|
|
return self._encoding
|
|
|
|
@encoding.setter
|
|
def encoding(self, value: str):
|
|
"""Set encoding"""
|
|
if not value:
|
|
raise ValueError("encoding cannot be blank")
|
|
self._encoding = value
|
|
self._writer_buffer.set_encoding(value)
|
|
|
|
@property
|
|
def date_pattern(self) -> str:
|
|
"""Get date pattern"""
|
|
return self._date_pattern
|
|
|
|
@date_pattern.setter
|
|
def date_pattern(self, value: str):
|
|
"""Set date pattern"""
|
|
if not value:
|
|
raise ValueError("datePattern cannot be blank")
|
|
self._date_pattern = value
|
|
|
|
@property
|
|
def rounding_mode(self) -> str:
|
|
"""Get rounding mode"""
|
|
return self._rounding_mode
|
|
|
|
@rounding_mode.setter
|
|
def rounding_mode(self, value: str):
|
|
"""Set rounding mode"""
|
|
self._rounding_mode = value
|
|
|
|
@property
|
|
def writer_buffer(self) -> WriterBuffer:
|
|
"""Get writer buffer"""
|
|
return self._writer_buffer
|
|
|
|
@property
|
|
def source_factory(self) -> ISourceFactory:
|
|
"""Get source factory"""
|
|
return self._source_factory
|
|
|
|
@source_factory.setter
|
|
def source_factory(self, value: ISourceFactory):
|
|
"""Set source factory"""
|
|
if value is None:
|
|
raise ValueError("sourceFactory cannot be null")
|
|
self._source_factory = value
|
|
|
|
@property
|
|
def shared_function_map(self) -> Dict[str, Define]:
|
|
"""Get shared function map"""
|
|
return self._shared_function_map.copy()
|
|
|
|
@property
|
|
def shared_object_map(self) -> Optional[Dict[str, Any]]:
|
|
"""Get shared object map"""
|
|
return self._shared_object_map.copy() if self._shared_object_map else None
|
|
|
|
|
|
# Import needed classes to avoid circular imports
|
|
from .Env import Env
|