/*
 * $Id: ComponentProvider.java 3152 2008-12-23 18:12:39Z kschaefe $
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.renderer;

import java.io.Serializable;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;

/**
 * Abstract base class of a provider for a cell rendering component. Configures
 * the component's content and default visuals depending on the renderee's state
 * as captured in a <code>CellContext</code>. It's basically re-usable across
 * all types of renderees (JTable, JList, JTree).
 * <p>
 * 
 * To ease content configuration, it supports a pluggable
 * <code>StringValue</code> which purpose is to create and return a string
 * representation of a given object. Implemenations of a ComponentProvider can
 * use it to configure their rendering component as appropriate.<p>
 * 
 * F.i. to show a Contributor cell object as "Busywoman, Herta" implement a
 * custom StringValue and use it in a text rendering provider. (Note that SwingX
 * default implementations of Table/List/TreeCellRenderer have convenience
 * constructors to take the converter and create a default LabelProvider which
 * uses it).
 * 
 * <pre><code>
 * StringValue stringValue = new StringValue() {
 * 
 *     public String getString(Object value) {
 *         if (!(value instanceof Contributor))
 *             return TO_STRING.getString(value);
 *         Contributor contributor = (Contributor) value;
 *         return contributor.lastName + &quot;, &quot; + contributor.firstName;
 *     }
 * 
 * };
 * table.setDefaultRenderer(Contributor.class, new DefaultTableRenderer(
 *         stringValue));
 * list.setCellRenderer(new DefaultListRenderer(stringValue));
 * tree.setCellRenderer(new DefaultTreeRenderer(stringValue));
 * 
 * </code></pre>
 * 
 * To ease handling of formatted localizable content, there's a
 * <code>FormatStringValue</code> which is pluggable with a
 * <code>Format</code>. <p>
 * 
 * F.i. to show a Date's time in the default Locale's SHORT
 * version and right align the cell
 * 
 * <pre><code>
 *   StringValue stringValue = new FormatStringValue(
 *       DateFormat.getTimeInstance(DateFormat.SHORT));
 *   table.getColumnExt(&quot;timeID&quot;).setCellRenderer(
 *       new DefaultTableRenderer(stringValue, JLabel.RIGHT);  
 * </code></pre>
 * 
 * 
 * <p>
 * 
 * Guarantees to completely configure the visual properties listed below. As a
 * consequence, client code (f.i. in <code>Highlighter</code>s) can safely
 * change them without long-lasting visual artefacts.
 * 
 * <ul>
 * <li> foreground and background, depending on selected and focused state
 * <li> border
 * <li> font
 * <li> Painter (if applicable)
 * <li> enabled
 * <li> componentOrientation
 * <li> tooltipText
 * <li> minimum-, maximum-, preferredSize
 * <li> horizontal alignment (if applicable)
 * </ul>
 * 
 * As this internally delegates default visual configuration to a
 * <code>DefaultVisuals</code> (which handles the first eight items)
 * subclasses have to guarantee the alignment only.
 * <p>
 * 
 * 
 * @see StringValue
 * @see FormatStringValue
 * @see IconValue
 * @see BooleanValue
 * @see CellContext
 * @see DefaultTableRenderer
 * @see DefaultListRenderer
 * @see DefaultTreeRenderer
 * @see DefaultVisuals
 */
