note
description: "XML file preference storage implementation."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
PREFERENCES_STORAGE_XML
inherit
PREFERENCES_STORAGE_I
redefine
initialize_with_preferences,
make_with_location,
save_preferences
end
XML_CALLBACKS_FILTER_FACTORY
export
{NONE} all
end
REFACTORING_HELPER
create
make_empty,
make_versioned,
make_with_location,
make_with_location_and_version
feature {NONE} -- Initialization
make_empty
-- Create preferences storage in XML file. File will be created based on name of application.
-- For operating systems which support it it will stored in the users home directory. For operating
-- systems that do not support home directories it will be stored in the current working directory.
local
l_loc: detachable PATH
l_exec: EXECUTION_ENVIRONMENT
do
create l_exec
if operating_environment.home_directory_supported then
l_loc := l_exec.home_directory_path
if l_loc = Void then
create l_loc.make_current
end
else
l_loc := l_exec.current_working_path
end
make_with_location (l_loc.extended ("stored_preferences.xml").name)
end
make_with_location (a_location: READABLE_STRING_GENERAL)
-- Create preference storage in the XML file at location `a_location'.
-- If file does not exist create new one.
do
Precursor {PREFERENCES_STORAGE_I} (a_location)
create xml_structure.make_with_root_named ("EIFFEL_DOCUMENT", create {XML_NAMESPACE}.make_default)
end
feature {PREFERENCES} -- Initialization
initialize_with_preferences (a_preferences: PREFERENCES)
do
Precursor (a_preferences)
extract_preferences_from_file
end
feature {PREFERENCES} -- Resource Management
exists: BOOLEAN
-- Does storage exists ?
local
u: FILE_UTILITIES
do
Result := u.file_exists (location)
end
has_preference (a_name: READABLE_STRING_GENERAL): BOOLEAN
-- Does the underlying store contain a preference with `a_name'?
do
Result := session_values.has (a_name)
end
get_preference_value (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- Retrieve the preference string value from the underlying store.
do
Result := session_values.item (a_name)
end
save_preference (a_preference: PREFERENCE)
-- Save `a_preference' to the file on disk.
do
-- TODO: neilc. How to save only a single preference to the file?
if attached preferences as l_preferences then
save_preferences (l_preferences.preferences.linear_representation, True)
end
end
save_preferences (a_preferences: ARRAYED_LIST [PREFERENCE]; a_save_modified_values_only: BOOLEAN)
-- Save all preferences in `a_preferences' to storage device.
-- If `a_save_modified_values_only' then only preferences whose value is different
-- from the default one are saved, otherwise all preferences are saved.
local
l_preference: PREFERENCE
l_file: PLAIN_TEXT_FILE
pref_string1, pref_string2, pref_string3: STRING
do
pref_string1 := "%N%T"
create l_file.make_with_name (location)
safe_open_write (l_file)
if l_file.is_open_write then
l_file.put_string ("")
from
a_preferences.start
until
a_preferences.after
loop
l_preference := a_preferences.item
if not a_save_modified_values_only or else not l_preference.is_default_value then
l_file.put_string (pref_string1)
l_file.put_string (escape_xml (l_preference.name))
l_file.put_string (pref_string2)
l_file.put_string (escape_xml (l_preference.text_value))
l_file.put_string (pref_string3)
else
-- nothing to do to remove them from file.
end
a_preferences.forth
end
l_file.put_string ("%N")
l_file.close
else
debug ("refactor_fixme")
fixme ("Add code to let callers that `preferences' could not be saved")
end
end
end
remove_preference (a_preference: PREFERENCE)
-- Remove `preference' from storage device.
do
end
feature {NONE} -- Implementation
xml_structure: detachable XML_DOCUMENT
-- XML structure built from parsing of `file'.
extract_preferences_from_file
-- Extract from the `file' the saved preference values.
require
location_not_void: location /= Void
local
parser: XML_STOPPABLE_PARSER
l_file: PLAIN_TEXT_FILE
l_tree: XML_CALLBACKS_DOCUMENT
l_resolver: XML_NAMESPACE_RESOLVER
l_root_element: XML_ELEMENT
t_preference, t_name, t_value: STRING
do
create parser.make
create l_tree.make_null
create l_resolver.set_next (l_tree)
parser.set_callbacks (l_resolver)
create l_file.make_with_name (location)
if l_file.exists and then l_file.is_readable then
safe_open_read (l_file)
if l_file.is_open_read then
parser.parse_from_file (l_file)
l_file.close
if not parser.error_occurred and then
(attached l_tree.document as l_xml_structure) -- should be implied by `not has_error'
then
xml_structure := l_xml_structure
from
t_preference := "PREFERENCE"
t_name := "NAME"
t_value := "VALUE"
l_root_element := l_xml_structure.root_element
l_root_element.start
until
l_root_element.after
loop
if attached {XML_ELEMENT} l_root_element.item_for_iteration as node then
if
node.has_same_name (t_preference) and then
attached node.attribute_by_name (t_name) as l_pref_name and then
attached node.attribute_by_name (t_value) as l_pref_value
then
session_values.put (l_pref_value.value, l_pref_name.value)
end
end
l_root_element.forth
end
else
debug ("refactor_fixme")
fixme ("Add code to let callers know that XML file was invalid")
end
end
else
debug ("refactor_fixme")
fixme ("Add code to let callers that we could not open preference file")
end
end
else
debug ("refactor_fixme")
fixme ("Add code to let callers that preference file can not be read")
end
end
end
escape_xml (a_string: READABLE_STRING_GENERAL): STRING
-- Escape xml entities in `a_string'.
require
a_string_not_void: a_string /= Void
do
Result := xml_utilities.escaped_xml (a_string)
ensure
result_not_void: Result /= Void
end
Lt_string: STRING
once
create Result.make (1)
Result.append_character (lt_char)
end
Gt_string: STRING
once
create Result.make (1)
Result.append_character (gt_char)
end
Amp_string: STRING
once
create Result.make (1)
Result.append_character (amp_char)
end
Quot_string: STRING
once
create Result.make (1)
Result.append_character (quot_char)
end
safe_open_read (a_file: FILE)
-- Safely open `a_file' with read mode.
require
a_file_not_void: a_file /= Void
a_file_closed: a_file.is_closed
local
retried: BOOLEAN
do
if not retried then
a_file.open_read
elseif not a_file.is_closed then
a_file.close
end
rescue
if not retried then
retried := True
retry
end
end
safe_open_write (a_file: FILE)
-- Safely open `a_file' with write mode.
require
a_file_not_void: a_file /= Void
a_file_closed: a_file.is_closed
local
retried: BOOLEAN
do
if not retried then
a_file.open_write
elseif not a_file.is_closed then
a_file.close
end
rescue
if not retried then
retried := True
retry
end
end
feature {NONE} -- XML markup and entities constants
Lt_char: CHARACTER = '<'
Gt_char: CHARACTER = '>'
Amp_char: CHARACTER = '&'
Quot_char: CHARACTER = '%"'
Lt_entity: STRING = "<"
Gt_entity: STRING = ">"
Amp_entity: STRING = "&"
Quot_entity: STRING = """
xml_utilities: XML_UTILITIES
-- XML utilities
once
create Result
end
invariant
has_session_values: session_values /= Void
has_xml_structure: xml_structure /= Void
note
copyright: "Copyright (c) 1984-2015, 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