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.reflect.ReflectionUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.io.*;
023import java.time.*;
024import java.util.*;
025import java.util.function.*;
026
027import org.apache.juneau.commons.function.*;
028
029
030/**
031 * Used for assertion calls against generic POJOs.
032 *
033 * <p>
034 * Extends from {@link FluentObjectAssertion} allowing you to perform basic assertions, but adds several transform
035 * methods to convert to more-specific assertion types.
036 *
037 * <h5 class='section'>Example:</h5>
038 * <p class='bjava'>
039 *    <jk>import static</jk> org.apache.juneau.bct.assertions.Assertions.*;
040 *
041 *    List&lt;MyBean&gt; <jv>listOfBeans</jv> = ...;
042 *    <jsm>assertList</jsm>(<jv>listOfBeans</jv>)
043 *       .asItem(1)  <jc>// Returns an AnyAssertion.</jc>
044 *       .asBean()  <jc>// Transforms to BeanAssertion.</jc>
045 *          .asProperty(<js>"foo"</js>)  <jc>// Returns an AnyAssertion.</jc>
046 *          .asString()  <jc>// Transforms to StringAssertion.</jc>
047 *             .is(<js>"bar"</js>);  <jc>// Performs test.</jc>
048 * </p>
049 *
050 * <h5 class='section'>Test Methods:</h5>
051 * <p>
052 * <ul class='javatree'>
053 *    <li class='jc'>{@link FluentObjectAssertion}
054 *    <ul class='javatreec'>
055 *       <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()}
056 *       <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)}
057 *       <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)}
058 *       <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)}
059 *       <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)}
060 *       <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)}
061 *       <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()}
062 *       <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()}
063 *       <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)}
064 *       <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)}
065 *       <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)}
066 *       <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)}
067 *    </ul>
068 * </ul>
069 *
070 * <h5 class='section'>Transform Methods:</h5>
071 * <p>
072 * <ul class='javatree'>
073 *    <li class='jc'>{@link FluentAnyAssertion}
074 *    <ul class='javatreec'>
075 *       <li class='jm'>{@link FluentAnyAssertion#asArray(Class) asArray(Class)}
076 *       <li class='jm'>{@link FluentAnyAssertion#asIntArray() asIntArray()}
077 *       <li class='jm'>{@link FluentAnyAssertion#asLongArray() asLongArray()}
078 *       <li class='jm'>{@link FluentAnyAssertion#asShortArray() asShortArray()}
079 *       <li class='jm'>{@link FluentAnyAssertion#asFloatArray() asFloatArray()}
080 *       <li class='jm'>{@link FluentAnyAssertion#asDoubleArray() asDoubleArray()}
081 *       <li class='jm'>{@link FluentAnyAssertion#asCharArray() asCharArray()}
082 *       <li class='jm'>{@link FluentAnyAssertion#asByteArray() asByteArray()}
083 *       <li class='jm'>{@link FluentAnyAssertion#asBooleanArray() asBooleanArray()}
084 *       <li class='jm'>{@link FluentAnyAssertion#asBoolean() asBoolean()}
085 *       <li class='jm'>{@link FluentAnyAssertion#asBytes() asBytes()}
086 *       <li class='jm'>{@link FluentAnyAssertion#asCollection() asCollection()}
087 *       <li class='jm'>{@link FluentAnyAssertion#asCollection(Class) asCollection(Class)}
088 *       <li class='jm'>{@link FluentAnyAssertion#asStringList() asStringList()}
089 *       <li class='jm'>{@link FluentAnyAssertion#asComparable() asComparable()}
090 *       <li class='jm'>{@link FluentAnyAssertion#asDate() asDate()}
091 *       <li class='jm'>{@link FluentAnyAssertion#asInteger() asInteger()}
092 *       <li class='jm'>{@link FluentAnyAssertion#asLong() asLong()}
093 *       <li class='jm'>{@link FluentAnyAssertion#asList() asList()}
094 *       <li class='jm'>{@link FluentAnyAssertion#asList(Class) asList(Class)}
095 *       <li class='jm'>{@link FluentAnyAssertion#asMap() asMap()}
096 *       <li class='jm'>{@link FluentAnyAssertion#asMap(Class,Class) asMap(Class,Class)}
097 *       <li class='jm'>{@link FluentAnyAssertion#asBean() asBean()}
098 *       <li class='jm'>{@link FluentAnyAssertion#asBean(Class) asBean(Class)}
099 *       <li class='jm'>{@link FluentAnyAssertion#asBeanList(Class) asBeanList(Class)}
100 *       <li class='jm'>{@link FluentAnyAssertion#asZonedDateTime() asZonedDateTime()}
101 *    </ul>
102 *    <li class='jc'>{@link FluentObjectAssertion}
103 *    <ul class='javatreec'>
104 *       <li class='jm'>{@link FluentObjectAssertion#asString() asString()}
105 *       <li class='jm'>{@link FluentObjectAssertion#asString(ThrowingFunction) asString(ThrowingFunction)}
106 *       <li class='jm'>{@link FluentObjectAssertion#asTransformed(UnaryOperator) asTransformed(UnaryOperator)}
107 *       <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()}
108 * </ul>
109 * </ul>
110 *
111 * <h5 class='section'>Configuration Methods:</h5>
112 * <p>
113 * <ul class='javatree'>
114 *    <li class='jc'>{@link Assertion}
115 *    <ul class='javatreec'>
116 *       <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)}
117 *       <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)}
118 *       <li class='jm'>{@link Assertion#setSilent() setSilent()}
119 *       <li class='jm'>{@link Assertion#setStdOut() setStdOut()}
120 *       <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)}
121 *    </ul>
122 * </ul>
123 *
124 * <h5 class='section'>See Also:</h5><ul>
125 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem</a>
126 * </ul>
127 *
128 * @param <T> The object type.
129 * @param <R> The return type.
130 */
131@SuppressWarnings({
132   "java:S115", // Constants use UPPER_snakeCase convention
133   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
134   "unchecked" // The as*() narrowing methods re-type this assertion's value via cast(), which cannot be verified at compile time
135})
136public class FluentAnyAssertion<T,R> extends FluentObjectAssertion<T,R> {
137
138   // @formatter:off
139   private static final Messages MESSAGES = Messages.of(FluentAnyAssertion.class, "Messages");
140   private static final String
141      MSG_objectWasNotType = MESSAGES.getString("objectWasNotType");
142   // @formatter:on
143
144   /**
145    * Chained constructor.
146    *
147    * <p>
148    * Used when transforming one assertion into another so that the assertion config can be used by the new assertion.
149    *
150    * @param creator
151    *    The assertion that created this assertion.
152    *    <br>Should be <jk>null</jk> if this is the top-level assertion.
153    * @param value
154    *    The object being tested.
155    *    <br>Can be <jk>null</jk> (assertions such as {@code isNull()} then operate on the absence of a value).
156    * @param returns
157    *    The object to return after a test method is called.
158    *    <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be
159    * used on the same assertion.
160    */
161   public FluentAnyAssertion(Assertion creator, T value, R returns) {
162      super(creator, value, returns);
163   }
164
165   /**
166    * Constructor.
167    *
168    * @param value
169    *    The object being tested.
170    *    <br>Can be <jk>null</jk> (assertions such as {@code isNull()} then operate on the absence of a value).
171    * @param returns
172    *    The object to return after a test method is called.
173    *    <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be
174    * used on the same assertion.
175    */
176   public FluentAnyAssertion(T value, R returns) {
177      this(null, value, returns);
178   }
179
180   /**
181    * Converts this object assertion into an array assertion.
182    *
183    * @param <E> The element type of the array.
184    * @param elementType The element type of the array.
185    * @return A new assertion.
186    * @throws AssertionError If object is not an array.
187    */
188   public <E> FluentArrayAssertion<E,R> asArray(Class<E> elementType) throws AssertionError {
189      reqnn("elementType", elementType);
190      return new FluentArrayAssertion<>(this, cast(arrayClass(elementType)), returns());
191   }
192
193   /**
194    * Converts this object assertion into a bean assertion.
195    *
196    * @return A new assertion.
197    * @throws AssertionError If object is not a bean.
198    */
199   public FluentBeanAssertion<T,R> asBean() {
200      return new FluentBeanAssertion<>(this, orElse(null), returns());
201   }
202
203   /**
204    * Converts this object assertion into a bean assertion.
205    *
206    * @param <T2> The bean type.
207    * @param beanType The bean type.
208    * @return A new assertion.
209    * @throws AssertionError If object is not a bean.
210    */
211   public <T2> FluentBeanAssertion<T2,R> asBean(Class<T2> beanType) {
212      reqnn("beanType", beanType);
213      return new FluentBeanAssertion<>(this, cast(beanType), returns());
214   }
215
216   /**
217    * Converts this object assertion into a list-of-beans assertion.
218    *
219    * @param <T2> The bean type.
220    * @param beanType The bean type.
221    * @return A new assertion.
222    * @throws AssertionError If object is not a bean.
223    */
224   public <T2> FluentBeanListAssertion<T2,R> asBeanList(Class<T2> beanType) {
225      reqnn("beanType", beanType);
226      return new FluentBeanListAssertion<>(this, cast(List.class), returns());
227   }
228
229   /**
230    * Converts this object assertion into a boolean assertion.
231    *
232    * @return A new assertion.
233    * @throws AssertionError If object is not a boolean.
234    */
235   public FluentBooleanAssertion<R> asBoolean() {
236      return new FluentBooleanAssertion<>(this, cast(Boolean.class), returns());
237   }
238
239   /**
240    * Converts this object assertion into a primitive boolean array assertion.
241    *
242    * @return A new assertion.
243    * @throws AssertionError If object is not an boolean array.
244    */
245   public FluentPrimitiveArrayAssertion<Boolean,boolean[],R> asBooleanArray() throws AssertionError {
246      return new FluentPrimitiveArrayAssertion<>(this, cast(boolean[].class), returns());
247   }
248
249   /**
250    * Converts this object assertion into a primitive byte array assertion.
251    *
252    * @return A new assertion.
253    * @throws AssertionError If object is not an byte array.
254    */
255   public FluentPrimitiveArrayAssertion<Byte,byte[],R> asByteArray() throws AssertionError {
256      return new FluentPrimitiveArrayAssertion<>(this, cast(byte[].class), returns());
257   }
258
259   /**
260    * Converts this object assertion into a byte array assertion.
261    *
262    * @return A new assertion.
263    * @throws AssertionError If object is not a byte array.
264    */
265   public FluentByteArrayAssertion<R> asBytes() {
266      return new FluentByteArrayAssertion<>(this, cast(byte[].class), returns());
267   }
268
269   /**
270    * Converts this object assertion into a primitive char array assertion.
271    *
272    * @return A new assertion.
273    * @throws AssertionError If object is not an char array.
274    */
275   public FluentPrimitiveArrayAssertion<Character,char[],R> asCharArray() throws AssertionError {
276      return new FluentPrimitiveArrayAssertion<>(this, cast(char[].class), returns());
277   }
278
279   /**
280    * Converts this object assertion into a collection assertion.
281    *
282    * @return A new assertion.
283    * @throws AssertionError If object is not a collection.
284    */
285   public FluentCollectionAssertion<Object,R> asCollection() {
286      return asCollection(Object.class);
287   }
288
289   /**
290    * Converts this object assertion into a collection assertion.
291    *
292    * @param <E> The element type of the collection.
293    * @param elementType The element type of the collection.
294    * @return A new assertion.
295    * @throws AssertionError If object is not a collection.
296    */
297   public <E> FluentCollectionAssertion<E,R> asCollection(Class<E> elementType) {
298      reqnn("elementType", elementType);
299      return new FluentCollectionAssertion<>(this, cast(Collection.class), returns());
300   }
301
302   /**
303    * Converts this object assertion into a comparable object assertion.
304    *
305    * @param <T2> The comparable type.
306    * @return A new assertion.
307    * @throws AssertionError If object is not an instance of {@link Comparable}.
308    */
309   public <T2 extends Comparable<T2>> FluentComparableAssertion<T2,R> asComparable() {
310      return new FluentComparableAssertion<>(this, (T2)cast(Comparable.class), returns());
311   }
312
313   /**
314    * Converts this object assertion into a date assertion.
315    *
316    * @return A new assertion.
317    * @throws AssertionError If object is not a date.
318    */
319   public FluentDateAssertion<R> asDate() {
320      return new FluentDateAssertion<>(this, cast(Date.class), returns());
321   }
322
323   /**
324    * Converts this object assertion into a primitive double array assertion.
325    *
326    * @return A new assertion.
327    * @throws AssertionError If object is not an double array.
328    */
329   public FluentPrimitiveArrayAssertion<Double,double[],R> asDoubleArray() throws AssertionError {
330      return new FluentPrimitiveArrayAssertion<>(this, cast(double[].class), returns());
331   }
332
333   /**
334    * Converts this object assertion into a primitive float array assertion.
335    *
336    * @return A new assertion.
337    * @throws AssertionError If object is not an float array.
338    */
339   public FluentPrimitiveArrayAssertion<Float,float[],R> asFloatArray() throws AssertionError {
340      return new FluentPrimitiveArrayAssertion<>(this, cast(float[].class), returns());
341   }
342
343   /**
344    * Converts this object assertion into a primitive int array assertion.
345    *
346    * @return A new assertion.
347    * @throws AssertionError If object is not an int array.
348    */
349   public FluentPrimitiveArrayAssertion<Integer,int[],R> asIntArray() throws AssertionError {
350      return new FluentPrimitiveArrayAssertion<>(this, cast(int[].class), returns());
351   }
352
353   /**
354    * Converts this object assertion into an integer assertion.
355    *
356    * @return A new assertion.
357    * @throws AssertionError If object is not an integer.
358    */
359   public FluentIntegerAssertion<R> asInteger() {
360      return new FluentIntegerAssertion<>(this, cast(Integer.class), returns());
361   }
362
363   /**
364    * Converts this object assertion into a list assertion.
365    *
366    * @return A new assertion.
367    * @throws AssertionError If object is not a list.
368    */
369   public FluentListAssertion<Object,R> asList() {
370      return asList(Object.class);
371   }
372
373   /**
374    * Converts this object assertion into a list assertion.
375    *
376    * @param <E> The element type.
377    * @param elementType The element type.
378    * @return A new assertion.
379    * @throws AssertionError If object is not a list.
380    */
381   public <E> FluentListAssertion<E,R> asList(Class<E> elementType) {
382      reqnn("elementType", elementType);
383      return new FluentListAssertion<>(this, cast(List.class), returns());
384   }
385
386   /**
387    * Converts this object assertion into a long assertion.
388    *
389    * @return A new assertion.
390    * @throws AssertionError If object is not a long.
391    */
392   public FluentLongAssertion<R> asLong() {
393      return new FluentLongAssertion<>(this, cast(Long.class), returns());
394   }
395
396   /**
397    * Converts this object assertion into a primitive long array assertion.
398    *
399    * @return A new assertion.
400    * @throws AssertionError If object is not an long array.
401    */
402   public FluentPrimitiveArrayAssertion<Long,long[],R> asLongArray() throws AssertionError {
403      return new FluentPrimitiveArrayAssertion<>(this, cast(long[].class), returns());
404   }
405
406   /**
407    * Converts this object assertion into a map assertion.
408    *
409    * @return A new assertion.
410    * @throws AssertionError If object is not a map.
411    */
412   public FluentMapAssertion<String,Object,R> asMap() {
413      return asMap(String.class, Object.class);
414   }
415
416   /**
417    * Converts this object assertion into a map assertion with the specified key and value types.
418    *
419    * @param <K> The key type.
420    * @param <V> The value type.
421    * @param keyType The key type.
422    * @param valueType The value type.
423    * @return A new assertion.
424    * @throws AssertionError If object is not a map.
425    */
426   public <K,V> FluentMapAssertion<K,V,R> asMap(Class<K> keyType, Class<V> valueType) {
427      reqnn("keyType", keyType);
428      reqnn("valueType", valueType);
429      return new FluentMapAssertion<>(this, cast(Map.class), returns());
430   }
431
432   /**
433    * Converts this object assertion into a primitive short array assertion.
434    *
435    * @return A new assertion.
436    * @throws AssertionError If object is not an short array.
437    */
438   public FluentPrimitiveArrayAssertion<Short,short[],R> asShortArray() throws AssertionError {
439      return new FluentPrimitiveArrayAssertion<>(this, cast(short[].class), returns());
440   }
441
442   /**
443    * Converts this object assertion into a collection assertion.
444    *
445    * @return A new assertion.
446    * @throws AssertionError If object is not a collection.
447    */
448   public FluentStringListAssertion<R> asStringList() {
449      return new FluentStringListAssertion<>(this, cast(List.class), returns());
450   }
451
452   /**
453    * Converts this object assertion into a zoned-datetime assertion.
454    *
455    * @return A new assertion.
456    * @throws AssertionError If object is not a zoned-datetime.
457    */
458   public FluentZonedDateTimeAssertion<R> asZonedDateTime() {
459      return new FluentZonedDateTimeAssertion<>(this, cast(ZonedDateTime.class), returns());
460   }
461
462   @Override /* Overridden from Assertion */
463   public FluentAnyAssertion<T,R> setMsg(String msg, Object...args) {
464      super.setMsg(msg, args);
465      return this;
466   }
467
468   @Override /* Overridden from Assertion */
469   public FluentAnyAssertion<T,R> setOut(PrintStream value) {
470      super.setOut(value);
471      return this;
472   }
473
474   @Override /* Overridden from Assertion */
475   public FluentAnyAssertion<T,R> setSilent() {
476      super.setSilent();
477      return this;
478   }
479
480   @Override /* Overridden from Assertion */
481   public FluentAnyAssertion<T,R> setStdOut() {
482      super.setStdOut();
483      return this;
484   }
485
486   @Override /* Overridden from Assertion */
487   public FluentAnyAssertion<T,R> setThrowable(Class<? extends RuntimeException> value) {
488      super.setThrowable(value);
489      return this;
490   }
491
492   private <T2> T2 cast(Class<T2> c) throws AssertionError {
493      Object o = orElse(null);
494      if (o == null || c.isInstance(o))
495         return c.cast(o);
496      throw new BasicAssertionError(MSG_objectWasNotType, info(c).getNameFull(), cn(o.getClass()));
497   }
498}