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.*; 016import static org.apache.juneau.common.internal.StringUtils.*; 017import static org.apache.juneau.internal.CollectionUtils.*; 018import static org.apache.juneau.internal.ObjectUtils.*; 019import static java.util.Arrays.*; 020 021import java.io.*; 022import java.util.*; 023import java.util.function.*; 024import java.util.stream.*; 025 026import org.apache.juneau.common.internal.*; 027import org.apache.juneau.cp.*; 028import org.apache.juneau.internal.*; 029import org.apache.juneau.serializer.*; 030 031/** 032 * Used for fluent assertion calls against array objects. 033 * 034 * <h5 class='section'>Test Methods:</h5> 035 * <p> 036 * <ul class='javatree'> 037 * <li class='jc'>{@link FluentArrayAssertion} 038 * <ul class='javatreec'> 039 * <li class='jm'>{@link FluentArrayAssertion#isHas(Object[]) isHas(Object[])} 040 * <li class='jm'>{@link FluentArrayAssertion#is(Predicate) is(Predicate)} 041 * <li class='jm'>{@link FluentArrayAssertion#isAny(Predicate) isAny(Predicate)} 042 * <li class='jm'>{@link FluentArrayAssertion#isAll(Predicate) isAll(Predicate)} 043 * <li class='jm'>{@link FluentArrayAssertion#isEmpty() isEmpty()} 044 * <li class='jm'>{@link FluentArrayAssertion#isNotEmpty() isNotEmpty()} 045 * <li class='jm'>{@link FluentArrayAssertion#isSize(int size) isSize(int size)} 046 * <li class='jm'>{@link FluentArrayAssertion#isContains(Object) isContains(Object)} 047 * <li class='jm'>{@link FluentArrayAssertion#isNotContains(Object) isNotContains(Object)} 048 * </ul> 049 * <li class='jc'>{@link FluentObjectAssertion} 050 * <ul class='javatreec'> 051 * <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()} 052 * <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)} 053 * <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)} 054 * <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)} 055 * <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)} 056 * <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)} 057 * <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()} 058 * <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()} 059 * <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)} 060 * <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)} 061 * <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)} 062 * <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)} 063 * <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)} 064 * <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)} 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 FluentArrayAssertion} 074 * <ul class='javatreec'> 075 * <li class='jm'>{@link FluentArrayAssertion#asStrings() asStrings()} 076 * <li class='jm'>{@link FluentArrayAssertion#asStrings(Function) asStrings(Function)} 077 * <li class='jm'>{@link FluentArrayAssertion#asCdl() asCdl()} 078 * <li class='jm'>{@link FluentArrayAssertion#asCdl(Function) asCdl(Function)} 079 * <li class='jm'>{@link FluentArrayAssertion#asBeanList() asBeanList()} 080 * <li class='jm'>{@link FluentArrayAssertion#asItem(int) asItem(int)} 081 * <li class='jm'>{@link FluentArrayAssertion#asSorted() asSorted()} 082 * <li class='jm'>{@link FluentArrayAssertion#asSorted(Comparator) asSorted(Comparator)} 083 * </ul> 084 * <li class='jc'>{@link FluentObjectAssertion} 085 * <ul class='javatreec'> 086 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 087 * <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)} 088 * <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)} 089 * <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()} 090 * <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()} 091 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)} 092 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 093 * </ul> 094 * </ul> 095 * 096 * <h5 class='section'>Configuration Methods:</h5> 097 * <p> 098 * <ul class='javatree'> 099 * <li class='jc'>{@link Assertion} 100 * <ul class='javatreec'> 101 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 102 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 103 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 104 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 105 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 106 * </ul> 107 * </ul> 108 * 109 * <h5 class='section'>See Also:</h5><ul> 110 * <li class='link'><a class="doclink" href="../../../../index.html#ja.Overview">Overview > juneau-assertions > Overview</a> 111 * </ul> 112 * 113 * @param <E> The entry type. 114 * @param <R> The return type. 115 */ 116@FluentSetters(returns="FluentArrayAssertion<E,R>") 117public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> { 118 119 //----------------------------------------------------------------------------------------------------------------- 120 // Static 121 //----------------------------------------------------------------------------------------------------------------- 122 123 private static final Messages MESSAGES = Messages.of(FluentArrayAssertion.class, "Messages"); 124 private static final String 125 MSG_arrayWasNotEmpty = MESSAGES.getString("arrayWasNotEmpty"), 126 MSG_arrayWasEmpty = MESSAGES.getString("arrayWasEmpty"), 127 MSG_arrayUnexpectedSize = MESSAGES.getString("arrayUnexpectedSize"), 128 MSG_arrayDidNotContainExpectedValue = MESSAGES.getString("arrayDidNotContainExpectedValue"), 129 MSG_arrayContainedUnexpectedValue = MESSAGES.getString("arrayContainedUnexpectedValue"), 130 MSG_arrayDidNotContainExpectedValueAt = MESSAGES.getString("arrayDidNotContainExpectedValueAt"), 131 MSG_arrayDidntContainAnyMatchingValue = MESSAGES.getString("arrayDidntContainAnyMatchingValue"), 132 MSG_arrayContainedNonMatchingValueAt = MESSAGES.getString("arrayContainedNonMatchingValueAt"); 133 134 //----------------------------------------------------------------------------------------------------------------- 135 // Instance 136 //----------------------------------------------------------------------------------------------------------------- 137 138 /** 139 * Constructor. 140 * 141 * @param value 142 * The object being tested. 143 * <br>Can be <jk>null</jk>. 144 * @param returns 145 * The object to return after a test method is called. 146 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 147 * used on the same assertion. 148 */ 149 public FluentArrayAssertion(E[] value, R returns) { 150 this(null, value, returns); 151 } 152 153 /** 154 * Chained constructor. 155 * 156 * <p> 157 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 158 * 159 * @param creator 160 * The assertion that created this assertion. 161 * <br>Should be <jk>null</jk> if this is the top-level assertion. 162 * @param value 163 * The object being tested. 164 * <br>Can be <jk>null</jk>. 165 * @param returns 166 * The object to return after a test method is called. 167 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 168 * used on the same assertion. 169 */ 170 public FluentArrayAssertion(Assertion creator, E[] value, R returns) { 171 super(creator, value, returns); 172 } 173 174 //----------------------------------------------------------------------------------------------------------------- 175 // Transform methods 176 //----------------------------------------------------------------------------------------------------------------- 177 178 @Override /* FluentObjectAssertion */ 179 public FluentArrayAssertion<E,R> asTransformed(Function<E[],E[]> function) { 180 return new FluentArrayAssertion<>(this, function.apply(orElse(null)), returns()); 181 } 182 183 @Override /* FluentBaseAssertion */ 184 public FluentStringAssertion<R> asString() { 185 return new FluentStringAssertion<>(this, toString(), returns()); 186 } 187 188 /** 189 * Converts this assertion into a {@link FluentListAssertion} of strings. 190 * 191 * @return A new fluent string assertion. 192 */ 193 public FluentStringListAssertion<R> asStrings() { 194 return new FluentStringListAssertion<>(this, valueIsNull() ? null : stream(value()).map(StringUtils::stringify).collect(Collectors.toList()), returns()); 195 } 196 197 /** 198 * Runs the stringify function against all values in this list and returns it as a fluent string list assertion. 199 * 200 * @param function The function to apply to all values in this list. 201 * @return A new fluent string list assertion. Never <jk>null</jk>. 202 */ 203 public FluentStringListAssertion<R> asStrings(Function<E,String> function) { 204 List<String> l = valueIsNull() ? null : stream(value()).map(x -> function.apply(x)).collect(Collectors.toList()); 205 return new FluentStringListAssertion<>(this, l, returns()); 206 } 207 208 /** 209 * Converts the entries in this list to a simple comma-delimited list and returns the value as a fluent string assertion. 210 * 211 * @return A fluent string assertion. Never <jk>null</jk>. 212 */ 213 public FluentStringAssertion<R> asCdl() { 214 return new FluentStringAssertion<>(this, valueIsNull() ? null : StringUtils.join(value(), ','), returns()); 215 } 216 217 /** 218 * Converts the entries to strings using the specified stringify function, combines them into a simple comma-delimited list, and returns the value as a fluent string assertion. 219 * 220 * @param function The function to apply to all values in this list. 221 * @return A fluent string assertion. Never <jk>null</jk>. 222 */ 223 public FluentStringAssertion<R> asCdl(Function<E,String> function) { 224 List<String> l = valueIsNull() ? null : stream(value()).map(x -> function.apply(x)).collect(Collectors.toList()); 225 return new FluentStringAssertion<>(this, join(l, ','), returns()); 226 } 227 228 /** 229 * Converts this assertion into a {@link FluentBeanListAssertion}. 230 * 231 * <h5 class='section'>Example:</h5> 232 * <p class='bjava'> 233 * <jc>// Extracts the 'foo' property from an array of beans and validates their values.</jc>. 234 * <jsm>assertObject</jsm>(<jv>myArrayOfBeans</jv>) 235 * .asBeanList() 236 * .asProperty(<js>"foo"</js>) 237 * .asSorted() 238 * .equals(<js>"value1"</js>,<js>"value2"</js>,<js>"value3"</js>); 239 * </p> 240 * 241 * @return A new fluent string assertion. 242 */ 243 public FluentBeanListAssertion<E,R> asBeanList() { 244 return new FluentBeanListAssertion<>(this, toList(), returns()); 245 } 246 247 /** 248 * Returns an object assertion on the item specified at the specified index. 249 * 250 * <p> 251 * If the array is <jk>null</jk> or the index is out-of-bounds, the returned assertion is a null assertion 252 * (meaning {@link FluentAnyAssertion#isExists()} returns <jk>false</jk>). 253 * 254 * @param index The index of the item to retrieve from the array. 255 * @return A new assertion. 256 */ 257 public FluentAnyAssertion<E,R> asItem(int index) { 258 return new FluentAnyAssertion<>(this, at(index), returns()); 259 } 260 261 /** 262 * Sorts the entries in this list. 263 * 264 * @return A new list assertion. The contents of the original list remain unchanged. 265 */ 266 public FluentListAssertion<E,R> asSorted() { 267 return new FluentListAssertion<>(this, toSortedList(null), returns()); 268 } 269 270 /** 271 * Sorts the entries in this list using the specified comparator. 272 * 273 * @param comparator The comparator to use to sort the list. 274 * @return A new list assertion. The contents of the original list remain unchanged. 275 */ 276 public FluentListAssertion<E,R> asSorted(Comparator<E> comparator) { 277 return new FluentListAssertion<>(this, toSortedList(comparator), returns()); 278 } 279 280 //----------------------------------------------------------------------------------------------------------------- 281 // Test methods 282 //----------------------------------------------------------------------------------------------------------------- 283 284 /** 285 * Asserts that the contents of this list contain the specified values. 286 * 287 * @param entries The expected entries in this list. 288 * @return This object. 289 * @throws AssertionError If assertion failed. 290 */ 291 @SuppressWarnings("unchecked") 292 public R isHas(E...entries) throws AssertionError { 293 assertArgNotNull("entries", entries); 294 Predicate<E>[] p = stream(entries).map(AssertionPredicates::eq).toArray(Predicate[]::new); 295 return is(p); 296 } 297 298 /** 299 * Asserts that the contents of this list pass the specified tests. 300 * 301 * @param tests The tests to run. <jk>null</jk> entries are ignored. 302 * @return This object. 303 * @throws AssertionError If assertion failed. 304 */ 305 @SafeVarargs 306 public final R is(Predicate<E>...tests) throws AssertionError { 307 isSize(tests.length); 308 for (int i = 0, j = length(); i < j; i++) { 309 Predicate<E> t = tests[i]; 310 if (t != null) 311 if (! t.test(at(i))) 312 throw error(MSG_arrayDidNotContainExpectedValueAt, i, getFailureMessage(t, at(i))); 313 } 314 return returns(); 315 } 316 317 /** 318 * Asserts that at least one value in the array passes the specified test. 319 * 320 * @param test The predicate test. 321 * @return The fluent return object. 322 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 323 */ 324 public R isAny(Predicate<E> test) throws AssertionError { 325 assertArgNotNull("test", test); 326 for (E v : value()) 327 if (test.test(v)) 328 return returns(); 329 throw error(MSG_arrayDidntContainAnyMatchingValue, (Object)value()); 330 } 331 332 /** 333 * Asserts that all values in the array passes the specified test. 334 * 335 * @param test The predicate test. 336 * @return The fluent return object. 337 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 338 */ 339 public R isAll(Predicate<E> test) throws AssertionError { 340 assertArgNotNull("test", test); 341 for (int i = 0, j = length(); i < j; i++) 342 if (! test.test(at(i))) 343 throw error(MSG_arrayContainedNonMatchingValueAt, i, getFailureMessage(test, at(i))); 344 return returns(); 345 } 346 347 /** 348 * Asserts that the collection exists and is empty. 349 * 350 * @return The fluent return object. 351 * @throws AssertionError If assertion failed. 352 */ 353 public R isEmpty() throws AssertionError { 354 if (length() != 0) 355 throw error(MSG_arrayWasNotEmpty); 356 return returns(); 357 } 358 359 /** 360 * Asserts that the collection exists and is not empty. 361 * 362 * @return The fluent return object. 363 * @throws AssertionError If assertion failed. 364 */ 365 public R isNotEmpty() throws AssertionError { 366 if (length() == 0) 367 throw error(MSG_arrayWasEmpty); 368 return returns(); 369 } 370 371 /** 372 * Asserts that the collection exists and is the specified size. 373 * 374 * @param size The expected size. 375 * @return The fluent return object. 376 * @throws AssertionError If assertion failed. 377 */ 378 public R isSize(int size) throws AssertionError { 379 if (length() != size) 380 throw error(MSG_arrayUnexpectedSize, size, length()); 381 return returns(); 382 } 383 384 /** 385 * Asserts that the array contains the expected value. 386 * 387 * @param entry The value to check for. 388 * @return The fluent return object. 389 * @throws AssertionError If assertion failed. 390 */ 391 public R isContains(E entry) throws AssertionError { 392 for (int i = 0, j = length(); i < j; i++) 393 if (eq(at(i), entry)) 394 return returns(); 395 throw error(MSG_arrayDidNotContainExpectedValue, entry, toString()); 396 } 397 398 /** 399 * Asserts that the array does not contain the expected value. 400 * 401 * @param entry The value to check for. 402 * @return The fluent return object. 403 * @throws AssertionError If assertion failed. 404 */ 405 public R isNotContains(E entry) throws AssertionError { 406 for (int i = 0, j = length(); i < j; i++) 407 if (eq(at(i), entry)) 408 throw error(MSG_arrayContainedUnexpectedValue, entry, toString()); 409 return returns(); 410 } 411 412 //----------------------------------------------------------------------------------------------------------------- 413 // Fluent setters 414 //----------------------------------------------------------------------------------------------------------------- 415 416 // <FluentSetters> 417 418 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 419 public FluentArrayAssertion<E,R> setMsg(String msg, Object...args) { 420 super.setMsg(msg, args); 421 return this; 422 } 423 424 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 425 public FluentArrayAssertion<E,R> setOut(PrintStream value) { 426 super.setOut(value); 427 return this; 428 } 429 430 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 431 public FluentArrayAssertion<E,R> setSilent() { 432 super.setSilent(); 433 return this; 434 } 435 436 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 437 public FluentArrayAssertion<E,R> setStdOut() { 438 super.setStdOut(); 439 return this; 440 } 441 442 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 443 public FluentArrayAssertion<E,R> setThrowable(Class<? extends java.lang.RuntimeException> value) { 444 super.setThrowable(value); 445 return this; 446 } 447 448 // </FluentSetters> 449 450 //----------------------------------------------------------------------------------------------------------------- 451 // Utility methods 452 //----------------------------------------------------------------------------------------------------------------- 453 454 private int length() { 455 return value().length; 456 } 457 458 private List<E> toList() { 459 return valueIsNull() ? null : list(value()); 460 } 461 462 private List<E> toSortedList(Comparator<E> comparator) { 463 return valueIsNull() ? null : sortedList(comparator, value()); 464 } 465 466 private E at(int index) { 467 return valueIsNull() || index >= length() || index < 0 ? null : value()[index]; 468 } 469 470 @Override 471 public String toString() { 472 return valueIsNull() ? null : Arrays.toString(value()); 473 } 474}