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 }