1 /* 2 * Copyright (c) 2016 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.matchers.text; 6 7 import java.lang.reflect.Array; 8 import java.util.Iterator; 9 import java.util.Map; 10 11 import static java.lang.String.valueOf; 12 13 /** 14 * Prints a Java object value in a way humans can read it neatly. 15 * Inspired on hamcrest. Used for printing arguments in verification errors. 16 */ 17 public class ValuePrinter { 18 19 private ValuePrinter(){} 20 21 /** 22 * Prints given value so that it is neatly readable by humans. 23 * Handles explosive toString() implementations. 24 */ 25 public static String print(final Object value) { 26 if (value == null) { 27 return "null"; 28 } 29 if (value instanceof String) { 30 return '"' + value.toString() + '"'; 31 } 32 if (value instanceof Character) { 33 return printChar((Character) value); 34 } 35 if (value instanceof Long) { 36 return value + "L"; 37 } 38 if (value instanceof Double) { 39 return value + "d"; 40 } 41 if (value instanceof Float) { 42 return value + "f"; 43 } 44 if (value instanceof Short) { 45 return "(short) " + value; 46 } 47 if (value instanceof Byte) { 48 return String.format("(byte) 0x%02X", (Byte) value); 49 } 50 if (value instanceof Map) { 51 return printMap((Map<?, ?>) value); 52 } 53 if (value.getClass().isArray()) { 54 return printValues("[", ", ", "]", new Iterator<Object>() { 55 private int currentIndex = 0; 56 57 public boolean hasNext() { 58 return currentIndex < Array.getLength(value); 59 } 60 61 public Object next() { 62 return Array.get(value, currentIndex++); 63 } 64 65 public void remove() { 66 throw new UnsupportedOperationException("cannot remove items from an array"); 67 } 68 }); 69 } 70 if (value instanceof FormattedText) { 71 return (((FormattedText) value).getText()); 72 } 73 74 return descriptionOf(value); 75 } 76 77 private static String printMap(Map<?,?> map) { 78 StringBuilder result = new StringBuilder(); 79 Iterator<? extends Map.Entry<?, ?>> iterator = map.entrySet().iterator(); 80 while (iterator.hasNext()) { 81 Map.Entry<?, ?> entry = iterator.next(); 82 result.append(print(entry.getKey())).append(" = ").append(print(entry.getValue())); 83 if (iterator.hasNext()) { 84 result.append(", "); 85 } 86 } 87 return "{" + result.toString() + "}"; 88 } 89 90 /** 91 * Print values in a nice format, e.g. (1, 2, 3) 92 * 93 * @param start the beginning of the values, e.g. "(" 94 * @param separator the separator of values, e.g. ", " 95 * @param end the end of the values, e.g. ")" 96 * @param values the values to print 97 * 98 * @return neatly formatted value list 99 */ 100 public static String printValues(String start, String separator, String end, Iterator<?> values) { 101 if(start == null){ 102 start = "("; 103 } 104 if (separator == null){ 105 separator = ","; 106 } 107 if (end == null){ 108 end = ")"; 109 } 110 111 StringBuilder sb = new StringBuilder(start); 112 while(values.hasNext()) { 113 sb.append(print(values.next())); 114 if (values.hasNext()) { 115 sb.append(separator); 116 } 117 } 118 return sb.append(end).toString(); 119 } 120 121 private static String printChar(char value) { 122 StringBuilder sb = new StringBuilder(); 123 sb.append('\''); 124 switch (value) { 125 case '"': 126 sb.append("\\\""); 127 break; 128 case '\n': 129 sb.append("\\n"); 130 break; 131 case '\r': 132 sb.append("\\r"); 133 break; 134 case '\t': 135 sb.append("\\t"); 136 break; 137 default: 138 sb.append(value); 139 } 140 sb.append('\''); 141 return sb.toString(); 142 } 143 144 private static String descriptionOf(Object value) { 145 try { 146 return valueOf(value); 147 } 148 catch (Exception e) { 149 return value.getClass().getName() + "@" + Integer.toHexString(value.hashCode()); 150 } 151 } 152 } 153