All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lyonesgamer.propertygrid.PGProperty Maven / Gradle / Ivy

The newest version!
package com.lyonesgamer.propertygrid;

import com.lyonesgamer.propertygrid.properties.PropertyCategory;
import net.jodah.typetools.TypeResolver;

import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import java.util.*;
import java.awt.Color;
import java.awt.Font;
import java.util.function.Function;

/**
 * A single property. Represented as a single row in the grid, corresponds to one value.
 *
 * @param  The type of value this property holds.
 * @author Tristan Patch
 * @since 1.0
 */
public abstract class PGProperty implements Comparable {

    /**
     * The grid this property will be shown in.
     */
    protected JPropertyGrid parent = null;

    /**
     * The category this property is under.
     */
    protected PropertyCategory category = null;

    /**
     * The displayed name of the property on the grid.
     */
    protected String name = "";

    /**
     * An object that can be associated with any property. It isn't used by the property,
     * but is only provided for the user.
     */
    protected Optional clientObject = Optional.empty();

    /**
     * The cell that displays the name of the property. Shown on the left in e.g. English.
     */
    protected PropertyCell nameCell;

    /**
     * The cell that displays the value of the property. Shown on the right in e.g. English.
     */
    protected PropertyCell valueCell;

    /**
     * The string that appears in the tooltip for this property.
     */
    protected String helpString = "";

    /**
     * Whether this property is disabled. Disabled properties are greyed out and read-only.
     */
    protected boolean disabled = false;

    /**
     * Whether this property is visible. Hidden properties maintain state but don't appear to the user.
     */
    protected boolean visible = true;

    /**
     * A custom to use to validate this property.
     */
    protected Function validationFunction = null;

    /**
     * Creates a new property. Should be called by any subclasses.
     * @param name The name of the property.
     */
    public PGProperty(String name) {
        setName(name);
    }

    /**
     * Returns the type of value that this property holds -- i.e. the `E` it was constructed with.
     * @return This property's type.
     */
    public Class getType() {
        return TypeResolver.resolveRawArgument(PGProperty.class, this.getClass());
    }

    /**
     * Sets the custom data object.
     * @param object The client data.
     * @param  The type of object the client data is.
     */
    public  void setClientObject(Optional object) {
        this.clientObject = object;
    }

    /**
     * Gets the custom data associated with this property.
     * @return The client data.
     */
    public Optional getClientObject() {
        return clientObject;
    }

    /**
     * Defines the function used to validate this property. If not set, uses default (i.e. property-defined) functionality
     * to determine if the given value is a valid value for its type (e.g. non-negative in an unsigned integer property).
     *
     * @param validationFunction An arbitrary function to use to validate input.
     */
    public void setValidationFunction(Function validationFunction) {
        this.validationFunction = validationFunction;
    }

    /**
     * Called from the property grid when it gets added.
     * @param grid The grid this has been added to.
     * @param parent The category this property belongs to.
     */
    protected void afterAdded(JPropertyGrid grid, PropertyCategory parent) {
        this.parent = grid;
        this.category = parent;

        doAfterAdded();
    }

    /**
     * Called by {@link #afterAdded(JPropertyGrid, com.lyonesgamer.propertygrid.properties.PropertyCategory)}. Used to
     * handle custom setup actions.
     */
    protected abstract void doAfterAdded();

    /**
     * Sets the name of this property.
     * @param name The name of the property.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * If this property has been added to a category, returns the name of the category followed by the name of this,
     * separated by a slash. If category is null, returns the name of this property.
     *
     * @return The fully-qualified name of this property.
     */
    public String getName() {
        if (category == null)
            return name;

        return category.getName() + "/" + name;
    }

    /**
     * Returns the grid this property belongs to.
     * @return This property's parent grid.
     */
    public JPropertyGrid getParent() {
        return parent;
    }

    /**
     * Show the property if it was previously hidden.
     */
    public void show() {
        this.visible = true;
    }

    /**
     * Hide the property. Does nothing if already hidden. Hidden properties maintain their state and can be expanded,
     * collapsed, change property, etc. but don't appear to the user.
     */
    public void hide() {
        this.visible = false;
    }

    /**
     * Determines if a value is "valid" for this property. If it returns false, values cannot be changed.
     * Internally, this function either calls the custom `validation function`, or defaults to calling
     * {@link #onValidateValue(Object)}.
     *
     * @param value The value to be validated.
     * @return If the value is valid.
     */
    public boolean validateValue(E value) {
        if (validationFunction != null)
            return validationFunction.apply(value);
        else
            return onValidateValue(value);
    }

    /**
     * Determine if value is a "valid" value for this property. Values cannot be changed if this returns false.
     *
     * @param value The value to be validated.
     * @return If the value is valid.
     */
    protected abstract boolean onValidateValue(E value);

    /**
     * Custom handling in case the validation fails. Defaults to no behavior. Should only be overriden if additional handling
     * is necessary on top of denying value change.
     *
     * @param value The value that failed.
     */
    public void onValidationFailure(E value) {}

    /**
     * Sets the value of this property. Must be implemented to change the value. Called by setValue() after
     * validating the value.
     *
     * @param value The value to set to.
     */
    protected abstract void onSetValue(E value);

