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 java.util.Arrays.*; 020 021import java.io.*; 022import java.lang.reflect.*; 023import java.util.*; 024import java.util.function.*; 025 026import org.apache.juneau.*; 027import org.apache.juneau.common.utils.*; 028import org.apache.juneau.cp.*; 029import org.apache.juneau.internal.*; 030import org.apache.juneau.serializer.*; 031 032/** 033 * Used for fluent assertion calls against primitive array objects (e.g. <c><jk>int</jk>[]</c>). 034 * 035 * <h5 class='section'>Test Methods:</h5> 036 * <p> 037 * <ul class='javatree'> 038 * <li class='jc'>{@link FluentPrimitiveArrayAssertion} 039 * <ul class='javatreec'> 040 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isHas(Object...) isHas(Object...)} 041 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#is(Predicate) is(Predicate)} 042 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isAny(Predicate) isAny(Predicate)} 043 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isAll(Predicate) isAll(Predicate)} 044 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isEmpty() isEmpty()} 045 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isNotEmpty() isNotEmpty()} 046 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isSize(int) isSize(int)} 047 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isContains(Object) isContains(Object)} 048 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isNotContains(Object) isNotContains(Object)} 049 * </ul> 050 * <li class='jc'>{@link FluentObjectAssertion} 051 * <ul class='javatreec'> 052 * <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()} 053 * <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)} 054 * <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)} 055 * <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)} 056 * <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)} 057 * <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)} 058 * <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()} 059 * <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()} 060 * <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)} 061 * <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)} 062 * <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)} 063 * <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)} 064 * <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)} 065 * <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)} 066 * <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)} 067 * <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)} 068 * </ul> 069 * </ul> 070 * 071 * <h5 class='section'>Transform Methods:</h5> 072 * <p> 073 * <ul class='javatree'> 074 * <li class='jc'>{@link FluentPrimitiveArrayAssertion} 075 * <ul class='javatreec'> 076 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#asItem(int) asItem(int)} 077 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#asLength() asLength()} 078 * </ul> 079 * <li class='jc'>{@link FluentObjectAssertion} 080 * <ul class='javatreec'> 081 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 082 * <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)} 083 * <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)} 084 * <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()} 085 * <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()} 086 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)} 087 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 088 * </ul> 089 * </ul> 090 * 091 * <h5 class='section'>Configuration Methods:</h5> 092 * <p> 093 * <ul class='javatree'> 094 * <li class='jc'>{@link Assertion} 095 * <ul class='javatreec'> 096 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 097 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 098 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 099 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 100 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 101 * </ul> 102 * </ul> 103 * 104 * <h5 class='section'>See Also:</h5><ul> 105 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem Overview</a> 106 * </ul> 107 * 108 * @param <E> The array element type. 109 * @param <T> The array type. 110 * @param <R> The return type. 111 */ 112public class FluentPrimitiveArrayAssertion<E,T,R> extends FluentObjectAssertion<T,R> { 113 114 //----------------------------------------------------------------------------------------------------------------- 115 // Static 116 //----------------------------------------------------------------------------------------------------------------- 117 118 private static final Map<Class<?>,Function<Object,String>> STRINGIFIERS = new HashMap<>(); 119 static { 120 STRINGIFIERS.put(boolean.class, x -> Arrays.toString((boolean[])x)); 121 STRINGIFIERS.put(byte.class, x -> Arrays.toString((byte[])x)); 122 STRINGIFIERS.put(char.class, x -> Arrays.toString((char[])x)); 123 STRINGIFIERS.put(double.class, x -> Arrays.toString((double[])x)); 124 STRINGIFIERS.put(float.class, x -> Arrays.toString((float[])x)); 125 STRINGIFIERS.put(int.class, x -> Arrays.toString((int[])x)); 126 STRINGIFIERS.put(long.class, x -> Arrays.toString((long[])x)); 127 STRINGIFIERS.put(short.class, x -> Arrays.toString((short[])x)); 128 } 129 130 private static final Messages MESSAGES = Messages.of(FluentPrimitiveArrayAssertion.class, "Messages"); 131 static final String 132 MSG_objectWasNotAnArray = MESSAGES.getString("objectWasNotAnArray"), 133 MSG_arrayWasNotEmpty = MESSAGES.getString("arrayWasNotEmpty"), 134 MSG_arrayWasEmpty = MESSAGES.getString("arrayWasEmpty"), 135 MSG_arrayDidNotHaveExpectedSize = MESSAGES.getString("arrayDidNotHaveExpectedSize"), 136 MSG_arrayDidNotContainExpectedValue = MESSAGES.getString("arrayDidNotContainExpectedValue"), 137 MSG_arrayDidNotContainExpectedValueAt = MESSAGES.getString("arrayDidNotContainExpectedValueAt"), 138 MSG_arrayContainedUnexpectedValue = MESSAGES.getString("arrayContainedUnexpectedValue"), 139 MSG_arrayDidntContainAnyMatchingValue = MESSAGES.getString("arrayDidntContainAnyMatchingValue"), 140 MSG_arrayContainedNonMatchingValueAt = MESSAGES.getString("arrayContainedNonMatchingValueAt"); 141 142 //----------------------------------------------------------------------------------------------------------------- 143 // Instance 144 //----------------------------------------------------------------------------------------------------------------- 145 146 /** 147 * Constructor. 148 * 149 * @param value 150 * The object being tested. 151 * <br>Can be <jk>null</jk>. 152 * @param returns 153 * The object to return after a test method is called. 154 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 155 * used on the same assertion. 156 */ 157 public FluentPrimitiveArrayAssertion(T value, R returns) { 158 this(null, value, returns); 159 } 160 161 /** 162 * Chained constructor. 163 * 164 * <p> 165 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 166 * 167 * @param creator 168 * The assertion that created this assertion. 169 * <br>Should be <jk>null</jk> if this is the top-level assertion. 170 * @param value 171 * The object being tested. 172 * <br>Can be <jk>null</jk>. 173 * @param returns 174 * The object to return after a test method is called. 175 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 176 * used on the same assertion. 177 */ 178 public FluentPrimitiveArrayAssertion(Assertion creator, T value, R returns) { 179 super(creator, value, returns); 180 if (value != null) { 181 var c = value.getClass(); 182 if (! (c.isArray() && c.getComponentType().isPrimitive())) 183 throw new BasicAssertionError(MSG_objectWasNotAnArray, value.getClass()); 184 } 185 } 186 187 //----------------------------------------------------------------------------------------------------------------- 188 // Transform methods 189 //----------------------------------------------------------------------------------------------------------------- 190 191 @Override /* FluentObjectAssertion */ 192 public FluentPrimitiveArrayAssertion<E,T,R> asTransformed(Function<T,T> function) { // NOSONAR - Intentional. 193 return new FluentPrimitiveArrayAssertion<>(this, function.apply(orElse(null)), returns()); 194 } 195 196 @Override /* FluentBaseAssertion */ 197 public FluentStringAssertion<R> asString() { 198 return new FluentStringAssertion<>(this, toString(), returns()); 199 } 200 201 /** 202 * Returns an object assertion on the item specified at the specified index. 203 * 204 * <p> 205 * If the array is <jk>null</jk> or the index is out-of-bounds, the returned assertion is a null assertion 206 * (meaning {@link FluentAnyAssertion#isExists()} returns <jk>false</jk>). 207 * 208 * @param index The index of the item to retrieve from the array. 209 * @return A new assertion. 210 */ 211 public FluentAnyAssertion<E,R> asItem(int index) { 212 return new FluentAnyAssertion<>(this, at(index), returns()); 213 } 214 215 /** 216 * Returns an integer assertion on the length of this array. 217 * 218 * <p> 219 * If the array is <jk>null</jk> or the index is out-of-bounds, the returned assertion is a null assertion 220 * (meaning {@link FluentIntegerAssertion#isExists()} returns <jk>false</jk>). 221 * 222 * @return A new assertion. 223 */ 224 public FluentIntegerAssertion<R> asLength() { 225 return new FluentIntegerAssertion<>(this, valueIsNull() ? null : Array.getLength(value()), returns()); 226 } 227 228 //----------------------------------------------------------------------------------------------------------------- 229 // Test methods 230 //----------------------------------------------------------------------------------------------------------------- 231 232 /** 233 * Asserts that the contents of this list contain the specified values. 234 * 235 * @param entries The expected entries in this list. 236 * @return This object. 237 * @throws AssertionError If assertion failed. 238 */ 239 public R isHas(E...entries) throws AssertionError { 240 Utils.assertArgNotNull("entries", entries); 241 Predicate<E>[] p = stream(entries).map(AssertionPredicates::eq).toArray(Predicate[]::new); 242 return is(p); 243 } 244 245 /** 246 * Asserts that the contents of this list pass the specified tests. 247 * 248 * @param tests The tests to run. <jk>null</jk> entries are ignored. 249 * @return This object. 250 * @throws AssertionError If assertion failed. 251 */ 252 @SafeVarargs 253 public final R is(Predicate<E>...tests) throws AssertionError { 254 isSize(tests.length); 255 for (int i = 0, j = length2(); i < j; i++) { 256 var t = tests[i]; 257 if (t != null && ! t.test(at(i))) 258 throw error(MSG_arrayDidNotContainExpectedValueAt, i, getFailureMessage(t, at(i))); 259 } 260 return returns(); 261 } 262 263 /** 264 * Asserts that at least one value in the array passes the specified test. 265 * 266 * @param test The predicate test. 267 * @return The fluent return object. 268 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 269 */ 270 public R isAny(Predicate<E> test) throws AssertionError { 271 Utils.assertArgNotNull("test", test); 272 for (int i = 0, j = length2(); i < j; i++) 273 if (test.test(at(i))) 274 return returns(); 275 throw error(MSG_arrayDidntContainAnyMatchingValue, value()); 276 } 277 278 /** 279 * Asserts that all values in the array pass the specified test. 280 * 281 * @param test The predicate test. 282 * @return The fluent return object. 283 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 284 */ 285 public R isAll(Predicate<E> test) throws AssertionError { 286 Utils.assertArgNotNull("test", test); 287 for (int i = 0, j = length2(); i < j; i++) 288 if (! test.test(at(i))) 289 throw error(MSG_arrayContainedNonMatchingValueAt, i, getFailureMessage(test, at(i))); 290 return returns(); 291 } 292 293 /** 294 * Asserts that the collection exists and is empty. 295 * 296 * @return The fluent return object. 297 * @throws AssertionError If assertion failed. 298 */ 299 public R isEmpty() throws AssertionError { 300 if (length2() != 0) 301 throw error(MSG_arrayWasNotEmpty); 302 return returns(); 303 } 304 305 /** 306 * Asserts that the collection exists and is not empty. 307 * 308 * @return The fluent return object. 309 * @throws AssertionError If assertion failed. 310 */ 311 public R isNotEmpty() throws AssertionError { 312 if (length2() == 0) 313 throw error(MSG_arrayWasEmpty); 314 return returns(); 315 } 316 317 /** 318 * Asserts that the collection exists and is the specified size. 319 * 320 * @param size The expected size. 321 * @return The fluent return object. 322 * @throws AssertionError If assertion failed. 323 */ 324 public R isSize(int size) throws AssertionError { 325 if (length2() != size) 326 throw error(MSG_arrayDidNotHaveExpectedSize, size, asLength()); 327 return returns(); 328 } 329 330 /** 331 * Asserts that the array contains the expected entry. 332 * 333 * @param entry The value to check for. 334 * @return The fluent return object. 335 * @throws AssertionError If assertion failed. 336 */ 337 public R isContains(E entry) throws AssertionError { 338 for (int i = 0, j = length2(); i < j; i++) 339 if (Utils.eq(at(i), entry)) 340 return returns(); 341 throw error(MSG_arrayDidNotContainExpectedValue, entry, value()); 342 } 343 344 /** 345 * Asserts that the array does not contain the expected value. 346 * 347 * @param entry The value to check for. 348 * @return The fluent return object. 349 * @throws AssertionError If assertion failed. 350 */ 351 public R isNotContains(E entry) throws AssertionError { 352 for (var i = 0; i < length2(); i++) 353 if (Utils.eq(at(i), entry)) 354 throw error(MSG_arrayContainedUnexpectedValue, entry, value()); 355 return returns(); 356 } 357 358 //----------------------------------------------------------------------------------------------------------------- 359 // Fluent setters 360 //----------------------------------------------------------------------------------------------------------------- 361 @Override /* Overridden from Assertion */ 362 public FluentPrimitiveArrayAssertion<E,T,R> setMsg(String msg, Object...args) { 363 super.setMsg(msg, args); 364 return this; 365 } 366 367 @Override /* Overridden from Assertion */ 368 public FluentPrimitiveArrayAssertion<E,T,R> setOut(PrintStream value) { 369 super.setOut(value); 370 return this; 371 } 372 373 @Override /* Overridden from Assertion */ 374 public FluentPrimitiveArrayAssertion<E,T,R> setSilent() { 375 super.setSilent(); 376 return this; 377 } 378 379 @Override /* Overridden from Assertion */ 380 public FluentPrimitiveArrayAssertion<E,T,R> setStdOut() { 381 super.setStdOut(); 382 return this; 383 } 384 385 @Override /* Overridden from Assertion */ 386 public FluentPrimitiveArrayAssertion<E,T,R> setThrowable(Class<? extends java.lang.RuntimeException> value) { 387 super.setThrowable(value); 388 return this; 389 } 390 //----------------------------------------------------------------------------------------------------------------- 391 // Utility methods 392 //----------------------------------------------------------------------------------------------------------------- 393 394 private E at(int index) { 395 return valueIsNull() || index < 0 || index >= length2() ? null : (E)Array.get(value(), index); 396 } 397 398 private int length2() { 399 return Array.getLength(value()); 400 } 401 402 @Override 403 public String toString() { 404 if (valueIsNull()) 405 return null; // NOSONAR - Intentional. 406 return STRINGIFIERS.get(value().getClass().getComponentType()).apply(value()); 407 } 408}