/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander 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.
 *
 * muCommander 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.extension;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

import javax.swing.LookAndFeel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Class filter for look and feels.
 * <p>
 * This filter will only accept classes if:
 * <ul>
 *   <li>They subclass <code>javax.swing.LookAndFeel</code>.</li>
 *   <li>They are public and not abstract.</li>
 *   <li>They have a public, no-arg constructor.</li>
 *   <li>Their <code>isSupportedLookAndFeel</code> method returns <code>true</code>.</li>
 *   <li>They are not an inner class.</li>
 * </ul>
 * </p>
 * @author Nicolas Rinaudo
 */
public class LookAndFeelFilter implements ClassFilter {
	private static final Logger LOGGER = LoggerFactory.getLogger(LookAndFeelFilter.class);
	
	/**
     * Creates a new instance of <code>LookAndFeelFilter</code>.
     */
    public LookAndFeelFilter() {}

    /**
     * Filters out everything but available look and feels.
     * @param c class to check.
     * @return <code>true</code> if c is an available look and feel, <code>false</code> otherwise.
     */
    public boolean accept(Class<?> c) {
        int            modifiers;   // Class' modifiers.
        Constructor<?> constructor; // Public, no-arg constructor.
        Class<?>       buffer;      // Used to explore c's ancestors.

        // Ignores inner classes.
        if(c.getDeclaringClass() != null)
            return false;

        // Makes sure the class is public and non abstract.
        modifiers = c.getModifiers();
        if(!Modifier.isPublic(modifiers) || Modifier.isAbstract(modifiers))
            return false;

        // Makes sure the class has a public, no-arg constructor.
        try {constructor = c.getDeclaredConstructor(new Class[0]);}
        catch(Exception e) {return false;}
        if(!Modifier.isPublic(constructor.getModifiers()))
            return false;

        // Makes sure the class extends javax.swing.LookAndFeel and that if it does,
        // it's supported by the system.
        buffer = c;
        while(buffer != null) {
            // c is a LookAndFeel, makes sure it's supported.
            if(buffer.equals(LookAndFeel.class)) {
                try {return ((LookAndFeel)c.newInstance()).isSupportedLookAndFeel();}
                catch(Throwable e) {
                    LOGGER.debug("Caught exception", e);
                    return false;
                }
            }
            buffer = buffer.getSuperclass();
        }
        return false;
    }
}
