This ClassNormalizer implementation will check the class that is passed into it and all of its super classes and interfaces to determine which class should actually be introspected. It looks for .betwixt mappings for each class and returns the first class that has a .betwixt mapping. This allows Betwixt to gracefully defer to super class and interface mappings. A RuntimeException is thrown if no mappings are found. This is intended to serve as a sanity check to make sure that everything has a mapping of some sort (either directly or on a super class or interface). The rationale is that if the default Betwixt behavior was being used then a customer ClassNormalizer probably isn't necessary or useful. (For example, how would you know which super class or interface to pick?) However, this behavior can be overridden with the second constructor form to return the same class that was passed in if no mappings are found.

import java.util.*;
import org.apache.commons.betwixt.strategy.*;
import org.apache.commons.logging.*;

/**
 * @author Jesse Sweetland
 */
public class HierarchalClassNormalizer extends ClassNormalizer {
    private Log _log = LogFactory.getLog(getClass());
    private boolean _exceptionIfNoMapping = true;
    
    public HierarchalClassNormalizer() {
        this(true);
    }
    
    /**
     * @param exceptionIfNoMapping <tt>true</tt> to throw a
     * <tt>RuntimeException</tt> if no mappings are found; 
     * <tt>false</tt> to return the class that is being
     * normalized
     */
    public HierarchalClassNormalizer(boolean exceptionIfNoMapping) {
        _exceptionIfNoMapping = exceptionIfNoMapping;
    }
    
    private Set<Class> getSuperClassesAndInterfaces(Class c) {
        Set<Class> superClassesAndInterfaces = new LinkedHashSet<Class>();
        Class temp = c;
        while(temp != null) {
            superClassesAndInterfaces.add(temp);
            for(Class ifc: temp.getInterfaces()) {
                superClassesAndInterfaces.add(ifc);
            }
            temp = temp.getSuperclass();
        }
        return superClassesAndInterfaces;
    }
    
    protected String getDotBetwixtResourcePath(Class c) {
        return c.getName().substring(c.getName().lastIndexOf('.') + 1) + ".betwixt";
    }
    
    public Class getNormalizedClass(Object o) {
        if(o == null) throw new NullPointerException();
        Class c = o.getClass();
        for(Class d: getSuperClassesAndInterfaces(c)) {
            // Check to see if there is a betwixt file for this class
            String dotBetwixtResourcePath = getDotBetwixtResourcePath(d);
            if(d.getResourceAsStream(dotBetwixtResourcePath) != null) {
                _log.debug("Class " + c.getName() + " => " + d.getName());
                return d;
            }
        }
        
        if(_exceptionIfNoMapping) {
            throw new RuntimeException("No .betwixt mapping found for class " + c.getName() + " or any of its superclasses or interfaces");
        } else {
            return c;
        }
    }
    
    public Class normalize(Class c) {
        for(Class d: getSuperClassesAndInterfaces(c)) {
            // Check to see if there is a betwixt file for this class
            String dotBetwixtResourcePath = getDotBetwixtResourcePath(d);
            if(d.getResourceAsStream(dotBetwixtResourcePath) != null) {
                _log.debug("Class " + c.getName() + " => " + d.getName());
                return d;
            }
        }
        
        if(_exceptionIfNoMapping) {
            throw new RuntimeException("No .betwixt mapping found for class " + c.getName() + " or any of its superclasses or interfaces");
        } else {
            return c;
        }
    }
}
  • No labels