Class AbstractProperty

java.lang.Object
ca.corbett.extras.properties.AbstractProperty
Direct Known Subclasses:
BooleanProperty, ButtonProperty, CollapsiblePanelProperty, ColorProperty, ComboProperty, DecimalProperty, DirectoryProperty, EnumProperty, FileProperty, FontProperty, HtmlLabelProperty, ImagePanelConfig, IntegerProperty, KeyStrokeProperty, LabelProperty, ListProperty, ListSubsetProperty, LogConsoleTheme, LogoProperty, LongTextProperty, LookAndFeelProperty, MarginsProperty, PanelProperty, PasswordProperty, ShortTextProperty, SliderProperty, WaveformConfig

public abstract class AbstractProperty extends Object
A generic abstract base class for property fields, to be used with PropertiesManager. The available field types correspond pretty tightly to the available types in the ca.corbett.forms.fields package.

The goal here is to make managing application preferences easy and consistent across apps. Instead of each application having code to load and save preferences, as well as provide a UI for the user to view and change them, the classes in this package allow applications to just define the preferences by type and name, and then you can use PropertiesManager to handle the loading and saving of them, and PropertiesDialog to handle the UI for them. The burden on the application itself is MUCH lighter.

The naming of preferences is important for organizing preferences into user-visible groups. Every property has a fully-qualified dot-separated name that is used by this package to determine what category a property belongs to. Refer to the constructor for example usages.

Note: I use the terms "property" and "preference" pretty much interchangeably throughout these docs. In hindsight maybe I should have called these "Preferences".

