[[BackLinksMenu]] [[TicketQuery(summary=PLUGIN_SUPPORT_LIB_CONFIGURING_R1, format=table, col=summary|owner|status|type|component|priority|effort|importance, rows=description|analysis_owners|analysis_reviewers|analysis_score|design_owners|design_reviewers|design_score|implementation_owners|implementation_reviewers|implementation_score|test_owners|test_reviewers|test_score|)]] = Analysis = == Overview == The goal of this task is to provide ability to load and save configuration settings that are not defined by skins. For example, bug reporting requires user's e-mail address to be persisted. == Task requirements == * Configuration settings are stored as pairs (key, value). * Loading and saving configuration settings should be easy and made in uniform way: * get : key -> value - reads the value from configuration file. * set : key, value - updates the value so any part of Sophie that depends on it is updated. * Convention for configuration files: * Configuration files can be stored in {{{distrib/conf/}}} directory of the corresponding plugin. * Their names should be unique for the application. * Use the name of the corresponding Sophie module, for example: * {{{distrib/conf/org.sophie2.main.layout.mydoggy.conf}}} * {{{distrib/conf/org.sophie2.main.layout.mydoggy/some_name}}} * Configuration files are saved in XML format. * Keys should be unique in a given configuration file, but there's no need to be globally unique, since the file names are unique. * Make at least one of the following settings to be persisted in configuration files: * User's e-mail address in the bug report form. * Main window's size, location and state (maximized/normal). * Current skin - default or alternative. == Task result == * Source code. == Implementation idea == * This functionality has much in common with skins, but cannot be implemented as skins. So create a new module. * Create a singleton class {{{ConfigurationManager}}} (or something else) that has the described {{{get}}} and {{{set}}} methods. * Create a class {{{Configuration}}} that represents a single configuration file. * Use list of {{{ListEntry}}}s that stores all key-value pairs in that file. * Create a generic class {{{ConfigKey}}} that encapsulates all of the information about a key: * key name * configuration file * default value (if the key is not present in the file) * schema (for the persistence) * {{{ConfigKey}}}s should be defined as constants in the code, for example: * {{{public static final ConfigKey USER_EMAIL = ...;}}} * {{{email = ConfigurationManager.get().get(USER_EMAIL);}}} == Related == * [wiki:GROUP_PLUGINS_R0] (encompasses PLUGIN_SUPPORT_LIB_CONFIGURING_R0) * [wiki:APP_PLUGIN_MANAGER_CONFIGURE_R0] == How to demo == * Open Sophie. * Open the bug report form (in Help menu) and enter a valid e-mail address. * Close the form. * Restart Sophie. * Open the bug report and show the saved e-mail address. = Design = * New module {{{org.sophie2.base.config}}}. * Immutable generic class {{{ConfigKey}}} that encapsulates all of the information about a key: * name (of the key) - String * configuration file - String - relative path. For example, if the file is "distrib/conf/a/b.conf", this should be "a/b.conf". * default value (if the key is not present in the file) - T * schema (for the persistence) - String, for example "imm:int|storage|decimal" * Class {{{BaseConfigModule}}}. * Extension point of type {{{ConfigKey}}}. * Class {{{ConfigEntry}}} that extends {{{ListEntry}}} * This class represents a key-value pair in a configuration file. * Override {{{getKey}}} to return a {{{ConfigKey}}}. * Class {{{Configuration}}} - represents a single configuration file. * configuration file - File * list property of {{{ConfigEntry}}}s that stores all key-value pairs in that file. * {{{T getValue(ConfigKey key)}}} - searches the list for a given entry and returns the corresponding value or the default value for the key. * {{{void setValue(ConfigKey key, T value)}}} - sets the value in the list. * Package {{{org.sophie2.base.config.persistence}}} * Class {{{ConfigurationStoragePersister}}} that loads/saves configuration files from/to Storage. * Use the following structure for the storage, where "value" is the storage for the value of the configuration key, created with the according persister: * entry * key - attribute * value - text content of entry * entry * key - attribute * value - text content of entry * Schema: "configuration|storage|conf" * When reading, iterate over all registered extensions - config keys - for the given configuration file. * Class {{{ConfigurationFilePersister}}} that loads/saves Storage from/to *.conf files. * Use the same structure as the storage for the XML file. * Name the root tag "configuration". * Schema: "storage|xml|conf" * Register the persisters as extensions. * Singleton class {{{ConfigurationManager}}}. * list of entries (String, Configuration), which maps Configuration objects to configuration file names. * {{{T getValue(ConfigKey key)}}} * Given the file name in the key, finds the corresponding Configuration and then returns the result of Configuration.getValue. * {{{setValue(ConfigKey key, T value)}}} * Given the file name in the key, finds the corresponding Configuration and sets the new value for that key. * When a Configuration for a given file is constructed, all entries are read from that file and stored in the list. * Method {{{saveAll}}} that saves all configurations using the persisters. * This method should be invoking when stopping the {{{base.config}}} module. * In {{{BugReportLogic}}} * Remove the file "distrib/email" and create a new one "distrib/conf/org.sophie2.main.func.help.conf". * Create a ConfigKey {{{userEmail}}} and add it as an extension for the help module. * Remove the method {{{readEmail}}} and use {{{getValue}}} in ConfigurationManager. * In {{{SAVE_EMAIL}}} use {{{setValue}}} in ConfigurationManager. * [3228], [3238], [3284] = Implementation = * [3228], [3238], [3284], [3427] (most of the work is here), [3428], [3430] Merged to the trunk in [3539]. = Testing = ^(Place the testing results here.)^ = Comments = ^(Write comments for this or later revisions here.)