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