class EVOLICITY_TEMPLATES
Top level class for Evolicity accessible via class EVOLICITY_SHARED_TEMPLATES
The templating substitution language was named Evolicity as a portmanteau of "Evolve" and "Felicity" which is also a partial anagram of "Velocity" the Apache project which inspired it. It also bows to an established tradition of naming Eiffel orientated projects starting with the letter 'E'.
note
description: "[
Top level class for Evolicity accessible via class ${EVOLICITY_SHARED_TEMPLATES}
The templating substitution language was named `Evolicity' as a portmanteau of "Evolve" and "Felicity"
which is also a partial anagram of "Velocity" the Apache project which inspired it.
It also bows to an established tradition of naming Eiffel orientated projects starting with the letter 'E'.
]"
author: "Finnian Reilly"
copyright: "Copyright (c) 2001-2022 Finnian Reilly"
contact: "finnian at eiffel hyphen loop dot com"
license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
date: "2024-10-06 10:38:31 GMT (Sunday 6th October 2024)"
revision: "38"
class
EVOLICITY_TEMPLATES
inherit
EL_THREAD_ACCESS [HASH_TABLE [EVOLICITY_COMPILER, FILE_PATH]]
EL_MODULE_EXCEPTION
EL_STRING_GENERAL_ROUTINES
EL_MODULE_DEFERRED_LOCALE
EL_ZSTRING_CONSTANTS
create
make
feature {NONE} -- Initialization
make
--
do
create stack_table.make_equal (19)
enable_indentation
end
feature -- Basic operations
merge (a_name: FILE_PATH; context: EVOLICITY_CONTEXT; output: EL_OUTPUT_MEDIUM)
--
require
output_writeable: output.is_open_write and output.is_writable
local
template: EVOLICITY_COMPILED_TEMPLATE; stack: like stack_table.stack
found: BOOLEAN
do
if stack_table.has_key (a_name) then
stack := stack_table.found_stack
else
-- using a stack enables use of recursive templates
create stack.make (5)
stack_table.extend (stack, a_name)
end
if stack.is_empty then
if attached restricted_access (Mutex_compiler_table) as table then
if table.has_key (a_name) then
-- Changed 23 Nov 2013
-- Before it used to make a deep_twin of an existing compiled template
template := table.found_item.compiled_template
-- log.put_string_field ("Compiled template", a_name.to_string)
-- log.put_new_line
stack.put (template)
found := True
else
Exception.raise_developer ("Template [%S] not found", [a_name])
end
end_restriction
end
else
template := stack_table.found_stack.item
found := True
end
if found then
if template.has_file_source and then a_name.modification_time > template.modification_time then
-- File was modified
stack_table.remove (a_name)
put_file (a_name, template.encoding)
-- Try again
merge (a_name, context, output)
else
stack.remove
if attached {EL_STRING_IO_MEDIUM} output as text_output then
text_output.grow (template.minimum_buffer_length)
end
template.execute (context, output)
stack.put (template)
end
end
end
merge_to_file (a_name: FILE_PATH; context: EVOLICITY_CONTEXT; text_file: EL_PLAIN_TEXT_FILE)
--
require
writeable: text_file.is_open_write
do
merge (a_name, context, text_file)
text_file.flush; text_file.close
end
feature -- String output
merged_to_string (a_name: FILE_PATH; context: EVOLICITY_CONTEXT): ZSTRING
local
medium: EL_ZSTRING_IO_MEDIUM
do
create medium.make_open_write (1024)
merge (a_name, context, medium)
medium.close
Result := medium.text
end
merged_to_utf_8 (a_name: FILE_PATH; context: EVOLICITY_CONTEXT): STRING
--
local
medium: EL_STRING_8_IO_MEDIUM
do
create medium.make_open_write (1024)
merge (a_name, context, medium)
medium.close
Result := medium.text
end
feature -- Status query
has (a_name: FILE_PATH): BOOLEAN
do
if attached restricted_access (Mutex_compiler_table) as table then
Result := table.has (a_name)
end_restriction
end
end
is_nested_output_indented: BOOLEAN
-- is the indenting feature that nicely indents nested XML, active?
-- Active by default.
feature -- Element change
put_file (file_path: FILE_PATH; encoding: EL_ENCODING_BASE)
--
require
file_exists: file_path.exists
do
put (file_path, Empty_string, encoding)
end
put_source (a_name: FILE_PATH; template_source: READABLE_STRING_GENERAL)
do
put (a_name, as_zstring (template_source), Void)
end
feature -- Status change
disable_indentation
-- Turn off the indenting feature that nicely indents nested XML
-- This will make application performance better
do
is_nested_output_indented := False
end
enable_indentation
-- Turn on the indenting feature that nicely indents nested XML
do
is_nested_output_indented := True
end
feature -- Removal
clear_all
-- Clear all parsed templates
do
if attached restricted_access (Mutex_compiler_table) as table then
table.wipe_out
end_restriction
end
end
remove (a_name: FILE_PATH)
-- remove template
do
if attached restricted_access (Mutex_compiler_table) as table then
table.remove (a_name)
end_restriction
end
end
feature -- Factory
new_translation_key_table: EL_ZSTRING_TABLE
-- table of translation keys of the form "{evol.<variable-name>}" by Evolicity variable name
local
var_name: STRING; l_key: ZSTRING -- translation key
do
create Result.make_sized (20)
across Locale.translation_keys as key loop
l_key := key.item
if l_key.enclosed_with (Braces) and then l_key.starts_with (Translation_key_prefix) then
var_name := l_key.substring (Translation_key_prefix.count + 1, l_key.count - 1)
Result.extend (l_key, var_name)
end
end
end
feature -- Contract Support
is_type_template (key_path: FILE_PATH): BOOLEAN
do
Result := key_path.has_extension ("template") and then key_path.base_name.enclosed_with (Braces)
end
feature {NONE} -- Implementation
put (key_path: FILE_PATH; template_source: ZSTRING; file_encoding: detachable EL_ENCODING_BASE)
-- put compiled template into the thread safe global `Mutex_compiler_table' template table
-- if `file_encoding' attached then compile template stored in file path `key_path'
-- or else recompile existing template if file modified date is newer
-- if not `file_encoding' attached then compile `template_source' and store
-- with key `key_path'
require
valid_key_path: not attached file_encoding implies (is_type_template (key_path) and not template_source.is_empty)
local
compiler: EVOLICITY_COMPILER; source_is_new_or_updated: BOOLEAN
do
if attached restricted_access (Mutex_compiler_table) as table then
if table.has_key (key_path) then
if attached file_encoding then
source_is_new_or_updated := key_path.modification_time > table.found_item.modification_time
end
else
source_is_new_or_updated := True
end
if source_is_new_or_updated then
create compiler.make
if attached file_encoding as encoding then
compiler.set_encoding_from_other (encoding)
compiler.set_source_text_from_file (key_path)
else
compiler.set_source_text (template_source)
end
compiler.parse
if compiler.parse_succeeded then
table [key_path] := compiler
else
Exception.raise_developer ("Evolicity compilation failed %S", [key_path])
end
end
end_restriction
end
end
feature {NONE} -- Internal attributes
stack_table: EVOLICITY_TEMPLATE_STACK_TABLE
-- stacks of compiled templates
-- This design enables use of recursive templates
feature {NONE} -- Global attributes
Mutex_compiler_table: EL_MUTEX_REFERENCE [HASH_TABLE [EVOLICITY_COMPILER, FILE_PATH]]
-- Global template compilers table
once ("PROCESS")
create Result.make (create {like Mutex_compiler_table.item}.make (11))
end
feature {NONE} -- Constants
Braces: ZSTRING
once
Result := "{}"
end
Translation_key_prefix: ZSTRING
once
Result := "{evol."
end
end