Class AppProperties<T extends AppExtension>

java.lang.Object
ca.corbett.extensions.AppProperties<T>

public abstract class AppProperties<T extends AppExtension> extends Object
This class encapsulates a PropertiesManager and an ExtensionManager together into one handy utility that client projects can use more easily than the old approach of having the client projects maintain these things separately.

Enabling and disabling
Both this class and ExtensionManager have methods for enabling and disabling an extension. But ExtensionManager knows nothing about this class. This class therefore tries to automatically reconcile these status flags across the two classes, and generally you shouldn't need to worry about it. If an extension is enabled or disabled in ExtensionManager, this class will notice the change and will enable or disable all extension properties as needed. This means that you shouldn't see properties from disabled extensions in the PropertiesDialog, but the values for them will still be saved and loaded to the propsFile. When you re-enable an extension, either in this class or in ExtensionManager, then its properties will once again show up in the PropertiesDialog.

Loading and saving
Unlike older releases of swing-extras (1.8 and prior), there's nothing wonky that client apps need to do in order to load AppProperties and Extensions. You can simply invoke load() in this class and it will all just work. Any extensions that were disabled the last time you invoked save() will be correctly loaded in a disabled state. This means that their property values will be loaded correctly, but they won't show up in the PropertiesDialog until the extension is enabled again.

Customizing the generated PropertiesDialog
When you invoke showPropertiesDialog(), this class will generate a default PropertiesDialog for you, and then show it to the user. But what if you want to customize the generated dialog before it's shown to the user? For example, you might want to make it larger than the default size, or add extra padding around the FormPanels, or customize the ActionPanel if it's an ActionPanelPropertiesDialog. You can do all of that by overriding the propertiesDialogCreated() method, which is invoked after the dialog is created but before it's shown to the user.

Refer to the swing-extras documentation for more information on how AppProperties works, and how applications can take full advantage of its features!

