001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.assertions;
014
015import static org.apache.juneau.common.internal.StringUtils.*;
016
017import java.text.*;
018import java.util.function.*;
019
020import org.apache.juneau.common.internal.StringUtils;
021import org.apache.juneau.cp.*;
022
023/**
024 * Wrapper around a {@link Predicate} that allows for an error message for when the predicate fails.
025 *
026 * <p>
027 * Typically used wherever predicates are allowed for testing of {@link Assertion} objects such as...
028 * <ul>
029 *    <li>{@link FluentObjectAssertion#is(Predicate)}
030 *    <li>{@link FluentArrayAssertion#is(Predicate...)}
031 *    <li>{@link FluentPrimitiveArrayAssertion#is(Predicate...)}
032 *    <li>{@link FluentListAssertion#isEach(Predicate...)}
033 * </ul>
034 *
035 * <p>
036 * See {@link AssertionPredicates} for a set of predefined predicates for common use cases.
037 *
038 * <h5 class='section'>Example:</h5>
039 * <p class='bjava'>
040 *    <jc>// Asserts that a bean passes a custom test.</jc>
041 *    <jc>// AssertionError with specified message is thrown otherwise.</jc>
042 *    Predicate&lt;MyBean&gt; <jv>predicate</jv> = <jk>new</jk> AssertionPredicate&lt;MyBean&gt;(
043 *       <jv>x</jv> -&gt; <jv>x</jv>.getFoo().equals(<js>"bar"</js>),
044 *       <js>"Foo did not equal bar.  Bean was=''{0}''"</js>,
045 *       <jsf>VALUE</jsf>
046 *    );
047 *    <jsm>assertObject</jsm>(<jv>myBean</jv>).is(<jv>predicate</jv>);
048 * </p>
049 *
050 * <h5 class='section'>See Also:</h5>
051 * <ul>
052 *    <li class='link'><a class="doclink" href="../../../../index.html#ja.Overview">Overview &gt; juneau-assertions &gt; Overview</a>
053 * </ul>
054 *
055 * @param <T> the type of input being tested.
056 */
057public class AssertionPredicate<T> implements Predicate<T> {
058
059   //-----------------------------------------------------------------------------------------------------------------
060   // Static
061   //-----------------------------------------------------------------------------------------------------------------
062
063   /**
064    * Argument placeholder for tested value.
065    */
066   public static final Function<Object,String> VALUE = StringUtils::stringifyDeep;
067
068   private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, "Messages");
069   private static final String
070      MSG_valueDidNotPassTest = MESSAGES.getString("valueDidNotPassTest"),
071      MSG_valueDidNotPassTestWithValue = MESSAGES.getString("valueDidNotPassTestWithValue");
072
073   //-----------------------------------------------------------------------------------------------------------------
074   // Instance
075   //-----------------------------------------------------------------------------------------------------------------
076
077   private final Predicate<T> inner;
078   private final String message;
079   private final Object[] args;
080   final ThreadLocal<String> failedMessage = new ThreadLocal<>();
081
082   /**
083    * Constructor.
084    *
085    * @param inner The predicate test.
086    * @param message
087    *    The error message if predicate fails.
088    *    <br>Supports {@link MessageFormat}-style arguments.
089    * @param args
090    *    Optional message arguments.
091    *    <br>Can contain {@link #VALUE} to specify the value itself as an argument.
092    *    <br>Can contain {@link Function functions} to apply to the tested value.
093    */
094   public AssertionPredicate(Predicate<T> inner, String message, Object...args) {
095      this.inner = inner;
096      if (message != null) {
097         this.message = message;
098         this.args = args;
099      } else if (inner instanceof AssertionPredicate) {
100         this.message = MSG_valueDidNotPassTest;
101         this.args = new Object[]{};
102      } else {
103         this.message = MSG_valueDidNotPassTestWithValue;
104         this.args = new Object[]{VALUE};
105      }
106   }
107
108   AssertionPredicate() {
109      this.inner = null;
110      this.message = null;
111      this.args = null;
112   }
113
114   //-----------------------------------------------------------------------------------------------------------------
115   // Test methods
116   //-----------------------------------------------------------------------------------------------------------------
117
118   @Override /* Predicate */
119   @SuppressWarnings({"unchecked","rawtypes"})
120   public boolean test(T t) {
121      failedMessage.remove();
122      boolean b = inner.test(t);
123      if (! b) {
124         String m = message;
125         Object[] args = new Object[this.args.length];
126         for (int i = 0; i < args.length; i++) {
127            Object a = this.args[i];
128            if (a instanceof Function)
129               args[i] = ((Function)a).apply(t);
130            else
131               args[i] = a;
132         }
133         m = format(m, args);
134         if (inner instanceof AssertionPredicate)
135            m += "\n\t" + ((AssertionPredicate<?>)inner).getFailureMessage();
136         failedMessage.set(m);
137      }
138      return inner.test(t);
139   }
140
141   //-----------------------------------------------------------------------------------------------------------------
142   // Utility methods
143   //-----------------------------------------------------------------------------------------------------------------
144
145   /**
146    * Returns the error message from the last call to this assertion.
147    *
148    * @return The error message, or <jk>null</jk> if there was no failure.
149    */
150   protected String getFailureMessage() {
151      return failedMessage.get();
152   }
153
154   //-----------------------------------------------------------------------------------------------------------------
155   // Subclasses
156   //-----------------------------------------------------------------------------------------------------------------
157
158   /**
159    * Encapsulates multiple predicates into a single AND operation.
160    *
161    * <p>
162    * Similar to <c><jsm>stream</jsm>(<jv>predicates</jv>).reduce(<jv>x</jv>-&gt;<jk>true</jk>, Predicate::and)</c> but
163    * provides for {@link #getFailureMessage()} to return a useful message.
164    *
165    * @param <T> the type of input being tested.
166    */
167   public static class And<T> extends AssertionPredicate<T> {
168
169      private final Predicate<T>[] inner;
170
171      private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, "Messages");
172      private static final String
173         MSG_predicateTestFailed = MESSAGES.getString("predicateTestFailed");
174
175      /**
176       * Constructor.
177       *
178       * @param inner The inner predicates to run.
179       */
180      @SafeVarargs
181      public And(Predicate<T>...inner) {
182         this.inner = inner;
183      }
184
185      @Override /* Predicate */
186      public boolean test(T t) {
187         failedMessage.remove();
188         for (int i = 0; i < inner.length; i++) {
189            Predicate<T> p = inner[i];
190            if (p != null) {
191               boolean b = p.test(t);
192               if (! b) {
193                  String m = format(MSG_predicateTestFailed, i+1);
194                  if (p instanceof AssertionPredicate)
195                     m += "\n\t" + ((AssertionPredicate<?>)p).getFailureMessage();
196                  failedMessage.set(m);
197                  return false;
198               }
199            }
200         }
201         return true;
202      }
203   }
204
205   /**
206    * Encapsulates multiple predicates into a single OR operation.
207    *
208    * <p>
209    * Similar to <c><jsm>stream</jsm>(<jv>predicates</jv>).reduce(<jv>x</jv>-&gt;<jk>true</jk>, Predicate::or)</c> but
210    * provides for {@link #getFailureMessage()} to return a useful message.
211    *
212    * @param <T> the type of input being tested.
213    */
214   public static class Or<T> extends AssertionPredicate<T> {
215
216      private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, "Messages");
217      private static final String
218         MSG_noPredicateTestsPassed = MESSAGES.getString("noPredicateTestsPassed");
219
220      private final Predicate<T>[] inner;
221
222      /**
223       * Constructor.
224       *
225       * @param inner The inner predicates to run.
226       */
227      @SafeVarargs
228      public Or(Predicate<T>...inner) {
229         this.inner = inner;
230      }
231
232      @Override /* Predicate */
233      public boolean test(T t) {
234         failedMessage.remove();
235         for (Predicate<T> p : inner)
236            if (p != null)
237               if (p.test(t))
238                  return true;
239         String m = format(MSG_noPredicateTestsPassed);
240         failedMessage.set(m);
241         return false;
242      }
243   }
244
245   /**
246    * Negates an assertion.
247    *
248    * <p>
249    * Similar to <c><jv>predicate</jv>.negate()</c> but provides for {@link #getFailureMessage()} to return a useful message.
250    *
251    * @param <T> the type of input being tested.
252    */
253   public static class Not<T> extends AssertionPredicate<T> {
254
255      private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, "Messages");
256      private static final String
257         MSG_predicateTestsUnexpectedlyPassed = MESSAGES.getString("predicateTestsUnexpectedlyPassed");
258
259      private final Predicate<T> inner;
260
261      /**
262       * Constructor.
263       *
264       * @param inner The inner predicates to run.
265       */
266      public Not(Predicate<T> inner) {
267         this.inner = inner;
268      }
269
270      @Override /* Predicate */
271      public boolean test(T t) {
272         failedMessage.remove();
273         Predicate<T> p = inner;
274         if (p != null) {
275            boolean b = p.test(t);
276            if (b) {
277               failedMessage.set(format(MSG_predicateTestsUnexpectedlyPassed));
278               return false;
279            }
280         }
281         return true;
282      }
283   }
284}