001 /**
002 * Copyright 2007 Mike Kroutikov.
003 *
004 * This program is free software; you can redistribute it and/or modify
005 * it under the terms of the Lesser GNU General Public License as
006 * published by the Free Software Foundation; either version 3 of
007 * the License, or (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * Lesser GNU General Public License for more details.
013 *
014 * You should have received a copy of the Lesser GNU General Public License
015 * along with this program. If not, see <http://www.gnu.org/licenses/>.
016 */
017
018 package org.otfeed.samples;
019
020 import java.lang.reflect.Method;
021 import java.util.Map;
022 import java.util.HashMap;
023 import java.util.List;
024 import java.util.LinkedList;
025
026 /**
027 * Class that provides CSV formatting for Java POJO beans.
028 *
029 */
030 public class CSVFormatter {
031
032 private String delimeter = ", ";
033
034 public String getDelimeter() { return delimeter; }
035 public void setDelimeter(String val) { delimeter = val; }
036
037 private static class Prop {
038 public final String name;
039 public final Method method;
040
041 private Prop(String name, Method method) {
042 this.name = name;
043 this.method = method;
044 }
045 }
046
047 private final Map<Class<?>,List<Prop>> map
048 = new HashMap<Class<?>,List<Prop>>();
049
050 private final Map<Class<?>,IPropertyFormatter> customFormatters
051 = new HashMap<Class<?>,IPropertyFormatter>();
052
053 public void setCustomPropertyFormatter(Class<?> cls, IPropertyFormatter fmt) {
054 customFormatters.put(cls, fmt);
055 }
056
057 private static String normalizeName(String name) {
058 if(name.startsWith("get")) {
059 name = name.substring(3);
060 } else if(name.startsWith("is")) {
061 name = name.substring(2);
062 }
063
064 if(name.length() > 1 && Character.isLowerCase(name.charAt(1))) {
065 return name.substring(0, 1).toLowerCase() + name.substring(1);
066 } else {
067 return name;
068 }
069 }
070
071 private static boolean isReadableProperty(Method method) {
072 String name = method.getName();
073
074 if(method.getParameterTypes().length > 0) return false;
075
076 if(name.length() > 3 && name.startsWith("get")) {
077 return true;
078 } else if(name.length() > 2 && name.startsWith("is")) {
079 // FIXME: check for boolean class on output??
080 return true;
081 }
082
083 return false;
084 }
085
086 private static List<Prop> introspect(Class<?> cls) throws Exception {
087
088 Method[] method = cls.getDeclaredMethods();
089
090 List<Prop> list = new LinkedList<Prop>();
091 for(int i = 0; i < method.length; i++) {
092 if(!isReadableProperty(method[i])) continue;
093
094 String name = normalizeName(method[i].getName());
095 list.add(new Prop(name, method[i]));
096 }
097
098 return list;
099 }
100
101 public String header(Class<?> cls) {
102
103 List<Prop> prop = map.get(cls);
104 if(prop == null) {
105 try {
106 prop = introspect(cls);
107 map.put(cls, prop);
108 } catch(Exception ex) {
109 throw new AssertionError();
110 }
111 }
112
113 String out = "";
114 for(Prop p : prop) {
115 if(out.length() > 0) out += ", ";
116 out += p.name;
117 }
118
119 return out;
120 }
121
122 public String format(Object obj) {
123 if(obj == null) return "null";
124
125 Class<?> cls = obj.getClass();
126 List<Prop> prop = map.get(cls);
127 if(prop == null) {
128 try {
129 prop = introspect(cls);
130 map.put(cls, prop);
131 } catch(Exception ex) {
132 throw new AssertionError();
133 }
134 }
135
136 String out = "";
137 for(Prop p : prop) {
138 if(out.length() > 0) out += ", ";
139 try {
140 Object val = p.method.invoke(obj);
141 IPropertyFormatter fmt = customFormatters.get(val.getClass());
142 if(fmt != null) {
143 out += fmt.format(val);
144 } else {
145 out += val;
146 }
147 } catch(Exception ex) {
148 throw new AssertionError();
149 }
150 }
151
152 return out;
153 }
154 }