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 }