001package jmri.beans;
002
003import java.beans.PropertyChangeEvent;
004import javax.annotation.Nonnull;
005import jmri.profile.Profile;
006
007/**
008 * Bean that implements some common code for preferences objects.
009 *
010 * @author Randall Wood (C) 2017, 2020
011 */
012public abstract class PreferencesBean extends Bean {
013
014    /**
015     * Property indicating preferences item do/do not need to be saved.
016     *
017     * {@value #DIRTY}
018     */
019    public static final String DIRTY = "dirty"; // NOI18N
020    /**
021     * Property indicating preferences item requires restart to be applied.
022     *
023     * {@value #RESTART_REQUIRED}
024     */
025    public static final String RESTART_REQUIRED = "restartRequired"; // NOI18N
026    private boolean restartRequired = false;
027    private boolean isDirty = false;
028    private final Profile profile;
029
030    /**
031     * Create the PreferencesBean.
032     *
033     * @param profile the Profile this PreferencesBean is associated with; if
034     * null is not associated with a Profile, but applies application wide
035     */
036    public PreferencesBean(Profile profile) {
037        super(false);
038        this.profile = profile;
039    }
040
041    /**
042     * Get the profile associated with this PreferencesBean.
043     *
044     * @return the profile
045     */
046    @Nonnull
047    public Profile getProfile() {
048        return this.profile;
049    }
050
051    /**
052     * Check if this preferences bean has a state that needs to be saved.
053     *
054     * @return true if unsaved; false otherwise
055     */
056    public boolean isDirty() {
057        return this.isDirty;
058    }
059
060    /**
061     * Check if this preferences bean requires the application to be restarted
062     * to take effect.
063     *
064     * @return true if a restart is required; false otherwise
065     */
066    public boolean isRestartRequired() {
067        return this.restartRequired;
068    }
069
070    /**
071     * Set if restart needs to be required for some preferences to take effect.
072     */
073    protected void setRestartRequired() {
074        if (!this.restartRequired) {
075            this.restartRequired = true;
076            this.firePropertyChange(RESTART_REQUIRED, false, true);
077        }
078    }
079
080    /**
081     * Set if preferences need to be saved.
082     *
083     * @param value true to indicate need to save; false otherwise
084     */
085    protected void setIsDirty(boolean value) {
086        boolean old = this.isDirty;
087        this.isDirty = value;
088        this.firePropertyChange(DIRTY, old, value);
089    }
090
091    /**
092     * {@inheritDoc}
093     * <p>
094     * As a side effect, calls to {@link #isDirty} will return {@code true} if
095     * oldValue and newValue differ or are null.
096     */
097    @Override
098    public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
099        if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
100            this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
101            this.setIsDirty(true);
102        }
103    }
104
105    /**
106     * {@inheritDoc}
107     * <p>
108     * As a side effect, calls to {@link #isDirty} will return {@code true} if
109     * oldValue and newValue differ and propertyName is not {@value #DIRTY}.
110     */
111    @Override
112    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
113        if (oldValue != newValue) {
114            this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
115            // don't force dirty to true if we just changed dirty
116            if (!DIRTY.equals(propertyName)) {
117                this.setIsDirty(true);
118            }
119        }
120    }
121
122    /**
123     * {@inheritDoc}
124     * <p>
125     * As a side effect, calls to {@link #isDirty} will return {@code true} if
126     * oldValue and newValue differ.
127     */
128    @Override
129    public void firePropertyChange(String propertyName, int oldValue, int newValue) {
130        if (oldValue != newValue) {
131            this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
132            this.setIsDirty(true);
133        }
134    }
135
136    /**
137     * {@inheritDoc}
138     * <p>
139     * As a side effect, calls to {@link #isDirty} will return {@code true}. To
140     * avoid that side effect, call
141     * {@link PropertyChangeSupport#firePropertyChange(java.beans.PropertyChangeEvent)}
142     * on {@link #propertyChangeSupport} directly.
143     */
144    @Override
145    public void firePropertyChange(PropertyChangeEvent evt) {
146        this.propertyChangeSupport.firePropertyChange(evt);
147        if (!DIRTY.equals(evt.getPropertyName())) {
148            this.setIsDirty(true);
149        }
150    }
151}