Files
py_enjoy/template/EngineConfig.py
2026-02-27 14:37:10 +08:00

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