初始提交,未完全测试

This commit is contained in:
2026-02-27 14:37:10 +08:00
parent 76c0f469be
commit e270f02073
68 changed files with 5886 additions and 0 deletions

401
template/Engine.py Normal file
View File

@@ -0,0 +1,401 @@
#!/usr/bin/env python3.9
# -*- coding: utf-8 -*-
"""
JFinal Engine - Template Engine Main Class
"""
from typing import Dict, Optional, Set, Type, Any, Callable
from .EngineConfig import EngineConfig
from .Env import Env
from .Template import Template
from .stat.Parser import Parser
from .stat.ast.Stat import Stat
from .source.ISourceFactory import ISourceFactory
from .source.ClassPathSourceFactory import ClassPathSourceFactory
from .source.StringSource import StringSource
from .io.WriterBuffer import WriterBuffer
from .io.EncoderFactory import EncoderFactory
from .io.JdkEncoderFactory import JdkEncoderFactory
from .stat.Compressor import Compressor
from .expr.ast.FieldKit import FieldKit
from .expr.ast.FieldKeyBuilder import FieldKeyBuilder
from .expr.ast.MethodKit import MethodKit
from .Directive import Directive
from .stat.OutputDirectiveFactory import OutputDirectiveFactory
from ..kit.SyncWriteMap import SyncWriteMap
from ..kit.StrKit import StrKit
from ..kit.HashKit import HashKit
class Engine:
"""Template Engine - Main class for JFinal Template Engine"""
MAIN_ENGINE_NAME = "main"
_MAIN_ENGINE: 'Engine' = None
_engine_map: Dict[str, 'Engine'] = {}
def __init__(self, engine_name: str = None):
"""
Initialize engine
Args:
engine_name: Engine name (optional)
"""
self._name = engine_name or "NO_NAME"
self._dev_mode = True
self._cache_string_template = False
self._config = EngineConfig()
self._source_factory = self._config.source_factory
self._template_cache = SyncWriteMap(2048, 0.5)
@classmethod
def use(cls, engine_name: str = None) -> 'Engine':
"""
Get engine instance
Args:
engine_name: Engine name (optional, uses main engine if not specified)
Returns:
Engine instance
"""
if engine_name is None:
if cls._MAIN_ENGINE is None:
cls._MAIN_ENGINE = cls(cls.MAIN_ENGINE_NAME)
cls._engine_map[cls.MAIN_ENGINE_NAME] = cls._MAIN_ENGINE
return cls._MAIN_ENGINE
return cls._engine_map.get(engine_name)
@classmethod
def create(cls, engine_name: str, configurator: Callable[['Engine'], None] = None) -> 'Engine':
"""
Create new engine with name
Args:
engine_name: Engine name
configurator: Configuration callback
Returns:
New engine instance
"""
if StrKit.is_blank(engine_name):
raise ValueError("Engine name cannot be blank")
engine_name = engine_name.strip()
if engine_name in cls._engine_map:
raise ValueError(f"Engine already exists: {engine_name}")
new_engine = cls(engine_name)
cls._engine_map[engine_name] = new_engine
if configurator:
configurator(new_engine)
return new_engine
@classmethod
def create_if_absent(cls, engine_name: str, configurator: Callable[['Engine'], None] = None) -> 'Engine':
"""
Create engine if absent
Args:
engine_name: Engine name
configurator: Configuration callback
Returns:
Engine instance
"""
ret = cls._engine_map.get(engine_name)
if ret is None:
# Double-check locking
if engine_name not in cls._engine_map:
ret = cls.create(engine_name)
if configurator:
configurator(ret)
return ret
@classmethod
def remove(cls, engine_name: str) -> Optional['Engine']:
"""
Remove engine by name
Args:
engine_name: Engine name
Returns:
Removed engine or None
"""
removed = cls._engine_map.pop(engine_name, None)
if removed and cls.MAIN_ENGINE_NAME == removed.name:
cls._MAIN_ENGINE = None
return removed
@classmethod
def set_main_engine(cls, engine: 'Engine'):
"""
Set main engine
Args:
engine: Engine to set as main
"""
if engine is None:
raise ValueError("Engine cannot be null")
engine._name = cls.MAIN_ENGINE_NAME
cls._engine_map[cls.MAIN_ENGINE_NAME] = engine
cls._MAIN_ENGINE = engine
def get_template(self, file_name: str) -> Template:
"""
Get template by file name
Args:
file_name: Template file name
Returns:
Template instance
"""
template = self._template_cache.get(file_name)
if template is None:
template = self._build_template_by_source_factory(file_name)
self._template_cache[file_name] = template
elif self._dev_mode and template.is_modified():
template = self._build_template_by_source_factory(file_name)
self._template_cache[file_name] = template
return template
def _build_template_by_source_factory(self, file_name: str) -> Template:
"""Build template using source factory"""
source = self._source_factory.get_source(
self._config.base_template_path,
file_name,
self._config.encoding
)
return self._build_template(source, file_name)
def get_template_by_string(self, content: str, cache: bool = False) -> Template:
"""
Get template by string content
Args:
content: Template content
cache: Whether to cache the template
Returns:
Template instance
"""
if not cache:
source = StringSource(content, False)
return self._build_template(source, None)
cache_key = HashKit.md5(content)
template = self._template_cache.get(cache_key)
if template is None:
source = StringSource(content, cache_key)
template = self._build_template(source, cache_key)
self._template_cache[cache_key] = template
elif self._dev_mode and template.is_modified():
source = StringSource(content, cache_key)
template = self._build_template(source, cache_key)
self._template_cache[cache_key] = template
return template
def _build_template(self, source, cache_key: str = None) -> Template:
"""Build template from source"""
env = Env(self._config)
if self._dev_mode:
env.add_source(source)
parser = Parser(env, source.get_content(), cache_key)
stat = parser.parse()
return Template(env, stat)
def add_shared_function(self, file_name: str) -> 'Engine':
"""Add shared function from file"""
self._config.add_shared_function(file_name)
return self
def add_shared_function_by_string(self, content: str) -> 'Engine':
"""Add shared function from string"""
self._config.add_shared_function_by_content(content)
return self
def add_shared_object(self, name: str, obj: Any) -> 'Engine':
"""Add shared object"""
self._config.add_shared_object(name, obj)
return self
def remove_shared_object(self, name: str) -> 'Engine':
"""Remove shared object"""
self._config.remove_shared_object(name)
return self
def add_directive(self, directive_name: str, directive_class: Type[Directive],
keep_line_blank: bool = False) -> 'Engine':
"""Add custom directive"""
self._config.add_directive(directive_name, directive_class, keep_line_blank)
return self
def remove_directive(self, directive_name: str) -> 'Engine':
"""Remove directive"""
self._config.remove_directive(directive_name)
return self
def add_shared_method(self, obj: Any) -> 'Engine':
"""Add shared method from object"""
self._config.add_shared_method(obj)
return self
def add_shared_method_from_class(self, clazz: Type) -> 'Engine':
"""Add shared method from class"""
self._config.add_shared_method_from_class(clazz)
return self
def add_shared_static_method(self, clazz: Type) -> 'Engine':
"""Add shared static method from class"""
self._config.add_shared_static_method(clazz)
return self
def remove_shared_method(self, method_name: str) -> 'Engine':
"""Remove shared method by name"""
self._config.remove_shared_method(method_name)
return self
def remove_template_cache(self, cache_key: str):
"""Remove template cache by key"""
self._template_cache.pop(cache_key, None)
def remove_all_template_cache(self):
"""Remove all template cache"""
self._template_cache.clear()
@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
self._config.dev_mode = value
if self._dev_mode:
self.remove_all_template_cache()
@property
def cache_string_template(self) -> bool:
"""Get cache string template setting"""
return self._cache_string_template
@cache_string_template.setter
def cache_string_template(self, value: bool):
"""Set cache string template"""
self._cache_string_template = value
@property
def name(self) -> str:
"""Get engine name"""
return self._name
@property
def config(self) -> EngineConfig:
"""Get engine configuration"""
return self._config
@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"""
self._config.source_factory = value
self._source_factory = value
def set_to_class_path_source_factory(self) -> 'Engine':
"""Set to class path source factory"""
self.source_factory = ClassPathSourceFactory()
return self
@property
def base_template_path(self) -> Optional[str]:
"""Get base template path"""
return self._config.base_template_path
@base_template_path.setter
def base_template_path(self, value: str):
"""Set base template path"""
self._config.base_template_path = value
@property
def encoding(self) -> str:
"""Get encoding"""
return self._config.encoding
@encoding.setter
def encoding(self, value: str):
"""Set encoding"""
self._config.encoding = value
@property
def date_pattern(self) -> str:
"""Get date pattern"""
return self._config.date_pattern
@date_pattern.setter
def date_pattern(self, value: str):
"""Set date pattern"""
self._config.date_pattern = value
def set_compressor(self, compressor: Compressor) -> 'Engine':
"""Set compressor"""
self._config._compressor = compressor
return self
def set_compressor_on(self, separator: str = '\n') -> 'Engine':
"""Set compressor on"""
self._config._compressor = Compressor(separator)
return self
def set_encoder_factory(self, encoder_factory: EncoderFactory) -> 'Engine':
"""Set encoder factory"""
self._config.writer_buffer.set_encoder_factory(encoder_factory)
self._config.writer_buffer.set_encoding(self._config.encoding)
return self
def set_to_jdk_encoder_factory(self) -> 'Engine':
"""Set to JDK encoder factory"""
self.set_encoder_factory(JdkEncoderFactory())
return self
def set_buffer_size(self, buffer_size: int) -> 'Engine':
"""Set buffer size"""
self._config.writer_buffer.set_buffer_size(buffer_size)
return self
def set_reentrant_buffer_size(self, reentrant_buffer_size: int) -> 'Engine':
"""Set reentrant buffer size"""
self._config.writer_buffer.set_reentrant_buffer_size(reentrant_buffer_size)
return self
@property
def template_cache_size(self) -> int:
"""Get template cache size"""
return len(self._template_cache)
def __repr__(self) -> str:
return f"Template Engine: {self._name}"
# Initialize main engine
Engine._MAIN_ENGINE = Engine(Engine.MAIN_ENGINE_NAME)
Engine._engine_map[Engine.MAIN_ENGINE_NAME] = Engine._MAIN_ENGINE