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