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 java.util.Collections.*; 016import static java.util.stream.Collectors.*; 017import static java.util.Arrays.*; 018 019import java.io.*; 020import java.util.*; 021import java.util.function.*; 022 023import org.apache.juneau.cp.*; 024import org.apache.juneau.internal.*; 025import org.apache.juneau.serializer.*; 026 027/** 028 * Used for fluent assertion calls against maps. 029 * 030 * <h5 class='section'>Test Methods:</h5> 031 * <p> 032 * <ul class='javatree'> 033 * <li class='jc'>{@link FluentMapAssertion} 034 * <ul class='javatreec'> 035 * <li class='jm'>{@link FluentMapAssertion#isEmpty() isEmpty()} 036 * <li class='jm'>{@link FluentMapAssertion#isNotEmpty() isNotEmpty()} 037 * <li class='jm'>{@link FluentMapAssertion#isContainsKey(String) isContainsKey(String)} 038 * <li class='jm'>{@link FluentMapAssertion#isNotContainsKey(String) isNotContainsKey(String)} 039 * <li class='jm'>{@link FluentMapAssertion#isSize(int) isSize(int)} 040 * </ul> 041 * <li class='jc'>{@link FluentObjectAssertion} 042 * <ul class='javatreec'> 043 * <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()} 044 * <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)} 045 * <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)} 046 * <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)} 047 * <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)} 048 * <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)} 049 * <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()} 050 * <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()} 051 * <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)} 052 * <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)} 053 * <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)} 054 * <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)} 055 * <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)} 056 * <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)} 057 * <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)} 058 * <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)} 059 * </ul> 060 * </ul> 061 * 062 * <h5 class='section'>Transform Methods:</h5> 063 * <p> 064 * <ul class='javatree'> 065 * <li class='jc'>{@link FluentMapAssertion} 066 * <ul class='javatreec'> 067 * <li class='jm'>{@link FluentMapAssertion#asValue(Object) asValue(Object)} 068 * <li class='jm'>{@link FluentMapAssertion#asValues(Object...) asValues(Object...)} 069 * <li class='jm'>{@link FluentMapAssertion#asValueMap(Object...) asValueMap(Object...)} 070 * <li class='jm'>{@link FluentMapAssertion#asSize() asSize()} 071 * </ul> 072 * <li class='jc'>{@link FluentObjectAssertion} 073 * <ul class='javatreec'> 074 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 075 * <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)} 076 * <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)} 077 * <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()} 078 * <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()} 079 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)} 080 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 081 * </ul> 082 * </ul> 083 * 084 * <h5 class='section'>Configuration Methods:</h5> 085 * <p> 086 * <ul class='javatree'> 087 * <li class='jc'>{@link Assertion} 088 * <ul class='javatreec'> 089 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 090 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 091 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 092 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 093 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 094 * </ul> 095 * </ul> 096 * 097 * <h5 class='section'>See Also:</h5><ul> 098 * <li class='link'><a class="doclink" href="../../../../index.html#ja.Overview">Overview > juneau-assertions > Overview</a> 099 * </ul> 100 * 101 * @param <K> The key type. 102 * @param <V> The value type. 103 * @param <R> The return type. 104 */ 105@FluentSetters(returns="FluentMapAssertion<K,V,R>") 106public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R> { 107 108 //----------------------------------------------------------------------------------------------------------------- 109 // Static 110 //----------------------------------------------------------------------------------------------------------------- 111 112 private static final Messages MESSAGES = Messages.of(FluentMapAssertion.class, "Messages"); 113 private static final String 114 MSG_mapWasNotEmpty = MESSAGES.getString("mapWasNotEmpty"), 115 MSG_mapDidNotContainExpectedKey = MESSAGES.getString("mapDidNotContainExpectedKey"), 116 MSG_mapContainedUnexpectedKey = MESSAGES.getString("mapContainedUnexpectedKey"), 117 MSG_mapWasEmpty = MESSAGES.getString("mapWasEmpty"), 118 MSG_mapDidNotHaveTheExpectedSize = MESSAGES.getString("mapDidNotHaveTheExpectedSize"); 119 120 //----------------------------------------------------------------------------------------------------------------- 121 // Instance 122 //----------------------------------------------------------------------------------------------------------------- 123 124 /** 125 * Constructor. 126 * 127 * @param value 128 * The object being tested. 129 * <br>Can be <jk>null</jk>. 130 * @param returns 131 * The object to return after a test method is called. 132 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 133 * used on the same assertion. 134 */ 135 public FluentMapAssertion(Map<K,V> value, R returns) { 136 this(null, value, returns); 137 } 138 139 /** 140 * Chained constructor. 141 * 142 * <p> 143 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 144 * 145 * @param creator 146 * The assertion that created this assertion. 147 * <br>Should be <jk>null</jk> if this is the top-level assertion. 148 * @param value 149 * The object being tested. 150 * <br>Can be <jk>null</jk>. 151 * @param returns 152 * The object to return after a test method is called. 153 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 154 * used on the same assertion. 155 */ 156 public FluentMapAssertion(Assertion creator, Map<K,V> value, R returns) { 157 super(creator, value, returns); 158 } 159 160 //----------------------------------------------------------------------------------------------------------------- 161 // Transform methods 162 //----------------------------------------------------------------------------------------------------------------- 163 164 @Override /* FluentObjectAssertion */ 165 public FluentMapAssertion<K,V,R> asTransformed(Function<Map<K,V>,Map<K,V>> function) { 166 return new FluentMapAssertion<>(this, function.apply(orElse(null)), returns()); 167 } 168 169 /** 170 * Returns an object assertion on the value specified at the specified key. 171 * 172 * <p> 173 * If the map is <jk>null</jk> or the map doesn't contain the specified key, the returned assertion is a null assertion 174 * (meaning {@link FluentAnyAssertion#isExists()} returns <jk>false</jk>). 175 * 176 * @param key The key of the item to retrieve from the map. 177 * @return A new assertion. 178 */ 179 public FluentAnyAssertion<V,R> asValue(K key) { 180 return new FluentAnyAssertion<>(this, get(key), returns()); 181 } 182 183 /** 184 * Returns a {@link FluentListAssertion} of the values of the specified keys. 185 * 186 * If the map is <jk>null</jk>, the returned assertion is a null assertion 187 * (meaning {@link FluentObjectAssertion#isExists()} returns <jk>false</jk>). 188 * 189 * @param keys The keys of the values to retrieve from the map. 190 * @return A new assertion. 191 */ 192 public FluentListAssertion<Object,R> asValues(@SuppressWarnings("unchecked") K...keys) { 193 return new FluentListAssertion<>(this, valueIsNull() ? null : stream(keys).map(x -> get(x)).collect(toList()), returns()); 194 } 195 196 /** 197 * Extracts a subset of this map. 198 * 199 * @param keys The entries to extract. 200 * @return This object. 201 */ 202 @SuppressWarnings("unchecked") 203 public FluentMapAssertion<K,V,R> asValueMap(K...keys) { 204 if (valueIsNull()) 205 return new FluentMapAssertion<>(this, null, returns()); 206 Map<K,V> m1 = value(), m2 = CollectionUtils.map(); 207 if (m1 != null) 208 for (K k : keys) 209 m2.put(k, m1.get(k)); 210 return new FluentMapAssertion<>(this, m2, returns()); 211 } 212 213 /** 214 * Returns an integer assertion on the size of this map. 215 * 216 * <p> 217 * If the map is <jk>null</jk>, the returned assertion is a null assertion 218 * (meaning {@link FluentIntegerAssertion#isExists()} returns <jk>false</jk>). 219 * 220 * @return A new assertion. 221 */ 222 public FluentIntegerAssertion<R> asSize() { 223 return new FluentIntegerAssertion<>(this, valueIsNull() ? null : value().size(), returns()); 224 } 225 226 //----------------------------------------------------------------------------------------------------------------- 227 // Test methods 228 //----------------------------------------------------------------------------------------------------------------- 229 230 /** 231 * Asserts that the map exists and is empty. 232 * 233 * @return The fluent return object. 234 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 235 */ 236 public R isEmpty() throws AssertionError { 237 if (! value().isEmpty()) 238 throw error(MSG_mapWasNotEmpty); 239 return returns(); 240 } 241 242 /** 243 * Asserts that the map exists and is not empty. 244 * 245 * @return The fluent return object. 246 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 247 */ 248 public R isNotEmpty() throws AssertionError { 249 if (value().isEmpty()) 250 throw error(MSG_mapWasEmpty); 251 return returns(); 252 } 253 254 /** 255 * Asserts that the map contains the expected key. 256 * 257 * @param name The key name to check for. 258 * @return The fluent return object. 259 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 260 */ 261 public R isContainsKey(String name) throws AssertionError { 262 if (value().containsKey(name)) 263 return returns(); 264 throw error(MSG_mapDidNotContainExpectedKey, name, value()); 265 } 266 267 /** 268 * Asserts that the map contains the expected key. 269 * 270 * @param name The key name to check for. 271 * @return The fluent return object. 272 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 273 */ 274 public R isNotContainsKey(String name) throws AssertionError { 275 if (! value().containsKey(name)) 276 return returns(); 277 throw error(MSG_mapContainedUnexpectedKey, name, value()); 278 } 279 280 /** 281 * Asserts that the map exists and is the specified size. 282 * 283 * @param size The expected size. 284 * @return The fluent return object. 285 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 286 */ 287 public R isSize(int size) throws AssertionError { 288 if (size2() != size) 289 throw error(MSG_mapDidNotHaveTheExpectedSize, size, size2()); 290 return returns(); 291 } 292 293 //----------------------------------------------------------------------------------------------------------------- 294 // Fluent setters 295 //----------------------------------------------------------------------------------------------------------------- 296 297 // <FluentSetters> 298 299 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 300 public FluentMapAssertion<K,V,R> setMsg(String msg, Object...args) { 301 super.setMsg(msg, args); 302 return this; 303 } 304 305 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 306 public FluentMapAssertion<K,V,R> setOut(PrintStream value) { 307 super.setOut(value); 308 return this; 309 } 310 311 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 312 public FluentMapAssertion<K,V,R> setSilent() { 313 super.setSilent(); 314 return this; 315 } 316 317 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 318 public FluentMapAssertion<K,V,R> setStdOut() { 319 super.setStdOut(); 320 return this; 321 } 322 323 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 324 public FluentMapAssertion<K,V,R> setThrowable(Class<? extends java.lang.RuntimeException> value) { 325 super.setThrowable(value); 326 return this; 327 } 328 329 // </FluentSetters> 330 331 //----------------------------------------------------------------------------------------------------------------- 332 // Utility methods 333 //----------------------------------------------------------------------------------------------------------------- 334 335 private V get(K key) { 336 return orElse(emptyMap()).get(key); 337 } 338 339 private int size2() { 340 return value().size(); 341 } 342}