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 org.apache.juneau.common.utils.StringUtils.*; 020 021import java.io.*; 022import java.lang.reflect.*; 023 024import org.apache.juneau.*; 025import org.apache.juneau.cp.*; 026import org.apache.juneau.internal.*; 027 028/** 029 * Base class for all assertion objects. 030 * 031 * <h5 class='section'>Test Methods:</h5> 032 * <p> 033 * <ul class='javatree'> 034 * <li>None 035 * </ul> 036 * 037 * <h5 class='section'>Transform Methods:</h5> 038 * <p> 039 * <ul class='javatree'> 040 * <li>None 041 * </ul> 042 * 043 * <h5 class='section'>Configuration Methods:</h5> 044 * <p> 045 * <ul class='javatree'> 046 * <li class='jc'>{@link Assertion} 047 * <ul class='javatreec'> 048 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 049 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 050 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 051 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 052 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 053 * </ul> 054 * </ul> 055 * 056 * <ul class='seealso'> 057 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem Overview</a> 058 * </ul> 059 */ 060public class Assertion { 061 062 //----------------------------------------------------------------------------------------------------------------- 063 // Static 064 //----------------------------------------------------------------------------------------------------------------- 065 066 private static final Messages MESSAGES = Messages.of(Assertion.class, "Messages"); 067 068 static final String 069 MSG_parameterCannotBeNull = MESSAGES.getString("parameterCannotBeNull"), 070 MSG_causedBy = MESSAGES.getString("causedBy"); 071 072 //----------------------------------------------------------------------------------------------------------------- 073 // Instance 074 //----------------------------------------------------------------------------------------------------------------- 075 076 private String msg; 077 private Object[] msgArgs; 078 private PrintStream out = System.err; // NOSONAR - Intentional. 079 private Class<? extends RuntimeException> throwable; 080 081 /** 082 * Constructor used when this assertion is being created from within another assertion. 083 * 084 * @param creator The creator of this assertion. 085 */ 086 protected Assertion(Assertion creator) { 087 if (creator != null) { 088 this.msg = creator.msg; 089 this.msgArgs = creator.msgArgs; 090 this.out = creator.out; 091 this.throwable = creator.throwable; 092 } 093 } 094 095 //----------------------------------------------------------------------------------------------------------------- 096 // Config 097 //----------------------------------------------------------------------------------------------------------------- 098 099 /** 100 * Allows you to override the assertion failure message. 101 * 102 * <p> 103 * String can contain <js>"{msg}"</js> to represent the original message. 104 * 105 * <h5 class='section'>Example:</h5> 106 * <p class='bjava'> 107 * <jk>import static</jk> org.apache.juneau.assertions.Assertions.*; 108 * 109 * <jc>// Throws an assertion with a custom message instead of the default "Value was null."</jc> 110 * <jsm>assertString</jsm>(<jv>myString</jv>) 111 * .setMsg(<js>"My string was bad: {msg}"</js>) 112 * .isNotNull(); 113 * </p> 114 * 115 * @param msg The assertion failure message. 116 * @param args Optional message arguments. 117 * @return This object. 118 */ 119 public Assertion setMsg(String msg, Object...args) { 120 this.msg = msg.replace("{msg}", "<<<MSG>>>"); 121 this.msgArgs = args; 122 return this; 123 } 124 125 /** 126 * If an error occurs, send the error message to STDOUT instead of STDERR. 127 * 128 * @return This object. 129 */ 130 public Assertion setStdOut() { 131 return setOut(System.out); // NOSONAR - Intentional. 132 } 133 134 /** 135 * If an error occurs, send the error message to the specified stream instead of STDERR. 136 * 137 * @param value 138 * The output stream. 139 * Can be <jk>null</jk> to suppress output. 140 * @return This object. 141 */ 142 public Assertion setOut(PrintStream value) { 143 this.out = value; 144 return this; 145 } 146 147 /** 148 * Suppresses output to STDERR. 149 * 150 * <p> 151 * This is the equivalent to calling <c>out(<jk>null</jk>)</c>. 152 * 153 * @return This object. 154 */ 155 public Assertion setSilent() { 156 return setOut(null); 157 } 158 159 /** 160 * If an error occurs, throw this exception instead of the standard {@link AssertionError}. 161 * 162 * <p> 163 * The throwable class must have a public constructor that takes in any of the following parameters: 164 * <ul> 165 * <li>{@link Throwable} - The caused-by exception (if there is one). 166 * <li>{@link String} - The assertion failure message. 167 * </ul> 168 * 169 * <p> 170 * If the throwable cannot be instantiated, a {@link RuntimeException} is thrown instead. 171 * 172 * <h5 class='section'>Example:</h5> 173 * <p class='bjava'> 174 * <jk>import static</jk> org.apache.juneau.assertions.Assertions.*; 175 * 176 * <jc>// Throws a BadRequest instead of an AssertionError if the string is null.</jc> 177 * <jsm>assertString</jsm>(<jv>myString</jv>) 178 * .setThrowable(BadRequest.<jk>class</jk>) 179 * .isNotNull(); 180 * </p> 181 * 182 * @param value The new value for this setting. 183 * @return This object. 184 */ 185 public Assertion setThrowable(Class<? extends RuntimeException> value) { 186 this.throwable = value; 187 return this; 188 } 189 190 //----------------------------------------------------------------------------------------------------------------- 191 // Fluent setters 192 //----------------------------------------------------------------------------------------------------------------- 193 //----------------------------------------------------------------------------------------------------------------- 194 // Utility methods 195 //----------------------------------------------------------------------------------------------------------------- 196 197 198 /** 199 * Creates a new {@link BasicAssertionError}. 200 * 201 * @param msg The message. 202 * @param args The message arguments. 203 * @return A new {@link BasicAssertionError}. 204 */ 205 protected BasicAssertionError error(String msg, Object...args) { 206 return error(null, msg, args); 207 } 208 209 /** 210 * Creates a new {@link BasicAssertionError}. 211 * 212 * @param cause Optional caused-by throwable. 213 * @param msg The message. 214 * @param args The message arguments. 215 * @return A new {@link BasicAssertionError}. 216 */ 217 protected BasicAssertionError error(Throwable cause, String msg, Object...args) { 218 msg = format(msg, args); 219 if (this.msg != null) 220 msg = format(this.msg, this.msgArgs).replace("<<<MSG>>>", msg); 221 if (out != null) 222 out.println(msg); 223 if (throwable != null) { 224 try { 225 throw BeanStore 226 .create() 227 .build() 228 .addBean(Throwable.class, cause) 229 .addBean(String.class, msg) 230 .addBean(Object[].class,new Object[0]) 231 .createBean(throwable) 232 .run(); 233 } catch (ExecutableException e) { 234 // If we couldn't create requested exception, just throw a RuntimeException. 235 throw new BasicRuntimeException(cause, msg); 236 } 237 } 238 return new BasicAssertionError(cause, msg); 239 } 240 241 /** 242 * Convenience method for getting the class name for an object. 243 * 244 * @param o The object to get the class name for. 245 * @return The class name for an object. 246 */ 247 protected static String className(Object o) { 248 return ClassUtils.className(o); 249 } 250 251 /** 252 * Convenience method for getting the array class of the specified element type. 253 * 254 * @param <E> The element type. 255 * @param c The object to get the class name for. 256 * @return The class name for an object. 257 */ 258 protected static <E> Class<E[]> arrayClass(Class<E> c) { 259 return (Class<E[]>)Array.newInstance(c,0).getClass(); 260 } 261}