Since:
2024-12-30
Author:
scorbo2
  • Field Details

    • DEFAULT_CATEGORY

      public static final String DEFAULT_CATEGORY
      If a property is not explicitly given a category, it will be placed in a default category called "General".
      See Also:
    • DEFAULT_PROPERTY_NAME

      public static final String DEFAULT_PROPERTY_NAME
      If a property is not explicitly given a name, it will show up as "(unnamed property)".
      See Also:
    • fullyQualifiedName

      protected final String fullyQualifiedName
    • helpText

      protected String helpText
    • categoryName

      protected String categoryName
    • subCategoryName

      protected String subCategoryName
    • propertyName

      protected String propertyName
    • propertyLabel

      protected String propertyLabel
    • isExposed

      protected boolean isExposed
    • isEnabled

      protected boolean isEnabled
    • isInitiallyEditable

      protected boolean isInitiallyEditable
    • isInitiallyVisible

      protected boolean isInitiallyVisible
    • extraAttributes

      protected final Map<String,Object> extraAttributes
    • marginPadding

      protected Margins marginPadding
  • Constructor Details

    • AbstractProperty

      public AbstractProperty(String name, String label)
      Each property has a fully qualified name that can optionally specify a top-level category and up to one subcategory. The name should be dot separated to make use of category and subcategory. The format is as follows:
      [category.[subcategory.]]propertyName

      If category name is not specified, a default name of "General" will be used. If subcategory is not specified, a default name of "General" will be used Some examples:

      • UI.windowState - creates a property called "windowState" belonging to an implied subcategory of "General" within the "UI" category.
      • UI.window.state - creates a property called "state" in the subcategory of "window" within the top-level category of "UI".
      • windowState - creates a property called "windowState" in an implied top-level category of "General" with an implied subcategory of "General"
      • UI.window.state.isMaximized - creates a property called "state.isMaximized" within the "window" subcategory in the "UI" top-level category. Note that further dots after the second one are basically ignored and are considered part of the property name. So, you can't have sub-sub-categories.
      Parameters:
      name - A fully qualified name as described above. If null, will be "(unnamed field)".
      label - A human-readable label for this field. Largely ignored by this code.
  • Method Details

    • getFullyQualifiedName

      public String getFullyQualifiedName()
      Returns the fully qualified name of this property as it was passed to the constructor. See also getCategoryName(), getSubCategoryName(), and getPropertyName() to retrieve the individual components of the name separately.
      Returns:
      The fully qualified name of this property as it was passed to the constructor.
    • getCategoryName

      public String getCategoryName()
      Returns the category name for this property. If none was supplied to the constructor, then this will return the default category name of "General".
      Returns:
      The property's category name.
    • getSubCategoryName

      public String getSubCategoryName()
      Returns the subcategory name for this property, if one is defined.
      Returns:
      The property's subcategory name, or null if there is no subcategory for this property.
    • getPropertyName

      public String getPropertyName()
      Returns the property name without category or subcategory. Most of the time, you probably want to call getFullyQualifiedName() instead of this. For example, for a property named "UI.window.state", this method will return "state".
      Returns:
      The bare field name of this property.
    • getPropertyLabel

      public String getPropertyLabel()
      Returns the human-readable label for this property.
      Returns:
      A human-readable label.
    • setPropertyLabel

      public AbstractProperty setPropertyLabel(String label)
      Sets or updates the human-readable label for this property.
      Parameters:
      label - The new label value.
    • setExposed

      public AbstractProperty setExposed(boolean expose)
      Sets whether this property is to be exposed in the UI. Generally this is set once when the property is created and does not change, as opposed to isEnabled, which can vary at runtime. A field is only visible in the UI if (isExposed && isEnabled).

      Question: why would I want to mark a property as not exposed? - There may be internal preferences that your app uses that are never shown to the user directly. This might include stuff like storing window state (maximized, screen location, width, height, etc) that would make no sense to show in an application preferences dialog. You can use this method to "hide" a property in the PropertiesDialog while still being able to save and load it via PropertiesManager.

      Parameters:
      expose - True to expose this field in PropertiesDialog.
    • isExposed

      public boolean isExposed()
      Reports whether this property is meant to be exposed in the UI. Marking a property as not exposed is useful for storing additional internal information that is not meant to be fiddled with by the user directly (but can still be manipulated by the application).
      Returns:
      true if this field is to be shown in the PropertiesDialog (default true)
    • setEnabled

      public AbstractProperty setEnabled(boolean enable)
      Setting a property to disabled will prevent this property from appearing in the PropertiesDialog. It can therefore no longer be directly manipulated by the user. The difference between setEnabled(false) and setExposed(false) is that enabled/disabled are intended to be short term and dynamic, while exposed/not exposed are intended to be permanent attributes of that property. Basically isExposed=false means the property will never appear in the UI, whereas isEnabled=false means the property will temporarily ot appear in the UI (until enabled).
      Parameters:
      enable - Whether to enable or disable this property.
    • isEnabled

      public boolean isEnabled()
      Reports whether this property is currently enabled. If false, the property should be hidden temporarily in the UI (but may still contain a valid value).
      Returns:
      Whether this property is currently enabled.
    • isInitiallyEditable

      public boolean isInitiallyEditable()
      Reports whether this property will be editable when shown in the PropertiesDialog.

      Question: Why would I want a property to be read only? - a common use case with forms is to make certain controls visible/editable only if certain conditions are met elsewhere on the form. You can use isInitiallyEditable to set the initial state of this property when it is added to the PropertiesDialog (it can still be changed at runtime as a result of action handlers on other form fields).

      Another use case is for displaying programmatically configurable properties that can be set by the code but not by the user. It may still be desirable to show these properties on the PropertiesDialog and their current value, without allowing the user to change them. Unlike using a static label for this purpose, these properties will still save and load their values to and from properties.

    • setInitiallyEditable

      public AbstractProperty setInitiallyEditable(boolean initiallyEditable)
      Set whether this property is to be editable by default when shown on a PropertiesDialog. Note that this reflects the initial state of the property on the PropertiesDialog. This can be changed at runtime by action handlers on other form fields (for example, field B is only editable if field A contains a specific value).
    • isInitiallyVisible

      public boolean isInitiallyVisible()
      Reports whether this property will be visible when added to the PropertiesDialog.

      Question: Why would I want a property to be invisible? - a common use case with forms is to make certain controls visible/editable only if certain conditions are met elsewhere on the form. You can use isInitiallyVisible to set the initial state of this property when it is added to the PropertiesDialog (it can still be changed at runtime as a result of action handlers on other form fields).

    • setInitiallyVisible

      public AbstractProperty setInitiallyVisible(boolean initiallyVisible)
      Sets whether this property is to be visible by default when shown on a PropertiesDialog. Note that this reflects the initial state of the property on the PropertiesDialog. This can be changed at runtime by action handlers on other form fields (for example, field B is only visible if field A contains a specific value).
    • getHelpText

      public String getHelpText()
      Returns the optional help text for this property, if any is set, otherwise a blank string.
      Returns:
      The help text for this property, or empty string if not set.
    • setHelpText

      public AbstractProperty setHelpText(String helpText)
      Sets optional help text for this property. This will be used when generating the form field for this property, to give the user an informational icon on the generated form. Multi-line tooltips are supported by wrapping the contents in an <html> tag and using <br> to separate lines.
      Parameters:
      helpText - The new help text, or null to unset it.
    • setExtraAttribute

      public AbstractProperty setExtraAttribute(String name, Object value)
      Set an arbitrary extra attribute to this property. The given value is not validated nor used within this class. It's just extra data that can be attached by the caller. Any extra attributes set here will be passed on as-is to any generated FormField. However, they are NOT persisted by saveToProps.
      Parameters:
      name - The unique name of the value to set. Will overwrite any previous value by that name.
      value - The value to set.
    • getExtraAttribute

      public Object getExtraAttribute(String name)
      Returns a named extra attribute's value, if it exists.
      Parameters:
      name - The unique name of the value in question.
      Returns:
      The value associated with that name, or null if no such value.
    • clearExtraAttributes

      public void clearExtraAttributes()
      Removes all extra attributes and their associated values from this AbstractProperty.
    • clearExtraAttribute

      public void clearExtraAttribute(String name)
      Removes the value for the named extra attribute.
      Parameters:
      name - The unique name of the attribute in question.
    • setAllExtraAttributes

      public AbstractProperty setAllExtraAttributes(Map<String,Object> newAttributes)
      Clears any extra attributes currently held by this AbstractProperty and then accepts the given list of attributes. Any extra attributes set here will be passed on as-is to any generated FormField. However, they are NOT persisted by saveToProps.
      Parameters:
      newAttributes - A map of String name to some arbitrary Object value.
    • addAllExtraAttributes

      public AbstractProperty addAllExtraAttributes(Map<String,Object> newAttributes)
      Adds the map of extra attributes to our existing list. Any name conflicts will result in the existing values being overwritten by the new values. Any extra attributes set here will be passed on as-is to any generated FormField. However, they are NOT persisted by saveToProps.
      Parameters:
      newAttributes - A map of String name to some arbitrary Object value.
    • addLeftPadding

      public AbstractProperty addLeftPadding(int left)
      Will add the specified amount of left padding to the generated FormField's margins. This ADDS to the default margin value, it does not replace it.
    • addRightPadding

      public AbstractProperty addRightPadding(int right)
      Will add the specified amount of right padding to the generated FormField's margins. This ADDS to the default margin value, it does not replace it.
    • addTopPadding

      public AbstractProperty addTopPadding(int top)
      Will add the specified amount of top padding to the generated FormField's margins. This ADDS to the default margin value, it does not replace it.
    • addBottomPadding

      public AbstractProperty addBottomPadding(int bottom)
      Will add the specified amount of bottom padding to the generated FormField's margins. This ADDS to the default margin value, it does not replace it.
    • addInnerPadding

      public AbstractProperty addInnerPadding(int inner)
      Will add the specified amount of inner padding (internal spacing) to the generated FormField's margins. This ADDS to the default margin value, it does not replace it.
    • addPadding

      public AbstractProperty addPadding(int left, int top, int right, int bottom, int inner)
      Add the specified padding values to the generated FormField's margins. This ADDS to the default margin values, it does not replace them.
    • getMarginPadding

      public Margins getMarginPadding()
      Returns a copy of the margin padding that will be applied to the generated FormField. This method is for inspection only - to modify the values, use addPadding() or one of the other convenience methods instead.
    • isAllowsUserInput

      public boolean isAllowsUserInput()
      Most properties will generate FormField instances that allow user input, so the default value of "true" can be left alone. If your AbstractProperty implementation generates FormField instances that do not allow user input (for example, a static label), then you can override this method to return false. This is currently used only by the unit tests, to decide whether to test valid/invalid user inputs.
      Returns:
      true if the FormField generated by this property allows user input, false if it is read-only.
    • saveToProps

      public abstract void saveToProps(Properties props)
      Saves the current value(s) of this property to the given Properties instance.
      Parameters:
      props - Any Properties instance which will receive the value(s) of this property.
    • loadFromProps

      public abstract void loadFromProps(Properties props)
      Loads the value(s) for this property from the given Properties instance, overwriting any current value. The current value of this property will be used as a default value in the event that this property does not exist in the given Properties instance.
      Parameters:
      props - Any Properties instance which contains value(s) for this property.
    • generateFormFieldImpl

      protected abstract FormField generateFormFieldImpl()
      Descendant classes must implement this method to generate a FormField associated with this property. The generateFormField() method in this class will call this abstract method to create the FormField, which will then be augmented with our fully qualified name, read-only state, help text, and extra attributes.
      Returns:
      A FormField associated with this property.
    • generateFormField

      public final FormField generateFormField(FormPanel formPanel)
      Generates a FormField instance for this AbstractProperty, depending on our type. The returned FormField will be populated based on the current value of this property. There's no guarantee that it will pass form validation, though, as the default value of the property is out of our control and may or may not actually be valid.

      Descendant classes should implement generateFormFieldImpl() and not this method.

      NOTE: you should generally never need to invoke this method directly. It is invoked as needed by the PropertiesManager. Of course, if you are manually building your own FormPanel, you can use this directly. However, note that a new FormField instance will be created each time you invoke this method. So, any listener you add to the generated FormField, or any change you make to it, will effectively be lost the next time you invoke this method. Also, when your FormPanel has been validated and you wish to propagate the FormField's value(s) back into this property, you must manually invoke loadFromFormField(), passing in the FormField you got from generateFormField(). If you are going through PropertiesManager, all of this is done for you.

      Parameters:
      formPanel - The containing FormPanel for the generated FormField, if known (null is fine).
      Returns:
      A FormField representing this AbstractProperty.
    • generateFormField

      public final FormField generateFormField()
      Delegates to generateFormField(null).
    • loadFromFormField

      public abstract void loadFromFormField(FormField field)
      Populates this Property's value(s) from the given form field, assuming the field is of the correct type.
      Parameters:
      field - The FormField containing a value for this property.
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • equals

      public boolean equals(Object obj)
      Overrides:
      equals in class Object
    • addFormFieldChangeListener

      public AbstractProperty addFormFieldChangeListener(PropertyFormFieldChangeListener listener)
      Register to receive a change notification when a generated FormField's value is changed by the user. This hook provides context about where the change happened (that is, which FormPanel contains the FormField in question, and of course the FormField itself).
    • removeFormFieldChangeListener

      public void removeFormFieldChangeListener(PropertyFormFieldChangeListener listener)
    • removeAllFormFieldChangeListeners

      public void removeAllFormFieldChangeListeners()
    • fireFormFieldChangedEvent

      protected void fireFormFieldChangedEvent(FormPanel formPanel, FormField field, ActionEvent changeEvent)
    • addFormFieldGenerationListener

      public AbstractProperty addFormFieldGenerationListener(FormFieldGenerationListener listener)
      Register to receive a notification when this AbstractProperty instance generates a FormField. You can use this hook to tweak the generated FormField, if needed, before it is added to whatever FormPanel requested the field.
    • removeFormFieldGenerationListener

      public void removeFormFieldGenerationListener(FormFieldGenerationListener listener)
    • removeAllFormFieldGenerationListeners

      public void removeAllFormFieldGenerationListeners()
    • fireFormFieldGeneratedEvent

      protected void fireFormFieldGeneratedEvent(FormField formField)