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.generator.dynamic; 017 018import static java.util.Objects.requireNonNull; 019 020import java.util.Collection; 021import java.util.Map; 022import java.util.Optional; 023 024import de.cuioss.test.generator.Generators; 025import de.cuioss.test.generator.TypedGenerator; 026import de.cuioss.test.valueobjects.generator.TypedGeneratorRegistry; 027import de.cuioss.test.valueobjects.generator.dynamic.impl.ArraysGenerator; 028import de.cuioss.test.valueobjects.generator.dynamic.impl.CollectionTypeGenerator; 029import de.cuioss.test.valueobjects.generator.dynamic.impl.ConstructorBasedGenerator; 030import de.cuioss.test.valueobjects.generator.dynamic.impl.DynamicProxyGenerator; 031import de.cuioss.test.valueobjects.generator.dynamic.impl.EmptyMapGenerator; 032import de.cuioss.test.valueobjects.generator.dynamic.impl.InterfaceProxyGenerator; 033import de.cuioss.test.valueobjects.property.util.CollectionType; 034import de.cuioss.tools.logging.CuiLogger; 035import lombok.AccessLevel; 036import lombok.NoArgsConstructor; 037 038/** 039 * Provides strategies for creating objects dynamically 040 * 041 * @author Oliver Wolff 042 */ 043@NoArgsConstructor(access = AccessLevel.PRIVATE) 044public final class GeneratorResolver { 045 046 private static final String FOUND_GENERATOR_FOR_TYPE = "Found generator {} for type {}"; 047 048 private static final String TYPE_MUST_NOT_BE_NULL = "type must not be null"; 049 050 private static final CuiLogger log = new CuiLogger(GeneratorResolver.class); 051 052 /** 053 * Central method for finding / accessing a concrete {@link TypedGenerator} for 054 * the given type. It works through all existing find methods. as last resort is 055 * uses {@link InterfaceProxyGenerator} or {@link DynamicProxyGenerator} that 056 * will always return a valid one <em>Caution:</em> The resolving system relies 057 * on {@link TypedGeneratorRegistry} being configured properly, saying 058 * {@link TypedGeneratorRegistry#registerBasicTypes()} has been called prior to 059 * this method 060 * 061 * @param type must not be null 062 * @return a concrete {@link TypedGenerator} for the given type 063 */ 064 public static <T> TypedGenerator<T> resolveGenerator(final Class<T> type) { 065 requireNonNull(type, TYPE_MUST_NOT_BE_NULL); 066 log.debug("resolving generator for {}", type.getName()); 067 068 Optional<TypedGenerator<T>> found = TypedGeneratorRegistry.getGenerator(type); 069 if (found.isPresent()) { 070 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 071 return found.get(); 072 } 073 found = Generators.enumValuesIfAvailable(type); 074 if (found.isPresent()) { 075 TypedGeneratorRegistry.registerGenerator(found.get()); 076 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 077 return found.get(); 078 } 079 found = ArraysGenerator.getGeneratorForType(type); 080 if (found.isPresent()) { 081 TypedGeneratorRegistry.registerGenerator(found.get()); 082 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 083 return found.get(); 084 } 085 found = resolveCollectionGenerator(type); 086 if (found.isPresent()) { 087 TypedGeneratorRegistry.registerGenerator(found.get()); 088 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 089 return found.get(); 090 } 091 found = ConstructorBasedGenerator.getGeneratorForType(type); 092 if (found.isPresent()) { 093 TypedGeneratorRegistry.registerGenerator(found.get()); 094 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 095 return found.get(); 096 } 097 return resolveProxyGenerator(type); 098 } 099 100 private static <T> TypedGenerator<T> resolveProxyGenerator(final Class<T> type) { 101 log.debug("resolveProxyGenerator for type {}", type.getName()); 102 Optional<TypedGenerator<T>> found = InterfaceProxyGenerator.getGeneratorForType(type); 103 if (found.isPresent()) { 104 TypedGeneratorRegistry.registerGenerator(found.get()); 105 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 106 return found.get(); 107 } 108 found = DynamicProxyGenerator.getGeneratorForType(type); 109 if (found.isPresent()) { 110 TypedGeneratorRegistry.registerGenerator(found.get()); 111 log.trace(FOUND_GENERATOR_FOR_TYPE, found.get().getClass().getName(), type.getName()); 112 return found.get(); 113 } 114 throw new IllegalArgumentException("Unable to determine generator for type=" + type); 115 } 116 117 /** 118 * Provides a {@link TypedGenerator} for generating empty {@link Iterable} / 119 * {@link Collection} or {@link Map}s for given interfaces 120 * 121 * @param type to be checked 122 * @return an {@link TypedGenerator} if applicable or or <code>not 123 * {@link Optional#isPresent()}</code> 124 */ 125 @SuppressWarnings("unchecked") // Checked beforehand 126 public static <T> Optional<TypedGenerator<T>> resolveCollectionGenerator(final Class<T> type) { 127 if (null == type || !type.isInterface()) { 128 return Optional.empty(); 129 } 130 final var optional = CollectionType.findResponsibleCollectionType(type); 131 if (optional.isPresent()) { 132 return Optional.of(new CollectionTypeGenerator<>(type, optional.get())); 133 } 134 if (Map.class.isAssignableFrom(type)) { 135 return Optional.of((TypedGenerator<T>) new EmptyMapGenerator()); 136 } 137 return Optional.empty(); 138 } 139}