1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.ochafik.lang.reflect;
20
21 import java.io.PrintStream;
22 import java.lang.reflect.Array;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Method;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Comparator;
28 import java.util.Set;
29 import java.util.TreeSet;
30
31 import com.ochafik.io.StringBufferOutputStream;
32 import com.ochafik.lang.AssertUtils;
33
34 public class DebugUtils {
35
36 public static abstract class FieldAccessor {
37 public abstract Object access(Field f, Object target) throws IllegalArgumentException, IllegalAccessException;
38
39
40
41 }
42
43 public static final void print(Object o, FieldAccessor accessor) {
44 print(o, System.out, false, false, "", accessor);
45 }
46
47 public static final void printErr(Object o, FieldAccessor accessor) {
48 print(o, System.err, false, false, "", accessor);
49 }
50
51 public static final void println(Object o, FieldAccessor accessor) {
52 print(o, System.out, true, false, "", accessor);
53 }
54
55 public static final void printlnErr(Object o, FieldAccessor accessor) {
56 print(o, System.err, true, false, "", accessor);
57 }
58
59 public static final void print(Object o) {
60 print(o, System.out, false, false, "", null);
61 }
62
63 public static final void printErr(Object o) {
64 print(o, System.err, false, false, "", null);
65 }
66
67 public static final void println(Object o) {
68 print(o, System.out, true, false, "", null);
69 }
70 public static final void println(Object o, PrintStream out) {
71 print(o, out, true, false, "", null);
72 }
73
74 public static final void printlnErr(Object o) {
75 print(o, System.err, true, false, "", null);
76 }
77
78 protected static String escape(String s) {
79 return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\t", "\\t").replace("\r", "");
80 }
81 public static final void printAsCharSequence(CharSequence o, PrintStream out, boolean lines, boolean startIndent, String indent, FieldAccessor accessor) {
82 out.print('"');
83 out.print(escape(((CharSequence)o).toString()));
84 out.print('"');
85 if (lines) out.println();
86 }
87
88 public static final void printStructureInsides(Object o, PrintStream out, boolean lines, boolean startIndent, String indent, FieldAccessor accessor) {
89 Class<?> type = o.getClass();
90 Set<Field> fields = getFields(type);
91 int i = 0;
92 for (Field f : fields) {
93 boolean inaccessibleValue = false;
94 Object v;
95 try {
96
97 v = accessor == null ? f.get(o) : accessor.access(f, o);
98 } catch (IllegalAccessException ex) {
99
100 String fn = f.getName();
101 try {
102 Method m = type.getMethod("get" + fn.substring(0,1).toUpperCase() + fn.substring(1), new Class[0]);
103 v = m.invoke(o, new Object[0]);
104 } catch (Exception e) {
105 inaccessibleValue = true;
106 v = null;
107 }
108 } catch (Exception e) {
109 throw new RuntimeException(e);
110 }
111 if (lines) {
112 out.print(indent);
113 } else if (i++ != 0) out.print("; ");
114
115 out.print(f.getName());
116 out.print(" = ");
117 if (inaccessibleValue) {
118 out.print('?');
119 } else {
120 print(v, out, lines, false, indent, accessor);
121 }
122 }
123 }
124
125 public static final void print(Object o, PrintStream out, boolean lines, boolean startIndent, String indent, FieldAccessor accessor) {
126 if (lines && startIndent) out.print(indent);
127
128 if (o == null) {
129 out.print(o);
130 if (lines) out.println();
131 return;
132 }
133 Class<?> type = o.getClass();
134 boolean isCollection = o instanceof Collection<?>;
135
136 if (o instanceof CharSequence) {
137 out.print('"');
138 out.print(escape(o.toString()));
139 out.print('"');
140 if (lines) out.println();
141 return;
142 } else if (type == Character.class) {
143 out.print('\'');
144 out.print(escape(o.toString()));
145 out.print('\'');
146 if (lines) out.println();
147 return;
148 } else if (!isCollection && hasToStringMethod(type)) {
149 out.print(o);
150 if (lines) out.println();
151 return;
152 }
153 out.print(type.getSimpleName());
154 out.print(" {");
155 if (lines) out.println();
156
157 String newIndent = indent + "\t";
158
159 if (type.isArray()) {
160 for (int i = 0, n = Array.getLength(o); i < n; i++) {
161 if (!lines && i != 0) out.print(", ");
162 print(Array.get(o, i), out, lines, true, newIndent, accessor);
163 }
164 } else if (isCollection) {
165 int i = 0;
166 for (Object e : (Collection<?>)o) {
167 if (!lines && i++ != 0) out.print(", ");
168 print(e, out, lines, true, newIndent, accessor);
169 }
170 } else {
171 printStructureInsides(o, out, lines, startIndent, newIndent, accessor);
172 }
173 if (lines) out.print(indent);
174 out.print("}");
175 if (lines) out.println();
176 }
177
178 public static Set<Field> getFields(Class<?> type) {
179 Set<Field> fields = new TreeSet<Field>(new Comparator<Field>() { public int compare(Field o1, Field o2) {
180 return o1.getName().compareTo(o2.getName());
181 }});
182 fields.addAll(Arrays.asList(type.getFields()));
183 do {
184 fields.addAll(Arrays.asList(type.getDeclaredFields()));
185 } while ((type = type.getSuperclass()) != null);
186 return fields;
187 }
188 protected static void getFields_aux(Class<?> type, Set<Field> fields) {
189
190 }
191
192 public static boolean hasToStringMethod(Class<?> c) {
193 if (c == Object.class) return false;
194 for (Method m : c.getDeclaredMethods()) {
195 if (m.getName().equals("toString") && m.getParameterTypes().length == 0) {
196 return true;
197 }
198 }
199 return hasToStringMethod(c.getSuperclass());
200 }
201
202 public static void main(String[] args) {
203
204 println(new Object[] { new AssertUtils.Test(), }, new FieldAccessor() {
205 public Object access(Field f, Object target) throws IllegalArgumentException, IllegalAccessException {
206 return f.get(target);
207 }
208 });
209 }
210
211 public static String toString(Object object) {
212 StringBufferOutputStream out = new StringBufferOutputStream();
213 print(object, new PrintStream(out), false, false,"", null);
214 return out.toString();
215 }
216
217 }