public Factory findFactory()

in johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java [193:374]


    public Factory findFactory(final Class<?> clazz, final Function<AnnotatedElement, String>... parameterNameExtractors) {
        Constructor<?> constructor = null;
        Method factory = null;
        boolean invalidConstructorForDeserialization = false;
        for (final Constructor<?> c : supportsPrivateAccess ? clazz.getDeclaredConstructors() : clazz.getConstructors()) {
            if (c.isAnnotationPresent(JsonbCreator.class)) {
                if (constructor != null) {
                    throw new JsonbException("Only one constructor or method can have @JsonbCreator");
                }
                if (!c.isAccessible()) {
                    c.setAccessible(true);
                }
                constructor = c;
            }
        }
        for (final Method m : findPotentialFactoryMethods(clazz).collect(toList())) {
            final int modifiers = m.getModifiers();
            if ((!supportsPrivateAccess && !Modifier.isPublic(modifiers)) || !m.isAnnotationPresent(JsonbCreator.class)) {
                continue;
            }
            if (constructor != null || factory != null) {
                throw new JsonbException("Only one constructor or method can have @JsonbCreator");
            }
            if (!m.isAccessible()) {
                m.setAccessible(true);
            }
            factory = m;
        }
        final boolean record = isRecord(clazz) || Meta.getAnnotation(clazz, JohnzonRecord.class) != null;
        if (constructor == null && record) {
            constructor = findRecordConstructor(clazz).orElse(null);
        }
        if (constructor == null && factory == null) {
            invalidConstructorForDeserialization = Stream.of(clazz.getDeclaredConstructors())
                    .anyMatch(it -> it.getParameterCount() == 0 &&
                            !(Modifier.isPublic(it.getModifiers()) || Modifier.isProtected(it.getModifiers())));
        }
        final Constructor<?> finalConstructor = constructor;
        final Method finalFactory = factory;
        final Consumer<Object[]> factoryValidator = failOnMissingCreatorValues ?
                args -> {
                    if (args == null || Stream.of(args).anyMatch(Objects::isNull)) {
                        throw new JsonbException("Missing @JsonbCreator argument");
                    }
                } :
                args -> {
                };
        final Type[] types;
        final String[] params;
        final Adapter<?, ?>[] converters;
        final Adapter<?, ?>[] itemConverters;
        final ObjectConverter.Codec<?>[] objectConverters;
        if (finalConstructor != null || finalFactory != null) {
            types = finalConstructor != null ? finalConstructor.getGenericParameterTypes() : finalFactory.getGenericParameterTypes();
            params = new String[types.length];
            converters = new Adapter<?, ?>[types.length];
            itemConverters = new Adapter<?, ?>[types.length];
            objectConverters = new ObjectConverter.Codec<?>[types.length];

            int i = 0;
            for (final Parameter parameter : (finalConstructor == null ? finalFactory : finalConstructor).getParameters()) {
                final JsonbProperty property = getAnnotation(parameter, JsonbProperty.class);
                params[i] = property != null && !property.value().isEmpty() ?
                        property.value() :
                        (record ?
                                ofNullable(parameter.getAnnotation(JohnzonRecord.Name.class))
                                        .map(JohnzonRecord.Name::value)
                                        .orElseGet(() -> naming.translateName(parameter.getName())) :
                                naming.translateName(parameter.getName()));

                final JsonbTypeAdapter adapter = getAnnotation(parameter, JsonbTypeAdapter.class);
                final JsonbDateFormat dateFormat = getAnnotation(parameter, JsonbDateFormat.class);
                final JsonbNumberFormat numberFormat = getAnnotation(parameter, JsonbNumberFormat.class);
                final JohnzonConverter johnzonConverter = getAnnotation(parameter, JohnzonConverter.class);
                final JsonbTypeDeserializer deserializer = getAnnotation(parameter, JsonbTypeDeserializer.class);
                if (adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null && deserializer == null) {
                    converters[i] = defaultConverters.get(parameter.getType());
                    itemConverters[i] = null;
                } else {
                    validateAnnotations(parameter, adapter, dateFormat, numberFormat, johnzonConverter);

                    try {
                        if (adapter != null || dateFormat != null || numberFormat != null) {
                            final Adapter converter = toConverter(
                                    this.types, parameter.getType(), adapter, dateFormat, numberFormat);
                            if (matches(parameter.getParameterizedType(), converter)) {
                                converters[i] = converter;
                                itemConverters[i] = null;
                            } else {
                                converters[i] = null;
                                itemConverters[i] = converter;
                            }
                        } else if (johnzonConverter != null) {
                            objectConverters[i] = (ObjectConverter.Codec<?>) johnzonConverter.value().newInstance();

                        } else if (deserializer != null) {
                            final Class<? extends JsonbDeserializer> value = deserializer.value();
                            final JohnzonAdapterFactory.Instance<? extends JsonbDeserializer> instance = newInstance(value);
                            final ParameterizedType pt = this.types.findParameterizedType(value, JsonbDeserializer.class);
                            final Class<?> mappedType = this.types.findParamType(pt, JsonbDeserializer.class);
                            toRelease.add(instance);
                            final JsonBuilderFactory builderFactoryInstance = builderFactory.get();
                            final Type[] arguments = this.types.findParameterizedType(value, JsonbDeserializer.class).getActualTypeArguments();
                            final boolean global = arguments.length == 1 && arguments[0] != null && arguments[0].equals(parameter.getType());
                            objectConverters[i] = new ObjectConverter.Codec() {
                                private final ConcurrentMap<Type, BiFunction<JsonValue, MappingParser, Object>> impl =
                                    new ConcurrentHashMap<>();
                                @Override
                                public Object fromJson(final JsonValue value, final Type targetType, final MappingParser parser) {
                                    final JsonbDeserializer jsonbDeserializer = instance.getValue();
                                    if (global || targetType == mappedType) { // fast test and matches most cases
                                        return mapItem(value, targetType, parser, jsonbDeserializer);
                                    }

                                    BiFunction<JsonValue, MappingParser, Object> fn = impl.get(targetType);
                                    if (fn == null) {
                                        if (value.getValueType() == JsonValue.ValueType.ARRAY) {
                                            if (ParameterizedType.class.isInstance(targetType)) {
                                                final ParameterizedType parameterizedType = ParameterizedType.class.cast(targetType);
                                                final Class<?> paramType = JsonbAccessMode.this.types.findParamType(parameterizedType, Collection.class);
                                                if (paramType != null && (mappedType == null /*Object*/ || mappedType.isAssignableFrom(paramType))) {
                                                    final Collector<Object, ?, ? extends Collection<Object>> collector =
                                                        Set.class.isAssignableFrom(
                                                            JsonbAccessMode.this.types.asClass(parameterizedType.getRawType())) ? toSet() : toList();
                                                    fn = (json, mp) -> json.asJsonArray().stream()
                                                                           .map(i -> mapItem(i, paramType, mp, jsonbDeserializer))
                                                                           .collect(collector);
                                                }
                                            }
                                        }
                                        if (fn == null) {
                                            fn = (json, mp) -> mapItem(json, targetType, mp, jsonbDeserializer);
                                        }
                                        impl.putIfAbsent(targetType, fn);
                                    }
                                    return fn.apply(value, parser);
                                }

                                private Object mapItem(final JsonValue jsonValue, final Type targetType,
                                                       final MappingParser parser, final JsonbDeserializer jsonbDeserializer) {
                                    return jsonbDeserializer.deserialize(
                                        JsonValueParserAdapter.createFor(jsonValue, parserFactory),
                                        new JohnzonDeserializationContext(parser, builderFactoryInstance, jsonProvider),
                                        targetType);
                                }

                                @Override
                                public void writeJson(final Object instance, final MappingGenerator jsonbGenerator) {
                                    // no-op, it's for factories only
                                }
                            };
                        }
                    } catch (final InstantiationException | IllegalAccessException e) {
                        throw new IllegalArgumentException(e);
                    }

                }
                i++;
            }
        } else {
            types = null;
            params = null;
            converters = null;
            itemConverters = null;
            objectConverters = null;
        }

        if (constructor == null && factory == null && !invalidConstructorForDeserialization) {
            final Stream<Function<AnnotatedElement, String>> jsonbFn = Stream.of(this::getJsonbProperty);
            final Factory delegateFactory = delegate.findFactory(
                    clazz,
                    (parameterNameExtractors == null ?
                            jsonbFn : Stream.concat(jsonbFn, Stream.of(parameterNameExtractors))).toArray(Function[]::new));
            return delegateFactory;
        }
        if (constructor != null || invalidConstructorForDeserialization) {
            return constructorFactory(finalConstructor, invalidConstructorForDeserialization ? (Consumer<Object[]>) objects -> {
                throw new JsonbException("No available constructor");
            } : factoryValidator, types, params, converters, itemConverters, objectConverters);
        }
        return methodFactory(clazz, finalFactory, factoryValidator, types, params, converters, itemConverters, objectConverters);
    }