001/*
002 * $Id: ComponentProvider.java 3927 2011-02-22 16:34:11Z kleopatra $
003 *
004 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 * 
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 * 
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021package org.jdesktop.swingx.renderer;
022
023import java.io.Serializable;
024
025import javax.swing.Icon;
026import javax.swing.JComponent;
027import javax.swing.JLabel;
028import javax.swing.SwingUtilities;
029
030import org.jdesktop.swingx.plaf.UIDependent;
031
032/**
033 * Abstract base class of a provider for a cell rendering component. Configures
034 * the component's content and default visuals depending on the renderee's state
035 * as captured in a <code>CellContext</code>. It's basically re-usable across
036 * all types of renderees (JTable, JList, JTree).
037 * <p>
038 * 
039 * <h2> Content </h2>
040 * 
041 * A provider guarantees to configure the "content" properties completely
042 * for any given object. The most frequent mappings are to text and/or icon
043 * properties of the rendering components. The former is controlled by a 
044 * StringValue (see below), the latter by an IconValue. Subclasses which
045 * hand out component of type IconAware guarantee to reset its icon property
046 * always. <p>
047 * 
048 * To ease content configuration, it supports a pluggable
049 * <code>StringValue</code> which purpose is to create and return a string
050 * representation of a given object. Implemenations of a ComponentProvider can
051 * use it to configure their rendering component as appropriate.<p>
052 * 
053 * F.i. to show a Contributor cell object as "Busywoman, Herta" implement a
054 * custom StringValue and use it in a text rendering provider. (Note that SwingX
055 * default implementations of Table/List/TreeCellRenderer have convenience
056 * constructors to take the converter and create a default LabelProvider which
057 * uses it).
058 * 
059 * <pre><code>
060 * StringValue stringValue = new StringValue() {
061 * 
062 *     public String getString(Object value) {
063 *         if (!(value instanceof Contributor))
064 *             return TO_STRING.getString(value);
065 *         Contributor contributor = (Contributor) value;
066 *         return contributor.lastName + &quot;, &quot; + contributor.firstName;
067 *     }
068 * 
069 * };
070 * table.setDefaultRenderer(Contributor.class, new DefaultTableRenderer(
071 *         stringValue));
072 * list.setCellRenderer(new DefaultListRenderer(stringValue));
073 * tree.setCellRenderer(new DefaultTreeRenderer(stringValue));
074 * 
075 * </code></pre>
076 * 
077 * To ease handling of formatted localizable content, there's a
078 * <code>FormatStringValue</code> which is pluggable with a
079 * <code>Format</code>. <p>
080 * 
081 * F.i. to show a Date's time in the default Locale's SHORT
082 * version and right align the cell
083 * 
084 * <pre><code>
085 *   StringValue stringValue = new FormatStringValue(
086 *       DateFormat.getTimeInstance(DateFormat.SHORT));
087 *   table.getColumnExt(&quot;timeID&quot;).setCellRenderer(
088 *       new DefaultTableRenderer(stringValue, JLabel.RIGHT);  
089 * </code></pre>
090 * 
091 * 
092 * <p>
093 * 
094 * 
095 * <h2> Default Visuals </h2>
096 * 
097 * Guarantees to completely configure the visual properties listed below. As a
098 * consequence, client code (f.i. in <code>Highlighter</code>s) can safely
099 * change them without long-lasting visual artefacts.
100 * 
101 * <ul>
102 * <li> foreground and background, depending on selected and focused state
103 * <li> border
104 * <li> font
105 * <li> Painter (if applicable)
106 * <li> enabled
107 * <li> componentOrientation
108 * <li> tooltipText
109 * <li> minimum-, maximum-, preferredSize
110 * <li> horizontal alignment (if applicable)
111 * </ul>
112 * 
113 * As this internally delegates default visual configuration to a
114 * <code>DefaultVisuals</code> (which handles the first eight items)
115 * subclasses have to guarantee the alignment only.
116 * <p>
117 * 
118 * 
119 * @see StringValue
120 * @see FormatStringValue
121 * @see IconValue
122 * @see BooleanValue
123 * @see CellContext
124 * @see DefaultTableRenderer
125 * @see DefaultListRenderer
126 * @see DefaultTreeRenderer
127 * @see DefaultVisuals
128 */
129public abstract class ComponentProvider<T extends JComponent> 
130    implements Serializable, UIDependent {
131    /** component to render with. */
132    protected T rendererComponent;
133    /** configurator of default visuals. */
134    protected DefaultVisuals<T> defaultVisuals;
135    /** horizontal (text) alignment of component. 
136     * PENDING: useful only for labels, buttons? */
137    protected int alignment;
138    /** the converter to use for string representation. 
139     * PENDING: IconValue? */
140    protected StringValue formatter;
141    
142    /**
143     * Instantiates a component provider with LEADING
144     * horizontal alignment and default to-String converter. <p> 
145     *
146     */
147    public ComponentProvider() {
148        this(null, JLabel.LEADING);
149    }
150
151    /**
152     * Instantiates a component provider with LEADING
153     * horizontal alignment and the given converter. <p> 
154     *
155     * @param converter the converter to use for mapping the cell value to a
156     *        String representation.
157     */
158    public ComponentProvider(StringValue converter) {
159        this(converter, JLabel.LEADING);
160    }
161
162    /**
163     * Instantiates a LabelProvider with given to-String converter and given
164     * horizontal alignment. If the converter is null, the default TO_STRING is
165     * used.
166     * 
167     * @param converter the converter to use for mapping the cell value to a
168     *        String representation.
169     * @param alignment the horizontal alignment.
170     */
171    public ComponentProvider(StringValue converter, int alignment) {
172        setHorizontalAlignment(alignment);
173        setStringValue(converter);
174        rendererComponent = createRendererComponent();
175        defaultVisuals = createDefaultVisuals();
176    }
177
178    /**
179     * Configures and returns an appropriate component to render a cell
180     * in the given context. If the context is null, returns the
181     * component in its current state.
182     * 
183     * @param context the cell context to configure from
184     * @return a component to render a cell in the given context.
185     */
186    public T getRendererComponent(CellContext context) {
187        if (context != null) {
188            configureVisuals(context);
189            configureContent(context);
190        }
191        return rendererComponent;
192    }
193    
194    /**
195     * Sets the horizontal alignment property to configure the component with.
196     * Allowed values are those accepted by corresponding JLabel setter. The
197     * default value is JLabel.LEADING. This controller guarantees to apply the
198     * alignment on each request for a configured rendering component, if 
199     * possible. Note that not all components have a horizontal alignment
200     * property.
201     * 
202     * @param alignment the horizontal alignment to use when configuring the
203     *   rendering component.
204     */
205    public void setHorizontalAlignment(int alignment) {
206       this.alignment = alignment; 
207    }
208    
209    /**
210     * Returns the horizontal alignment.
211     * 
212     * @return the horizontal alignment of the rendering component.
213     * 
214     * @see #setHorizontalAlignment(int)
215     * 
216     */
217    public int getHorizontalAlignment() {
218        return alignment;
219    }
220
221    /**
222     * Sets the StringValue to use. If the given StringValue is null,
223     * defaults to <code>StringValue.TO_STRING</code>. <p>
224     * 
225     * @param formatter the format to use.
226     */
227    public void setStringValue(StringValue formatter) {
228        if (formatter == null) {
229            formatter = StringValues.TO_STRING;
230        }
231        this.formatter = formatter;
232    }
233
234    /**
235     * Returns the StringValue to use for obtaining 
236     * the String representation. <p>
237     * 
238     * @return the StringValue used by this provider, guaranteed to
239     *   be not null.
240     */
241    public StringValue getStringValue() {
242        return formatter;
243    }
244
245    /**
246     * Returns a string representation of the content.
247     * <p>
248     * 
249     * This method guarantees to return the same string representation as would
250     * appear in the renderer, given that the corresponding cellContext has the
251     * same value as the parameter passed-in here. That is (assuming that the
252     * rendering component has a getText())
253     * 
254     * <pre><code>
255     * if (equals(value, context.getValue()) {
256     *     assertEquals(provider.getString(value), 
257     *     provider.getRenderingComponent(context).getText());
258     * }
259     * </code></pre>
260     * 
261     * This implementation simply delegates to its StringValue. Subclasses might
262     * need to override to comply.
263     * <p>
264     * 
265     * This is a second attempt - the driving force is the need for a consistent
266     * string representation across all (new and old) themes: rendering,
267     * (pattern) filtering/highlighting, searching, auto-complete ...
268     * <p>
269     * 
270     * @param value the object to represent as string.
271     * @return a appropriate string representation of the cell's content.
272     */
273    public String getString(Object value) {
274        return formatter.getString(value);
275    }
276    
277    /**
278     * Returns a String representation of the content.<p>
279     * 
280     * This method messages the 
281     * <code>StringValue</code> to get the String rep. Meant as 
282     * a convenience for subclasses.
283     * 
284     * @param context the cell context, must not be null.
285     * @return a appropriate string representation of the cell's content.
286     */
287    protected String getValueAsString(CellContext context) {
288        Object value = context.getValue();
289        return formatter.getString(value);
290    }
291
292    /**
293     * Returns a Icon representation of the content.<p>
294     * 
295     * This method messages the 
296     * <code>IconValue</code> to get the Icon rep. Meant as 
297     * a convenience for subclasses.
298     * 
299     * @param context the cell context, must not be null.
300     * @return a appropriate icon representation of the cell's content,
301     *   or null if non if available.
302     */
303    protected Icon getValueAsIcon(CellContext context) {
304        Object value = context.getValue();
305        if (formatter instanceof IconValue) {
306            return ((IconValue) formatter).getIcon(value);
307        }
308        return null;
309    }
310
311    /**
312     * Configures the rendering component's default visuals frome
313     * the given cell context. Here: delegates to the renderer
314     * controller.
315     * 
316     * @param context the cell context to configure from, must not be null.
317     * @see DefaultVisuals
318     */
319    protected void configureVisuals(CellContext context) {
320        defaultVisuals.configureVisuals(rendererComponent, context);
321    }
322
323    /**
324     * Configures the renderering component's content and state from the
325     * given cell context.
326     * 
327     * @param context the cell context to configure from, must not be null.
328     * 
329     * @see #configureState(CellContext)
330     * @see #format(CellContext)
331     */
332    protected void configureContent(CellContext context) {
333        configureState(context);
334        format(context);
335    }
336
337    /**
338     * Formats the renderering component's content from the
339     * given cell context.
340     * 
341     * @param context the cell context to configure from, must not be null.
342     */
343    protected abstract void format(CellContext context);
344
345    /**
346     * Configures the rendering component's state from the
347     * given cell context.
348     * @param context the cell context to configure from, must not be null.
349     */
350    protected abstract void configureState(CellContext context); 
351
352    /**
353     * Factory method to create and return the component to use for rendering.<p>
354     * 
355     * @return the component to use for rendering.
356     */
357    protected abstract T createRendererComponent();
358
359    /**
360     * Factory method to create and return the DefaultVisuals used by this
361     * to configure the default visuals. Here: creates the default controller
362     * parameterized to the same type as this.
363     * 
364     * @return the controller used to configure the default visuals of
365     *   the rendering component.
366     */
367    protected DefaultVisuals<T> createDefaultVisuals() {
368        return new DefaultVisuals<T>();
369    }
370
371    /**
372     * Intermediate exposure during refactoring...
373     * 
374     * @return the default visual configurator used by this.
375     */
376    protected DefaultVisuals<T> getDefaultVisuals() {
377        return defaultVisuals;
378    }
379
380    /**
381     * {@inheritDoc}
382     */
383    @Override
384    public void updateUI() {
385        SwingUtilities.updateComponentTreeUI(rendererComponent);
386    }
387}