001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.test.valueobjects.objects.impl; 017 018import static de.cuioss.test.valueobjects.objects.impl.ExceptionHelper.extractCauseMessageFromThrowable; 019import static de.cuioss.tools.string.MoreStrings.emptyToNull; 020import static java.util.Objects.requireNonNull; 021 022import java.lang.reflect.InvocationTargetException; 023import java.lang.reflect.Method; 024 025import de.cuioss.test.valueobjects.contract.BuilderContractImpl; 026import de.cuioss.test.valueobjects.objects.BuilderInstantiator; 027import de.cuioss.test.valueobjects.objects.ObjectInstantiator; 028import lombok.Getter; 029import lombok.ToString; 030 031/** 032 * Used for creating instances of a builder. This variant relies on the 033 * builder-class having a parameter-free constructor. 034 * 035 * @author Oliver Wolff 036 * @param <T> identifying the type of the {@link Object} created by the builder 037 */ 038@ToString 039public class BuilderConstructorBasedInstantiator<T> implements BuilderInstantiator<T> { 040 041 private final Method builderMethod; 042 043 @Getter 044 private final Class<T> targetClass; 045 046 @Getter 047 private final Class<?> builderClass; 048 049 private final ObjectInstantiator<?> builderInstantiator; 050 051 /** 052 * Constructor. Shortcut for calling 053 * {@link #BuilderConstructorBasedInstantiator(Class, String)} with 054 * {@link BuilderContractImpl#DEFAULT_BUILD_METHOD_NAME} 055 * 056 * @param builderType identifying the actual type of the builder. It is assumed 057 * that it provides is parameter-free public constructor 058 */ 059 public BuilderConstructorBasedInstantiator(final Class<?> builderType) { 060 this(builderType, BuilderContractImpl.DEFAULT_BUILD_METHOD_NAME); 061 } 062 063 /** 064 * Constructor. 065 * 066 * @param builderType identifying the actual type of the builder. It is 067 * assumed that it provides is parameter-free public 068 * constructor 069 * @param buildMethodName the actual name or the builder-method, must not be 070 * null nor empty 071 */ 072 @SuppressWarnings("unchecked") 073 public BuilderConstructorBasedInstantiator(final Class<?> builderType, final String buildMethodName) { 074 075 requireNonNull(builderType, "builderType must not be null"); 076 requireNonNull(emptyToNull(buildMethodName), "builderMethodName must not be null"); 077 078 builderClass = builderType; 079 builderInstantiator = new DefaultInstantiator<>(builderType); 080 081 try { 082 builderMethod = builderInstantiator.getTargetClass().getDeclaredMethod(buildMethodName); 083 targetClass = (Class<T>) builderMethod.getReturnType(); 084 } catch (NoSuchMethodException | SecurityException e) { 085 throw new AssertionError("Unable to access method " + buildMethodName + " on type " + builderType.getName() 086 + ", due to " + extractCauseMessageFromThrowable(e), e); 087 } 088 089 } 090 091 @Override 092 public Object newBuilderInstance() { 093 return builderInstantiator.newInstance(); 094 } 095 096 @SuppressWarnings("unchecked") 097 @Override 098 public T build(final Object builder) { 099 try { 100 return (T) builderMethod.invoke(builder); 101 } catch (IllegalAccessException | InvocationTargetException | RuntimeException e) { 102 throw new AssertionError("Unable to access method " + builderMethod.getName() + " on type " 103 + getBuilderClass().getName() + ", due to " + extractCauseMessageFromThrowable(e), e); 104 } 105 } 106}