#!/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