001: /*
002: * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.binding.value;
032:
033: import java.beans.PropertyChangeEvent;
034: import java.beans.PropertyChangeListener;
035: import java.lang.ref.WeakReference;
036:
037: /**
038: * An abstract class that minimizes the effort required to implement
039: * a type converter. A type converter is a ValueModel that converts the type
040: * of an object being held as a value in one ValueModel into another type.<p>
041: *
042: * More formally, a converting ValueModel <i>VM1</i> converts the type
043: * <i>T2</i> of an object being held as a value in one ValueModel <i>VM2</i>
044: * into another type <i>T1</i>. When reading a value from VM1,
045: * instances of T2 are read from VM2 and are converted to T1. When storing
046: * a new value to VM1, the type converter will perform the inverse conversion
047: * and will convert an instance of T1 to T2.<p>
048: *
049: * The conversion must be performed when reading and writing values,
050: * as well as in the change notification. This class specifies abstract
051: * methods for the conversion from source to output, which is used to
052: * convert in <code>#getValue</code> and in the change notification.
053: * For the write conversion you must implement <code>#setValue</code>.<p>
054: *
055: * Most converters can set values converted by <code>#convertFromSubject</code>
056: * with <code>#setValue</code>. However, a converter may reject subject values
057: * to be converted and may reject values to be set - as any ValueModel.<p>
058: *
059: * Type converters should be used judiciously and only to bridge two
060: * <code>ValueModel</code>s. Converters often use a generic but weak
061: * conversion, and so can be limited w.r.t. to localized
062: * formatting conventions.<p>
063: *
064: * When binding non-String values to a text UI component, consider
065: * using a {@link javax.swing.JFormattedTextField}. Formatted text fields
066: * provide a powerful means to convert strings to objects and handle many cases
067: * that arise around invalid input. Formatted text fields can be bound
068: * to <code>ValueModel</code>s using the
069: * {@link com.jgoodies.binding.beans.PropertyConnector} class.
070: *
071: *
072: * @author Karsten Lentzsch
073: * @version $Revision: 1.9 $
074: *
075: * @see ValueModel
076: * @see ConverterFactory
077: * @see javax.swing.JFormattedTextField
078: * @see com.jgoodies.binding.beans.PropertyConnector
079: */
080: public abstract class AbstractConverter extends AbstractValueModel {
081:
082: /**
083: * Holds the ValueModel that in turn holds the source value.
084: */
085: protected final ValueModel subject;
086:
087: private final PropertyChangeListener subjectValueChangeHandler;
088:
089: // Instance creation ******************************************************
090:
091: /**
092: * Constructs an AbstractConverter on the given subject.
093: *
094: * @param subject the ValueModel that holds the source value
095: * @throws NullPointerException if the subject is <code>null</code>
096: */
097: public AbstractConverter(ValueModel subject) {
098: this .subject = subject;
099: this .subjectValueChangeHandler = new SubjectValueChangeHandler();
100: subject.addValueChangeListener(subjectValueChangeHandler);
101: }
102:
103: // Abstract Behavior ******************************************************
104:
105: /**
106: * Converts a value from the subject to the type or format used
107: * by this converter.
108: *
109: * @param subjectValue the subject's value
110: * @return the converted value in the type or format used by this converter
111: */
112: public abstract Object convertFromSubject(Object subjectValue);
113:
114: // ValueModel Implementation **********************************************
115:
116: /**
117: * Converts the subject's value and returns the converted value.
118: *
119: * @return the converted subject value
120: */
121: public Object getValue() {
122: return convertFromSubject(subject.getValue());
123: }
124:
125: // Misc *******************************************************************
126:
127: /**
128: * Removes the internal subject value change handler from the subject.
129: * The listener has been registered during construction. Useful to avoid
130: * memory leaks, if the subject lives much longer than this converter.
131: * As an alternative you can use event listener lists in your ValueModels
132: * that implement references with <code>WeakReference</code>.<p>
133: *
134: * This converter must not be used anymore once #release has been called.
135: *
136: * @see WeakReference
137: *
138: * @since 1.3
139: */
140: public final void release() {
141: subject.removeValueChangeListener(subjectValueChangeHandler);
142: }
143:
144: // Helper Class ***********************************************************
145:
146: /**
147: * Listens to subect value changes, converts this value and updates
148: * the converter's value.
149: */
150: private final class SubjectValueChangeHandler implements
151: PropertyChangeListener {
152:
153: /**
154: * Notifies listeners about a change in the underlying subject.
155: * The old and new value used in the PropertyChangeEvent to be fired
156: * are converted versions of the observed old and new values.
157: * The observed old and new value are converted only
158: * if they are non-null. This is because <code>null</code>
159: * may be a valid value or may indicate <em>not available</em>.<p>
160: *
161: * TODO: We may need to check the identity, not equity.
162: *
163: * @param evt the property change event to be handled
164: */
165: public void propertyChange(PropertyChangeEvent evt) {
166: Object oldValue = evt.getOldValue() == null ? null
167: : convertFromSubject(evt.getOldValue());
168: Object newValue = evt.getNewValue() == null ? null
169: : convertFromSubject(evt.getNewValue());
170: fireValueChange(oldValue, newValue);
171: }
172:
173: }
174:
175: }
|