/* * Copyright (c) 2011 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.truth; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.StringUtil.format; import javax.annotation.Nullable; /*>>>import org.checkerframework.checker.nullness.compatqual.NullableType;*/ /** A parent type for some infrastructure used in the Verb. */ // TODO(cgruber) Remove the FailureMessageHolder inheritance and restructure to simplify verbs. public abstract class AbstractVerb<T extends AbstractVerb<T>> extends FailureContext { private final FailureStrategy failureStrategy; public AbstractVerb(FailureStrategy failureStrategy) { this(failureStrategy, null); } public AbstractVerb( FailureStrategy failureStrategy, @Nullable String format, Object /*@NullableType*/... args) { super(format, args); this.failureStrategy = checkNotNull(failureStrategy); } protected FailureStrategy getFailureStrategy() { // TODO(cgruber): Extract this logic solely into the withFailureMessage() methods. return hasFailureMessage() ? new MessagePrependingFailureStrategy(failureStrategy, this) : failureStrategy; } /** Triggers the failure strategy with an empty failure message */ public void fail() { getFailureStrategy().fail(""); } /** Triggers the failure strategy with the given failure message */ public void fail(@Nullable String format, Object /*@NullableType*/... args) { getFailureStrategy().fail(format(format, args)); } /** * Overrides the failure message of the subsequent subject's propositions. * * @see com.google.common.truth.delegation.DelegationTest * @param failureMessage a descriptive message. * @return A custom verb which will show the descriptive message along with the normal failure * text. */ // TODO(cgruber) try to delete this (binary incompatible, but see if there's a way. public abstract T withFailureMessage(@Nullable String failureMessage); /** * Overrides the failure message of the subsequent subject's propositions. * * @see com.google.common.truth.delegation.DelegationTest * @param format a descriptive message with formatting template content. * @param args object parameters to be substituted into the message template. * @return A custom verb which will show the descriptive message along with the normal failure * text. */ public abstract T withFailureMessage(@Nullable String format, Object /*@NullableType*/... args); /** * The recommended method of extension of Truth to new types, which is documented in {@link * com.google.common.truth.delegation.DelegationTest}. * * @see com.google.common.truth.delegation.DelegationTest * @param factory a {@code SubjectFactory<S, D>} implementation * @return A custom verb for the type returned by the SubjectFactory */ public <S extends Subject<S, D>, D, SF extends SubjectFactory<S, D>> DelegatedVerb<S, D> about( SF factory) { return new DelegatedVerb<S, D>(getFailureStrategy(), factory); } /** * A generic, advanced method of extension of Truth to new types, which is documented on {@link * DelegatedVerbFactory}. Extension creators should prefer {@link SubjectFactory} if possible. * * @param <V> the type of {@link AbstractDelegatedVerb} to return * @param factory a {@code DelegatedVerbFactory<V>} implementation * @return A custom verb of type {@code <V>} */ public <V extends AbstractDelegatedVerb<V>> V about(DelegatedVerbFactory<V> factory) { return factory.createVerb(getFailureStrategy()); } /** A special Verb implementation which wraps a SubjectFactory */ public static final class DelegatedVerb<S extends Subject<S, T>, T> extends AbstractDelegatedVerb<DelegatedVerb<S, T>> { private final SubjectFactory<S, T> subjectFactory; private static class Factory<S extends Subject<S, T>, T> implements DelegatedVerbFactory<DelegatedVerb<S, T>> { private final SubjectFactory<S, T> subjectFactory; private Factory(SubjectFactory<S, T> subjectFactory) { this.subjectFactory = subjectFactory; } @Override public DelegatedVerb<S, T> createVerb(FailureStrategy failureStrategy) { return new DelegatedVerb<S, T>(failureStrategy, subjectFactory); } } public DelegatedVerb(FailureStrategy failureStrategy, SubjectFactory<S, T> subjectFactory) { super(failureStrategy, new Factory<S, T>(subjectFactory)); this.subjectFactory = checkNotNull(subjectFactory); } public S that(@Nullable T target) { return subjectFactory.getSubject(failureStrategy, target); } } protected static class MessagePrependingFailureStrategy extends FailureStrategy { private final FailureStrategy delegate; private final FailureContext messageHolder; public MessagePrependingFailureStrategy( FailureStrategy delegate, FailureContext messageHolder) { this.delegate = checkNotNull(delegate); this.messageHolder = checkNotNull(messageHolder); } @Override public void fail(String message) { delegate.fail(prependFailureMessageIfAny(message)); } @Override public void fail(String message, Throwable cause) { delegate.fail(prependFailureMessageIfAny(message), cause); } @Override public void failComparing(String message, CharSequence expected, CharSequence actual) { delegate.failComparing(prependFailureMessageIfAny(message), expected, actual); } private String prependFailureMessageIfAny(String message) { return messageHolder.getFailureMessage() == null ? message : messageHolder.getFailureMessage() + ": " + message; } } }