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