/* * Copyright (c) 2010-2016 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.xml.namespace.QName; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldCallback; /** * * @author semancik */ public class DebugUtil { private static boolean detailedDebugDump = false; private static String prettyPrintBeansAs = null; public static boolean isDetailedDebugDump() { return detailedDebugDump; } public static void setDetailedDebugDump(boolean detailedDebugDump) { DebugUtil.detailedDebugDump = detailedDebugDump; } public static String getPrettyPrintBeansAs() { return prettyPrintBeansAs; } // Experimental. To be used e.g. in tests, for dumps to be easier to read. YAML looks like a good option here. // (It would be nice to serialize it with some 'no namespaces' option.) public static void setPrettyPrintBeansAs(String language) { DebugUtil.prettyPrintBeansAs = language; } public static String formatElementName(QName elementName) { if (elementName == null) { return "null"; } if (detailedDebugDump) { return PrettyPrinter.prettyPrint(elementName); } else { return elementName.getLocalPart(); } } public static String dump(DebugDumpable dumpable) { if (dumpable == null) { return "null"; } return dumpable.debugDump(); } public static String dump(Object object) { if (object == null) { return "null"; } if (object instanceof DebugDumpable) { return ((DebugDumpable)object).debugDump(); } if (object instanceof Map) { StringBuilder sb = new StringBuilder(); debugDumpMapMultiLine(sb, (Map)object, 0); return sb.toString(); } if (object instanceof Collection) { return debugDump((Collection)object); } return object.toString(); } public static String debugDump(Collection<?> dumpables) { return debugDump(dumpables, 0); } public static String debugDump(Collection<?> dumpables, int indent) { StringBuilder sb = new StringBuilder(); debugDump(sb, dumpables, indent, true); return sb.toString(); } public static String debugDump(Map<?,?> dumpables, int indent) { StringBuilder sb = new StringBuilder(); debugDumpMapMultiLine(sb, dumpables, indent, true); return sb.toString(); } public static void debugDump(StringBuilder sb, Collection<?> dumpables, int indent, boolean openCloseSymbols) { debugDump(sb, dumpables, indent, openCloseSymbols, null); } public static void debugDump(StringBuilder sb, Collection<?> dumpables, int indent, boolean openCloseSymbols, String dumpSuffix) { if (dumpables == null) { return; } if (openCloseSymbols) { indentDebugDump(sb, indent); sb.append(getCollectionOpeningSymbol(dumpables)); if (dumpSuffix != null) { sb.append(dumpSuffix); } sb.append("\n"); } Iterator<?> iterator = dumpables.iterator(); while (iterator.hasNext()) { Object item = iterator.next(); if (item == null) { indentDebugDump(sb, indent + 1); sb.append("null"); } else if (item instanceof DebugDumpable) { sb.append(((DebugDumpable)item).debugDump(indent + 1)); } else { indentDebugDump(sb, indent + 1); sb.append(item.toString()); } if (iterator.hasNext()) { sb.append("\n"); } } if (openCloseSymbols) { if (!dumpables.isEmpty()) { sb.append("\n"); } indentDebugDump(sb, indent); sb.append(getCollectionClosingSymbol(dumpables)); } } public static String debugDump(DebugDumpable dd) { return debugDump(dd, 0); } public static String debugDump(DebugDumpable dd, int indent) { if (dd == null) { StringBuilder sb = new StringBuilder(); indentDebugDump(sb, indent + 1); sb.append("null"); return sb.toString(); } else { return dd.debugDump(indent); } } public static String debugDump(Object object, int indent) { if (object == null) { StringBuilder sb = new StringBuilder(); indentDebugDump(sb, indent + 1); sb.append("null"); return sb.toString(); } if (object instanceof DebugDumpable) { return ((DebugDumpable)object).debugDump(indent); } else if (object instanceof Map) { return debugDump((Map)object, indent); } else if (object instanceof Collection) { return debugDump((Collection<?>)object, indent); } else { StringBuilder sb = new StringBuilder(); indentDebugDump(sb, indent + 1); sb.append(object.toString()); return sb.toString(); } } public static void debugDumpLabel(StringBuilder sb, String label, int indent) { indentDebugDump(sb, indent); sb.append(label).append(":"); } public static void debugDumpLabelLn(StringBuilder sb, String label, int indent) { debugDumpLabel(sb, label, indent); sb.append("\n"); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, DebugDumpable dd, int indent) { debugDumpWithLabel(sb,label,dd,indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, DebugDumpable dd, int indent) { debugDumpLabel(sb, label, indent); if (dd == null) { sb.append(" null"); } else { sb.append("\n"); sb.append(dd.debugDump(indent + 1)); } } public static void debugDumpWithLabel(StringBuilder sb, String label, String val, int indent) { debugDumpLabel(sb, label, indent); sb.append(" "); sb.append(val); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, String val, int indent) { debugDumpWithLabel(sb, label, val, indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, QName val, int indent) { debugDumpLabel(sb, label, indent); sb.append(" "); sb.append(PrettyPrinter.prettyPrint(val)); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, QName val, int indent) { debugDumpWithLabel(sb, label, val, indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, Boolean val, int indent) { debugDumpLabel(sb, label, indent); sb.append(" "); sb.append(val); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, Boolean val, int indent) { debugDumpWithLabel(sb, label, val, indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, Integer val, int indent) { debugDumpLabel(sb, label, indent); sb.append(" "); sb.append(val); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, Integer val, int indent) { debugDumpWithLabel(sb, label, val, indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, Long val, int indent) { debugDumpLabel(sb, label, indent); sb.append(" "); sb.append(val); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, Long val, int indent) { debugDumpWithLabel(sb, label, val, indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, Class val, int indent) { debugDumpLabel(sb, label, indent); sb.append(" "); sb.append(val); } public static void debugDumpWithLabelLn(StringBuilder sb, String label, Class val, int indent) { debugDumpWithLabel(sb, label, val, indent); sb.append("\n"); } public static void debugDumpWithLabel(StringBuilder sb, String label, Collection<?> values, int indent) { debugDumpLabel(sb, label, indent); if (values == null) { sb.append(" null"); } else if (values.isEmpty()) { sb.append(" "); sb.append(getCollectionOpeningSymbol(values)); sb.append(getCollectionClosingSymbol(values)); } else { sb.append("\n"); sb.append(debugDump(values, indent + 1)); } } public static void debugDumpWithLabelLn(StringBuilder sb, String label, Collection<?> values, int indent) { debugDumpWithLabel(sb, label, values, indent); sb.append("\n"); } public static <K, V> void debugDumpWithLabel(StringBuilder sb, String label, Map<K, V> map, int indent) { debugDumpLabel(sb, label, indent); if (map == null) { sb.append(" null"); } else { sb.append("\n"); debugDumpMapMultiLine(sb, map, indent + 1); } } public static <K, V> void debugDumpWithLabelLn(StringBuilder sb, String label, Map<K, V> map, int indent) { debugDumpWithLabel(sb, label, map, indent); sb.append("\n"); } public static void debugDumpWithLabelToString(StringBuilder sb, String label, Object object, int indent) { debugDumpLabel(sb, label, indent); if (object == null) { sb.append(" null"); } else { sb.append(" "); sb.append(object.toString()); } } public static void debugDumpWithLabelToStringLn(StringBuilder sb, String label, Object object, int indent) { debugDumpWithLabelToString(sb, label, object, indent); sb.append("\n"); } public static String debugDumpXsdAnyProperties(Collection<?> xsdAnyCollection, int indent) { StringBuilder sb = new StringBuilder(); indentDebugDump(sb, indent); sb.append(getCollectionOpeningSymbol(xsdAnyCollection)); for (Object element : xsdAnyCollection) { sb.append("\n"); indentDebugDump(sb, indent+1); sb.append(PrettyPrinter.prettyPrintElementAsProperty(element)); } sb.append("\n"); indentDebugDump(sb, indent); sb.append(getCollectionClosingSymbol(xsdAnyCollection)); return sb.toString(); } public static String getCollectionOpeningSymbol(Collection<?> col) { if (col instanceof List) { return "["; } if (col instanceof Set) { return "{"; } return col.getClass().getSimpleName()+"("; } public static String getCollectionClosingSymbol(Collection<?> col) { if (col instanceof List) { return "]"; } if (col instanceof Set) { return "}"; } return ")"; } public static void indentDebugDump(StringBuilder sb, int indent) { for(int i = 0; i < indent; i++) { sb.append(DebugDumpable.INDENT_STRING); } } public static <K, V> String debugDumpMapMultiLine(Map<K, V> map) { StringBuilder sb = new StringBuilder(); debugDumpMapMultiLine(sb, map, 0); return sb.toString(); } public static <K, V> void debugDumpMapMultiLine(StringBuilder sb, Map<K, V> map, int indent) { debugDumpMapMultiLine(sb, map, indent, false); } public static <K, V> void debugDumpMapMultiLine(StringBuilder sb, Map<K, V> map, int indent, boolean openCloseSymbols) { debugDumpMapMultiLine(sb, map, indent, openCloseSymbols, null); } public static <K, V> void debugDumpMapMultiLine(StringBuilder sb, Map<K, V> map, int indent, boolean openCloseSymbols, String dumpSuffix) { int inindent = indent; if (openCloseSymbols) { indentDebugDump(sb,indent); sb.append("("); if (dumpSuffix != null) { sb.append(dumpSuffix); } sb.append("\n"); inindent++; } Iterator<Entry<K, V>> i = map.entrySet().iterator(); while (i.hasNext()) { Entry<K,V> entry = i.next(); indentDebugDump(sb,inindent); sb.append(PrettyPrinter.prettyPrint(entry.getKey())); sb.append(" => "); V value = entry.getValue(); if (value == null) { sb.append("null"); } else if (value instanceof DebugDumpable) { sb.append("\n"); sb.append(((DebugDumpable)value).debugDump(inindent+1)); } else { sb.append(PrettyPrinter.prettyPrint(value)); } if (i.hasNext()) { sb.append("\n"); } } if (openCloseSymbols) { sb.append("\n"); indentDebugDump(sb,indent); sb.append(")"); } } public static <K, V> void debugDumpMapSingleLine(StringBuilder sb, Map<K, V> map, int indent) { Iterator<Entry<K, V>> i = map.entrySet().iterator(); while (i.hasNext()) { Entry<K,V> entry = i.next(); indentDebugDump(sb,indent); sb.append(PrettyPrinter.prettyPrint(entry.getKey())); sb.append(" => "); V value = entry.getValue(); if (value == null) { sb.append("null"); } else { sb.append(value); } if (i.hasNext()) { sb.append("\n"); } } } public static <T> String valueAndClass(T value) { if (value == null) { return "null"; } return value.getClass().getSimpleName()+":"+value.toString(); } public static String formatDate(Long millis) { if (millis == null) { return "null"; } Date date = new Date(millis); return PrettyPrinter.prettyPrint(date); } public static String excerpt(String input, int maxChars) { if (input == null) { return null; } int eolIndex = input.indexOf('\n'); if (eolIndex >= 0) { maxChars = eolIndex; } if (input.length() <= maxChars) { return input; } return input.substring(0, maxChars)+"..."; } public static String fixIndentInMultiline(int indent, String indentString, String s) { if (s == null) { return null; } int cr = s.indexOf('\r'); int lf = s.indexOf('\n'); String searchFor; if (cr < 0 && lf < 0) { return s; } else if (cr >= 0 && lf >= 0) { searchFor = "\r\n"; } else if (cr >= 0 && lf < 0) { searchFor = "\r"; } else { searchFor = "\n"; } StringBuilder indentation = new StringBuilder(); for(int i = 0; i < indent; i++) { indentation.append(indentString); } String indentationString = indentation.toString(); return s.replace(searchFor, System.lineSeparator() + indentationString); } public static int estimateObjectSize(Serializable o) { if (o == null) { return 0; } try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); return baos.size(); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } } public static void dumpObjectSizeEstimateLn(StringBuilder sb, String label, Serializable o, int indent) { dumpObjectSizeEstimate(sb, label, o, indent); sb.append("\n"); } public static void dumpObjectSizeEstimate(StringBuilder sb, String label, Serializable o, int indent) { indentDebugDump(sb, indent); sb.append(label).append(": "); sb.append(estimateObjectSize(o)); } public static String dumpObjectFieldsSizeEstimate(final Serializable o) { final StringBuilder sb = new StringBuilder(); sb.append(o).append(": ").append(estimateObjectSize(o)).append("\n"); ReflectionUtils.doWithFields(o.getClass(), new FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { int mods = field.getModifiers(); if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) { return; } if (Modifier.isTransient(mods)) { return; } field.setAccessible(true); sb.append("\n"); DebugUtil.indentDebugDump(sb, 1); sb.append(field.getName()); if (Modifier.isStatic(mods)) { sb.append(" (static)"); } sb.append(": "); Object value = field.get(o); if (value == null) { sb.append("null"); } else if (value instanceof Serializable) { sb.append(estimateObjectSize((Serializable)value)); } else { sb.append("non-serializable ("+value.getClass()+")"); } } }); return sb.toString(); } public static Object debugDumpLazily(DebugDumpable dumpable) { if (dumpable == null) { return null; } return new Object() { @Override public String toString() { return dumpable.debugDump(); } }; } public static Object toStringLazily(Object object) { if (object == null) { return null; } return new Object() { @Override public String toString() { return object.toString(); } }; } public static Object debugDumpLazily(Collection<? extends DebugDumpable> dumpables) { if (dumpables == null || dumpables.isEmpty()) { return dumpables; } return new Object() { @Override public String toString() { return debugDump(dumpables); } }; } }