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