/*
 * ReflectPropExample.java    3/11/2008, 16:15:14
 *
 * Copyright 2008 John Sands (Australia) Ltd. All rights reserved.
 * Use is subject to license terms.
 */
/*package org.brettryan.propertyexample;*/

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author Brett Ryan
 */
public class ReflectPropExample {

    private static final Logger logger =
            Logger.getLogger(ReflectPropExample.class.getName());

    private static final int PROP_GET = 1;
    private static final int PROP_SET = 2;

    /**
     * Return a collection of <c>PropInfo</c> objects that encapsulate
     * properties for <c>c</c>.
     *
     * @param c     Class to determine properties for.
     */
    private static <T> Collection<PropInfo<T>> getProperties(Class<T> c) {
        Map<String, PropInfo<T>> props = new HashMap<String, PropInfo<T>>() {
        };
        String propName;
        Class type = null;
        int accessor;
        for (Method m : c.getDeclaredMethods()) {
            if (m.getName().startsWith("get")) {
                if (m.getParameterTypes().length != 0) {
                    continue;
                }
                if (Void.TYPE.equals(m.getReturnType())) {
                    continue;
                }
                accessor = PROP_GET;
                type = m.getReturnType();
            } else if (m.getName().startsWith("set")) {
                if (m.getParameterTypes().length != 1) {
                    continue;
                }
                if (!Void.TYPE.equals(m.getReturnType())) {
                    continue;
                }
                accessor = PROP_SET;
                type = m.getParameterTypes()[0];
            } else {
                accessor = 0;
            }
            if (accessor > 0) {
                propName = m.getName().substring(3);
                PropInfo<T> propInfo;
                if (props.containsKey(propName)) {
                    propInfo = props.get(propName);
                    if (!propInfo.getType().equals(type)) {
                        logger.log(Level.WARNING,
                                "Property type mismatch for : " + propName);
                        continue;
                    }
                } else {
                    propInfo = new PropInfo<T>(propName, type);
                    props.put(propName, propInfo);
                }
                switch (accessor) {
                    case PROP_SET:
                        propInfo.setSetter(m);
                        break;
                    case PROP_GET:
                        propInfo.setGetter(m);
                        break;
                }
            }
        }
        return new ArrayList<PropInfo<T>>(props.values());
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Collection<PropInfo<javax.swing.JFrame>> props = getProperties(javax.swing.JFrame.class);

        javax.swing.JFrame jf = new javax.swing.JFrame();
        Object def;
        for (PropInfo p : props) {
            try {
                def = p.getValue(jf);
                System.out.println(p.getName() + " : " + p.getType().getName()
                        + " : " +
                        "Default = " + (def == null ? "null" : def.toString()));
            } catch (IllegalAccessException ex) {
                logger.log(Level.SEVERE, null, ex);
            } catch (IllegalArgumentException ex) {
                logger.log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                logger.log(Level.SEVERE,
                        " ** Target invocation threw exception while getting default for '" +
                        p.getName() + "'");
            }
        }

    }

}

/**
 * Encapsulates properties for a given class T.
 */
class PropInfo<T> {

    private String name;
    private Class type;
    private Method getter;
    private Method setter;

    public PropInfo(String name, Class type) {
        this.name = name;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public Class getType() {
        return type;
    }

    public Method getGetter() {
        return getter;
    }

    public void setGetter(Method getter) {
        this.getter = getter;
    }

    public Method getSetter() {
        return setter;
    }

    public void setSetter(Method setter) {
        this.setter = setter;
    }

    public boolean isReadOnly() {
        return (setter.getModifiers() & Modifier.PRIVATE) != 0;
    }

    public Object getValue(T object)
            throws IllegalAccessException,
                   IllegalArgumentException,
                   InvocationTargetException {
        return getter == null ? null : getter.invoke(object);
    }

    public void setValue(T object, Object value)
            throws IllegalAccessException,
                   IllegalArgumentException,
                   InvocationTargetException {
        setter.invoke(object, value);
    }

}