Since:
swing-extras 1.8, 2024-12-30
Author:
scorbo2
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    protected final ExtensionManager<T>
     
     
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    protected
    AppProperties(String appName, File propsFile, ExtensionManager<T> extManager)
    If your application has an ExtensionManager, you can supply it here and this class will handle loading and saving properties for all enabled extensions.
  • Method Summary

    Modifier and Type
    Method
    Description
    protected abstract List<AbstractProperty>
    Override this to specify whatever properties your application needs.
    Allows direct access to the underlying PropertiesManager, in case you need to interact directly with it (for example, to invoke generateUnrenderedFormPanels()).
    boolean
    isExtensionEnabled(String extName, boolean defaultValue)
    Reports whether the named extension is currently enabled.
    void
    If you want to take some action after props are loaded (for example, to set window dimensions or other ui state), you can override this method and put your updates AFTER you invoke super load().
    static String
    peek(File propsFile, String propName)
    Offers a peek directly into the given props file without going through the usual loading mechanism.
    protected void
    Subclasses can override this method to make adjustments to the PropertiesDialog after it's created, but before it's shown to the user.
    void
    Re-initializes the underlying PropertiesManager instance from scratch.
    void
    If you have specific extra properties you wish to set on save (window dimensions or other variable stuff), you can override this method and invoke super save() AFTER you have updated your property values.
    void
    setExtensionEnabled(String extName, boolean value)
    Enables or disables the specified extension.
    boolean
    Generates and shows an ExtensionManagerDialog to allow the user to view all currently loaded extensions, and to enable or disable them.
    boolean
    showExtensionDialog(Window owner, UpdateManager updateManager)
    Generates and shows an ExtensionManagerDialog to allow the user to view all currently loaded extensions, and to enable or disable them.
    boolean
    Generates and shows a PropertiesDialog to allow the user to view or change any of the current properties.
    boolean
    Generates and shows a PropertiesDialog to allow the user to view or change any of the current properties.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

  • Constructor Details

    • AppProperties

      protected AppProperties(String appName, File propsFile, ExtensionManager<T> extManager)
      If your application has an ExtensionManager, you can supply it here and this class will handle loading and saving properties for all enabled extensions.
      Parameters:
      appName - The name of this application.
      propsFile - A File in which properties will be stored for this application.
      extManager - An instance of your ExtensionManager implementation.
  • Method Details

    • peek

      public static String peek(File propsFile, String propName)
      Offers a peek directly into the given props file without going through the usual loading mechanism. This allows direct access to properties (in String form only) exactly as they currently exist in the given props file. This can be useful in rare cases where an extension needs to know a property value in order to initialize some other property value. The normal load mechanism prevents this because property values cannot be read until the AppProperties instance is fully initialized, leading to a circular problem.

      If the value does not exist or an error occurs while reading the props file, an empty string is returned.

      Parameters:
      propsFile - The properties file to read.
      propName - The fully qualified name of the property in question.
      Returns:
      The raw value in String form as it exists in the props file at the time of this call. May be empty.
    • load

      public void load()
      If you want to take some action after props are loaded (for example, to set window dimensions or other ui state), you can override this method and put your updates AFTER you invoke super load().
    • save

      public void save()
      If you have specific extra properties you wish to set on save (window dimensions or other variable stuff), you can override this method and invoke super save() AFTER you have updated your property values.

      But consider adding non-exposed properties for this purpose instead:

       IntegerProperty windowWidth = new IntegerProperty("window.width", "Window width",800);
       IntegerProperty windowHeight = new IntegerProperty("window.height", "Window height",600);
       windowWidth.setExposed(false); // prevents showing in PropertiesDialog
       windowHeight.setExposed(false); // prevents showing in PropertiesDialog
       

      That way, your non-exposed properties will be loaded and saved automatically via the normal mechanism, while still being hidden from the user in the PropertiesDialog. And, you would no longer need to override this method.

    • getPropertiesManager

      public PropertiesManager getPropertiesManager()
      Allows direct access to the underlying PropertiesManager, in case you need to interact directly with it (for example, to invoke generateUnrenderedFormPanels()).
      Returns:
      The underlying PropertiesManager instance.
    • showPropertiesDialog

      public boolean showPropertiesDialog(Frame owner)
      Generates and shows a PropertiesDialog to allow the user to view or change any of the current properties. If the user okays the dialog, changes are automatically saved.

      Note: Descendant classes can customize the PropertiesDialog before it is shown to the user! See the propertiesDialogCreated() method for details.

      Parameters:
      owner - The owning Frame (so we can make the dialog modal to that Frame).
      Returns:
      true if the user OK'd the dialog and changes were made - reload your UI!
    • showPropertiesDialog

      public boolean showPropertiesDialog(Frame owner, Alignment alignment)
      Generates and shows a PropertiesDialog to allow the user to view or change any of the current properties. If the user okays the dialog, changes are automatically saved.

      Note: Descendant classes can customize the PropertiesDialog before it is shown to the user! See the propertiesDialogCreated() method for details.

      Parameters:
      owner - The owning Frame (so we can make the dialog modal to that Frame).
      alignment - How the FormPanels should align themselves.
      Returns:
      true if the user OK'd the dialog and changes were made - reload your UI!
    • propertiesDialogCreated

      protected void propertiesDialogCreated(PropertiesDialog dialog)
      Subclasses can override this method to make adjustments to the PropertiesDialog after it's created, but before it's shown to the user. For example:
       @Override
       protected void propertiesDialogCreated(PropertiesDialog dialog) {
         dialog.setSize(800, 600); // make larger than default
         dialog.setMinimumSize(new Dimension(600, 400)); // but not smaller than this
         dialog.setBorderMargin(20); // add lots of extra padding around the FormPanels
      
         // If we're showing an ActionPanel, we can customize it here:
         if (dialog instanceof ActionPanelPropertiesDialog) {
           ActionPanelPropertiesDialog apd = (ActionPanelPropertiesDialog) dialog;
           apd.getActionPanel().getColorOptions().setFromTheme(ColorTheme.DARK);
         }
       }
       

      You can also get access to generated FormField instances using PropertiesDialog.findFormField(), if you want to make specific adjustments to the starting state of those form fields. However, it's better practice to add a FormFieldGenerationListener to your AbstractProperty instances for that purpose.

      Parameters:
      dialog - The PropertiesDialog that was just created, but has not yet been shown to the user.
    • showExtensionDialog

      public boolean showExtensionDialog(Window owner)
      Generates and shows an ExtensionManagerDialog to allow the user to view all currently loaded extensions, and to enable or disable them.

      Note: dynamic extension discovery and download will be disabled and hidden. Use showExtensionDialog(Window, UpdateManager) instead if you want this feature.

      Parameters:
      owner - The owning Frame (so we can make the dialog modal to that Frame).
      Returns:
      true if the user OK'd the dialog and changes were made - reload your UI!
    • showExtensionDialog

      public boolean showExtensionDialog(Window owner, UpdateManager updateManager)
      Generates and shows an ExtensionManagerDialog to allow the user to view all currently loaded extensions, and to enable or disable them. Additionally, the given UpdateManager will be queried to find and show a list of extensions available for download. The user can download new extensions or update existing ones using the "available" tab on the dialog.
    • isExtensionEnabled

      public boolean isExtensionEnabled(String extName, boolean defaultValue)
      Reports whether the named extension is currently enabled. If no such extension is found, this will return whatever defaultValue you specify. Note: this is shorthand for propsManager.getPropertiesInstance().getBoolean("extension.enabled."+extName, defaultValue);

      Note: enabled status is stored in the ExtensionManager as well as here. In the case of a discrepancy, the ExtensionManager will be considered the source of truth. That means that this method might have the side effect of enabling/disabling an extension here in AppProperties if we check and find that ExtensionManager's answer doesn't match ours.

      Parameters:
      extName - The fully qualified class name of the extension to check.
      defaultValue - A value to return if the status can't be found.
      Returns:
      Whether the named extension is enabled.
    • setExtensionEnabled

      public void setExtensionEnabled(String extName, boolean value)
      Enables or disables the specified extension. We will also update ExtensionManager, if we have one.
      Parameters:
      extName - The fully qualified class name of the extension to enable/disable
      value - The new enabled status for that extension.
    • createInternalProperties

      protected abstract List<AbstractProperty> createInternalProperties()
      Override this to specify whatever properties your application needs. This method will be invoked automatically upon creation, and whenever AppProperties is re-initialized.
      Returns:
      A List of zero or more AbstractProperty instances.
    • reinitialize

      public void reinitialize()
      Re-initializes the underlying PropertiesManager instance from scratch. This means both invoking our abstract createInternalProperties() method and also interrogating our ExtensionManager to get a list of all extension-supplied properties. This method is invoked automatically on initial creation. You can invoke it again later if you have manually added, removed, enabled, or disabled extensions, so that the list of properties is fully re-initialized to reflect the new state of things.

      For an example of why you might want to do this, consider an extension that supplies a bunch of options, some of which may appear as selectable options inside our own createInternalProperties(). For example, we have a dropdown of available application themes, but we only have one or two built-in themes. The list of additional themes is extension-supplied. If we disable that extension at runtime, we want to regenerate our combo box (which is created in createInternalProperties()) such that it no longer shows the extension-supplied options. Similarly, when we re-enable that extension later, we need to regenerate that combo box again so that the list of extension-supplied themes once again appear as selectable options.

      Basically, whenever the list of currently loaded and extensions changes, it's a good idea to reinitialize() this class to reflect those changes. (And, of course, to reload your UI, as there may be many other changes throughout your app as a result of enabling or disabling extensions).

      Note this method will end by invoking load() again to pick up whatever values were previously persisted.