    /**
     * Sets the value of this property. Calls validateValue() first, then onSetValue() to let subclasses handle it.
     *
     * @param value The value to be set.
     */
    public void setValue(E value) {
        if (validateValue(value))
            onSetValue(value);
        else
            onValidationFailure(value);
    }

    /**
     * Sets the value of this property from an arbitrary string. The string is not guaranteed to be parseable to
     * a valid value for this property; subclasses must be prepared to handle any string, as well as call
     * {@link #validateValue(Object)}. If the value is not valid, it need not set.
     *
     * @param value The string to attempt to set from.
     */
    public abstract void setValueFromString(String value);

    /**
     * Sets the value of this property from an arbitrary long. The value is not guaranteed to be associated with a valid
     * value for this property. If the value is not valid, it need not set.
     *
     * @param value The long to set this value from.
     */
    public abstract void setValueFromLong(long value);

    /**
     * Getter for the value.
     *
     * @return The value this property holds.
     */
    public abstract E getValue();

    /**
     * Converts a String to a value, likely using the same (or similar) algorithm as {@link #setValueFromString(String)}.
     * @param value The value to convert to a value type of this property.
     * @return The value this property holds.
     */
    public abstract E stringToValue(String value);

    /**
     * Converts an int to a value, likely using the same (or similar) algorithm as {@link #setValueFromLong(long)}.
     * @param value The value to convert to a value type of this property.
     * @return The value this property holds.
     */
    public abstract E longToValue(long value);

    /**
     * Returns the value as a String in a way that makes sense for this property.
     *
     * @param value The value to convert.
     * @return A String representation of this value.
     */
    public abstract String valueToString(E value);

    /**
     * Determines if this property is disabled. Disabled properties are greyed out and are read-only.
     * @return If this property is disabled.
     */
    public boolean isDisabled() {
        return disabled;
    }

    /**
     * Sets this property's disabled status. Disabled properties are greyed out and are read-only.
     * @param disabled Whether this property becomes disabled.
     */
    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    /**
     * Default implementation that can be overriden in children. Compares names alphabetically.
     * @param o The object to compare to.
     * @return True if the object is a PGProperty and the names are equal (case-sensitive), false otherwise.
     */
    @Override
    public boolean equals(Object o) {
        if (o == null || !(o instanceof PGProperty))
            return false;

        PGProperty category = (PGProperty)o;
        return name.equals(category.name);
    }

    /**
     * Default implementation that can be overriden in children. Uses name to generate code.
     * @return The hash code of this property.
     */
    @Override
    public int hashCode() {
        return 18*name.hashCode();
    }

    /**
     * Compares this property to the other alphabetically.
     *
     * The result of this function affects its ordering in the UI. However, it must be ensured that
     * `this.compareTo(property)` is opposite of `property.compareTo(this)` or it will result in undefined
     * behavior. Also note that the contract of {@link java.lang.Comparable} is that if `this.compareTo(prop) == 0`, then
     * `this.equals(prop)` MUST be true, or weird things happen (see the Comparable javadocs for more info).
     * It's recommended that custom subclasses don't override this, as the standard ones don't, and there is no guarantee
     * that the properties will be of the same type, meaning custom implementations will conflict with existing ones.
     *
     * @param property The category to compare to.
     * @return 0 if the categories have the same name, 1 if this property's name is greater than category's, and -1 if it is less than.
     */
    @SuppressWarnings("NullableProblems") //For IntelliJ, seemingly no way to fix the "overrides @NotNull" warning.
    @Override
    public int compareTo(PGProperty property) {
        if (property == null)
            throw new NullPointerException("Cannot pass null to compareTo()");

        return name.compareTo(property.name);
    }

    /**
     * Gets the renderer used to display the value of the property when not editing.
     * @return The property's unique renderer.
     */
    public abstract PGCellRenderer getRenderer();

    /**
     * Gets the renderer used to display a control allowing the user to change the value.
     * @return The property's unique editor.
     */
    public abstract PGCellEditor getEditor();


    /**
     * Metadata about an individual cell in a property. Defines properties used by the renderer to influence appearance.
     */
    public static class PropertyCell {

        /**
         * The String representation of either the property's label or value.
         */
        public String text;

        /**
         * An icon used to represent the value graphically. If it is null, no icon is displayed.
         */
        public ImageIcon icon;

        /**
         * The color of the cell.
         */
        public Color bgColor;

        /**
         * The color of text within the cell.
         */
        public Color textColor;

        /**
         * The font to use to render the cell's text.
         */
        public Font font;

        /**
         * Creates a new cell. Font is assumed to be system default.
         *
         * @param text The text to display inside the cell.
         * @param icon An icon to display inside the cell. If null, no icon is shown.
         * @param bgColor The color of the cell.
         * @param textColor The color of text within the cell.
         */
        public PropertyCell(String text, ImageIcon icon, Color bgColor, Color textColor) {
            this.text = text;
            this.icon = icon;
            this.bgColor = bgColor;
            this.textColor = textColor;
            this.font = new JLabel().getFont(); //Perhaps a bit hacky, but it gets the default font in this LaF.
        }
    }

    /**
     * A Swing control to allow for editing of the value of a given property.
     */
    public interface PGCellEditor extends TableCellEditor {
    }

    /**
     * A Swing control the render a given cell in the grid. Overriden by the editor if currently editing.
     */
    public interface PGCellRenderer extends TableCellRenderer {
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy