// PropertyHolderInspector.java
// $Id: PropertyHolderInspector.java,v 1.3 1997/07/30 14:07:16 ylafon Exp $  
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.tools.resources.impl;

import java.util.*;
import java.lang.reflect.*;
import java.beans.*;

/**
 * Why do we need this at all ?
 * <p>That's a good question. One problem with the beans introspection 
 * is that it doesn't allow to modify the default introspection behavior
 * on a class <em>hierarchy</em>.
 * <p>In some sense this class acts as an Introspector, specialized for 
 * the needs of resources. Note, however, that this is as compatible as 
 * possible with the default Introspector.
 */

public class PropertyHolderInspector {

    protected static Hashtable classes = new Hashtable();

    private static String getPropertyAccess(String prefix, String p) {
	if (p == null || p.length() == 0) {
	    return p;
	}
	if ((p.length() <= 1)
	    || Character.isUpperCase(p.charAt(1)) 
	    || Character.isUpperCase(p.charAt(0))) {
	    return prefix+p;
	}
	char chars[] = p.toCharArray();
	chars[0] = Character.toUpperCase(chars[0]);
	return prefix+(new String(chars));

    }

    private static Method lookupMethod(Class c, String name) {
	while ( c != null ) {
	    Method mths[] = c.getDeclaredMethods();
	    for (int i = 0 ; i < mths.length ; i++) {
		if ( mths[i].getName().equals(name) )
		    return mths[i];
	    }
	    c = c.getSuperclass();
	}
	return null;
    }

    public static void print(Class cls) {
	System.out.println("Properties of: "+cls.getName());
	// Print class infos related to that class:
	Hashtable   props = getProperties(cls);
	Enumeration e     = props.elements();
	while ( e.hasMoreElements() ) {
	    PropertyDescriptor p = (PropertyDescriptor) e.nextElement();
	    System.out.print("\t"+p.getName());
	    if ( p.getReadMethod() != null )
		System.out.print(", read="+p.getReadMethod().getName());
	    if ( p.getWriteMethod() != null )
		System.out.print(", write="+p.getWriteMethod().getName());
	    System.out.println("");
	}
    }

    public static Hashtable getProperties(Class cls) {
	// Do we already know about this class ?
	Hashtable ret = (Hashtable) classes.get(cls);
	if ( ret != null )
	    return ret;
	try {
	    // Compute that class properties:
	    BeanInfo           infos   = null;
	    PropertyDescriptor props[] = null;
	    ret   = new Hashtable(5);
	    infos = Introspector.getBeanInfo(cls);    
	    props = infos.getPropertyDescriptors();
	    // Filter on the supplied attribute names:
	    for (int ip = 0 ; ip < props.length ; ip++) {
		PropertyDescriptor p = props[ip];
		// Hack the descriptor a little
		String setter = getPropertyAccess("iget", p.getName());
		String getter = getPropertyAccess("iset", p.getName());
		Method get    = lookupMethod(cls, getter);
		Method set    = lookupMethod(cls, setter);
		// A property with no accessors is not a property:
		if ((set == null) && (get == null))
		    continue;
		// Register the descriptor
		ret.put(p.getName()
			, new PropertyDescriptor(p.getName(), set, get));
	    }
	} catch (Exception ex) {
	    throw new InternalError("unable to introspect: "+cls.getName());
	}
	classes.put(cls, ret);
	return ret;
    }

}
