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