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);
}