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.bct.assertions; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.function.*; 022 023import org.apache.juneau.commons.utils.*; 024 025/** 026 * Wrapper around a {@link Predicate} that allows for an error message for when the predicate fails. 027 * 028 * <p> 029 * Typically used wherever predicates are allowed for testing of {@link Assertion} objects such as... 030 * <ul> 031 * <li>{@link FluentObjectAssertion#is(Predicate)} 032 * <li>{@link FluentArrayAssertion#is(Predicate...)} 033 * <li>{@link FluentPrimitiveArrayAssertion#is(Predicate...)} 034 * <li>{@link FluentListAssertion#isEach(Predicate...)} 035 * </ul> 036 * 037 * <p> 038 * See {@link AssertionPredicates} for a set of predefined predicates for common use cases. 039 * 040 * <h5 class='section'>Example:</h5> 041 * <p class='bjava'> 042 * <jc>// Asserts that a bean passes a custom test.</jc> 043 * <jc>// AssertionError with specified message is thrown otherwise.</jc> 044 * Predicate<MyBean> <jv>predicate</jv> = <jk>new</jk> AssertionPredicate<MyBean>( 045 * <jv>x</jv> -> <jv>x</jv>.getFoo().equals(<js>"bar"</js>), 046 * <js>"Foo did not equal bar. Bean was='%s'"</js>, 047 * <jsf>VALUE</jsf> 048 * ); 049 * <jsm>assertObject</jsm>(<jv>myBean</jv>).is(<jv>predicate</jv>); 050 * </p> 051 * 052 * <h5 class='section'>See Also:</h5> 053 * <ul> 054 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem</a> 055 * </ul> 056 * 057 * @param <T> the type of input being tested. 058 */ 059@SuppressWarnings({ 060 "java:S115", // Constants use UPPER_snakeCase convention (e.g., MSG_predicateTestFailed, MSG_valueDidNotPassTest) 061 "java:S3740" // Raw Predicate/Function types used for dynamic assertion dispatch where T cannot be bounded further 062}) 063public class AssertionPredicate<T> implements Predicate<T> { 064 065 private static final String CONST_Messages = "Messages"; 066 067 /** 068 * Encapsulates multiple predicates into a single AND operation. 069 * 070 * <p> 071 * Similar to <c><jsm>stream</jsm>(<jv>predicates</jv>).reduce(<jv>x</jv>-><jk>true</jk>, Predicate::and)</c> but 072 * provides for {@link #getFailureMessage()} to return a useful message. 073 * 074 * @param <T> the type of input being tested. 075 */ 076 public static class And<T> extends AssertionPredicate<T> { 077 078 private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, CONST_Messages); 079 080 private static final String MSG_predicateTestFailed = MESSAGES.getString("predicateTestFailed"); 081 private final Predicate<T>[] inner; 082 083 /** 084 * Constructor. 085 * 086 * @param inner The inner predicates to run. 087 */ 088 @SafeVarargs 089 public And(Predicate<T>...inner) { 090 this.inner = inner; 091 } 092 093 @Override /* Overridden from Predicate */ 094 public boolean test(T t) { 095 failedMessage.remove(); 096 for (var i = 0; i < inner.length; i++) { 097 var p = inner[i]; 098 if (nn(p)) { 099 var b = p.test(t); 100 if (! b) { 101 var m = f(MSG_predicateTestFailed, i + 1); 102 if (p instanceof AssertionPredicate<?> p2) 103 m += "\n\t" + p2.getFailureMessage(); 104 failedMessage.set(m); 105 return false; 106 } 107 } 108 } 109 return true; 110 } 111 } 112 113 /** 114 * Negates an assertion. 115 * 116 * <p> 117 * Similar to <c><jv>predicate</jv>.negate()</c> but provides for {@link #getFailureMessage()} to return a useful message. 118 * 119 * @param <T> the type of input being tested. 120 */ 121 public static class Not<T> extends AssertionPredicate<T> { 122 123 private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, CONST_Messages); 124 private static final String MSG_predicateTestsUnexpectedlyPassed = MESSAGES.getString("predicateTestsUnexpectedlyPassed"); 125 126 private final Predicate<T> inner; 127 128 /** 129 * Constructor. 130 * 131 * @param inner The inner predicates to run. 132 */ 133 public Not(Predicate<T> inner) { 134 this.inner = inner; 135 } 136 137 @Override /* Overridden from Predicate */ 138 public boolean test(T t) { 139 failedMessage.remove(); 140 var p = inner; 141 if (nn(p)) { 142 var b = p.test(t); 143 if (b) { 144 failedMessage.set(f(MSG_predicateTestsUnexpectedlyPassed)); 145 return false; 146 } 147 } 148 return true; 149 } 150 } 151 152 /** 153 * Encapsulates multiple predicates into a single OR operation. 154 * 155 * <p> 156 * Similar to <c><jsm>stream</jsm>(<jv>predicates</jv>).reduce(<jv>x</jv>-><jk>true</jk>, Predicate::or)</c> but 157 * provides for {@link #getFailureMessage()} to return a useful message. 158 * 159 * @param <T> the type of input being tested. 160 */ 161 public static class Or<T> extends AssertionPredicate<T> { 162 163 private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, CONST_Messages); 164 private static final String MSG_noPredicateTestsPassed = MESSAGES.getString("noPredicateTestsPassed"); 165 166 private final Predicate<T>[] inner; 167 168 /** 169 * Constructor. 170 * 171 * @param inner The inner predicates to run. 172 */ 173 @SafeVarargs 174 public Or(Predicate<T>...inner) { 175 this.inner = inner; 176 } 177 178 @Override /* Overridden from Predicate */ 179 public boolean test(T t) { 180 failedMessage.remove(); 181 for (var p : inner) 182 if (nn(p) && p.test(t)) 183 return true; 184 var m = f(MSG_noPredicateTestsPassed); 185 failedMessage.set(m); 186 return false; 187 } 188 } 189 190 /** 191 * Argument placeholder for tested value. 192 */ 193 public static final Function<Object,String> VALUE = StringUtils::readable; 194 private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, CONST_Messages); 195 private static final String MSG_valueDidNotPassTest = MESSAGES.getString("valueDidNotPassTest"); 196 private static final String MSG_valueDidNotPassTestWithValue = MESSAGES.getString("valueDidNotPassTestWithValue"); 197 private final Predicate<T> inner; 198 199 private final String message; 200 201 private final Object[] args; 202 203 final ThreadLocal<String> failedMessage = new ThreadLocal<>(); 204 205 /** 206 * Constructor. 207 * 208 * @param inner The predicate test. 209 * @param message 210 * The error message if predicate fails. 211 * <br>Supports printf-style arguments. 212 * @param args 213 * Optional message arguments. 214 * <br>Can contain {@link #VALUE} to specify the value itself as an argument. 215 * <br>Can contain {@link Function functions} to apply to the tested value. 216 */ 217 public AssertionPredicate(Predicate<T> inner, String message, Object...args) { 218 this.inner = inner; 219 if (nn(message)) { 220 this.message = message; 221 this.args = args; 222 } else if (inner instanceof AssertionPredicate) { 223 this.message = MSG_valueDidNotPassTest; 224 this.args = a(); 225 } else { 226 this.message = MSG_valueDidNotPassTestWithValue; 227 this.args = a(VALUE); 228 } 229 } 230 231 AssertionPredicate() { 232 this.inner = null; 233 this.message = null; 234 this.args = null; 235 } 236 237 @Override /* Overridden from Predicate */ 238 @SuppressWarnings({ 239 "unchecked" // Type erasure requires cast for predicate test 240 }) 241 public boolean test(T t) { 242 failedMessage.remove(); 243 var b = inner.test(t); 244 if (! b) { 245 var m = message; 246 var oargs = new Object[this.args.length]; 247 for (var i = 0; i < oargs.length; i++) { 248 var a = this.args[i]; 249 if (a instanceof Function a2) 250 oargs[i] = r(a2.apply(t)); 251 else 252 oargs[i] = r(a); 253 } 254 m = f(m, oargs); 255 if (inner instanceof AssertionPredicate<?> inner2) 256 m += "\n\t" + inner2.getFailureMessage(); 257 failedMessage.set(m); 258 } 259 return inner.test(t); 260 } 261 262 /** 263 * Returns the error message from the last call to this assertion. 264 * 265 * @return The error message, or <jk>null</jk> if there was no failure. 266 */ 267 protected String getFailureMessage() { return failedMessage.get(); } 268}