All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.managers.style.AbstractComponentDescriptor Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.managers.style;

import com.alee.utils.LafUtils;
import com.alee.utils.ReflectUtils;
import com.alee.utils.TextUtils;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * Abstract {@link ComponentDescriptor} implementation that stores various common information about the component.
 * It is used by all abstract and basic descriptors within WebLaF.
 * It can also be used for creating descriptors for any custom {@link JComponent} implementations.
 *
 * @param  {@link JComponent} type
 * @param  base {@link ComponentUI} type
 * @author Mikle Garin
 * @see How to use StyleManager
 * @see StyleManager
 * @see StyleManager#registerComponentDescriptor(ComponentDescriptor)
 * @see StyleManager#unregisterComponentDescriptor(ComponentDescriptor)
 * @see StyleManager#initializeDescriptors()
 */
public abstract class AbstractComponentDescriptor implements ComponentDescriptor
{
    /**
     * todo 1. Add specific painter class definition
     * todo 2. Add adaptive painter class definition
     * todo 3. Automate painters installation/uninstallation within UIs (in StyleManager.installSkin/uninstallSkin)
     */

    /**
     * Component icons cache.
     * {@link Icon}s are saved per component class.
     */
    protected static final Map componentIcons = new HashMap ();

    /**
     * Component identifier.
     */
    protected final String id;

    /**
     * Component class.
     */
    protected final Class componentClass;

    /**
     * Component UI class identifier.
     */
    protected final String uiClassId;

    /**
     * Base UI class applicable to this component.
     */
    protected final Class baseUIClass;

    /**
     * UI class applied to the component by default.
     */
    protected final Class uiClass;

    /**
     * Component default style ID.
     */
    protected final StyleId defaultStyleId;

    /**
     * Constructs new {@link AbstractComponentDescriptor}.
     *
     * @param id             component identifier
     * @param componentClass component class
     * @param uiClassId      component UI class ID
     * @param baseUIClass    base UI class applicable to this component
     * @param uiClass        UI class applied to the component by default
     * @param defaultStyleId component default style ID
     */
    public AbstractComponentDescriptor ( final String id, final Class componentClass, final String uiClassId,
                                         final Class baseUIClass, final Class uiClass, final StyleId defaultStyleId )
    {
        super ();
        this.id = id;
        this.componentClass = componentClass;
        this.uiClassId = uiClassId;
        this.baseUIClass = baseUIClass;
        this.uiClass = uiClass;
        this.defaultStyleId = defaultStyleId;
    }

    @Override
    public Class getComponentClass ()
    {
        return componentClass;
    }

    @Override
    public String getUIClassId ()
    {
        return uiClassId;
    }

    @Override
    public Class getBaseUIClass ()
    {
        return baseUIClass;
    }

    @Override
    public Class getUIClass ()
    {
        return uiClass;
    }

    @Override
    public StyleId getDefaultStyleId ()
    {
        return defaultStyleId;
    }

    @Override
    public StyleId getDefaultStyleId ( final JComponent component )
    {
        final StyleId styleId;
        if ( component instanceof Styleable )
        {
            final Styleable styleable = ( Styleable ) component;
            styleId = styleable.getDefaultStyleId ();
        }
        else
        {
            styleId = getDefaultStyleId ();
        }
        return styleId;
    }

    @Override
    public String getId ()
    {
        return getDefaultStyleId ().getId ();
    }

    @Override
    public Icon getIcon ()
    {
        final Class key = getComponentClass ();
        if ( componentIcons.containsKey ( key ) )
        {
            return componentIcons.get ( key );
        }
        else
        {
            try
            {
                final ImageIcon icon = new ImageIcon ( getIconResource () );
                componentIcons.put ( key, icon );
                return icon;
            }
            catch ( final Exception e )
            {
                throw new StyleException ( "Unable to find component icon: " + key, e );
            }
        }
    }

    /**
     * Returns {@link Icon} resource.
     * We are simply using default style identifier as icon name for convenience.
     * It only works for WebLaF and Swing components as icons for all those components are predefined.
     * You can simply override this method for your custom component icons.
     *
     * @return {@link Icon} resource
     */
    protected URL getIconResource ()
    {
        final String path = "icons/styleable/" + getId () + ".png";
        return AbstractComponentDescriptor.class.getResource ( path );
    }

    @Override
    public String getTitle ()
    {
        return ReflectUtils.getClassName ( getComponentClass () );
    }

    @Override
    public void updateDefaults ( final UIDefaults table )
    {
        // Updating UI class mapping
        table.put ( getUIClassId (), getUIClass ().getName () );
    }

    @Override
    public void updateUI ( final C component )
    {
        // Check whether or not we need to create new UI instance
        // It will be created and applied if component doesn't have its own UI instance yet
        // It will also be created and applied if component UI instance class is not assignable to the base UI class
        final ComponentUI existingUI = LafUtils.getUI ( component );
        if ( existingUI == null || !getBaseUIClass ().isAssignableFrom ( existingUI.getClass () ) )
        {
            try
            {
                // Using default UI class
                // We don't need to get it from LaF as descriptor value considered to be more important
                final Class uiClass = getUIClass ();

                // Creating UI instance using common Swing way
                final ComponentUI uiInstance = createUI ( component, uiClass );

                // Installing UI into component
                LafUtils.setUI ( component, uiInstance );
            }
            catch ( final Exception e )
            {
                // We were unable to install new component UI
                throw new StyleException ( "Unable to setup component UI: " + component, e );
            }
        }
        else
        {
            // Reinstall existing UI into component
            LafUtils.setUI ( component, existingUI );
        }
    }

    /**
     * Returns {@code ComponentUI} implementation instance for the specified component.
     * Note that whether it is a new instance or reused existing one depends on the {@link ComponentUI} implementation.
     * This method simply invokes static {@code ComponentUI.createUI(component)} method to retrieve the instance.
     *
     * @param component {@code JComponent} to return {@code ComponentUI} implementation instance for
     * @param uiClass   {@link ComponentUI} class
     * @return {@code ComponentUI} implementation instance for the specified component
     */
    protected ComponentUI createUI ( final C component, final Class uiClass )
    {
        final ComponentUI ui;
        if ( uiClass != null )
        {
            try
            {
                // Creating UI through common static method
                ui = ReflectUtils.callStaticMethod ( uiClass, "createUI", component );
            }
            catch ( final Exception e )
            {
                // We were unable to create new component UI
                throw new StyleException ( "Unable to instantiate UI instance: " + uiClass, e );
            }
        }
        else
        {
            // Appropriate UI class was not provided
            throw new StyleException ( "Unable to instantiate UI instance for empty class" );
        }
        return ui;
    }

    @Override
    public String toString ()
    {
        final String parameters = TextUtils.arrayToString ( ", ", componentClass, uiClassId, baseUIClass, uiClass, defaultStyleId );
        return getId () + "[" + parameters + "]";
    }
}