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 java.util.Objects.requireNonNull; 020 021import java.lang.reflect.Constructor; 022import java.lang.reflect.InvocationTargetException; 023import java.util.ArrayList; 024import java.util.List; 025 026import de.cuioss.test.valueobjects.objects.ParameterizedInstantiator; 027import de.cuioss.test.valueobjects.objects.RuntimeProperties; 028import de.cuioss.tools.logging.CuiLogger; 029 030/** 031 * This {@link ParameterizedInstantiator} uses a constructor derived by the 032 * given {@link RuntimeProperties#getAllProperties()} in order to instantiate 033 * {@link Object}s 034 * 035 * @param <T> identifying the type of objects to be created 036 * 037 * @author Oliver Wolff 038 */ 039public class ConstructorBasedInstantiator<T> extends AbstractOrderedArgsInstantiator<T> { 040 041 private final Constructor<T> constructor; 042 043 private static final CuiLogger log = new CuiLogger(ConstructorBasedInstantiator.class); 044 045 /** 046 * Constructor. 047 * 048 * @param type identifying the actual type to be instantiated, must 049 * not be null 050 * @param runtimeProperties must not be null. defines the attributes in the 051 * exact order to be used for the constructor: 052 * {@link RuntimeProperties#getAllProperties()} 053 */ 054 public ConstructorBasedInstantiator(final Class<T> type, final RuntimeProperties runtimeProperties) { 055 056 super(runtimeProperties); 057 requireNonNull(type); 058 059 final List<Class<?>> parameter = new ArrayList<>(); 060 runtimeProperties.getAllProperties().forEach(meta -> parameter.add(meta.resolveActualClass())); 061 try { 062 if (parameter.isEmpty()) { 063 constructor = type.getConstructor(); 064 } else { 065 constructor = type.getConstructor(toClassArray(parameter)); 066 } 067 requireNonNull(constructor, "Unable to find a constructor with signature " + parameter); 068 } catch (NoSuchMethodException | SecurityException e) { 069 final var message = new StringBuilder("Unable to find a constructor with signature ").append(parameter) 070 .append(", for type ").append(type.getName()).toString(); 071 log.error(message, e); 072 for (Constructor<?> tempConstructor : type.getConstructors()) { 073 log.error("Found constructor: {}", tempConstructor.toGenericString()); 074 } 075 if (0 == type.getConstructors().length) { 076 log.error("No public constructor found!"); 077 } 078 throw new AssertionError(message); 079 } 080 } 081 082 @Override 083 protected T doInstantiate(final Object... args) { 084 try { 085 return constructor.newInstance(args); 086 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException 087 | InvocationTargetException e) { 088 final var message = new StringBuilder("Unable to invoke constructor ").append(", due to ") 089 .append(extractCauseMessageFromThrowable(e)).toString(); 090 throw new AssertionError(message, e); 091 } 092 } 093 094 @Override 095 public String toString() { 096 final var builder = new StringBuilder(getClass().getName()); 097 builder.append("\nConstructor: ").append(constructor); 098 builder.append("\nProperty Configuration: ").append(getRuntimeProperties().toString()); 099 return builder.toString(); 100 } 101}