| | * </dl> |
| | */ |
| | -public final class Workspace implements KeyConfiguration { |
| | - /** |
| | - * Main configuration values, single text strings. |
| | - */ |
| | - private final Map<Key, Property<?>> mValues = Map.ofEntries( |
| | - entry( KEY_META_VERSION, asStringProperty( getVersion() ) ), |
| | - entry( KEY_META_NAME, asStringProperty( "default" ) ), |
| | - |
| | - entry( KEY_EDITOR_AUTOSAVE, asIntegerProperty( 30 ) ), |
| | - |
| | - entry( KEY_R_SCRIPT, asStringProperty( "" ) ), |
| | - entry( KEY_R_DIR, asFileProperty( USER_DIRECTORY ) ), |
| | - entry( KEY_R_DELIM_BEGAN, asStringProperty( R_DELIM_BEGAN_DEFAULT ) ), |
| | - entry( KEY_R_DELIM_ENDED, asStringProperty( R_DELIM_ENDED_DEFAULT ) ), |
| | - |
| | - entry( KEY_IMAGES_DIR, asFileProperty( USER_DIRECTORY ) ), |
| | - entry( KEY_IMAGES_ORDER, asStringProperty( PERSIST_IMAGES_DEFAULT ) ), |
| | - entry( KEY_IMAGES_RESIZE, asBooleanProperty( true ) ), |
| | - entry( KEY_IMAGES_SERVER, asStringProperty( DIAGRAM_SERVER_NAME ) ), |
| | - |
| | - entry( KEY_DEF_PATH, asFileProperty( DEFINITION_DEFAULT ) ), |
| | - entry( KEY_DEF_DELIM_BEGAN, asStringProperty( DEF_DELIM_BEGAN_DEFAULT ) ), |
| | - entry( KEY_DEF_DELIM_ENDED, asStringProperty( DEF_DELIM_ENDED_DEFAULT ) ), |
| | - |
| | - entry( KEY_UI_RECENT_DIR, asFileProperty( USER_DIRECTORY ) ), |
| | - entry( KEY_UI_RECENT_DOCUMENT, asFileProperty( DOCUMENT_DEFAULT ) ), |
| | - entry( KEY_UI_RECENT_DEFINITION, asFileProperty( DEFINITION_DEFAULT ) ), |
| | - entry( KEY_UI_RECENT_EXPORT, asFileProperty( PDF_DEFAULT ) ), |
| | - |
| | - //@formatter:off |
| | - entry( KEY_UI_FONT_EDITOR_NAME, asStringProperty( FONT_NAME_EDITOR_DEFAULT ) ), |
| | - entry( KEY_UI_FONT_EDITOR_SIZE, asDoubleProperty( FONT_SIZE_EDITOR_DEFAULT ) ), |
| | - entry( KEY_UI_FONT_PREVIEW_NAME, asStringProperty( FONT_NAME_PREVIEW_DEFAULT ) ), |
| | - entry( KEY_UI_FONT_PREVIEW_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_DEFAULT ) ), |
| | - entry( KEY_UI_FONT_PREVIEW_MONO_NAME, asStringProperty( FONT_NAME_PREVIEW_MONO_NAME_DEFAULT ) ), |
| | - entry( KEY_UI_FONT_PREVIEW_MONO_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_MONO_SIZE_DEFAULT ) ), |
| | - |
| | - entry( KEY_UI_WINDOW_X, asDoubleProperty( WINDOW_X_DEFAULT ) ), |
| | - entry( KEY_UI_WINDOW_Y, asDoubleProperty( WINDOW_Y_DEFAULT ) ), |
| | - entry( KEY_UI_WINDOW_W, asDoubleProperty( WINDOW_W_DEFAULT ) ), |
| | - entry( KEY_UI_WINDOW_H, asDoubleProperty( WINDOW_H_DEFAULT ) ), |
| | - entry( KEY_UI_WINDOW_MAX, asBooleanProperty() ), |
| | - entry( KEY_UI_WINDOW_FULL, asBooleanProperty() ), |
| | - |
| | - entry( KEY_UI_SKIN_SELECTION, asSkinProperty( SKIN_DEFAULT ) ), |
| | - entry( KEY_UI_SKIN_CUSTOM, asFileProperty( SKIN_CUSTOM_DEFAULT ) ), |
| | - |
| | - entry( KEY_UI_PREVIEW_STYLESHEET, asFileProperty( PREVIEW_CUSTOM_DEFAULT ) ), |
| | - |
| | - entry( KEY_LANGUAGE_LOCALE, asLocaleProperty( LOCALE_DEFAULT ) ), |
| | - |
| | - entry( KEY_TYPESET_CONTEXT_CLEAN, asBooleanProperty( true ) ), |
| | - entry( KEY_TYPESET_CONTEXT_THEMES_PATH, asFileProperty( USER_DIRECTORY ) ), |
| | - entry( KEY_TYPESET_CONTEXT_THEME_SELECTION, asStringProperty( "boschet" ) ), |
| | - entry( KEY_TYPESET_TYPOGRAPHY_QUOTES, asBooleanProperty( true ) ) |
| | - //@formatter:on |
| | - ); |
| | - |
| | - /** |
| | - * Sets of configuration values, all the same type (e.g., file names), |
| | - * where the key name doesn't change per set. |
| | - */ |
| | - private final Map<Key, SetProperty<?>> mSets = Map.ofEntries( |
| | - entry( |
| | - KEY_UI_RECENT_OPEN_PATH, |
| | - createSetProperty( new HashSet<String>() ) |
| | - ) |
| | - ); |
| | - |
| | - /** |
| | - * Lists of configuration values, such as key-value pairs where both the |
| | - * key name and the value must be preserved per list. |
| | - */ |
| | - private final Map<Key, ListProperty<?>> mLists = Map.ofEntries( |
| | - entry( |
| | - KEY_DOC_META, |
| | - createListProperty( new LinkedList<Entry<String, String>>() ) |
| | - ) |
| | - ); |
| | - |
| | - /** |
| | - * Helps instantiate {@link Property} instances for XML configuration items. |
| | - */ |
| | - private static final Map<Class<?>, Function<String, Object>> UNMARSHALL = |
| | - Map.of( |
| | - LocaleProperty.class, LocaleProperty::parseLocale, |
| | - SimpleBooleanProperty.class, Boolean::parseBoolean, |
| | - SimpleIntegerProperty.class, Integer::parseInt, |
| | - SimpleDoubleProperty.class, Double::parseDouble, |
| | - SimpleFloatProperty.class, Float::parseFloat, |
| | - SimpleStringProperty.class, String::new, |
| | - SimpleObjectProperty.class, String::new, |
| | - SkinProperty.class, String::new, |
| | - FileProperty.class, File::new |
| | - ); |
| | - |
| | - /** |
| | - * The asymmetry with respect to {@link #UNMARSHALL} is because most objects |
| | - * can simply call {@link Object#toString()} to convert the value to a string. |
| | - */ |
| | - private static final Map<Class<?>, Function<String, Object>> MARSHALL = |
| | - Map.of( |
| | - LocaleProperty.class, LocaleProperty::toLanguageTag |
| | - ); |
| | - |
| | - /** |
| | - * Converts the given {@link Property} value to a string. |
| | - * |
| | - * @param property The {@link Property} to convert. |
| | - * @return A string representation of the given property, or the empty |
| | - * string if no conversion was possible. |
| | - */ |
| | - private static String marshall( final Property<?> property ) { |
| | - final var v = property.getValue(); |
| | - |
| | - return v == null |
| | - ? "" |
| | - : MARSHALL |
| | - .getOrDefault( property.getClass(), __ -> property.getValue() ) |
| | - .apply( v.toString() ) |
| | - .toString(); |
| | - } |
| | - |
| | - private static Object unmarshall( |
| | - final Property<?> property, final Object configValue ) { |
| | - final var v = configValue.toString(); |
| | - |
| | - return UNMARSHALL |
| | - .getOrDefault( property.getClass(), value -> property.getValue() ) |
| | - .apply( v ); |
| | - } |
| | - |
| | - /** |
| | - * Creates an instance of {@link ObservableList} that is based on a |
| | - * modifiable observable array list for the given items. |
| | - * |
| | - * @param items The items to wrap in an observable list. |
| | - * @param <E> The type of items to add to the list. |
| | - * @return An observable property that can have its contents modified. |
| | - */ |
| | - public static <E> ObservableList<E> listProperty( final Set<E> items ) { |
| | - return new SimpleListProperty<>( observableArrayList( items ) ); |
| | - } |
| | - |
| | - private static <E> SetProperty<E> createSetProperty( final Set<E> set ) { |
| | - return new SimpleSetProperty<>( observableSet( set ) ); |
| | - } |
| | - |
| | - private static <E> ListProperty<E> createListProperty( final List<E> list ) { |
| | - return new SimpleListProperty<>( observableArrayList( list ) ); |
| | - } |
| | - |
| | - private static StringProperty asStringProperty( final String value ) { |
| | - return new SimpleStringProperty( value ); |
| | - } |
| | - |
| | - private static BooleanProperty asBooleanProperty() { |
| | - return new SimpleBooleanProperty(); |
| | - } |
| | - |
| | - /** |
| | - * @param value Default value. |
| | - */ |
| | - @SuppressWarnings( "SameParameterValue" ) |
| | - private static BooleanProperty asBooleanProperty( final boolean value ) { |
| | - return new SimpleBooleanProperty( value ); |
| | - } |
| | - |
| | - /** |
| | - * @param value Default value. |
| | - */ |
| | - @SuppressWarnings( "SameParameterValue" ) |
| | - private static IntegerProperty asIntegerProperty( final int value ) { |
| | - return new SimpleIntegerProperty( value ); |
| | - } |
| | - |
| | - /** |
| | - * @param value Default value. |
| | - */ |
| | - private static DoubleProperty asDoubleProperty( final double value ) { |
| | - return new SimpleDoubleProperty( value ); |
| | - } |
| | - |
| | - /** |
| | - * @param value Default value. |
| | - */ |
| | - private static FileProperty asFileProperty( final File value ) { |
| | - return new FileProperty( value ); |
| | - } |
| | - |
| | - /** |
| | - * @param value Default value. |
| | - */ |
| | - @SuppressWarnings( "SameParameterValue" ) |
| | - private static LocaleProperty asLocaleProperty( final Locale value ) { |
| | - return new LocaleProperty( value ); |
| | - } |
| | - |
| | - /** |
| | - * @param value Default value. |
| | - */ |
| | - @SuppressWarnings( "SameParameterValue" ) |
| | - private static SkinProperty asSkinProperty( final String value ) { |
| | - return new SkinProperty( value ); |
| | - } |
| | - |
| | - /** |
| | - * Creates a new {@link Workspace} that will attempt to load the users' |
| | - * preferences. If the configuration file cannot be loaded, the workspace |
| | - * settings returns default values. |
| | - */ |
| | - public Workspace() { |
| | - load(); |
| | - } |
| | - |
| | - /** |
| | - * Attempts to load the app's configuration file. |
| | - */ |
| | - private void load() { |
| | - final var store = createXmlStore(); |
| | - store.load( FILE_PREFERENCES ); |
| | - |
| | - mValues.keySet().forEach( key -> { |
| | - try { |
| | - final var storeValue = store.getValue( key ); |
| | - final var property = valuesProperty( key ); |
| | - |
| | - property.setValue( unmarshall( property, storeValue ) ); |
| | - } catch( final NoSuchElementException ignored ) { |
| | - // When no configuration (item), use the default value. |
| | - } |
| | - } ); |
| | - |
| | - mSets.keySet().forEach( key -> { |
| | - final var set = store.getSet( key ); |
| | - final SetProperty<String> property = setsProperty( key ); |
| | - |
| | - property.setValue( observableSet( set ) ); |
| | - } ); |
| | - |
| | - mLists.keySet().forEach( key -> { |
| | - final var map = store.getMap( key ); |
| | - final ListProperty<Entry<String, String>> property = listsProperty( key ); |
| | - final var list = map |
| | - .entrySet() |
| | - .stream() |
| | - .toList(); |
| | - |
| | - property.setValue( observableArrayList( list ) ); |
| | - } ); |
| | - } |
| | - |
| | - /** |
| | - * Saves the current workspace. |
| | - */ |
| | - public void save() { |
| | - final var store = createXmlStore(); |
| | - |
| | - try { |
| | - // Update the string values to include the application version. |
| | - valuesProperty( KEY_META_VERSION ).setValue( getVersion() ); |
| | - |
| | - mValues.forEach( ( k, v ) -> store.setValue( k, marshall( v ) ) ); |
| | - mSets.forEach( store::setSet ); |
| | - mLists.forEach( store::setMap ); |
| | - |
| | - store.save( FILE_PREFERENCES ); |
| | - } catch( final Exception ex ) { |
| | - clue( ex ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Returns a value that represents a setting in the application that the user |
| | - * may configure, either directly or indirectly. |
| | - * |
| | - * @param key The reference to the users' preference stored in deference |
| | - * of app reëntrance. |
| | - * @return An observable property to be persisted. |
| | - */ |
| | - @SuppressWarnings( "unchecked" ) |
| | - public <T, U extends Property<T>> U valuesProperty( final Key key ) { |
| | - assert key != null; |
| | - return (U) mValues.get( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns a set of values that represent a setting in the application that |
| | - * the user may configure, either directly or indirectly. The property |
| | - * returned is backed by a {@link Set}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return An observable property to be persisted. |
| | - */ |
| | - @SuppressWarnings( "unchecked" ) |
| | - public <T> SetProperty<T> setsProperty( final Key key ) { |
| | - assert key != null; |
| | - return (SetProperty<T>) mSets.get( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns a list of values that represent a setting in the application that |
| | - * the user may configure, either directly or indirectly. The property |
| | - * returned is backed by a mutable {@link List}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return An observable property to be persisted. |
| | - */ |
| | - @SuppressWarnings( "unchecked" ) |
| | - public <K, V> ListProperty<Entry<K, V>> listsProperty( final Key key ) { |
| | - assert key != null; |
| | - return (ListProperty<Entry<K, V>>) mLists.get( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link String} {@link Property} associated with the given |
| | - * {@link Key} from the internal list of preference values. The caller |
| | - * must be sure that the given {@link Key} is associated with a {@link File} |
| | - * {@link Property}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - public StringProperty stringProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Boolean} {@link Property} associated with the given |
| | - * {@link Key} from the internal list of preference values. The caller |
| | - * must be sure that the given {@link Key} is associated with a {@link File} |
| | - * {@link Property}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - public BooleanProperty booleanProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Integer} {@link Property} associated with the given |
| | - * {@link Key} from the internal list of preference values. The caller |
| | - * must be sure that the given {@link Key} is associated with a {@link File} |
| | - * {@link Property}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - public IntegerProperty integerProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Double} {@link Property} associated with the given |
| | - * {@link Key} from the internal list of preference values. The caller |
| | - * must be sure that the given {@link Key} is associated with a {@link File} |
| | - * {@link Property}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - public DoubleProperty doubleProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link File} {@link Property} associated with the given |
| | - * {@link Key} from the internal list of preference values. The caller |
| | - * must be sure that the given {@link Key} is associated with a {@link File} |
| | - * {@link Property}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - public ObjectProperty<File> fileProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Locale} {@link Property} associated with the given |
| | - * {@link Key} from the internal list of preference values. The caller |
| | - * must be sure that the given {@link Key} is associated with a {@link File} |
| | - * {@link Property}. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - public LocaleProperty localeProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - public ObjectProperty<String> skinProperty( final Key key ) { |
| | - assert key != null; |
| | - return valuesProperty( key ); |
| | - } |
| | - |
| | - @Override |
| | - public String getString( final Key key ) { |
| | - assert key != null; |
| | - return stringProperty( key ).get(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Boolean} preference value associated with the given |
| | - * {@link Key}. The caller must be sure that the given {@link Key} is |
| | - * associated with a value that matches the return type. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - @Override |
| | - public boolean getBoolean( final Key key ) { |
| | - assert key != null; |
| | - return booleanProperty( key ).get(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Integer} preference value associated with the given |
| | - * {@link Key}. The caller must be sure that the given {@link Key} is |
| | - * associated with a value that matches the return type. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - @Override |
| | - public int getInteger( final Key key ) { |
| | - assert key != null; |
| | - return integerProperty( key ).get(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link Double} preference value associated with the given |
| | - * {@link Key}. The caller must be sure that the given {@link Key} is |
| | - * associated with a value that matches the return type. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - @Override |
| | - public double getDouble( final Key key ) { |
| | - assert key != null; |
| | - return doubleProperty( key ).get(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the {@link File} preference value associated with the given |
| | - * {@link Key}. The caller must be sure that the given {@link Key} is |
| | - * associated with a value that matches the return type. |
| | - * |
| | - * @param key The {@link Key} associated with a preference value. |
| | - * @return The value associated with the given {@link Key}. |
| | - */ |
| | - @Override |
| | +public final class Workspace { |
| | + |
| | + /** |
| | + * Main configuration values, single text strings. |
| | + */ |
| | + private final Map<Key, Property<?>> mValues = Map.ofEntries( |
| | + entry( KEY_META_VERSION, asStringProperty( getVersion() ) ), |
| | + entry( KEY_META_NAME, asStringProperty( "default" ) ), |
| | + |
| | + entry( KEY_EDITOR_AUTOSAVE, asIntegerProperty( 30 ) ), |
| | + |
| | + entry( KEY_R_SCRIPT, asStringProperty( "" ) ), |
| | + entry( KEY_R_DIR, asFileProperty( USER_DIRECTORY ) ), |
| | + entry( KEY_R_DELIM_BEGAN, asStringProperty( R_DELIM_BEGAN_DEFAULT ) ), |
| | + entry( KEY_R_DELIM_ENDED, asStringProperty( R_DELIM_ENDED_DEFAULT ) ), |
| | + |
| | + entry( KEY_IMAGES_DIR, asFileProperty( USER_DIRECTORY ) ), |
| | + entry( KEY_IMAGES_ORDER, asStringProperty( PERSIST_IMAGES_DEFAULT ) ), |
| | + entry( KEY_IMAGES_RESIZE, asBooleanProperty( true ) ), |
| | + entry( KEY_IMAGES_SERVER, asStringProperty( DIAGRAM_SERVER_NAME ) ), |
| | + |
| | + entry( KEY_DEF_PATH, asFileProperty( DEFINITION_DEFAULT ) ), |
| | + entry( KEY_DEF_DELIM_BEGAN, asStringProperty( DEF_DELIM_BEGAN_DEFAULT ) ), |
| | + entry( KEY_DEF_DELIM_ENDED, asStringProperty( DEF_DELIM_ENDED_DEFAULT ) ), |
| | + |
| | + entry( KEY_UI_RECENT_DIR, asFileProperty( USER_DIRECTORY ) ), |
| | + entry( KEY_UI_RECENT_DOCUMENT, asFileProperty( DOCUMENT_DEFAULT ) ), |
| | + entry( KEY_UI_RECENT_DEFINITION, asFileProperty( DEFINITION_DEFAULT ) ), |
| | + entry( KEY_UI_RECENT_EXPORT, asFileProperty( PDF_DEFAULT ) ), |
| | + |
| | + //@formatter:off |
| | + entry( KEY_UI_FONT_EDITOR_NAME, asStringProperty( FONT_NAME_EDITOR_DEFAULT ) ), |
| | + entry( KEY_UI_FONT_EDITOR_SIZE, asDoubleProperty( FONT_SIZE_EDITOR_DEFAULT ) ), |
| | + entry( KEY_UI_FONT_PREVIEW_NAME, asStringProperty( FONT_NAME_PREVIEW_DEFAULT ) ), |
| | + entry( KEY_UI_FONT_PREVIEW_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_DEFAULT ) ), |
| | + entry( KEY_UI_FONT_PREVIEW_MONO_NAME, asStringProperty( FONT_NAME_PREVIEW_MONO_NAME_DEFAULT ) ), |
| | + entry( KEY_UI_FONT_PREVIEW_MONO_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_MONO_SIZE_DEFAULT ) ), |
| | + |
| | + entry( KEY_UI_WINDOW_X, asDoubleProperty( WINDOW_X_DEFAULT ) ), |
| | + entry( KEY_UI_WINDOW_Y, asDoubleProperty( WINDOW_Y_DEFAULT ) ), |
| | + entry( KEY_UI_WINDOW_W, asDoubleProperty( WINDOW_W_DEFAULT ) ), |
| | + entry( KEY_UI_WINDOW_H, asDoubleProperty( WINDOW_H_DEFAULT ) ), |
| | + entry( KEY_UI_WINDOW_MAX, asBooleanProperty() ), |
| | + entry( KEY_UI_WINDOW_FULL, asBooleanProperty() ), |
| | + |
| | + entry( KEY_UI_SKIN_SELECTION, asSkinProperty( SKIN_DEFAULT ) ), |
| | + entry( KEY_UI_SKIN_CUSTOM, asFileProperty( SKIN_CUSTOM_DEFAULT ) ), |
| | + |
| | + entry( KEY_UI_PREVIEW_STYLESHEET, asFileProperty( PREVIEW_CUSTOM_DEFAULT ) ), |
| | + |
| | + entry( KEY_LANGUAGE_LOCALE, asLocaleProperty( LOCALE_DEFAULT ) ), |
| | + |
| | + entry( KEY_TYPESET_CONTEXT_CLEAN, asBooleanProperty( true ) ), |
| | + entry( KEY_TYPESET_CONTEXT_THEMES_PATH, asFileProperty( USER_DIRECTORY ) ), |
| | + entry( KEY_TYPESET_CONTEXT_THEME_SELECTION, asStringProperty( "boschet" ) ), |
| | + entry( KEY_TYPESET_TYPOGRAPHY_QUOTES, asBooleanProperty( true ) ) |
| | + //@formatter:on |
| | + ); |
| | + |
| | + /** |
| | + * Sets of configuration values, all the same type (e.g., file names), |
| | + * where the key name doesn't change per set. |
| | + */ |
| | + private final Map<Key, SetProperty<?>> mSets = Map.ofEntries( |
| | + entry( |
| | + KEY_UI_RECENT_OPEN_PATH, |
| | + createSetProperty( new HashSet<String>() ) |
| | + ) |
| | + ); |
| | + |
| | + /** |
| | + * Lists of configuration values, such as key-value pairs where both the |
| | + * key name and the value must be preserved per list. |
| | + */ |
| | + private final Map<Key, ListProperty<?>> mLists = Map.ofEntries( |
| | + entry( |
| | + KEY_DOC_META, |
| | + createListProperty( new LinkedList<Entry<String, String>>() ) |
| | + ) |
| | + ); |
| | + |
| | + /** |
| | + * Helps instantiate {@link Property} instances for XML configuration items. |
| | + */ |
| | + private static final Map<Class<?>, Function<String, Object>> UNMARSHALL = |
| | + Map.of( |
| | + LocaleProperty.class, LocaleProperty::parseLocale, |
| | + SimpleBooleanProperty.class, Boolean::parseBoolean, |
| | + SimpleIntegerProperty.class, Integer::parseInt, |
| | + SimpleDoubleProperty.class, Double::parseDouble, |
| | + SimpleFloatProperty.class, Float::parseFloat, |
| | + SimpleStringProperty.class, String::new, |
| | + SimpleObjectProperty.class, String::new, |
| | + SkinProperty.class, String::new, |
| | + FileProperty.class, File::new |
| | + ); |
| | + |
| | + /** |
| | + * The asymmetry with respect to {@link #UNMARSHALL} is because most objects |
| | + * can simply call {@link Object#toString()} to convert the value to a string. |
| | + */ |
| | + private static final Map<Class<?>, Function<String, Object>> MARSHALL = |
| | + Map.of( |
| | + LocaleProperty.class, LocaleProperty::toLanguageTag |
| | + ); |
| | + |
| | + /** |
| | + * Converts the given {@link Property} value to a string. |
| | + * |
| | + * @param property The {@link Property} to convert. |
| | + * @return A string representation of the given property, or the empty |
| | + * string if no conversion was possible. |
| | + */ |
| | + private static String marshall( final Property<?> property ) { |
| | + final var v = property.getValue(); |
| | + |
| | + return v == null |
| | + ? "" |
| | + : MARSHALL |
| | + .getOrDefault( property.getClass(), __ -> property.getValue() ) |
| | + .apply( v.toString() ) |
| | + .toString(); |
| | + } |
| | + |
| | + private static Object unmarshall( |
| | + final Property<?> property, final Object configValue ) { |
| | + final var v = configValue.toString(); |
| | + |
| | + return UNMARSHALL |
| | + .getOrDefault( property.getClass(), value -> property.getValue() ) |
| | + .apply( v ); |
| | + } |
| | + |
| | + /** |
| | + * Creates an instance of {@link ObservableList} that is based on a |
| | + * modifiable observable array list for the given items. |
| | + * |
| | + * @param items The items to wrap in an observable list. |
| | + * @param <E> The type of items to add to the list. |
| | + * @return An observable property that can have its contents modified. |
| | + */ |
| | + public static <E> ObservableList<E> listProperty( final Set<E> items ) { |
| | + return new SimpleListProperty<>( observableArrayList( items ) ); |
| | + } |
| | + |
| | + private static <E> SetProperty<E> createSetProperty( final Set<E> set ) { |
| | + return new SimpleSetProperty<>( observableSet( set ) ); |
| | + } |
| | + |
| | + private static <E> ListProperty<E> createListProperty( final List<E> list ) { |
| | + return new SimpleListProperty<>( observableArrayList( list ) ); |
| | + } |
| | + |
| | + private static StringProperty asStringProperty( final String value ) { |
| | + return new SimpleStringProperty( value ); |
| | + } |
| | + |
| | + private static BooleanProperty asBooleanProperty() { |
| | + return new SimpleBooleanProperty(); |
| | + } |
| | + |
| | + /** |
| | + * @param value Default value. |
| | + */ |
| | + @SuppressWarnings( "SameParameterValue" ) |
| | + private static BooleanProperty asBooleanProperty( final boolean value ) { |
| | + return new SimpleBooleanProperty( value ); |
| | + } |
| | + |
| | + /** |
| | + * @param value Default value. |
| | + */ |
| | + @SuppressWarnings( "SameParameterValue" ) |
| | + private static IntegerProperty asIntegerProperty( final int value ) { |
| | + return new SimpleIntegerProperty( value ); |
| | + } |
| | + |
| | + /** |
| | + * @param value Default value. |
| | + */ |
| | + private static DoubleProperty asDoubleProperty( final double value ) { |
| | + return new SimpleDoubleProperty( value ); |
| | + } |
| | + |
| | + /** |
| | + * @param value Default value. |
| | + */ |
| | + private static FileProperty asFileProperty( final File value ) { |
| | + return new FileProperty( value ); |
| | + } |
| | + |
| | + /** |
| | + * @param value Default value. |
| | + */ |
| | + @SuppressWarnings( "SameParameterValue" ) |
| | + private static LocaleProperty asLocaleProperty( final Locale value ) { |
| | + return new LocaleProperty( value ); |
| | + } |
| | + |
| | + /** |
| | + * @param value Default value. |
| | + */ |
| | + @SuppressWarnings( "SameParameterValue" ) |
| | + private static SkinProperty asSkinProperty( final String value ) { |
| | + return new SkinProperty( value ); |
| | + } |
| | + |
| | + /** |
| | + * Creates a new {@link Workspace} that will attempt to load the users' |
| | + * preferences. If the configuration file cannot be loaded, the workspace |
| | + * settings returns default values. |
| | + */ |
| | + public Workspace() { |
| | + load(); |
| | + } |
| | + |
| | + /** |
| | + * Attempts to load the app's configuration file. |
| | + */ |
| | + private void load() { |
| | + final var store = createXmlStore(); |
| | + store.load( FILE_PREFERENCES ); |
| | + |
| | + mValues.keySet().forEach( key -> { |
| | + try { |
| | + final var storeValue = store.getValue( key ); |
| | + final var property = valuesProperty( key ); |
| | + |
| | + property.setValue( unmarshall( property, storeValue ) ); |
| | + } catch( final NoSuchElementException ignored ) { |
| | + // When no configuration (item), use the default value. |
| | + } |
| | + } ); |
| | + |
| | + mSets.keySet().forEach( key -> { |
| | + final var set = store.getSet( key ); |
| | + final SetProperty<String> property = setsProperty( key ); |
| | + |
| | + property.setValue( observableSet( set ) ); |
| | + } ); |
| | + |
| | + mLists.keySet().forEach( key -> { |
| | + final var map = store.getMap( key ); |
| | + final ListProperty<Entry<String, String>> property = listsProperty( key ); |
| | + final var list = map |
| | + .entrySet() |
| | + .stream() |
| | + .toList(); |
| | + |
| | + property.setValue( observableArrayList( list ) ); |
| | + } ); |
| | + } |
| | + |
| | + /** |
| | + * Saves the current workspace. |
| | + */ |
| | + public void save() { |
| | + final var store = createXmlStore(); |
| | + |
| | + try { |
| | + // Update the string values to include the application version. |
| | + valuesProperty( KEY_META_VERSION ).setValue( getVersion() ); |
| | + |
| | + mValues.forEach( ( k, v ) -> store.setValue( k, marshall( v ) ) ); |
| | + mSets.forEach( store::setSet ); |
| | + mLists.forEach( store::setMap ); |
| | + |
| | + store.save( FILE_PREFERENCES ); |
| | + } catch( final Exception ex ) { |
| | + clue( ex ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Returns a value that represents a setting in the application that the user |
| | + * may configure, either directly or indirectly. |
| | + * |
| | + * @param key The reference to the users' preference stored in deference |
| | + * of app reëntrance. |
| | + * @return An observable property to be persisted. |
| | + */ |
| | + @SuppressWarnings( "unchecked" ) |
| | + public <T, U extends Property<T>> U valuesProperty( final Key key ) { |
| | + assert key != null; |
| | + return (U) mValues.get( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns a set of values that represent a setting in the application that |
| | + * the user may configure, either directly or indirectly. The property |
| | + * returned is backed by a {@link Set}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return An observable property to be persisted. |
| | + */ |
| | + @SuppressWarnings( "unchecked" ) |
| | + public <T> SetProperty<T> setsProperty( final Key key ) { |
| | + assert key != null; |
| | + return (SetProperty<T>) mSets.get( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns a list of values that represent a setting in the application that |
| | + * the user may configure, either directly or indirectly. The property |
| | + * returned is backed by a mutable {@link List}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return An observable property to be persisted. |
| | + */ |
| | + @SuppressWarnings( "unchecked" ) |
| | + public <K, V> ListProperty<Entry<K, V>> listsProperty( final Key key ) { |
| | + assert key != null; |
| | + return (ListProperty<Entry<K, V>>) mLists.get( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link String} {@link Property} associated with the given |
| | + * {@link Key} from the internal list of preference values. The caller |
| | + * must be sure that the given {@link Key} is associated with a {@link File} |
| | + * {@link Property}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public StringProperty stringProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Boolean} {@link Property} associated with the given |
| | + * {@link Key} from the internal list of preference values. The caller |
| | + * must be sure that the given {@link Key} is associated with a {@link File} |
| | + * {@link Property}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public BooleanProperty booleanProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Integer} {@link Property} associated with the given |
| | + * {@link Key} from the internal list of preference values. The caller |
| | + * must be sure that the given {@link Key} is associated with a {@link File} |
| | + * {@link Property}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public IntegerProperty integerProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Double} {@link Property} associated with the given |
| | + * {@link Key} from the internal list of preference values. The caller |
| | + * must be sure that the given {@link Key} is associated with a {@link File} |
| | + * {@link Property}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public DoubleProperty doubleProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link File} {@link Property} associated with the given |
| | + * {@link Key} from the internal list of preference values. The caller |
| | + * must be sure that the given {@link Key} is associated with a {@link File} |
| | + * {@link Property}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public ObjectProperty<File> fileProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Locale} {@link Property} associated with the given |
| | + * {@link Key} from the internal list of preference values. The caller |
| | + * must be sure that the given {@link Key} is associated with a {@link File} |
| | + * {@link Property}. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public LocaleProperty localeProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + public ObjectProperty<String> skinProperty( final Key key ) { |
| | + assert key != null; |
| | + return valuesProperty( key ); |
| | + } |
| | + |
| | + public String getString( final Key key ) { |
| | + assert key != null; |
| | + return stringProperty( key ).get(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Boolean} preference value associated with the given |
| | + * {@link Key}. The caller must be sure that the given {@link Key} is |
| | + * associated with a value that matches the return type. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public boolean getBoolean( final Key key ) { |
| | + assert key != null; |
| | + return booleanProperty( key ).get(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Integer} preference value associated with the given |
| | + * {@link Key}. The caller must be sure that the given {@link Key} is |
| | + * associated with a value that matches the return type. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public int getInteger( final Key key ) { |
| | + assert key != null; |
| | + return integerProperty( key ).get(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link Double} preference value associated with the given |
| | + * {@link Key}. The caller must be sure that the given {@link Key} is |
| | + * associated with a value that matches the return type. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | + public double getDouble( final Key key ) { |
| | + assert key != null; |
| | + return doubleProperty( key ).get(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the {@link File} preference value associated with the given |
| | + * {@link Key}. The caller must be sure that the given {@link Key} is |
| | + * associated with a value that matches the return type. |
| | + * |
| | + * @param key The {@link Key} associated with a preference value. |
| | + * @return The value associated with the given {@link Key}. |
| | + */ |
| | public File getFile( final Key key ) { |
| | assert key != null; |