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