1c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinpackage org.hamcrest;
2c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
3c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport org.hamcrest.internal.ArrayIterator;
4c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport org.hamcrest.internal.SelfDescribingValueIterator;
5c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
6c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport java.util.Arrays;
7c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport java.util.Iterator;
8c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
9c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport static java.lang.String.valueOf;
10c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
11c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin/**
12c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin * A {@link Description} that is stored as a string.
13c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin */
14c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinpublic abstract class BaseDescription implements Description {
15c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
16c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    @Override
17c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    public Description appendText(String text) {
18c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        append(text);
19c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return this;
20c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
21c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
22c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    @Override
23c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    public Description appendDescriptionOf(SelfDescribing value) {
24c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        value.describeTo(this);
25c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return this;
26c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
27c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
28c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    @Override
29c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    public Description appendValue(Object value) {
30c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        if (value == null) {
31c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append("null");
32c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else if (value instanceof String) {
33c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            toJavaSyntax((String) value);
34c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else if (value instanceof Character) {
35c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('"');
36c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            toJavaSyntax((Character) value);
37c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('"');
38c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else if (value instanceof Short) {
39c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('<');
40c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append(descriptionOf(value));
41c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append("s>");
42c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else if (value instanceof Long) {
43c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('<');
44c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append(descriptionOf(value));
45c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append("L>");
46c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else if (value instanceof Float) {
47c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('<');
48c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append(descriptionOf(value));
49c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append("F>");
50c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else if (value.getClass().isArray()) {
51c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            appendValueList("[",", ","]", new ArrayIterator(value));
52c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        } else {
53c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('<');
54c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append(descriptionOf(value));
55c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append('>');
56c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
57c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return this;
58c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
59c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
60c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    private String descriptionOf(Object value) {
61c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        try {
62c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            return valueOf(value);
63c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
64c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        catch (Exception e) {
65c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            return value.getClass().getName() + "@" + Integer.toHexString(value.hashCode());
66c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
67c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
68c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
69c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    @Override
70c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    public <T> Description appendValueList(String start, String separator, String end, T... values) {
71c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return appendValueList(start, separator, end, Arrays.asList(values));
72c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
73c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
74c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    @Override
75c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    public <T> Description appendValueList(String start, String separator, String end, Iterable<T> values) {
76c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return appendValueList(start, separator, end, values.iterator());
77c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
78c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
79c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    private <T> Description appendValueList(String start, String separator, String end, Iterator<T> values) {
80c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return appendList(start, separator, end, new SelfDescribingValueIterator<T>(values));
81c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
82c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
83c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    @Override
84c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    public Description appendList(String start, String separator, String end, Iterable<? extends SelfDescribing> values) {
85c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return appendList(start, separator, end, values.iterator());
86c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
87c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
88c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    private Description appendList(String start, String separator, String end, Iterator<? extends SelfDescribing> i) {
89c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        boolean separate = false;
90c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
91c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        append(start);
92c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        while (i.hasNext()) {
93c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            if (separate) append(separator);
94c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            appendDescriptionOf(i.next());
95c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            separate = true;
96c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
97c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        append(end);
98c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
99c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        return this;
100c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
101c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
102c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    /**
103c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin     * Append the String <var>str</var> to the description.
104c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin     * The default implementation passes every character to {@link #append(char)}.
105c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin     * Override in subclasses to provide an efficient implementation.
106c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin     */
107c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    protected void append(String str) {
108c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        for (int i = 0; i < str.length(); i++) {
109c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            append(str.charAt(i));
110c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
111c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
112c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
113c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    /**
114c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin     * Append the char <var>c</var> to the description.
115c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin     */
116c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    protected abstract void append(char c);
117c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
118c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    private void toJavaSyntax(String unformatted) {
119c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        append('"');
120c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        for (int i = 0; i < unformatted.length(); i++) {
121c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            toJavaSyntax(unformatted.charAt(i));
122c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
123c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        append('"');
124c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
125c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
126c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    private void toJavaSyntax(char ch) {
127c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        switch (ch) {
128c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            case '"':
129c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                append("\\\"");
130c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                break;
131c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            case '\n':
132c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                append("\\n");
133c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                break;
134c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            case '\r':
135c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                append("\\r");
136c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                break;
137c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            case '\t':
138c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                append("\\t");
139c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                break;
140c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin            default:
141c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin                append(ch);
142c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin        }
143c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin    }
144c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin}
145