Files
ROC/library/configuration/src/ini_config.e
Jocelyn Fiat 463105f29f Added feed aggregation module.
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).
2015-10-08 13:56:31 +02:00

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