001package jmri.beans;
002
003import java.beans.IndexedPropertyChangeEvent;
004import java.beans.PropertyChangeEvent;
005import java.beans.PropertyVetoException;
006import java.beans.VetoableChangeListener;
007import java.beans.VetoableChangeSupport;
008
009/**
010 * A Bean with support for {@link java.beans.VetoableChangeListener}s.
011 *
012 * @author Randall Wood
013 */
014public abstract class ConstrainedBean extends Bean implements VetoableChangeProvider {
015
016    protected final VetoableChangeSupport vetoableChangeSupport = new VetoableChangeSupport(this);
017
018    @Override
019    public void setProperty(String key, Object value) {
020        try {
021            this.fireVetoableChange(key, getProperty(key), value);
022            super.setProperty(key, value);
023        } catch (PropertyVetoException ex) {
024            // fire a property change that does not have the new value to indicate
025            // to any other listeners that the property was "reset" back to its
026            // original value as a result of the veto
027            this.firePropertyChange(key, getProperty(key), getProperty(key));
028        }
029    }
030
031    @Override
032    public void setIndexedProperty(String key, int index, Object value) {
033        try {
034            Object old = this.getIndexedPropertyOrNull(key, index);
035            this.fireVetoableChange(new IndexedPropertyChangeEvent(this, key, old, value, index));
036            super.setIndexedProperty(key, index, value);
037        } catch (PropertyVetoException ex) {
038            // fire a property change that does not have the new value to indicate
039            // to any other listeners that the property was "reset" back to its
040            // original value as a result of the veto
041            this.fireIndexedPropertyChange(key, index, getProperty(key), getProperty(key));
042        }
043    }
044
045    @Override
046    public void addVetoableChangeListener(VetoableChangeListener listener) {
047        this.vetoableChangeSupport.addVetoableChangeListener(listener);
048    }
049
050    @Override
051    public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) {
052        this.vetoableChangeSupport.addVetoableChangeListener(propertyName, listener);
053    }
054
055    @Override
056    public VetoableChangeListener[] getVetoableChangeListeners() {
057        return this.vetoableChangeSupport.getVetoableChangeListeners();
058    }
059
060    @Override
061    public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
062        return this.vetoableChangeSupport.getVetoableChangeListeners(propertyName);
063    }
064
065    @Override
066    public void removeVetoableChangeListener(VetoableChangeListener listener) {
067        this.vetoableChangeSupport.removeVetoableChangeListener(listener);
068    }
069
070    @Override
071    public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) {
072        this.vetoableChangeSupport.removeVetoableChangeListener(propertyName, listener);
073    }
074
075    /**
076     * Fire a vetoable property change on the current thread. Use
077     * {@link java.beans.VetoableChangeSupport#fireVetoableChange(java.beans.PropertyChangeEvent)}
078     * directly to fire this notification on another thread. If a
079     * PropertyVetoException is thrown, ensure the property change does not
080     * complete.
081     *
082     * @param event {@link PropertyChangeEvent} to be fired
083     * @throws PropertyVetoException if property update vetoed
084     */
085    public void fireVetoableChange(PropertyChangeEvent event) throws PropertyVetoException {
086        this.vetoableChangeSupport.fireVetoableChange(event);
087    }
088
089    /**
090     * Fire a vetoable property change on the current thread. Use
091     * {@link java.beans.VetoableChangeSupport#fireVetoableChange(java.lang.String, java.lang.Object, java.lang.Object)}
092     * directly to fire this notification on another thread. If a
093     * PropertyVetoException is thrown, ensure the property change does not
094     * complete.
095     *
096     * @param propertyName property that is about to change
097     * @param oldValue     old value of the property
098     * @param newValue     new value of the property
099     * @throws PropertyVetoException if property update vetoed
100     */
101    public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException {
102        this.vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
103    }
104
105    /**
106     * Fire a vetoable property change on the current thread. Use
107     * {@link java.beans.VetoableChangeSupport#fireVetoableChange(java.lang.String, int, int)}
108     * directly to fire this notification on another thread. If a
109     * PropertyVetoException is thrown, ensure the property change does not
110     * complete.
111     *
112     * @param propertyName property that is about to change
113     * @param oldValue     old value of the property
114     * @param newValue     new value of the property
115     * @throws PropertyVetoException if property update vetoed
116     */
117    public void fireVetoableChange(String propertyName, int oldValue, int newValue) throws PropertyVetoException {
118        this.vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
119    }
120
121    /**
122     * Fire a vetoable property change on the current thread. Use
123     * {@link java.beans.VetoableChangeSupport#fireVetoableChange(java.lang.String, boolean, boolean)}
124     * directly to fire this notification on another thread. If a
125     * PropertyVetoException is thrown, ensure the property change does not
126     * complete.
127     *
128     * @param propertyName property that is about to change
129     * @param oldValue     old value of the property
130     * @param newValue     new value of the property
131     * @throws PropertyVetoException if property update vetoed
132     */
133    public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue)
134            throws PropertyVetoException {
135        this.vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
136    }
137
138    /**
139     * Get the indexed property or return null if the index is invalid. Used to
140     * get the old value when setting an indexed property where the index may
141     * not previously have been set.
142     * 
143     * @param key   the property name
144     * @param index the index
145     * @return the value at index or null
146     */
147    protected Object getIndexedPropertyOrNull(String key, int index) {
148        try {
149            return this.getIndexedProperty(key, index);
150        } catch (IndexOutOfBoundsException ex) {
151            return null;
152        }
153
154    }
155}