001    /*  Copyright (c) 2000-2006 hamcrest.org
002     */
003    package org.hamcrest.core;
004    
005    import org.hamcrest.BaseMatcher;
006    import org.hamcrest.Description;
007    import org.hamcrest.Factory;
008    import org.hamcrest.Matcher;
009    
010    import java.util.regex.Pattern;
011    
012    import static java.lang.Integer.parseInt;
013    
014    /**
015     * Provides a custom description to another matcher.
016     */
017    public class DescribedAs<T> extends BaseMatcher<T> {
018        private final String descriptionTemplate;
019        private final Matcher<T> matcher;
020        private final Object[] values;
021        
022        private final static Pattern ARG_PATTERN = Pattern.compile("%([0-9]+)"); 
023        
024        public DescribedAs(String descriptionTemplate, Matcher<T> matcher, Object[] values) {
025            this.descriptionTemplate = descriptionTemplate;
026            this.matcher = matcher;
027            this.values = values.clone();
028        }
029        
030        @Override
031        public boolean matches(Object o) {
032            return matcher.matches(o);
033        }
034    
035        @Override
036        public void describeTo(Description description) {
037            java.util.regex.Matcher arg = ARG_PATTERN.matcher(descriptionTemplate);
038            
039            int textStart = 0;
040            while (arg.find()) {
041                description.appendText(descriptionTemplate.substring(textStart, arg.start()));
042                description.appendValue(values[parseInt(arg.group(1))]);
043                textStart = arg.end();
044            }
045            
046            if (textStart < descriptionTemplate.length()) {
047                description.appendText(descriptionTemplate.substring(textStart));
048            }
049        }
050        
051        @Override
052        public void describeMismatch(Object item, Description description) {
053            matcher.describeMismatch(item, description);
054        }
055    
056        /**
057         * Wraps an existing matcher, overriding its description with that specified.  All other functions are
058         * delegated to the decorated matcher, including its mismatch description.
059         * <p/>
060         * For example:
061         * <pre>describedAs("a big decimal equal to %0", equalTo(myBigDecimal), myBigDecimal.toPlainString())</pre> 
062         * 
063         * @param description
064         *     the new description for the wrapped matcher
065         * @param matcher
066         *     the matcher to wrap
067         * @param values
068         *     optional values to insert into the tokenised description
069         */
070        @Factory
071        public static <T> Matcher<T> describedAs(String description, Matcher<T> matcher, Object... values) {
072            return new DescribedAs<T>(description, matcher, values);
073        }
074    }