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    }