001    /*
002     * Copyright 2002-2005 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.binding.value.support;
017    
018    import java.beans.PropertyChangeEvent;
019    import java.beans.PropertyChangeListener;
020    
021    import org.apache.commons.logging.Log;
022    import org.apache.commons.logging.LogFactory;
023    import org.springframework.binding.value.ValueChangeDetector;
024    import org.springframework.binding.value.ValueModel;
025    import org.springframework.richclient.application.ApplicationServicesLocator;
026    
027    /**
028     * An abstract class that minimizes the effort required to implement
029     * the {@link ValueModel} interface. It provides convenience methods
030     * to convert boolean, double, float, int, and long to their
031     * corresponding Object values.
032     * 
033     * <p>Subclasses must implement <code>getValue()</code> and 
034     * <code>setValue(Object)</code> to get and set the observable value.
035     *
036     * @author Karsten Lentzsch
037     * @author Keith Donald
038     * @author Oliver Hutchison  
039     */
040    public abstract class AbstractValueModel extends AbstractPropertyChangePublisher implements ValueModel {
041    
042        protected final Log logger = LogFactory.getLog(getClass());
043    
044        private final ThreadLocal listenerToSkipHolder = new ThreadLocal();
045        
046        private ValueChangeDetector valueChangeDetector;
047    
048        public final void setValueSilently(Object newValue, PropertyChangeListener listenerToSkip) {
049            // We need to keep track of listenerToSkip on a per thread basis as it's 
050            // possible that this value model may be accessed from multiple threads.
051            final Object oldListenerToSkip = listenerToSkipHolder.get();
052            try {
053                listenerToSkipHolder.set(listenerToSkip);
054                setValue(newValue);
055            }
056            finally {
057                listenerToSkipHolder.set(oldListenerToSkip);
058            }
059        }
060    
061        public final void addValueChangeListener(PropertyChangeListener listener) {
062            addPropertyChangeListener(VALUE_PROPERTY, listener);
063        }
064    
065        public final void removeValueChangeListener(PropertyChangeListener listener) {
066            removePropertyChangeListener(VALUE_PROPERTY, listener);
067        }
068    
069        /**
070         * This method can be called when it in necessary to send a 
071         * PropertyChangeEvent to any registered PropertyChangeListeners 
072         * even though the encapsulated value has not changed.
073         * 
074         * FIXME: This needs a much better name!
075         */
076        protected void fireValueChangeWhenStillEqual() {
077            Object value = getValue();
078            fireValueChangeEvent(value, value);
079        }
080    
081        /**
082         * Notifies all listeners that have registered interest for
083         * notification on this event type.  The event instance 
084         * is lazily created using the parameters passed into 
085         * the fire method.
086         *
087         * @param oldValue the boolean value before the change
088         * @param newValue the boolean value after the change
089         */
090        protected final void fireValueChange(boolean oldValue, boolean newValue) {
091            fireValueChange(Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
092        }
093    
094        /**
095         * Notifies all listeners that have registered interest for
096         * notification on this event type.  The event instance 
097         * is lazily created using the parameters passed into 
098         * the fire method.
099         *
100         * @param oldValue the int value before the change
101         * @param newValue the int value after the change
102         */
103        protected final void fireValueChange(int oldValue, int newValue) {
104            fireValueChange(new Integer(oldValue), new Integer(newValue));
105        }
106    
107        /**
108         * Notifies all listeners that have registered interest for
109         * notification on this event type.  The event instance 
110         * is lazily created using the parameters passed into 
111         * the fire method.
112         *
113         * @param oldValue the long value before the change
114         * @param newValue the long value after the change
115         */
116        protected final void fireValueChange(long oldValue, long newValue) {
117            fireValueChange(new Long(oldValue), new Long(newValue));
118        }
119    
120        /**
121         * Notifies all listeners that have registered interest for
122         * notification on this event type.  The event instance 
123         * is lazily created using the parameters passed into 
124         * the fire method.
125         *
126         * @param oldValue the double value before the change
127         * @param newValue the double value after the change
128         */
129        protected final void fireValueChange(double oldValue, double newValue) {
130            fireValueChange(new Double(oldValue), new Double(newValue));
131        }
132    
133        /**
134         * Notifies all listeners that have registered interest for
135         * notification on this event type.  The event instance 
136         * is lazily created using the parameters passed into 
137         * the fire method.
138         *
139         * @param oldValue the float value before the change
140         * @param newValue the float value after the change
141         */
142        protected void fireValueChange(Object oldValue, Object newValue) {
143            if (hasValueChanged(oldValue, newValue)) {
144                fireValueChangeEvent(oldValue, newValue);
145            }
146        }
147    
148        /**
149         * Delegates to configured <code>ValueChangeDetector</code>.
150         */
151        protected boolean hasValueChanged(Object oldValue, Object newValue) {
152            return getValueChangeDetector().hasValueChanged(oldValue, newValue);
153        }
154    
155        /**
156         * Notifies all listeners that have registered interest for
157         * notification on this event type. This method does not check if there is any change
158         * between the old and new value unlike the various fireValueChanged() methods.
159         */
160        protected void fireValueChangeEvent(Object oldValue, Object newValue) {
161            if (logger.isDebugEnabled()) {
162                logger.debug("Firing value changed event. Old value='" + oldValue + "' new value='" + newValue + "'");
163            }
164            final PropertyChangeListener[] propertyChangeListeners = getPropertyChangeListeners(VALUE_PROPERTY);
165            if (propertyChangeListeners.length > 0) {
166                final Object listenerToSkip = listenerToSkipHolder.get();
167                final PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(this, VALUE_PROPERTY, oldValue,
168                        newValue);
169                for (int i = 0; i < propertyChangeListeners.length; i++) {
170                    PropertyChangeListener listener = propertyChangeListeners[i];
171                    if (listener != listenerToSkip) {
172                        listener.propertyChange(propertyChangeEvent);
173                    }
174                }
175            }
176        }
177    
178        /**
179         * Set the object that will be used to detect changes between two values.
180         * @param valueChangeDetector to use
181         */
182        public void setValueChangeDetector(ValueChangeDetector valueChangeDetector) {
183            this.valueChangeDetector = valueChangeDetector;
184        }
185    
186        /**
187         * Get the installed value change detector.  If none has been directly installed then
188         * get the one configured in the application context.
189         * @return value change detector to use
190         */
191        protected ValueChangeDetector getValueChangeDetector() {
192            if( valueChangeDetector == null ) {
193                valueChangeDetector = (ValueChangeDetector)ApplicationServicesLocator.services().getService(ValueChangeDetector.class);
194            }
195            return valueChangeDetector;
196        }
197    
198    }