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