001 package org.hamcrest;
002
003 import static java.lang.String.valueOf;
004
005 import java.util.Arrays;
006 import java.util.Iterator;
007
008 import org.hamcrest.internal.ArrayIterator;
009 import org.hamcrest.internal.SelfDescribingValueIterator;
010
011 /**
012 * A {@link Description} that is stored as a string.
013 */
014 public abstract class BaseDescription implements Description {
015
016 @Override
017 public Description appendText(String text) {
018 append(text);
019 return this;
020 }
021
022 @Override
023 public Description appendDescriptionOf(SelfDescribing value) {
024 value.describeTo(this);
025 return this;
026 }
027
028 @Override
029 public Description appendValue(Object value) {
030 if (value == null) {
031 append("null");
032 } else if (value instanceof String) {
033 toJavaSyntax((String) value);
034 } else if (value instanceof Character) {
035 append('"');
036 toJavaSyntax((Character) value);
037 append('"');
038 } else if (value instanceof Short) {
039 append('<');
040 append(descriptionOf(value));
041 append("s>");
042 } else if (value instanceof Long) {
043 append('<');
044 append(descriptionOf(value));
045 append("L>");
046 } else if (value instanceof Float) {
047 append('<');
048 append(descriptionOf(value));
049 append("F>");
050 } else if (value.getClass().isArray()) {
051 appendValueList("[",", ","]", new ArrayIterator(value));
052 } else {
053 append('<');
054 append(descriptionOf(value));
055 append('>');
056 }
057 return this;
058 }
059
060 private String descriptionOf(Object value) {
061 try {
062 return valueOf(value);
063 }
064 catch (Exception e) {
065 return value.getClass().getName() + "@" + Integer.toHexString(value.hashCode());
066 }
067 }
068
069 @Override
070 public <T> Description appendValueList(String start, String separator, String end, T... values) {
071 return appendValueList(start, separator, end, Arrays.asList(values));
072 }
073
074 @Override
075 public <T> Description appendValueList(String start, String separator, String end, Iterable<T> values) {
076 return appendValueList(start, separator, end, values.iterator());
077 }
078
079 private <T> Description appendValueList(String start, String separator, String end, Iterator<T> values) {
080 return appendList(start, separator, end, new SelfDescribingValueIterator<T>(values));
081 }
082
083 @Override
084 public Description appendList(String start, String separator, String end, Iterable<? extends SelfDescribing> values) {
085 return appendList(start, separator, end, values.iterator());
086 }
087
088 private Description appendList(String start, String separator, String end, Iterator<? extends SelfDescribing> i) {
089 boolean separate = false;
090
091 append(start);
092 while (i.hasNext()) {
093 if (separate) append(separator);
094 appendDescriptionOf(i.next());
095 separate = true;
096 }
097 append(end);
098
099 return this;
100 }
101
102 /**
103 * Append the String <var>str</var> to the description.
104 * The default implementation passes every character to {@link #append(char)}.
105 * Override in subclasses to provide an efficient implementation.
106 */
107 protected void append(String str) {
108 for (int i = 0; i < str.length(); i++) {
109 append(str.charAt(i));
110 }
111 }
112
113 /**
114 * Append the char <var>c</var> to the description.
115 */
116 protected abstract void append(char c);
117
118 private void toJavaSyntax(String unformatted) {
119 append('"');
120 for (int i = 0; i < unformatted.length(); i++) {
121 toJavaSyntax(unformatted.charAt(i));
122 }
123 append('"');
124 }
125
126 private void toJavaSyntax(char ch) {
127 switch (ch) {
128 case '"':
129 append("\\\"");
130 break;
131 case '\n':
132 append("\\n");
133 break;
134 case '\r':
135 append("\\r");
136 break;
137 case '\t':
138 append("\\t");
139 break;
140 default:
141 append(ch);
142 }
143 }
144 }