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