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.util; 017 018import static java.util.Objects.requireNonNull; 019 020import java.util.Arrays; 021import java.util.List; 022import java.util.Set; 023 024import de.cuioss.test.generator.TypedGenerator; 025import de.cuioss.test.valueobjects.api.contracts.VerifyConstructor; 026import de.cuioss.test.valueobjects.api.generator.PropertyGenerator; 027import de.cuioss.test.valueobjects.api.generator.PropertyGeneratorHint; 028import de.cuioss.test.valueobjects.api.generator.PropertyGeneratorHints; 029import de.cuioss.test.valueobjects.api.generator.PropertyGenerators; 030import de.cuioss.test.valueobjects.generator.TypedGeneratorRegistry; 031import de.cuioss.test.valueobjects.generator.dynamic.GeneratorResolver; 032import de.cuioss.test.valueobjects.objects.impl.DefaultInstantiator; 033import de.cuioss.tools.collect.CollectionBuilder; 034import de.cuioss.tools.reflect.MoreReflection; 035import lombok.experimental.UtilityClass; 036 037/** 038 * Provides utility methods for dealing with the creation of 039 * {@link TypedGenerator} usually in conjunction with annotations 040 * 041 * @author Oliver Wolff 042 */ 043@UtilityClass 044public final class GeneratorAnnotationHelper { 045 046 /** */ 047 public static final String UNABLE_TO_INSTANTIATE_GENERATOR = "Unable to instantiate generator, You must provide a no-arg public constructor: "; 048 049 /** 050 * Convenience method for handling all Generator specific aspects like initially 051 * calling {@link TypedGeneratorRegistry#registerBasicTypes()} and calling of 052 * the individual registering methods like 053 * {@link #handleUnitClassImplementation(Object)}, 054 * {@link #handlePropertyGenerator(Class)} and 055 * {@link #handleGeneratorHints(Class)} in case there are additionalGenerator 056 * given they will be registered as well 057 * 058 * @param testClass must not null 059 * @param additionalGenerator 060 */ 061 public static void handleGeneratorsForTestClass(final Object testClass, 062 final List<TypedGenerator<?>> additionalGenerator) { 063 requireNonNull(testClass); 064 // Handle Generator 065 TypedGeneratorRegistry.registerBasicTypes(); 066 handleUnitClassImplementation(testClass); 067 handlePropertyGenerator(testClass.getClass()); 068 handleGeneratorHints(testClass.getClass()); 069 if (null != additionalGenerator) { 070 for (final TypedGenerator<?> additional : additionalGenerator) { 071 TypedGeneratorRegistry.registerGenerator(additional); 072 } 073 } 074 } 075 076 /** 077 * Checks the given type for the annotation {@link PropertyGeneratorHint} and 078 * {@link PropertyGeneratorHints} and registers all found to the 079 * {@link TypedGeneratorRegistry} 080 * 081 * @param annotated the class that may or may not provide the annotations, must 082 * not be null 083 */ 084 public static void handleGeneratorHints(final Class<?> annotated) { 085 for (final PropertyGeneratorHint hint : extractConfiguredGeneratorHints(annotated)) { 086 087 final TypedGenerator<?> resolved = GeneratorResolver.resolveGenerator(hint.implementationType()); 088 TypedGeneratorRegistry.registerTypedGenerator(hint.declaredType(), 089 new WildcardDecoratorGenerator(hint.declaredType(), resolved)); 090 } 091 } 092 093 /** 094 * Checks the given type for the annotation {@link PropertyGenerator} and 095 * {@link PropertyGenerators} and registers all found to the 096 * {@link TypedGeneratorRegistry} 097 * 098 * @param annotated the class that may or may not provide the annotations, must 099 * not be null 100 */ 101 public static void handlePropertyGenerator(final Class<?> annotated) { 102 for (final PropertyGenerator config : extractConfiguredPropertyGenerator(annotated)) { 103 for (final Class<?> typedClass : config.value()) { 104 TypedGeneratorRegistry 105 .registerGenerator((TypedGenerator<?>) new DefaultInstantiator<>(typedClass).newInstance()); 106 } 107 } 108 } 109 110 /** 111 * Checks whether the actual implementation of the test implements 112 * {@link TypedGenerator}. If so it will be registered to the 113 * {@link TypedGeneratorRegistry} 114 * 115 * @param testClass the actual test-object 116 */ 117 public static void handleUnitClassImplementation(final Object testClass) { 118 if (testClass instanceof TypedGenerator<?> generator) { 119 TypedGeneratorRegistry.registerGenerator(generator); 120 } 121 } 122 123 /** 124 * Checks the given type for the annotation {@link PropertyGeneratorHint} and 125 * {@link PropertyGeneratorHints} and puts all found in the returned list 126 * 127 * @param annotated the class that may or may not provide the annotations, must 128 * not be null 129 * @return a {@link Set} of {@link VerifyConstructor} extracted from the 130 * annotations of the given type. May be empty but never null 131 */ 132 public static Set<PropertyGeneratorHint> extractConfiguredGeneratorHints(final Class<?> annotated) { 133 requireNonNull(annotated); 134 final var builder = new CollectionBuilder<PropertyGeneratorHint>(); 135 136 MoreReflection.extractAllAnnotations(annotated, PropertyGeneratorHints.class) 137 .forEach(contract -> builder.add(Arrays.asList(contract.value()))); 138 MoreReflection.extractAllAnnotations(annotated, PropertyGeneratorHint.class).forEach(builder::add); 139 140 return builder.toImmutableSet(); 141 } 142 143 /** 144 * Checks the given type for the annotation {@link PropertyGenerator} and 145 * {@link PropertyGenerators} and puts all found in the returned list 146 * 147 * @param annotated the class that may or may not provide the annotations, must 148 * not be null 149 * @return a {@link Set} of {@link PropertyGenerator} extract from the 150 * annotations of the given type. May be empty but never null 151 */ 152 public static Set<PropertyGenerator> extractConfiguredPropertyGenerator(final Class<?> annotated) { 153 requireNonNull(annotated); 154 final var builder = new CollectionBuilder<PropertyGenerator>(); 155 156 MoreReflection.extractAllAnnotations(annotated, PropertyGenerators.class) 157 .forEach(contract -> builder.add(Arrays.asList(contract.value()))); 158 MoreReflection.extractAllAnnotations(annotated, PropertyGenerator.class).forEach(builder::add); 159 160 return builder.toImmutableSet(); 161 } 162 163}