Redesigned the CMS_BLOCK system,
- added condition attribute. It can be set via configuration file
with
[blocks]
{blockid}.region={region_name}
{blockid}.conditions[]=is_front
{blockid}.conditions[]=path:location-path/foo/bar
- For backward compatibility, the CMS will check only conditions for block name prefixed by "?".
Improved the configuration library to support list and table properties.
Updated theme for now, to include the feed examples.
Added "cache" classes, to ease caching of html output for instance. (TODO: improve by providing a cache manager).
515 lines
12 KiB
Plaintext
515 lines
12 KiB
Plaintext
note
|
|
description: "[
|
|
Object parsing a .ini file.
|
|
|
|
Lines starting by '#', or ';', or "--" are comments
|
|
Section are declared using brackets "[section_name]"
|
|
Values are declared as "key = value"
|
|
List values are declared as multiple lines "key[] = value"
|
|
example:
|
|
--------------------
|
|
[first_section]
|
|
one = abc
|
|
|
|
[second_section]
|
|
two = def
|
|
lst[] = a
|
|
lst[] = b
|
|
lst[] = c
|
|
|
|
[third_section]
|
|
three = ghi
|
|
table[a] = foo
|
|
table[b] = bar
|
|
--------------------
|
|
|
|
Values are accessible from `items' or by section from `sections'
|
|
`item' has smart support for '.'
|
|
|
|
item ("one") -> abc
|
|
item ("two") -> def
|
|
item ("second_section.two") -> def
|
|
item ("table[b]") -> foo
|
|
item ("table.b") -> foo
|
|
item ("third_section.table[b]") -> foo
|
|
item ("third_section.table.b") -> foo
|
|
|
|
notes:
|
|
it is considered that the .ini file is utf-8 encoded
|
|
keys are unicode string
|
|
values are stored UTF-8 string, but one can use unicode_string_item to convert to STRING_32
|
|
|
|
Additional features:
|
|
- Include other files:
|
|
@include=file-to-include
|
|
|
|
]"
|
|
date: "$Date: 2015-03-09 19:25:49 +0100 (lun., 09 mars 2015) $"
|
|
revision: "$Revision: 96797 $"
|
|
|
|
class
|
|
INI_CONFIG
|
|
|
|
inherit
|
|
CONFIG_READER
|
|
|
|
TABLE_ITERABLE [ANY, READABLE_STRING_GENERAL]
|
|
|
|
SHARED_EXECUTION_ENVIRONMENT
|
|
|
|
create
|
|
make_from_file,
|
|
make_from_string,
|
|
make_from_items
|
|
|
|
feature {NONE} -- Initialization
|
|
|
|
make_from_file (p: PATH)
|
|
do
|
|
initialize
|
|
parse_file (p)
|
|
end
|
|
|
|
make_from_string (a_content: STRING_8)
|
|
do
|
|
initialize
|
|
parse_content (a_content)
|
|
end
|
|
|
|
make_from_items (a_items: like items)
|
|
do
|
|
initialize
|
|
across
|
|
a_items as ic
|
|
loop
|
|
items.force (ic.item, ic.key)
|
|
end
|
|
end
|
|
|
|
initialize
|
|
-- Initialize data.
|
|
do
|
|
associated_path := Void
|
|
create utf
|
|
create items.make (0)
|
|
create sections.make (0)
|
|
end
|
|
|
|
reset
|
|
-- Reset internal data.
|
|
do
|
|
has_error := False
|
|
items.wipe_out
|
|
sections.wipe_out
|
|
end
|
|
|
|
feature -- Status report
|
|
|
|
has_item (k: READABLE_STRING_GENERAL): BOOLEAN
|
|
-- Has item associated with key `k'?
|
|
do
|
|
Result := item (k) /= Void
|
|
end
|
|
|
|
has_error: BOOLEAN
|
|
-- Has error?
|
|
--| Syntax error, source not found, ...
|
|
|
|
feature -- Access: Config Reader
|
|
|
|
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
|
-- String item associated with key `k'.
|
|
do
|
|
Result := value_to_string_32 (item (k))
|
|
end
|
|
|
|
text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
|
|
-- List of String item associated with key `k'.
|
|
do
|
|
if attached {LIST [READABLE_STRING_8]} item (k) as l_list then
|
|
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (l_list.count)
|
|
Result.compare_objects
|
|
across
|
|
l_list as ic
|
|
until
|
|
Result = Void
|
|
loop
|
|
if attached value_to_string_32 (ic.item) as s32 then
|
|
Result.force (s32)
|
|
else
|
|
Result := Void
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
|
|
-- Table of String item associated with key `k'.
|
|
do
|
|
if attached {STRING_TABLE [READABLE_STRING_8]} item (k) as l_list then
|
|
create {STRING_TABLE [READABLE_STRING_32]} Result.make (l_list.count)
|
|
Result.compare_objects
|
|
across
|
|
l_list as ic
|
|
until
|
|
Result = Void
|
|
loop
|
|
if attached value_to_string_32 (ic.item) as s32 then
|
|
Result.force (s32, ic.key)
|
|
else
|
|
Result := Void
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
integer_item (k: READABLE_STRING_GENERAL): INTEGER
|
|
-- Integer item associated with key `k'.
|
|
do
|
|
if attached {READABLE_STRING_GENERAL} item (k) as s then
|
|
Result := s.to_integer
|
|
end
|
|
end
|
|
|
|
associated_path: detachable PATH
|
|
-- If current was built from a filename, return this path.
|
|
|
|
feature -- Duplication
|
|
|
|
sub_config (k: READABLE_STRING_GENERAL): detachable CONFIG_READER
|
|
-- Configuration representing a subset of Current for key `k'.
|
|
do
|
|
if attached sections.item (k) as l_items then
|
|
create {INI_CONFIG} Result.make_from_items (l_items)
|
|
end
|
|
end
|
|
|
|
feature -- Access
|
|
|
|
item (k: READABLE_STRING_GENERAL): detachable ANY
|
|
-- Value associated with key `k'.
|
|
local
|
|
i: INTEGER
|
|
s,sk: detachable READABLE_STRING_GENERAL
|
|
do
|
|
-- Try first directly in values
|
|
Result := items.item (k)
|
|
if Result = Void then
|
|
--| If there is a dot
|
|
--| this could be
|
|
--| section.variable
|
|
--| variable.name or variable[name]
|
|
i := k.index_of ('.', 1)
|
|
if i > 0 then
|
|
s := k.head (i - 1)
|
|
-- then search first in section
|
|
if attached sections.item (s) as l_section then
|
|
sk := k.substring (i + 1, k.count)
|
|
Result := l_section.item (sk)
|
|
if Result = Void then
|
|
Result := item_from_values (l_section, sk)
|
|
end
|
|
end
|
|
if Result = Void then
|
|
i := k.index_of ('.', i + 1)
|
|
if i > 0 then
|
|
-- There is another dot .. could be due to include
|
|
across
|
|
sections as ic
|
|
until
|
|
Result /= Void
|
|
loop
|
|
s := ic.key
|
|
-- If has other dot, this may be in sub sections ...
|
|
if s.has ('.') and then k.starts_with (s) and then k[s.count + 1] = '.' then
|
|
if attached sections.item (s) as l_section then
|
|
sk := k.substring (s.count + 2, k.count)
|
|
Result := l_section.item (sk)
|
|
if Result = Void then
|
|
Result := item_from_values (l_section, sk)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if Result = Void then
|
|
-- otherwise in values object.
|
|
Result := item_from_values (items, k)
|
|
end
|
|
end
|
|
else
|
|
--| Could be
|
|
--| variable[name]
|
|
Result := item_from_values (items, k)
|
|
end
|
|
end
|
|
end
|
|
|
|
items: STRING_TABLE [ANY]
|
|
|
|
sections: STRING_TABLE [like items]
|
|
|
|
feature -- Access
|
|
|
|
new_cursor: TABLE_ITERATION_CURSOR [ANY, READABLE_STRING_GENERAL]
|
|
-- Fresh cursor associated with current structure
|
|
do
|
|
Result := items.new_cursor
|
|
end
|
|
|
|
feature {NONE} -- Implementation
|
|
|
|
value_to_string_32 (obj: detachable ANY): detachable STRING_32
|
|
do
|
|
if attached {READABLE_STRING_32} obj as s32 then
|
|
Result := s32
|
|
elseif attached {READABLE_STRING_8} obj as s then
|
|
Result := utf.utf_8_string_8_to_escaped_string_32 (s)
|
|
end
|
|
end
|
|
|
|
item_from_values (a_values: STRING_TABLE [ANY]; k: READABLE_STRING_GENERAL): detachable ANY
|
|
local
|
|
i,j: INTEGER
|
|
s: READABLE_STRING_GENERAL
|
|
do
|
|
Result := a_values.item (k)
|
|
if Result = Void then
|
|
i := k.index_of ('.', 1)
|
|
if i > 0 then
|
|
s := k.head (i - 1)
|
|
if attached {STRING_TABLE [ANY]} a_values.item (s) as l_table then
|
|
Result := l_table.item (k.substring (i + 1, k.count))
|
|
end
|
|
else
|
|
i := k.index_of ('[', 1)
|
|
if i > 0 then
|
|
j := k.index_of (']', i + 1)
|
|
if j = k.count then
|
|
s := k.head (i - 1)
|
|
if attached {STRING_TABLE [ANY]} a_values.item (s) as l_table then
|
|
Result := l_table.item (k.substring (i + 1, j - 1))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
record_in_section (obj: ANY; k: READABLE_STRING_GENERAL; a_section: READABLE_STRING_GENERAL)
|
|
local
|
|
v: detachable like items
|
|
do
|
|
v := sections.item (a_section)
|
|
if v = Void then
|
|
create v.make (1)
|
|
sections.force (v, a_section)
|
|
end
|
|
v.force (obj, k)
|
|
end
|
|
|
|
parse_content (a_content: STRING_8)
|
|
local
|
|
i,j,n: INTEGER
|
|
s: READABLE_STRING_8
|
|
do
|
|
last_section_name := Void
|
|
from
|
|
i := 1
|
|
n := a_content.count
|
|
until
|
|
i > n
|
|
loop
|
|
j := a_content.index_of ('%N', i)
|
|
if j > 0 then
|
|
s := a_content.substring (i, j - 1)
|
|
i := j + 1
|
|
if i <= n and then a_content[i] = '%R' then
|
|
i := i + 1
|
|
end
|
|
else
|
|
j := n
|
|
s := a_content.substring (i, j)
|
|
i := j + 1
|
|
end
|
|
analyze_line (s, Void)
|
|
variant
|
|
i
|
|
end
|
|
last_section_name := Void
|
|
rescue
|
|
last_section_name := Void
|
|
has_error := True
|
|
end
|
|
|
|
parse_file (p: PATH)
|
|
do
|
|
associated_path := p
|
|
last_section_name := Void
|
|
import_path (p, Void)
|
|
last_section_name := Void
|
|
end
|
|
|
|
import_path (p: PATH; a_section_prefix: detachable READABLE_STRING_32)
|
|
-- Import config from path `p'.
|
|
local
|
|
f: PLAIN_TEXT_FILE
|
|
l_last_section_name: like last_section_name
|
|
retried: BOOLEAN
|
|
l_old_associated_path: like associated_path
|
|
do
|
|
l_old_associated_path := associated_path
|
|
if retried then
|
|
has_error := True
|
|
else
|
|
associated_path := p
|
|
l_last_section_name := last_section_name
|
|
last_section_name := Void
|
|
create f.make_with_path (p)
|
|
if f.exists and then f.is_access_readable then
|
|
f.open_read
|
|
from
|
|
until
|
|
f.exhausted or f.end_of_file
|
|
loop
|
|
f.read_line_thread_aware
|
|
analyze_line (f.last_string, a_section_prefix)
|
|
end
|
|
f.close
|
|
else
|
|
-- File not readable
|
|
has_error := True
|
|
end
|
|
end
|
|
last_section_name := l_last_section_name
|
|
associated_path := l_old_associated_path
|
|
rescue
|
|
retried := True
|
|
retry
|
|
end
|
|
|
|
analyze_line (a_line: STRING_8; a_section_prefix: detachable READABLE_STRING_32)
|
|
-- Analyze line `a_line'.
|
|
local
|
|
k,sk: STRING_32
|
|
v: STRING_8
|
|
obj: detachable ANY
|
|
lst: LIST [STRING_8]
|
|
tb: STRING_TABLE [STRING_8]
|
|
i,j: INTEGER
|
|
p: PATH
|
|
l_section_name: like last_section_name
|
|
do
|
|
obj := Void
|
|
a_line.left_adjust
|
|
if
|
|
a_line.is_empty
|
|
or a_line.is_whitespace
|
|
or a_line.starts_with_general ("#")
|
|
or a_line.starts_with_general (";")
|
|
or a_line.starts_with_general ("--")
|
|
then
|
|
-- Ignore
|
|
elseif a_line.starts_with_general ("[") then
|
|
i := a_line.index_of (']', 1)
|
|
l_section_name := utf.utf_8_string_8_to_string_32 (a_line.substring (2, i - 1))
|
|
l_section_name.left_adjust
|
|
l_section_name.right_adjust
|
|
if a_section_prefix /= Void then
|
|
l_section_name.prepend_character ('.')
|
|
l_section_name.prepend (a_section_prefix)
|
|
end
|
|
last_section_name := l_section_name
|
|
else
|
|
i := a_line.index_of ('=', 1)
|
|
if i > 1 then
|
|
k := utf.utf_8_string_8_to_string_32 (a_line.head (i - 1))
|
|
k.right_adjust
|
|
v := a_line.substring (i + 1, a_line.count)
|
|
v.left_adjust
|
|
v.right_adjust
|
|
|
|
|
|
if k.is_case_insensitive_equal_general ("@include") then
|
|
create p.make_from_string (v)
|
|
if not p.is_absolute and attached associated_path as l_path then
|
|
p := l_path.parent.extended_path (p)
|
|
end
|
|
import_path (p, last_section_name)
|
|
else
|
|
i := k.index_of ('[', 1)
|
|
if i > 0 then
|
|
j := k.index_of (']', i + 1)
|
|
if j = i + 1 then -- ends_with "[]"
|
|
k.keep_head (i - 1)
|
|
if attached {LIST [STRING_8]} items.item (k) as l_list then
|
|
lst := l_list
|
|
else
|
|
create {ARRAYED_LIST [STRING_8]} lst.make (1)
|
|
record_item (lst, k, a_section_prefix)
|
|
end
|
|
lst.force (v)
|
|
obj := lst
|
|
elseif j > i then
|
|
-- table
|
|
sk := k.substring (i + 1, j - 1)
|
|
sk.left_adjust
|
|
sk.right_adjust
|
|
k.keep_head (i - 1)
|
|
if attached {STRING_TABLE [STRING_8]} items.item (k) as l_table then
|
|
tb := l_table
|
|
else
|
|
create tb.make (1)
|
|
record_item (tb, k, a_section_prefix)
|
|
end
|
|
tb.force (v, sk)
|
|
obj := tb
|
|
else
|
|
-- Error missing closing ']'
|
|
has_error := True
|
|
end
|
|
else
|
|
record_item (v, k, a_section_prefix)
|
|
obj := v
|
|
end
|
|
|
|
if attached last_section_name as l_session and then obj /= Void then
|
|
record_in_section (obj, k, l_session)
|
|
end
|
|
end
|
|
else
|
|
-- Error
|
|
has_error := True
|
|
end
|
|
end
|
|
end
|
|
|
|
record_item (v: ANY; k: READABLE_STRING_32; a_section_prefix: detachable READABLE_STRING_32)
|
|
do
|
|
if a_section_prefix /= Void then
|
|
items.force (v, a_section_prefix + {STRING_32} "." + k)
|
|
else
|
|
items.force (v, k)
|
|
end
|
|
end
|
|
|
|
feature {NONE} -- Implementation
|
|
|
|
last_section_name: detachable STRING_32
|
|
|
|
utf: UTF_CONVERTER
|
|
|
|
invariant
|
|
|
|
note
|
|
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
|
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
|
source: "[
|
|
Eiffel Software
|
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
|
Telephone 805-685-1006, Fax 805-685-6869
|
|
Website http://www.eiffel.com
|
|
Customer support http://support.eiffel.com
|
|
]"
|
|
end
|