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