public abstract class ComponentProvider<T extends JComponent> 
    implements Serializable {
    /** component to render with. */
    protected T rendererComponent;
    /** configurator of default visuals. */
    protected DefaultVisuals<T> defaultVisuals;
    /** horizontal (text) alignment of component. 
     * PENDING: useful only for labels, buttons? */
    protected int alignment;
    /** the converter to use for string representation. 
     * PENDING: IconValue? */
    protected StringValue formatter;
    
    /**
     * Instantiates a component provider with LEADING
     * horizontal alignment and default to-String converter. <p> 
     *
     */
    public ComponentProvider() {
        this(null, JLabel.LEADING);
    }

    /**
     * Instantiates a component provider with LEADING
     * horizontal alignment and the given converter. <p> 
     *
     * @param converter the converter to use for mapping the cell value to a
     *        String representation.
     */
    public ComponentProvider(StringValue converter) {
        this(converter, JLabel.LEADING);
    }

    /**
     * Instantiates a LabelProvider with given to-String converter and given
     * horizontal alignment. If the converter is null, the default TO_STRING is
     * used.
     * 
     * @param converter the converter to use for mapping the cell value to a
     *        String representation.
     * @param alignment the horizontal alignment.
     */
    public ComponentProvider(StringValue converter, int alignment) {
        setHorizontalAlignment(alignment);
        setStringValue(converter);
        rendererComponent = createRendererComponent();
        defaultVisuals = createDefaultVisuals();
    }

    /**
     * Configures and returns an appropriate component to render a cell
     * in the given context. If the context is null, returns the
     * component in its current state.
     * 
     * @param context the cell context to configure from
     * @return a component to render a cell in the given context.
     */
    public T getRendererComponent(CellContext context) {
        if (context != null) {
            configureVisuals(context);
            configureContent(context);
        }
        return rendererComponent;
    }
    
    /**
     * Sets the horizontal alignment property to configure the component with.
     * Allowed values are those accepted by corresponding JLabel setter. The
     * default value is JLabel.LEADING. This controller guarantees to apply the
     * alignment on each request for a configured rendering component, if 
     * possible. Note that not all components have a horizontal alignment
     * property.
     * 
     * @param alignment the horizontal alignment to use when configuring the
     *   rendering component.
     */
    public void setHorizontalAlignment(int alignment) {
       this.alignment = alignment; 
    }
    
    /**
     * Returns the horizontal alignment.
     * 
     * @return the horizontal alignment of the rendering component.
     * 
     * @see #setHorizontalAlignment(int)
     * 
     */
    public int getHorizontalAlignment() {
        return alignment;
    }

    /**
     * Sets the StringValue to use. If the given StringValue is null,
     * defaults to <code>StringValue.TO_STRING</code>. <p>
     * 
     * @param formatter the format to use.
     */
    public void setStringValue(StringValue formatter) {
        if (formatter == null) {
            formatter = StringValues.TO_STRING;
        }
        this.formatter = formatter;
    }

    /**
     * Returns the StringValue to use for obtaining 
     * the String representation. <p>
     * 
     * @return the StringValue used by this provider, guaranteed to
     *   be not null.
     */
    public StringValue getStringValue() {
        return formatter;
    }

    /**
     * Returns a string representation of the content.
     * <p>
     * 
     * This method guarantees to return the same string representation as would
     * appear in the renderer, given that the corresponding cellContext has the
     * same value as the parameter passed-in here. That is (assuming that the
     * rendering component has a getText())
     * 
     * <pre><code>
     * if (equals(value, context.getValue()) {
     *     assertEquals(provider.getString(value), 
     *     provider.getRenderingComponent(context).getText());
     * }
     * </code></pre>
     * 
     * This implementation simply delegates to its StringValue. Subclasses might
     * need to override to comply.
     * <p>
     * 
     * This is a second attempt - the driving force is the need for a consistent
     * string representation across all (new and old) themes: rendering,
     * (pattern) filtering/highlighting, searching, auto-complete ...
     * <p>
     * 
     * @param value the object to represent as string.
     * @return a appropriate string representation of the cell's content.
     */
    public String getString(Object value) {
        return formatter.getString(value);
    }
    
    /**
     * Returns a String representation of the content.<p>
     * 
     * This method messages the 
     * <code>StringValue</code> to get the String rep. Meant as 
     * a convenience for subclasses.
     * 
     * @param context the cell context, must not be null.
     * @return a appropriate string representation of the cell's content.
     */
    protected String getValueAsString(CellContext context) {
        Object value = context.getValue();
        return formatter.getString(value);
    }

    /**
     * Returns a Icon representation of the content.<p>
     * 
     * This method messages the 
     * <code>IconValue</code> to get the Icon rep. Meant as 
     * a convenience for subclasses.
     * 
     * @param context the cell context, must not be null.
     * @return a appropriate icon representation of the cell's content,
     *   or null if non if available.
     */
    protected Icon getValueAsIcon(CellContext context) {
        Object value = context.getValue();
        if (formatter instanceof IconValue) {
            return ((IconValue) formatter).getIcon(value);
        }
        return null;
    }

    /**
     * Configures the rendering component's default visuals frome
     * the given cell context. Here: delegates to the renderer
     * controller.
     * 
     * @param context the cell context to configure from, must not be null.
     * @see DefaultVisuals
     */
    protected void configureVisuals(CellContext context) {
        defaultVisuals.configureVisuals(rendererComponent, context);
    }

    /**
     * Configures the renderering component's content and state from the
     * given cell context.
     * 
     * @param context the cell context to configure from, must not be null.
     * 
     * @see #configureState(CellContext)
     * @see #format(CellContext)
     */
    protected void configureContent(CellContext context) {
        configureState(context);
        format(context);
    }

    /**
     * Formats the renderering component's content from the
     * given cell context.
     * 
     * @param context the cell context to configure from, must not be null.
     */
    protected abstract void format(CellContext context);

    /**
     * Configures the renderering component's state from the
     * given cell context.
     * @param context the cell context to configure from, must not be null.
     */
    protected abstract void configureState(CellContext context); 

    /**
     * Factory method to create and return the component to use for rendering.<p>
     * 
     * @return the component to use for rendering.
     */
    protected abstract T createRendererComponent();

    /**
     * Factory method to create and return the DefaultVisuals used by this
     * to configure the default visuals. Here: creates the default controller
     * parameterized to the same type as this.
     * 
     * @return the controller used to configure the default visuals of
     *   the rendering component.
     */
    protected DefaultVisuals<T> createDefaultVisuals() {
        return new DefaultVisuals<T>();
    }

    /**
     * Intermediate exposure during refactoring...
     * 
     * @return the default visual configurator used by this.
     */
    protected DefaultVisuals<T> getDefaultVisuals() {
        return defaultVisuals;
    }


}