001    /*
002     * Copyright 2002-2005 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of 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,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under 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.springframework.binding.value.DerivedValueModel;
022    import org.springframework.binding.value.ValueModel;
023    
024    /**
025     * Abstract base class for value models that derive their value from one or more
026     * "source" value model. Provides a hook to notify when any of the "source"
027     * value models change.
028     *
029     * @author Oliver Hutchison
030     */
031    public abstract class AbstractDerivedValueModel extends AbstractValueModel implements DerivedValueModel {
032    
033            private final ValueModel[] sourceValueModels;
034    
035            private final PropertyChangeListener sourceChangeHandler;
036    
037            /**
038             * Create a derivedValueModel based on the given sourceValueModels. When any
039             * of these valueModels has their value changed, the derivedValueModel will
040             * update its value.
041             *
042             * @param sourceValueModels an <code>Array</code> of valueModels that
043             * influence the derived value.
044             */
045            protected AbstractDerivedValueModel(ValueModel[] sourceValueModels) {
046                    this.sourceValueModels = sourceValueModels;
047                    this.sourceChangeHandler = new PropertyChangeListener() {
048    
049                            public void propertyChange(PropertyChangeEvent evt) {
050                                    sourceValuesChanged();
051                            }
052                    };
053                    for (int i = 0; i < sourceValueModels.length; i++) {
054                            sourceValueModels[i].addValueChangeListener(sourceChangeHandler);
055                    }
056            }
057    
058            public ValueModel[] getSourceValueModels() {
059                    return sourceValueModels;
060            }
061    
062            /**
063             * Convenience method to extract values from all sourceValueModels that
064             * influence the derived value.
065             *
066             * @return an <code>Array</code> containing the source values in the same
067             * order as the source valueModels were defined.
068             */
069            protected Object[] getSourceValues() {
070                    Object[] values = new Object[sourceValueModels.length];
071                    for (int i = 0; i < values.length; i++) {
072                            values[i] = sourceValueModels[i].getValue();
073                    }
074                    return values;
075            }
076    
077            /**
078             * Derive the value from the source values and fire a valueChangeEvent to
079             * notify listeners.
080             */
081            protected abstract void sourceValuesChanged();
082    
083            /**
084             * A derived valueModel is always readOnly.
085             *
086             * @see #setValue(Object)
087             */
088            public boolean isReadOnly() {
089                    return true;
090            }
091    
092            /**
093             * A {@link DerivedValueModel}'s value is based on other valueModels, it
094             * cannot be set. Will throw an {@link UnsupportedOperationException} when
095             * used.
096             */
097            public void setValue(Object newValue) {
098                    throw new UnsupportedOperationException("This value model is read only");
099            }
100    }