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.bct.assertions;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021import static org.apache.juneau.bct.assertions.AssertionPredicate.*;
022
023import java.util.function.*;
024import java.util.regex.*;
025
026import org.apache.juneau.commons.utils.*;
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</a>
051 * </ul>
052 */
053@SuppressWarnings({
054   "java:S115", // Constants use UPPER_snakeCase convention
055   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
056})
057public class AssertionPredicates {
058
059   private static final Function<Object,String> TYPENAME = Shorts::cn;
060
061   private static final Messages MESSAGES = Messages.of(AssertionPredicates.class, "Messages");
062   private static final String MSG_valueWasNull = MESSAGES.getString("valueWasNull");
063   private static final String MSG_valueWasNotNull = MESSAGES.getString("valueWasNotNull");
064   private static final String MSG_valueDidNotMatchExpected = MESSAGES.getString("valueDidNotMatchExpected");
065   private static final String MSG_valueDidNotContainExpected = MESSAGES.getString("valueDidNotContainExpected");
066   private static final String MSG_valueUnexpectedlyMatched = MESSAGES.getString("valueUnexpectedlyMatched");
067   private static final String MSG_valueWasNotExpectedType = MESSAGES.getString("valueWasNotExpectedType");
068   private static final String MSG_valueDidNotMatchPattern = MESSAGES.getString("valueDidNotMatchPattern");
069
070   /**
071    * Combines the specified predicates into a singled AND'ed predicate.
072    *
073    * <p>
074    * Assertion error message is <js>"Predicate test #x failed."</js> followed by
075    * the inner failed message if the failed predicate extends from {@link AssertionPredicate}.
076    *
077    * @param <T> The predicate type.
078    * @param predicates The predicates to combine.
079    * @return The combined predicates.
080    */
081   @SafeVarargs
082   public static final <T> AssertionPredicate<T> and(Predicate<T>...predicates) {
083      return new AssertionPredicate.And<>(predicates);
084   }
085
086   /**
087    * Predicate that always returns <jk>true</jk>.
088    *
089    * <p>
090    * Note that this typically has the same affect as a <jk>null</jk> predicate.
091    *
092    * @param <T> The object type being tested.
093    * @return A new predicate.
094    */
095   public static final <T> AssertionPredicate<T> any() {
096      return test(x -> true, null);
097   }
098
099   /**
100    * Predicate that returns <jk>true</jk> if the tested value converted to a string contains the specified substring.
101    *
102    * <p>
103    * Assertion error message is <js>"Value did not contain expected.  Expected='%s', Actual='%s'."</js>.
104    *
105    * @param <T> The object type being tested.
106    * @param value The specified value.
107    * @return A new predicate.
108    */
109   public static final <T> AssertionPredicate<T> contains(String value) {
110      return test(x -> StringUtils.contains(s(x), value), MSG_valueDidNotContainExpected, value, VALUE);  // NOAI
111   }
112
113   /**
114    * Predicate that returns <jk>true</jk> if the tested value equals the specified value.
115    *
116    * <p>
117    * Uses standard Java equality for testing.
118    *
119    * <p>
120    * Assertion error message is <js>"Value did not match expected.  Expected='%s', Actual='%s'."</js>.
121    *
122    * @param <T> The object type being tested.
123    * @param value The specified value.
124    * @return A new predicate.
125    */
126   public static final <T> AssertionPredicate<T> eq(Object value) {
127      return test(x -> Shorts.eq(x, value), MSG_valueDidNotMatchExpected, value, VALUE);
128   }
129
130   /**
131    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified value.
132    *
133    * <p>
134    * Assertion error message is <js>"Value did not match expected.  Expected='%s', Actual='%s'."</js>.
135    *
136    * @param <T> The object type being tested.
137    * @param value The specified value.
138    * @return A new predicate.
139    */
140   public static final <T> AssertionPredicate<T> eq(String value) {
141      return test(x -> Shorts.eq(s(x), value), MSG_valueDidNotMatchExpected, value, VALUE);  // NOAI
142   }
143
144   /**
145    * Predicate that returns <jk>true</jk> if the tested value converted to a string does not match the specified value ignoring case.
146    *
147    * <p>
148    * Assertion error message is <js>"Value did not match expected.  Expected='%s', Actual='%s'."</js>.
149    *
150    * @param <T> The object type being tested.
151    * @param value The specified value.
152    * @return A new predicate.
153    */
154   public static final <T> AssertionPredicate<T> eqic(String value) {
155      return test(x -> Shorts.eqic(s(x), value), MSG_valueDidNotMatchExpected, value, VALUE);  // NOAI
156   }
157
158   /**
159    * Predicate that returns <jk>true</jk> if the tested value is exactly specified type.
160    *
161    * <p>
162    * Assertion error message is <js>"Value was not expected type.  Expected='%s', Actual='%s'."</js>.
163    *
164    * @param <T> The object type being tested.
165    * @param type The specified type.
166    * @return A new predicate.
167    */
168   public static final <T> AssertionPredicate<T> exactType(Class<?> type) {
169      reqnn("type", type);
170      return test(x -> x != null && x.getClass().equals(type), MSG_valueWasNotExpectedType, cn(type), TYPENAME);
171   }
172
173   /**
174    * Predicate that returns <jk>true</jk> if the tested value is null.
175    *
176    * <p>
177    * Assertion error message is <js>"Value was not null."</js>.
178    *
179    * @param <T> The object type being tested.
180    * @return A new predicate.
181    */
182   public static final <T> AssertionPredicate<T> isNull() { return test(x -> x == null, MSG_valueWasNotNull); }
183
184   /**
185    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified match pattern.
186    *
187    * <p>
188    * Match pattern can contain the <js>"*"</js> meta-character.
189    *
190    * <p>
191    * Assertion error message is <js>"Value did not match pattern.  Pattern='%s', Actual='%s'."</js>.
192    *
193    * @param <T> The object type being tested.
194    * @param value The specified value.
195    * @return A new predicate.
196    */
197   public static final <T> AssertionPredicate<T> match(String value) {
198      reqnn("value", value);
199      var p = getMatchPattern(value);
200      return test(x -> x != null && p.matcher(s(x)).matches(), MSG_valueDidNotMatchPattern, value, VALUE);
201   }
202
203   /**
204    * Predicate that returns <jk>true</jk> if the tested value does not match the specified value.
205    *
206    * <p>
207    * Assertion error message is <js>"Value unexpectedly matched.  Value='%s'."</js>.
208    *
209    * @param <T> The object type being tested.
210    * @param value The specified value.
211    * @return A new predicate.
212    */
213   public static final <T> AssertionPredicate<T> ne(Object value) {
214      return test(x -> neq(x, value), MSG_valueUnexpectedlyMatched, VALUE);
215   }
216
217   /**
218    * Predicate that returns <jk>true</jk> if the tested value converted to a string does not match the specified value.
219    *
220    * <p>
221    * Assertion error message is <js>"Value unexpectedly matched.  Value='%s'."</js>.
222    *
223    * @param <T> The object type being tested.
224    * @param value The specified value.
225    * @return A new predicate.
226    */
227   public static final <T> AssertionPredicate<T> ne(String value) {
228      return test(x -> neq(s(x), value), MSG_valueUnexpectedlyMatched, VALUE);
229   }
230
231   /**
232    * Negates the specified predicate.
233    *
234    * <p>
235    * Assertion error message is <js>"Predicate test unexpectedly passed."</js>.
236    *
237    * @param <T> The predicate type.
238    * @param predicate The predicate to negate.
239    * @return The combined predicates.
240    */
241   public static final <T> AssertionPredicate<T> not(Predicate<T> predicate) {
242      return new AssertionPredicate.Not<>(predicate);
243   }
244
245   /**
246    * Predicate that returns <jk>true</jk> if the tested value is not null.
247    *
248    * <p>
249    * Assertion error message is <js>"Value was null."</js>.
250    *
251    * @param <T> The object type being tested.
252    * @return A new predicate.
253    */
254   public static final <T> AssertionPredicate<T> notNull() {
255      return test(x -> x != null, MSG_valueWasNull);
256   }
257
258   /**
259    * Combines the specified predicates into a singled OR'ed predicate.
260    *
261    * <p>
262    * Assertion error message is <js>"No predicate tests passed."</js>.
263    *
264    * @param <T> The predicate type.
265    * @param predicates The predicates to combine.
266    * @return The combined predicates.
267    */
268   @SafeVarargs
269   public static final <T> AssertionPredicate<T> or(Predicate<T>...predicates) {
270      return new AssertionPredicate.Or<>(predicates);
271   }
272
273   /**
274    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
275    *
276    * <p>
277    * Assertion error message is <js>"Value did not match pattern.  Pattern='%s', Actual='%s'."</js>.
278    *
279    * @param <T> The object type being tested.
280    * @param value The regular expression to match.
281    * @return A new predicate.
282    */
283   public static final <T> AssertionPredicate<T> regex(Pattern value) {
284      reqnn("value", value);
285      return test(x -> x != null && value.matcher(s(x)).matches(), MSG_valueDidNotMatchPattern, value.pattern(), VALUE);
286   }
287
288   /**
289    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
290    *
291    * <p>
292    * Assertion error message is <js>"Value did not match pattern.  Pattern='%s', Actual='%s'."</js>.
293    *
294    * @param <T> The object type being tested.
295    * @param expression The regular expression to match.
296    * @return A new predicate.
297    */
298   public static final <T> AssertionPredicate<T> regex(String expression) {
299      reqnn("expression", expression);
300      var p = Pattern.compile(expression);
301      return test(x -> x != null && p.matcher(s(x)).matches(), MSG_valueDidNotMatchPattern, expression, VALUE);
302   }
303
304   /**
305    * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
306    *
307    * <p>
308    * Assertion error message is <js>"Value did not match pattern.  Pattern='%s', Actual='%s'."</js>.
309    *
310    * @param <T> The object type being tested.
311    * @param expression The regular expression to match.
312    * @param flags Match flags, a bit mask that may include:
313    *    <ul>
314    *       <li>{@link Pattern#CASE_INSENSITIVE CASE_INSENSITIVE}
315    *       <li>{@link Pattern#MULTILINE MULTILINE}
316    *       <li>{@link Pattern#DOTALL DOTALL}
317    *       <li>{@link Pattern#UNICODE_CASE UNICODE_CASE}
318    *       <li>{@link Pattern#CANON_EQ CANON_EQ}
319    *       <li>{@link Pattern#UNIX_LINES UNIX_LINES}
320    *       <li>{@link Pattern#LITERAL LITERAL}
321    *       <li>{@link Pattern#UNICODE_CHARACTER_CLASS UNICODE_CHARACTER_CLASS}
322    *       <li>{@link Pattern#COMMENTS COMMENTS}
323    * @return A new predicate.
324    */
325   public static final <T> AssertionPredicate<T> regex(String expression, int flags) {
326      reqnn("expression", expression);
327      var p = Pattern.compile(expression, flags);
328      return test(x -> x != null && p.matcher(s(x)).matches(), MSG_valueDidNotMatchPattern, expression, VALUE);
329   }
330
331   /**
332    * Predicate that wraps another predicate.
333    *
334    * <p>
335    * If the predicate extends from {@link AssertionPredicate}, the assertion error
336    * message is <js>"Value did not pass test."</js> followed by the inner assertion error.
337    * Otherwise the message is <js>"Value did not pass test.  Value='%s'."</js>
338    *
339    * @param <T> The object type being tested.
340    * @param predicate The predicate to run.
341    * @return A new predicate.
342    */
343   public static final <T> AssertionPredicate<T> test(Predicate<T> predicate) {
344      return new AssertionPredicate<>(predicate, null);
345   }
346
347   /**
348    * Predicate that wraps another predicate.
349    *
350    * <p>
351    * If the message specified is <jk>null</jk> and the predicate extends from {@link AssertionPredicate}, the assertion error
352    * message is <js>"Value did not pass test."</js> followed by the inner assertion error.
353    * Otherwise the message is <js>"Value did not pass test.  Value='%s'."</js>
354    *
355    * @param <T> The object type being tested.
356    * @param predicate The predicate to run.
357    * @param msg
358    *    The error message if predicate fails.
359    *    <br>Supports printf-style arguments.
360    * @param args
361    *    Optional message arguments.
362    *    <br>Can contain {@code #VALUE} to specify the value itself as an argument.
363    *    <br>Can contain {@link Function functions} to apply to the tested value.
364    * @return A new predicate.
365    */
366   public static final <T> AssertionPredicate<T> test(Predicate<T> predicate, String msg, Object...args) {
367      return new AssertionPredicate<>(predicate, msg, args);
368   }
369
370   /**
371    * Predicate that returns <jk>true</jk> if the tested value is the specified or child type.
372    *
373    * <p>
374    * Assertion error message is <js>"Value was not expected type.  Expected='%s', Actual='%s'."</js>.
375    *
376    * @param <T> The object type being tested.
377    * @param type The specified type.
378    * @return A new predicate.
379    */
380   public static final <T> AssertionPredicate<T> type(Class<?> type) {
381      reqnn("type", type);
382      return test(x -> x != null && type.isAssignableFrom(x.getClass()), MSG_valueWasNotExpectedType, cn(type), TYPENAME);
383   }
384
385   /**
386    * Constructor.
387    */
388   protected AssertionPredicates() {} // HTT - utility class; instantiation via subclass is theoretical
389}