001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.assertions;
018
019import static org.apache.juneau.assertions.AssertionPredicate.*;
020import java.text.*;
021import java.util.*;
022import java.util.function.*;
023import java.util.regex.*;
024
025import org.apache.juneau.common.utils.*;
026import org.apache.juneau.cp.*;
027
028/**
029 * Predefined {@link AssertionPredicate} objects.
030 *
031 * <p>
032 * Typically used wherever predicates are allowed for testing of {@link Assertion} objects such as...
033 * <ul>
034 *    <li>{@link FluentObjectAssertion#is(Predicate)}
035 *    <li>{@link FluentArrayAssertion#is(Predicate...)}
036 *    <li>{@link FluentPrimitiveArrayAssertion#is(Predicate...)}
037 *    <li>{@link FluentListAssertion#isEach(Predicate...)}
038 * </ul>
039 *
040 * <h5 class='section'>Example:</h5>
041 * <p class='bjava'>
042 *    <jc>// Asserts that a list contains te specified values.</jc>
043 *    List&lt;Object&gt; <jv>myList</jv> = AList.<jsm>of</jsm>(...);
044 *    <jsm>assertList</jsm>(<jv>myList</jv>)
045 *       .is(<jsm>eq</jsm>(<js>"foo"</js>), <jsm>any</jsm>(), <jsm>match</jsm>(<js>"bar*"</js>));
046 * </p>
047 *
048 * <h5 class='section'>See Also:</h5>
049 * <ul>
050 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem Overview</a>
051 * </ul>
052 */
053public class AssertionPredicates {
054
055   /**
056    * Constructor.
057    */
058   protected AssertionPredicates() {}
059
060   private static final Function<Object,String> TYPENAME = x -> x == null ? null : x.getClass().getName();
061
062   private static final Messages MESSAGES = Messages.of(AssertionPredicates.class, "Messages");
063   private static final String
064      MSG_valueWasNull = MESSAGES.getString("valueWasNull"),
065      MSG_valueWasNotNull = MESSAGES.getString("valueWasNotNull"),
066      MSG_valueDidNotMatchExpected = MESSAGES.getString("valueDidNotMatchExpected"),
067      MSG_valueDidNotContainExpected = MESSAGES.getString("valueDidNotContainExpected"),
068      MSG_valueUnexpectedlyMatched = MESSAGES.getString("valueUnexpectedlyMatched"),
069      MSG_valueWasNotExpectedType = MESSAGES.getString("valueWasNotExpectedType"),
070      MSG_valueDidNotMatchPattern = MESSAGES.getString("valueDidNotMatchPattern");
071
072   /**
073    * Predicate that always returns <jk>true</jk>.
074    *
075    * <p>
076    * Note that this typically has the same affect as a <jk>null</jk> predicate.
077    *
078    * @param <T> The object type being tested.
079    * @return A new predicate.
080    */
081   public static final <T> AssertionPredicate<T> any() {
082      return test(x -> true, null);
083   }
084
085   /**
086    * Predicate that returns <jk>true</jk> if the tested value is not null.
087    *
088    * <p>
089    * Assertion error message is <js>"Value was null."</js>.
090    *
091    * @param <T> The object type being tested.
092    * @return A new predicate.
093    */
094   public static final <T> AssertionPredicate<T> notNull() {
095      return test(x -> x != null, MSG_valueWasNull);
096   }
097
098   /**
099    * Predicate that returns <jk>true</jk> if the tested value is null.
100    *
101    * <p>
102    * Assertion error message is <js>"Value was not null."</js>.
103    *
104    * @param <T> The object type being tested.
105    * @return A new predicate.
106    */
107   public static final <T> AssertionPredicate<T> isNull() {
108      return test(x -> x == null, MSG_valueWasNotNull);
109   }
110
111   /**
112    * Predicate that returns <jk>true</jk> if the tested value equals the specified value.
113    *
114    * <p>
115    * Uses standard Java equality for testing.
116    *
117    * <p>
118    * Assertion error message is <js>"Value did not match expected.  Expected='{0}', Actual='{1}'."</js>.
119    *
120    * @param <T> The object type being tested.
121    * @param value The specified value.
122    * @return A new predicate.
123    */
124   public static final <T> AssertionPredicate<T> eq(Object value) {
125      return test(x -> Objects.equals(x, value), MSG_valueDidNotMatchExpected, value, VALUE);
126   }
127
128   /**
129    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified value.
130    *
131    * <p>
132    * Assertion error message is <js>"Value did not match expected.  Expected='{0}', Actual='{1}'."</js>.
133    *
134    * @param <T> The object type being tested.
135    * @param value The specified value.
136    * @return A new predicate.
137    */
138   public static final <T> AssertionPredicate<T> eq(String value) {
139      return test(x -> Objects.equals(Utils.s(x), value), MSG_valueDidNotMatchExpected, value, VALUE);
140   }
141
142   /**
143    * Predicate that returns <jk>true</jk> if the tested value does not match the specified value.
144    *
145    * <p>
146    * Assertion error message is <js>"Value unexpectedly matched.  Value='{0}'."</js>.
147    *
148    * @param <T> The object type being tested.
149    * @param value The specified value.
150    * @return A new predicate.
151    */
152   public static final <T> AssertionPredicate<T> ne(Object value) {
153      return test(x -> ! Objects.equals(x, value), MSG_valueUnexpectedlyMatched, VALUE);
154   }
155
156   /**
157    * Predicate that returns <jk>true</jk> if the tested value converted to a string does not match the specified value.
158    *
159    * <p>
160    * Assertion error message is <js>"Value unexpectedly matched.  Value='{0}'."</js>.
161    *
162    * @param <T> The object type being tested.
163    * @param value The specified value.
164    * @return A new predicate.
165    */
166   public static final <T> AssertionPredicate<T> ne(String value) {
167      return test(x -> ! Objects.equals(Utils.s(x), value), MSG_valueUnexpectedlyMatched, VALUE);
168   }
169
170   /**
171    * Predicate that returns <jk>true</jk> if the tested value converted to a string does not match the specified value ignoring case.
172    *
173    * <p>
174    * Assertion error message is <js>"Value did not match expected.  Expected='{0}', Actual='{1}'."</js>.
175    *
176    * @param <T> The object type being tested.
177    * @param value The specified value.
178    * @return A new predicate.
179    */
180   public static final <T> AssertionPredicate<T> eqic(String value) {
181      return test(x -> Utils.eqic(Utils.s(x), value), MSG_valueDidNotMatchExpected, value, VALUE);
182   }
183
184   /**
185    * Predicate that returns <jk>true</jk> if the tested value converted to a string contains the specified substring.
186    *
187    * <p>
188    * Assertion error message is <js>"Value did not contain expected.  Expected='{0}', Actual='{1}'."</js>.
189    *
190    * @param <T> The object type being tested.
191    * @param value The specified value.
192    * @return A new predicate.
193    */
194   public static final <T> AssertionPredicate<T> contains(String value) {
195      return test(x -> StringUtils.contains(Utils.s(x), value), MSG_valueDidNotContainExpected, value, VALUE);
196   }
197
198   /**
199    * Predicate that returns <jk>true</jk> if the tested value is the specified or child type.
200    *
201    * <p>
202    * Assertion error message is <js>"Value was not expected type.  Expected='{0}', Actual='{1}'."</js>.
203    *
204    * @param <T> The object type being tested.
205    * @param type The specified type.
206    * @return A new predicate.
207    */
208   public static final <T> AssertionPredicate<T> type(Class<?> type) {
209      Utils.assertArgNotNull("type", type);
210      return test(x -> x != null && type.isAssignableFrom(x.getClass()), MSG_valueWasNotExpectedType, type, TYPENAME);
211   }
212
213   /**
214    * Predicate that returns <jk>true</jk> if the tested value is exactly specified type.
215    *
216    * <p>
217    * Assertion error message is <js>"Value was not expected type.  Expected='{0}', Actual='{1}'."</js>.
218    *
219    * @param <T> The object type being tested.
220    * @param type The specified type.
221    * @return A new predicate.
222    */
223   public static final <T> AssertionPredicate<T> exactType(Class<?> type) {
224      Utils.assertArgNotNull("type", type);
225      return test(x -> x != null && x.getClass().equals(type), MSG_valueWasNotExpectedType, type, TYPENAME);
226   }
227
228   /**
229    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified match pattern.
230    *
231    * <p>
232    * Match pattern can contain the <js>"*"</js> meta-character.
233    *
234    * <p>
235    * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
236    *
237    * @param <T> The object type being tested.
238    * @param value The specified value.
239    * @return A new predicate.
240    */
241   public static final <T> AssertionPredicate<T> match(String value) {
242      Utils.assertArgNotNull("value", value);
243      var p = Utils.getMatchPattern3(value);
244      return test(x -> x != null && p.matcher(Utils.s(x)).matches(), MSG_valueDidNotMatchPattern, value, VALUE);
245   }
246
247   /**
248    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
249    *
250    * <p>
251    * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
252    *
253    * @param <T> The object type being tested.
254    * @param expression The regular expression to match.
255    * @return A new predicate.
256    */
257   public static final <T> AssertionPredicate<T> regex(String expression) {
258      Utils.assertArgNotNull("expression", expression);
259      var p = Pattern.compile(expression);
260      return test(x -> x != null && p.matcher(Utils.s(x)).matches(), MSG_valueDidNotMatchPattern, expression, VALUE);
261   }
262
263   /**
264    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
265    *
266    * <p>
267    * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
268    *
269    * @param <T> The object type being tested.
270    * @param expression The regular expression to match.
271    * @param flags Match flags, a bit mask that may include:
272    *    <ul>
273    *       <li>{@link Pattern#CASE_INSENSITIVE CASE_INSENSITIVE}
274    *       <li>{@link Pattern#MULTILINE MULTILINE}
275    *       <li>{@link Pattern#DOTALL DOTALL}
276    *       <li>{@link Pattern#UNICODE_CASE UNICODE_CASE}
277    *       <li>{@link Pattern#CANON_EQ CANON_EQ}
278    *       <li>{@link Pattern#UNIX_LINES UNIX_LINES}
279    *       <li>{@link Pattern#LITERAL LITERAL}
280    *       <li>{@link Pattern#UNICODE_CHARACTER_CLASS UNICODE_CHARACTER_CLASS}
281    *       <li>{@link Pattern#COMMENTS COMMENTS}
282    * @return A new predicate.
283    */
284   public static final <T> AssertionPredicate<T> regex(String expression, int flags) {
285      Utils.assertArgNotNull("expression", expression);
286      var p = Pattern.compile(expression, flags);
287      return test(x -> x != null && p.matcher(Utils.s(x)).matches(), MSG_valueDidNotMatchPattern, expression, VALUE);
288   }
289
290   /**
291    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
292    *
293    * <p>
294    * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
295    *
296    * @param <T> The object type being tested.
297    * @param value The regular expression to match.
298    * @return A new predicate.
299    */
300   public static final <T> AssertionPredicate<T> regex(Pattern value) {
301      Utils.assertArgNotNull("value", value);
302      return test(x -> x != null && value.matcher(Utils.s(x)).matches(), MSG_valueDidNotMatchPattern, value.pattern(), VALUE);
303   }
304
305   /**
306    * Predicate that wraps another predicate.
307    *
308    * <p>
309    * If the predicate extends from {@link AssertionPredicate}, the assertion error
310    * message is <js>"Value did not pass test."</js> followed by the inner assertion error.
311    * Otherwise the message is <js>"Value did not pass test.  Value='{0}'."</js>
312    *
313    * @param <T> The object type being tested.
314    * @param predicate The predicate to run.
315    * @return A new predicate.
316    */
317   public static final <T> AssertionPredicate<T> test(Predicate<T> predicate) {
318      return new AssertionPredicate<>(predicate, null);
319   }
320
321   /**
322    * Predicate that wraps another predicate.
323    *
324    * <p>
325    * If the message specified is <jk>null</jk> and the predicate extends from {@link AssertionPredicate}, the assertion error
326    * message is <js>"Value did not pass test."</js> followed by the inner assertion error.
327    * Otherwise the message is <js>"Value did not pass test.  Value='{0}'."</js>
328    *
329    * @param <T> The object type being tested.
330    * @param predicate The predicate to run.
331    * @param msg
332    *    The error message if predicate fails.
333    *    <br>Supports {@link MessageFormat}-style arguments.
334    * @param args
335    *    Optional message arguments.
336    *    <br>Can contain {@code #VALUE} to specify the value itself as an argument.
337    *    <br>Can contain {@link Function functions} to apply to the tested value.
338    * @return A new predicate.
339    */
340   public static final <T> AssertionPredicate<T> test(Predicate<T> predicate, String msg, Object...args) {
341      return new AssertionPredicate<>(predicate, msg, args);
342   }
343
344   /**
345    * Combines the specified predicates into a singled AND'ed predicate.
346    *
347    * <p>
348    * Assertion error message is <js>"Predicate test #x failed."</js> followed by
349    * the inner failed message if the failed predicate extends from {@link AssertionPredicate}.
350    *
351    * @param <T> The predicate type.
352    * @param predicates The predicates to combine.
353    * @return The combined predicates.
354    */
355   @SafeVarargs
356   public static final <T> AssertionPredicate<T> and(Predicate<T>...predicates) {
357      return new AssertionPredicate.And<>(predicates);
358   }
359
360   /**
361    * Combines the specified predicates into a singled OR'ed predicate.
362    *
363    * <p>
364    * Assertion error message is <js>"No predicate tests passed."</js>.
365    *
366    * @param <T> The predicate type.
367    * @param predicates The predicates to combine.
368    * @return The combined predicates.
369    */
370   @SafeVarargs
371   public static final <T> AssertionPredicate<T> or(Predicate<T>...predicates) {
372      return new AssertionPredicate.Or<>(predicates);
373   }
374
375   /**
376    * Negates the specified predicate.
377    *
378    * <p>
379    * Assertion error message is <js>"Predicate test unexpectedly passed."</js>.
380    *
381    * @param <T> The predicate type.
382    * @param predicate The predicate to negate.
383    * @return The combined predicates.
384    */
385   public static final <T> AssertionPredicate<T> not(Predicate<T> predicate) {
386      return new AssertionPredicate.Not<>(predicate);
387   }
388}