Home | History | Annotate | Download | only in text
      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