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.impl;
017
018import java.util.Optional;
019
020import de.cuioss.test.generator.TypedGenerator;
021import de.cuioss.tools.logging.CuiLogger;
022import javassist.util.proxy.ProxyFactory;
023import lombok.AccessLevel;
024import lombok.NonNull;
025import lombok.RequiredArgsConstructor;
026import lombok.ToString;
027
028/**
029 * Creates proxies using javassist for any type given that is not an interface
030 * nor annotation nor enum.
031 *
032 * @author Oliver Wolff
033 * @param <T> the type of objects to be generated
034 */
035@ToString(of = "wrappedGenerator")
036@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
037public class DynamicProxyGenerator<T> implements TypedGenerator<T> {
038
039    private static final CuiLogger log = new CuiLogger(DynamicProxyGenerator.class);
040
041    @NonNull
042    private final Class<T> type;
043
044    private final TypedGenerator<T> wrappedGenerator;
045
046    @Override
047    public T next() {
048        return wrappedGenerator.next();
049    }
050
051    @Override
052    public Class<T> getType() {
053        return type;
054    }
055
056    /**
057     * Factory method for creating an instance of {@link DynamicProxyGenerator}. It
058     * only works for any type given that is not an interface nor annotation nor
059     * enum.
060     *
061     * @param type to be checked,
062     * @return an {@link Optional} on the corresponding {@link TypedGenerator} if
063     *         the given type is applicable, otherwise {@link Optional#empty()}
064     */
065    public static final <T> Optional<TypedGenerator<T>> getGeneratorForType(final Class<T> type) {
066        if (null == type || type.isAnnotation() || type.isInterface() || type.isEnum()) {
067            return Optional.empty();
068        }
069        final var proxyFactory = new ProxyFactory();
070        proxyFactory.setSuperclass(type);
071        proxyFactory.setFilter(m -> "equals".equals(m.getName()));
072
073        Class<?> createClassType = proxyFactory.createClass();
074        @SuppressWarnings("unchecked")
075        final Optional<TypedGenerator<T>> constructorGenerator = ConstructorBasedGenerator
076                .getGeneratorForType((Class<T>) createClassType);
077        if (constructorGenerator.isPresent()) {
078            return Optional.of(new DynamicProxyGenerator<>(type, constructorGenerator.get()));
079        }
080        log.warn("Unable to determine generator for type " + type);
081        return Optional.empty();
082    }
083
084}