001package org.hamcrest;
002
003import static java.lang.String.valueOf;
004
005import java.util.Arrays;
006import java.util.Iterator;
007
008import org.hamcrest.internal.ArrayIterator;
009import org.hamcrest.internal.SelfDescribingValueIterator;
010
011/**
012 * A {@link Description} that is stored as a string.
013 */
014public 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}