001    /*
002            Copyright (c) 2009 Olivier Chafik, All Rights Reserved
003            
004            This file is part of JNAerator (http://jnaerator.googlecode.com/).
005            
006            JNAerator is free software: you can redistribute it and/or modify
007            it under the terms of the GNU General Public License as published by
008            the Free Software Foundation, either version 3 of the License, or
009            (at your option) any later version.
010            
011            JNAerator is distributed in the hope that it will be useful,
012            but WITHOUT ANY WARRANTY; without even the implied warranty of
013            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014            GNU General Public License for more details.
015            
016            You should have received a copy of the GNU General Public License
017            along with JNAerator.  If not, see <http://www.gnu.org/licenses/>.
018    */
019    package com.ochafik.lang.reflect;
020    
021    import java.io.PrintStream;
022    import java.lang.reflect.Array;
023    import java.lang.reflect.Field;
024    import java.lang.reflect.Method;
025    import java.util.Arrays;
026    import java.util.Collection;
027    import java.util.Comparator;
028    import java.util.Set;
029    import java.util.TreeSet;
030    
031    import com.ochafik.io.StringBufferOutputStream;
032    import com.ochafik.lang.AssertUtils;
033    
034    public class DebugUtils {
035    
036            public static abstract class FieldAccessor {
037                    public abstract Object access(Field f, Object target) throws IllegalArgumentException, IllegalAccessException;
038                    //public Object access(Field f, Object target) throws IllegalArgumentException, IllegalAccessException {
039                    //return f.get(target);
040                    //}
041            }
042    
043            public static final void print(Object o, FieldAccessor accessor) {
044                    print(o, System.out, false, false, "", accessor);
045            }
046    
047            public static final void printErr(Object o, FieldAccessor accessor) {
048                    print(o, System.err, false, false, "", accessor);
049            }
050    
051            public static final void println(Object o, FieldAccessor accessor) {
052                    print(o, System.out, true, false, "", accessor);
053            }
054    
055            public static final void printlnErr(Object o, FieldAccessor accessor) {
056                    print(o, System.err, true, false, "", accessor);
057            }
058    
059            public static final void print(Object o) {
060                    print(o, System.out, false, false, "", null);
061            }
062    
063            public static final void printErr(Object o) {
064                    print(o, System.err, false, false, "", null);
065            }
066    
067            public static final void println(Object o) {
068                    print(o, System.out, true, false, "", null);
069            }
070            public static final void println(Object o, PrintStream out) {
071                    print(o, out, true, false, "", null);
072            }
073    
074            public static final void printlnErr(Object o) {
075                    print(o, System.err, true, false, "", null);
076            }
077    
078            protected static String escape(String s) {
079                    return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\t", "\\t").replace("\r", "");
080            }
081            public static final void printAsCharSequence(CharSequence o, PrintStream out, boolean lines, boolean startIndent, String indent, FieldAccessor accessor) {
082                    out.print('"');
083                    out.print(escape(((CharSequence)o).toString()));
084                    out.print('"');
085                    if (lines) out.println();
086            }
087    
088            public static final void printStructureInsides(Object o, PrintStream out, boolean lines, boolean startIndent, String indent, FieldAccessor accessor) {
089                    Class<?> type = o.getClass();
090                    Set<Field> fields = getFields(type);
091                    int i = 0;
092                    for (Field f : fields) {
093                            boolean inaccessibleValue = false;
094                            Object v;
095                            try {
096                                    //f.isAccessible()
097                                    v = accessor == null ? f.get(o) : accessor.access(f, o);
098                            } catch (IllegalAccessException ex) {
099                                    // field is private and from a different package than the accessor ! Try using a getter
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                    //print(new int[] { 1, 2, 3, 4});
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    }