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