001/*  Copyright (c) 2000-2006 hamcrest.org
002 */
003package org.hamcrest.core;
004
005import org.hamcrest.BaseMatcher;
006import org.hamcrest.Description;
007import org.hamcrest.Factory;
008import org.hamcrest.Matcher;
009
010import java.util.regex.Pattern;
011
012import static java.lang.Integer.parseInt;
013
014/**
015 * Provides a custom description to another matcher.
016 */
017public 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}