public Factory findFactory()

in johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java [119:269]


    public Factory findFactory(final Class<?> clazz, final Function<AnnotatedElement, String>... parameterNameExtractors) {
        Constructor<?> constructor = null;
        final boolean record = isRecord(clazz);
        if (record || Meta.getAnnotation(clazz, JohnzonRecord.class) != null) {
            constructor = findRecordConstructor(clazz);
        } else {
            for (final Constructor<?> c : clazz.getDeclaredConstructors()) {
                if (c.getParameterTypes().length == 0) {
                    if (!Modifier.isPublic(c.getModifiers()) && acceptHiddenConstructor) {
                        c.setAccessible(true);
                    }
                    constructor = c;
                    if (!useConstructor) {
                        break;
                    }
                } else if (c.getAnnotation(ConstructorProperties.class) != null) {
                    constructor = c;
                    break;
                }
            }
            if (constructor == null) {
                try {
                    constructor = clazz.getConstructor();
                } catch (final NoSuchMethodException e) {
                    return null; // readOnly class
                }
            }
        }

        final boolean constructorHasArguments = constructor != null && constructor.getGenericParameterTypes().length > 0;
        final Type[] factoryParameterTypes;
        final String[] constructorParameters;
        final Adapter<?, ?>[] constructorParameterConverters;
        final Adapter<?, ?>[] constructorItemParameterConverters;
        final ObjectConverter.Codec<?>[] objectConverters;
        if (constructorHasArguments) {
            factoryParameterTypes = constructor.getGenericParameterTypes();

            constructorParameters = new String[constructor.getGenericParameterTypes().length];

            final Constructor<?> fc = constructor;
            final String[] constructorProperties = ofNullable(constructor.getAnnotation(ConstructorProperties.class))
                    .map(ConstructorProperties::value)
                    .orElseGet(() -> {
                        if (record) {
                            return Stream.of(fc.getParameters())
                                    .map(p -> {
                                        try {
                                            if (parameterNameExtractors != null) {
                                                return Stream.of(parameterNameExtractors)
                                                        .map(fn -> fn.apply(p))
                                                        .filter(Objects::nonNull)
                                                        .findFirst()
                                                        .orElseGet(p::getName);
                                            }
                                            final JohnzonProperty property = Meta.getAnnotation(
                                                    clazz.getMethod(p.getName()), JohnzonProperty.class);
                                            return property != null ? property.value() : p.getName();
                                        } catch (final NoSuchMethodException e) {
                                            return p.getName();
                                        }
                                    })
                                    .toArray(String[]::new);
                        }
                        return Stream.of(fc.getParameters())
                                .map(p -> ofNullable(p.getAnnotation(JohnzonRecord.Name.class))
                                        .map(JohnzonRecord.Name::value)
                                        .orElseGet(p::getName))
                                .toArray(String[]::new);
                    });
            System.arraycopy(constructorProperties, 0, constructorParameters, 0, constructorParameters.length);

            constructorParameterConverters = new Adapter<?, ?>[constructor.getGenericParameterTypes().length];
            constructorItemParameterConverters = new Adapter<?, ?>[constructorParameterConverters.length];
            objectConverters = new ObjectConverter.Codec[constructorParameterConverters.length];
            for (int i = 0; i < constructorParameters.length; i++) {
                for (final Annotation a : constructor.getParameterAnnotations()[i]) {
                    if (a.annotationType() == JohnzonConverter.class) {
                        try {
                            MapperConverter mapperConverter = JohnzonConverter.class.cast(a).value().newInstance();
                            if (mapperConverter instanceof Converter) {
                                final Adapter<?, ?> converter = new ConverterAdapter((Converter) mapperConverter, constructor.getGenericParameterTypes()[i]);
                                if (matches(constructor.getParameterTypes()[i], converter)) {
                                    constructorParameterConverters[i] = converter;
                                    constructorItemParameterConverters[i] = null;
                                } else {
                                    constructorParameterConverters[i] = null;
                                    constructorItemParameterConverters[i] = converter;
                                }
                            } else {
                                objectConverters[i] = (ObjectConverter.Codec<?>) mapperConverter;
                            }
                        } catch (final Exception e) {
                            throw new IllegalArgumentException(e);
                        }
                    }
                }
            }
        } else {
            factoryParameterTypes = NO_PARAMS;
            constructorParameters = null;
            constructorParameterConverters = null;
            constructorItemParameterConverters = null;
            objectConverters = null;
        }

        final Constructor<?> cons = constructor;
        if (cons != null && !cons.isAccessible()) {
            cons.setAccessible(true);
        }
        return new Factory() {
            @Override
            public Object create(final Object[] params) {
                if (cons == null) {
                    throw new IllegalArgumentException(clazz.getName() + " can't be instantiated by Johnzon, this is a write only class");
                }
                try {
                    return params == null ? cons.newInstance() : cons.newInstance(params);
                } catch (final InstantiationException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                } catch (final InvocationTargetException e) {
                    throw new IllegalStateException(e.getCause());
                }
            }

            @Override
            public Type[] getParameterTypes() {
                return factoryParameterTypes;
            }

            @Override
            public String[] getParameterNames() {
                return constructorParameters;
            }

            @Override
            public Adapter<?, ?>[] getParameterConverter() {
                return constructorParameterConverters;
            }

            @Override
            public Adapter<?, ?>[] getParameterItemConverter() {
                return constructorItemParameterConverters;
            }

            @Override
            public ObjectConverter.Codec<?>[] getObjectConverter() {
                return objectConverters;
            }
        };
    }