Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Change list to set for configuration preferences

AuthorDaveJarvis <email>
Date2020-12-20 18:00:34 GMT-0800
Commit973e6990d8ac8e0b0f36b83a2f1dc4053f5c699c
Parentabaedc9
Delta43 lines added, 162 lines removed, 119-line decrease
src/main/java/com/keenwrite/preferences/Workspace.java
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.fluent.Configurations;
+import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileHandler;
-import java.nio.file.Path;
-import java.util.*;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
import java.util.function.Function;
import static com.keenwrite.Bootstrap.APP_TITLE_LOWERCASE;
import static com.keenwrite.Constants.FILE_PREFERENCES;
import static com.keenwrite.StatusBarNotifier.clue;
-import static java.lang.String.format;
-import static javafx.collections.FXCollections.observableArrayList;
+import static javafx.collections.FXCollections.observableSet;
/**
*/
private final WorkspacePreferences mPreferences;
-
- /**
- * Application configuration file used to persist both user preferences and
- * project settings. The user preferences include items such as locale
- * and font sizes while the project settings include items such as last
- * opened directory and window sizes. That is, user preferences can be
- * changed directly by the user through the preferences dialog; whereas,
- * project settings reflect application interactions.
- */
- private final XMLConfiguration mConfig;
/**
* Constructs a new workspace with the given identifier. This will attempt
* to read the configuration file stored in the
*/
public Workspace( final WorkspacePreferences preferences ) {
mPreferences = preferences;
- mConfig = load();
+ load( preferences );
}
/**
* Attempts to load the {@link Constants#FILE_PREFERENCES} configuration file.
* If not found, this will fall back to an empty configuration file, leaving
* the application to fill in default values.
- *
- * @return Configuration instance representing last known state of the
- * application's user preferences and project settings.
*/
- private XMLConfiguration load() {
+ private void load( final WorkspacePreferences preferences ) {
try {
- final var config = new Configurations().xml( FILE_PREFERENCES );
+ final var config = createConfiguration();
- mPreferences.consumeValueKeys( ( key ) -> {
+ preferences.consumeValueKeys( ( key ) -> {
final var configValue = config.getProperty( key.toString() );
- final var propertyValue = mPreferences.valuesProperty( key );
+ final var propertyValue = preferences.valuesProperty( key );
propertyValue.setValue( unmarshall( propertyValue, configValue ) );
} );
- mPreferences.consumeListKeys( ( key ) -> {
- final var configList = config.getList( key.toString() );
- final var propertyList = mPreferences.listsProperty( key );
- propertyList.setValue( observableArrayList( configList ) );
+ preferences.consumeSetKeys( ( key ) -> {
+ final var configList =
+ new HashSet<>( config.getList( key.toString() ) );
+ final var propertySet = preferences.setsProperty( key );
+ propertySet.setValue( observableSet( configList ) );
} );
-
- return config;
} catch( final Exception ex ) {
clue( ex );
-
- final var config = new XMLConfiguration();
-
- // The root config key can only be set for an empty configuration file.
- config.setRootElementName( APP_TITLE_LOWERCASE );
- return config;
}
}
/**
* Saves the current workspace.
*/
public void save() {
try {
- mPreferences.consumeValues( ( key, value ) -> mConfig.setProperty(
+ final var config = createConfiguration();
+
+ // The root config key can only be set for an empty configuration file.
+ config.setRootElementName( APP_TITLE_LOWERCASE );
+
+ mPreferences.consumeValues( ( key, value ) -> config.setProperty(
key.toString(), value.getValue() )
);
- mPreferences.consumeLists(
+ mPreferences.consumeSets(
( key, set ) -> {
final String keyName = key.toString();
- set.forEach( ( value ) -> mConfig.addProperty( keyName, value ) );
+ set.forEach( ( value ) -> config.addProperty( keyName, value ) );
}
);
- new FileHandler( mConfig ).save( FILE_PREFERENCES );
+ new FileHandler( config ).save( FILE_PREFERENCES );
} catch( final Exception ex ) {
clue( ex );
}
- }
-
- private Object unmarshall(
- final Property<?> property, final Object configValue ) {
- return UNMARSHALL
- .getOrDefault( property.getClass(), ( value ) -> value )
- .apply( configValue.toString() );
- }
-
- /**
- * Delegates to {@link #set(Key, String)} after converting the given
- * {@link Path} to a string (using the absolute path).
- *
- * @param key The document key to change.
- * @param path Path to a file or directory to store in the settings.
- */
- public void set( final Key key, final Path path ) {
- set( key, toString( path ) );
- }
-
- /**
- * Delegates to {@link XMLConfiguration#setProperty(String, Object)} to
- * change the value for the given key. If the key doesn't exist, it will be
- * created in the hierarchy; otherwise, the existing value is overwritten.
- *
- * @param key The document key to change.
- * @param value The new value for the key.
- */
- public void set( final Key key, final String value ) {
- mConfig.setProperty( key.toString(), value );
- }
-
- /**
- * Returns the value for the key from the application settings.
- *
- * @param key The key to look up in the settings.
- * @param defaultValue The default value to return if the key is not set.
- * @return The value for the given key, or the given default if none found.
- */
- public String get( final Key key, final String defaultValue ) {
- final var prop = mConfig.getProperty( key.toString() );
- return prop == null ? defaultValue : prop.toString();
- }
-
- /**
- * Delegates to {@link XMLConfiguration#addProperty(String, Object)} to
- * add the given {@link File} to the list of files specified by the
- * given key.
- *
- * @param key The document hierarchy key name.
- * @param file Absolute path of filename stored at the given key.
- */
- public void addListItem( final Key key, final File file ) {
- mConfig.addProperty( key.toString(), toString( file ) );
- }
-
- /**
- * Returns the list of files opened for this {@link Workspace}.
- *
- * @param key The document hierarchy key name.
- * @return A non-null, possibly empty list of {@link File} instances.
- */
- public List<File> getListFiles( final Key key ) {
- final var items = getListItems( key );
- final var files = new HashSet<File>( items.size() );
- items.forEach( ( item ) -> {
- final var file = new File( item );
-
- if( file.exists() ) {
- files.add( file );
- }
- } );
-
- // Removes duplicate and missing files. The configuration is re-populated
- // on saving because the UI will re-open the files in the list that's
- // returned by this method. Re-opening adds the files back to the config.
- // This ensures that the list never grows beyond a reasonable number.
- mConfig.clearProperty( key.toString() );
-
- return new ArrayList<>( files );
- }
-
- /**
- * Returns a list of items for a given key name.
- *
- * @param key The document hierarchy key name.
- * @return The list of strings in the document hierarchy corresponding to the
- * given key.
- */
- private List<String> getListItems( final Key key ) {
- return mConfig.getList( String.class, key.toString(), new ArrayList<>() );
}
/**
- * Removes the given file from the workspace so that when the application
- * is restarted, the file will not be automatically loaded. This calls
- * {@link XMLConfiguration#clearTree(String)} to remove the given file
- * from the list that corresponds to the key.
+ * Attempts to create a configuration that can read and write from the
+ * {@link Constants#FILE_PREFERENCES} file.
*
- * @param key The document hierarchy key name.
- * @param file The file to remove from the list files opened for editing.
+ * @return Configuration instance that can read and write project settings.
*/
- public void purgeListItem( final Key key, final File file ) {
- final var items = getListItems( key );
- final var index = items.indexOf( toString( file ) );
-
- // The list index is 0-based.
- if( index >= 0 ) {
- mConfig.clearTree( format( "%s(%d)", key, index ) );
- }
- }
-
- public WorkspacePreferences getPreferences() {
- return mPreferences;
- }
-
- private String toString( final Path path ) {
- return toString( new File( path.toFile() ) );
+ private XMLConfiguration createConfiguration() throws ConfigurationException {
+ return new Configurations().xml( FILE_PREFERENCES );
}
- private String toString( final File file ) {
- return file.getAbsolutePath();
+ private Object unmarshall(
+ final Property<?> property, final Object configValue ) {
+ return UNMARSHALL
+ .getOrDefault( property.getClass(), ( value ) -> value )
+ .apply( configValue.toString() );
}
}
src/main/java/com/keenwrite/preferences/WorkspacePreferences.java
//@formatter:on
- private final Map<Key, ListProperty<?>> LISTS = Map.ofEntries(
- entry( KEY_UI_FILES_PATH, new SimpleListProperty<File>() )
+ private final Map<Key, SetProperty<?>> SETS = Map.ofEntries(
+ entry( KEY_UI_FILES_PATH, new SimpleSetProperty<>() )
);
*/
@SuppressWarnings("unchecked")
- public <T> ListProperty<T> listsProperty( final Key key ) {
+ public <T> SetProperty<T> setsProperty( final Key key ) {
// The type that goes into the map must come out.
- return (ListProperty<T>) LISTS.get( key );
+ return (SetProperty<T>) SETS.get( key );
}
/**
* Calls the given consumer for all single-value keys. For lists, see
- * {@link #consumeLists(BiConsumer)}.
+ * {@link #consumeSets(BiConsumer)}.
*
* @param consumer Called to accept each preference key value.
* @param consumer Called to accept each preference key list.
*/
- public void consumeLists( final BiConsumer<Key, ListProperty<?>> consumer ) {
- LISTS.forEach( consumer );
+ public void consumeSets( final BiConsumer<Key, SetProperty<?>> consumer ) {
+ SETS.forEach( consumer );
}
public void consumeValueKeys( final Consumer<Key> consumer ) {
VALUES.keySet().forEach( consumer );
}
- public void consumeListKeys( final Consumer<Key> consumer ) {
- LISTS.keySet().forEach( consumer );
+ public void consumeSetKeys( final Consumer<Key> consumer ) {
+ SETS.keySet().forEach( consumer );
}