private Object buildObject()

in johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java [233:475]


    private Object buildObject(final Type inType, final JsonObject object, final boolean applyObjectConverter,
                               final JsonPointerTracker jsonPointer, final Collection<Class<?>> skippedConverters) {
        final Type type = inType == Object.class ? new JohnzonParameterizedType(Map.class, String.class, Object.class) : inType;
        if (applyObjectConverter && !(type instanceof ParameterizedType)) {
            if (!(type instanceof Class)) {
                throw new MapperException("ObjectConverters are only supported for Classes not Types");
            }

            final Class clazz = (Class) type;
            if (skippedConverters == null || !skippedConverters.contains(clazz)) {
                ObjectConverter.Reader objectConverter = config.findObjectConverterReader(clazz);
                if (objectConverter != null) {
                    final Collection<Class<?>> skipped = skippedConverters == null ? new ArrayList<>() : skippedConverters;
                    skipped.add(clazz);
                    return objectConverter.fromJson(
                            object, type,
                            new SuppressConversionMappingParser(this, object, skipped));
                }
            }
        }

        final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(type);
        if (classMapping != null && classMapping.polymorphicDeserializedTypeResolver != null && inType instanceof Class) {
            Class<?> nestedType = classMapping.polymorphicDeserializedTypeResolver.apply(object, (Class<?>) inType);
            if (nestedType != null && nestedType != inType) {
                return buildObject(nestedType, object, applyObjectConverter, jsonPointer, skippedConverters);
            }
        }

        if (classMapping == null) {
            if (ParameterizedType.class.isInstance(type)) {
                final ParameterizedType aType = ParameterizedType.class.cast(type);
                final Type[] fieldArgTypes = aType.getActualTypeArguments();
                if (fieldArgTypes.length >= 2) {
                    final Class<?> raw = Class.class.cast(aType.getRawType());
                    final Map map;
                    if (SortedMap.class.isAssignableFrom(raw) || NavigableMap.class == raw || TreeMap.class == raw) {
                        map = config.getAttributeOrder() == null ? new TreeMap() : new TreeMap(config.getAttributeOrder());
                    } else if (ConcurrentMap.class.isAssignableFrom(raw)) {
                        map = new ConcurrentHashMap(object.size());
                    } else if (EnumMap.class.isAssignableFrom(raw)) {
                        if (!config.isSupportEnumContainerDeserialization()) {
                            throw new MapperException("JSON-B forbids EnumMap deserialization, " +
                                    "set supportEnumMapDeserialization=true to disable that arbitrary limitation");
                        }
                        map = new EnumMap(Class.class.cast(fieldArgTypes[0]));
                    } else if (Map.class.isAssignableFrom(raw)) {
                        map = new LinkedHashMap(object.size()); // todo: configurable from config.getNewDefaultMap()?
                    } else {
                        map = null;
                    }

                    if (map != null) {
                        final Type keyType = fieldArgTypes[0];
                        final boolean any = fieldArgTypes.length < 2 || fieldArgTypes[1] == Object.class;
                        for (final Map.Entry<String, JsonValue> value : object.entrySet()) {
                            final JsonValue jsonValue = value.getValue();
                            if (JsonNumber.class.isInstance(jsonValue) && any) {
                                map.put(value.getKey(), config.isUseBigDecimalForObjectNumbers() ?
                                        JsonNumber.class.cast(jsonValue).bigDecimalValue() : toNumberValue(JsonNumber.class.cast(jsonValue)));
                            } else if (JsonString.class.isInstance(jsonValue) && any) {
                                map.put(value.getKey(), JsonString.class.cast(jsonValue).getString());
                            } else {
                                map.put(convertTo(keyType, value.getKey()), toObject(null, jsonValue, fieldArgTypes[1], null, jsonPointer, Object.class));
                            }
                        }
                        return map;
                    }
                }
            } else if (Map.class == type || HashMap.class == type || LinkedHashMap.class == type) {
                final Map<String, Object> map = new LinkedHashMap<String, Object>();
                for (final Map.Entry<String, JsonValue> value : object.entrySet()) {
                    map.put(value.getKey(), toObject(null, value.getValue(), Object.class, null, jsonPointer, Object.class));
                }
                return map;
            }
        }
        if (classMapping == null) {
            final String snippet = config.getSnippet().of(object);
            final String description = ExceptionMessages.description(object);
            throw new MapperException("Unable to map " + description + " to " + type + ": " + snippet);
        }

        if (applyObjectConverter && classMapping.reader != null && (skippedConverters == null || !skippedConverters.contains(type))) {
            final Collection<Class<?>> skipped = skippedConverters == null ? new ArrayList<>() : skippedConverters;
            if (Class.class.isInstance(type)) { // more than likely, drop this check?
                skipped.add(Class.class.cast(type));
            }
            return classMapping.reader.fromJson(object, type, new SuppressConversionMappingParser(this, object, skipped));
        }
        /* doesn't work yet
        if (classMapping.adapter != null) {
            return classMapping.adapter.from(t);
        }
        */

        if (classMapping.factory == null) {
            throw new MissingFactoryException(classMapping.clazz, object, config.getSnippet().of(object));
        }
        if (config.isFailOnUnknown()) {
            if (!classMapping.setters.keySet().containsAll(object.keySet())) {
                throw new MapperException("(fail on unknown properties): " +
                        object.keySet().stream().filter(it -> !classMapping.setters.containsKey(it)).collect(joining(", ", "[", "]")));
            }
        }

        Object t;
        try {
            if (classMapping.factory.getParameterTypes() == null || classMapping.factory.getParameterTypes().length == 0) {
                t = classMapping.factory.create(null);
            } else {
                t = classMapping.factory.create(createParameters(classMapping, object, jsonPointer, e -> {
                    if (FactoryCreateException.class.isInstance(e)) {
                        throw FactoryCreateException.class.cast(e);
                    }
                    throw new FactoryCreateException(type, object, config.getSnippet().of(object), e);
                }));
            }
        } catch (final FactoryCreateException e){
            throw e;
        } catch (final Exception e) {
            throw new FactoryCreateException(type, object, config.getSnippet().of(object), e);
        }

        // store the new object under it's jsonPointer in case it gets referenced later
        if (jsonPointers == null) {
            if (classMapping.deduplicateObjects || config.isDeduplicateObjects()) {
                jsonPointers = new HashMap<>();
                jsonPointers.put(jsonPointer == null ? "/" : jsonPointer.toString(), t);
            } else {
                jsonPointers = Collections.emptyMap();
            }
        } else if (isDedup()) {
            jsonPointers.put(jsonPointer == null ? "/" : jsonPointer.toString(), t);
        }

        for (final Map.Entry<String, JsonValue> jsonEntry : object.entrySet()) {
            final Mappings.Setter value = classMapping.setters.get(jsonEntry.getKey());
            if (value == null) {
                continue;
            }

            final JsonValue jsonValue = jsonEntry.getValue();
            final JsonValue.ValueType valueType = jsonValue != null ? jsonValue.getValueType() : null;
            try {
                if (JsonValue.class == value.paramType) {
                    value.writer.write(t, jsonValue);
                    continue;
                }
                if (jsonValue == null) {
                    continue;
                }

                final AccessMode.Writer setterMethod = value.writer;
                if (NULL == valueType) { // forced
                    setterMethod.write(t, null);
                } else {
                    Object existingInstance = null;
                    if (config.isReadAttributeBeforeWrite()) {
                        final Mappings.Getter getter = classMapping.getters.get(jsonEntry.getKey());
                        if (getter != null) {
                            try {
                                existingInstance = getter.reader.read(t);
                            } catch (final RuntimeException re) {
                                // backward compatibility
                            }
                        }
                    }
                    final Object convertedValue = toValue(
                            existingInstance, jsonValue, value.converter, value.itemConverter,
                            value.paramType, value.objectConverter,
                            isDedup() ? new JsonPointerTracker(jsonPointer, jsonEntry.getKey()) : null, inType,
                            e -> {
                                if (SetterMappingException.class.isInstance(e)) {
                                    throw SetterMappingException.class.cast(e);
                                }
                                final String snippet = config.getSnippet().of(jsonValue);
                                throw new SetterMappingException(
                                        classMapping.clazz, jsonEntry.getKey(), value.writer.getType(), valueType, snippet, e);
                            });
                    if (convertedValue != null) {
                        setterMethod.write(t, convertedValue);
                    }
                }
            } catch (final SetterMappingException alreadyHandled) {
                throw alreadyHandled;
            } catch (final Exception e) {
                final String snippet = jsonValue == null? "null": config.getSnippet().of(jsonValue);
                throw new SetterMappingException(classMapping.clazz, jsonEntry.getKey(), value.writer.getType(), valueType, snippet, e);
            }
        }
        if (classMapping.anySetter != null) {
            for (final Map.Entry<String, JsonValue> entry : object.entrySet()) {
                final String key = entry.getKey();
                if (!classMapping.setters.containsKey(key)) {
                    try {
                        classMapping.anySetter.invoke(t, key,
                                toValue(null, entry.getValue(), null, null,
                                        classMapping.anySetter.getGenericParameterTypes()[1], null,
                                        isDedup() ? new JsonPointerTracker(jsonPointer, entry.getKey()) : null, type,
                                        MapperException::new));
                    } catch (final IllegalAccessException e) {
                        throw new IllegalStateException(e);
                    } catch (final InvocationTargetException e) {
                        throw new MapperException(e.getCause());
                    }
                }
            }
        } else if (classMapping.anyField != null) {
            try {
                classMapping.anyField.set(t, object.entrySet().stream()
                    .filter(it -> !classMapping.setters.containsKey(it.getKey()))
                    .collect(toMap(Map.Entry::getKey, e -> toValue(null, e.getValue(), null, null,
                            ParameterizedType.class.cast(classMapping.anyField.getGenericType()).getActualTypeArguments()[1], null,
                            isDedup() ? new JsonPointerTracker(jsonPointer, e.getKey()) : null, type,
                            MapperException::new))));
            } catch (final IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
        if (classMapping.mapAdder != null) {
            object.entrySet().stream()
                .filter(it -> !classMapping.setters.containsKey(it.getKey()))
                .filter(it -> it.getValue().getValueType() != NULL)
                .forEach(e -> {
                    final Object convertedValue = toValue(
                            null, e.getValue(), null, null,
                            classMapping.mapAdderType, null,
                            new JsonPointerTracker(jsonPointer, e.getKey()), inType,
                            MapperException::new);
                    if (convertedValue != null) {
                        try {
                            classMapping.mapAdder.invoke(t, e.getKey(), convertedValue);
                        } catch (final IllegalAccessException ex) {
                            throw new IllegalStateException(ex);
                        } catch (final InvocationTargetException ex) {
                            throw new MapperException(ex.getCause());
                        }
                    }
                });
        }
        return t;
    }