Thursday, October 15, 2009

How to simply write bean objects to String

This utility provide ability to write objects to string. Example says everything:
public class Person {

    private PersonName name;
    private Integer age;

    public static class PersonName {

        private String firstName;
        private String lastName;

        public PersonName(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    }

    public Person(PersonName name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public PersonName getName() {
        return name;
    }

    public void setName(PersonName name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return StringUtils.objectToString("person", this);
    }
}


Then we call:
    public static void main(String[] args) {
        System.out.println(new Person(new PersonName("Astar", "Sheran"), 2009));
    }


and output is:
person.age = 2009
person.name = new Person$PersonName()
person.name.firstName = Astar
person.name.lastName = Sheran


Utility implementation:

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Michal Hlavac
 */
public class StringUtils {

    private static final Logger log = Logger.getLogger(StringUtils.class.getName());

    private static final void toString(String alias, Object obj, StringBuffer sb, List<Object> objects) {
        if (obj == null) {
            return;
        }
        if (objects.contains(obj)) {
            return;
        }
        objects.add(obj);
        try {
            BeanInfo info = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
            for (int i = 0; i < descriptors.length; i++) {
                PropertyDescriptor descriptor = descriptors[i];
                if (descriptor.getReadMethod().getDeclaringClass().equals(Object.class)) {
                    log.fine("property defined on Object class: skipping: " + descriptor.getName());
                    continue;
                }
                Object value = null;
                try {
                    value = descriptor.getReadMethod().invoke(obj, (Object[]) null);
                } catch (Throwable ex) {
                    log.fine("Can't read value of attribute " + obj.getClass().getName() + "." + descriptor.getName());
                }
                if (value == null) {
                    log.fine("property value is null: skipping: " + descriptor.getName());
                    continue;
                }
                Class<?> returnType = descriptor.getReadMethod().getReturnType();
                if (!returnType.isPrimitive() && !returnType.isArray() && !returnType.getName().startsWith("java.") &&
                        !returnType.isEnum()) {
                    log.fine("property is unknown type or not primitive [" + returnType.getName() + "]");
                    String simpleName = value.getClass().getName().substring(returnType.getName().lastIndexOf('.') + 1);
                    sb.append(alias).append(".").append(descriptor.getName()).append(" = new ").append(simpleName).append("()\n");
                    toString(alias + "." + descriptor.getName(), value, sb, objects);
                    continue;
                }
                if (value instanceof java.util.Collection<?>) {
                    try {
                        Iterator<?> iter = ((Collection<?>)  value).iterator();                        int num = 0;
                        while (iter.hasNext()) {
                            Object item = iter.next();
                            toString(alias + "." + descriptor.getName() + "[" + num + "]", item, sb, objects);
                            num++;
                        }
                    } catch (Throwable t) {
                        log.fine("Can't read value of attribute: " + alias + "." + descriptor.getName());
                    }
                    continue;
                } else if (value.getClass().isArray()) {
                    try {
                        Object[] valueArray = (Object[]) value;
                        for (int idx = 0; idx < valueArray.length; idx++) {
                            toString(alias + "." + descriptor.getName() + "[" + idx + "]", valueArray[idx], sb, objects);
                        }
                    } catch (Throwable t) {
                        log.fine("Can't read value of attribute: " + alias + "." + descriptor.getName());
                    }
                    continue;
                }
                sb.append(alias).append(".").append(descriptor.getName()).append(" = ").append(value.toString()).append('\n');
            }
        } catch (Exception e) {
            log.log(Level.SEVERE, "hql example criterion building error", e);
        }
    }

    public static final String objectToString(String alias, Object obj) {
        StringBuffer buff = new StringBuffer();
        toString(alias, obj, buff, new ArrayList<Object>());
        return buff.toString();
    }

    public static final String collectionToString(String alias, Collection<?> collection) {
        StringBuffer buff = new StringBuffer();
        int idx = 0;
        for (Object obj : collection) {
            toString(alias + "[" + idx++ + "]", obj, buff, new ArrayList<Object>());
        }
        return buff.toString();
    }

    public static final String toStringLine(Object obj) {
        if (obj == null) {
            return "";
        }
        String clazz = obj.getClass().getName();
        String simpleClazz = clazz.substring(clazz.lastIndexOf('.') + 1);
        StringBuffer sb = new StringBuffer(simpleClazz).append("[");
        try {
            BeanInfo info = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
            for (int i = 0; i < descriptors.length; i++) {
                PropertyDescriptor descriptor = descriptors[i];
                if (descriptor.getPropertyType() == null ||
                        Collection.class.isAssignableFrom(descriptor.getPropertyType())) {
                    log.fine("collection handling not supported: skipping property: " + descriptor.getName());
                    continue;
                }
                if (descriptor.getReadMethod().getDeclaringClass().equals(Object.class)) {
                    log.fine("property defined on Object class: skipping: " + descriptor.getName());
                    continue;
                }
                Object value = descriptor.getReadMethod().invoke(obj, (Object[]) null);
                if (value == null) {
                    log.fine("property value is null: skipping: " + descriptor.getName());
                    continue;
                }
                Class<?> returnType = descriptor.getReadMethod().getReturnType();
                if (!returnType.isPrimitive() && !returnType.getName().startsWith("java.") && !returnType.isEnum()) {
                    log.fine("property is unknown type or not primitive [" + returnType.getName() + "]");
                    continue;
                }
                sb.append(descriptor.getName()).append(" = ").append(value).append(" | ");
            }
        } catch (Exception e) {
            log.log(Level.SEVERE, "hql example criterion building error", e);
            return null;
        }
        sb.append("]");
        return sb.toString();
    }
}