public Object doUnmarshal()

in xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java [279:483]


    public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader,
            final UnmarshallingContext context) {
        final Class<?> resultType = result.getClass();
        @SuppressWarnings("serial")
        final Set<FastField> seenFields = new HashSet<FastField>() {
            @Override
            public boolean add(final FastField e) {
                if (!super.add(e)) {
                    throw new DuplicateFieldException(e.getName());
                }
                return true;
            }
        };

        // process attributes before recursing into child elements.
        final Iterator<String> it = reader.getAttributeNames();
        while (it.hasNext()) {
            final String attrAlias = it.next();
            // TODO: realMember should return FastField
            final String attrName = mapper.realMember(resultType, mapper.attributeForAlias(attrAlias));
            final Field field = reflectionProvider.getFieldOrNull(resultType, attrName);
            if (field != null && shouldUnmarshalField(field)) {
                final Class<?> classDefiningField = field.getDeclaringClass();
                if (!mapper.shouldSerializeMember(classDefiningField, attrName)) {
                    continue;
                }

                // we need a converter that produces a string representation only
                Class<?> type = field.getType();
                final SingleValueConverter converter = mapper.getConverterFromAttribute(classDefiningField, attrName,
                    type);
                if (converter != null) {
                    final Object value = converter.fromString(reader.getAttribute(attrAlias));
                    if (type.isPrimitive()) {
                        type = Primitives.box(type);
                    }
                    if (value != null && !type.isAssignableFrom(value.getClass())) {
                        final ConversionException exception = new ConversionException("Cannot convert type");
                        exception.add("source-type", value.getClass().getName());
                        exception.add("target-type", type.getName());
                        throw exception;
                    }
                    seenFields.add(new FastField(classDefiningField, attrName));
                    reflectionProvider.writeField(result, attrName, value, classDefiningField);
                }
            }
        }

        Map<FieldLocation, Collection<? super Object>> implicitCollectionsForCurrentObject = null;
        while (reader.hasMoreChildren()) {
            reader.moveDown();

            final String originalNodeName = reader.getNodeName();
            final Class<?> explicitDeclaringClass = readDeclaringClass(reader);
            final Class<?> fieldDeclaringClass = explicitDeclaringClass == null ? resultType : explicitDeclaringClass;
            final String fieldName = mapper.realMember(fieldDeclaringClass, originalNodeName);
            final Mapper.ImplicitCollectionMapping implicitCollectionMapping = mapper
                .getImplicitCollectionDefForFieldName(fieldDeclaringClass, fieldName);
            final Object value;
            String implicitFieldName = null;
            Field field = null;
            Class<?> type = null;
            if (implicitCollectionMapping == null) {
                // no item of an implicit collection for this name ... do we have a field?
                field = reflectionProvider.getFieldOrNull(fieldDeclaringClass, fieldName);
                if (field == null) {
                    // it is not a field ... do we have a field alias?
                    final Class<?> itemType = mapper.getItemTypeForItemFieldName(fieldDeclaringClass, fieldName);
                    if (itemType != null) {
                        final String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper);
                        if (classAttribute != null) {
                            type = mapper.realClass(classAttribute);
                        } else {
                            type = itemType;
                        }
                    } else {
                        // it is not an alias ... do we have an element of an implicit
                        // collection based on type only?
                        try {
                            type = mapper.realClass(originalNodeName);
                            implicitFieldName = mapper.getFieldNameForItemTypeAndName(fieldDeclaringClass, type,
                                originalNodeName);
                        } catch (final CannotResolveClassException e) {
                            // type stays null ...
                        }
                        if (type == null || type != null && implicitFieldName == null) {
                            // either not a type or element is a type alias, but does not
                            // belong to an implicit field
                            handleUnknownField(explicitDeclaringClass, fieldName, fieldDeclaringClass,
                                originalNodeName);

                            // element is unknown in declaring class, ignore it now
                            type = null;
                        }
                    }
                    if (type == null) {
                        // no type, no value
                        value = null;
                    } else {
                        if (Map.Entry.class.equals(type)) {
                            // it is an element of an implicit map with two elements now for
                            // key and value
                            reader.moveDown();
                            final Object key = context.convertAnother(result, HierarchicalStreams.readClassType(reader,
                                mapper));
                            reader.moveUp();
                            reader.moveDown();
                            final Object v = context.convertAnother(result, HierarchicalStreams.readClassType(reader,
                                mapper));
                            reader.moveUp();
                            value = Collections.singletonMap(key, v).entrySet().iterator().next();
                        } else {
                            // recurse info hierarchy
                            value = context.convertAnother(result, type);
                        }
                    }
                } else {
                    boolean fieldAlreadyChecked = false;

                    // we have a field, but do we have to address a hidden one?
                    if (explicitDeclaringClass == null) {
                        while (field != null
                            && !(fieldAlreadyChecked = shouldUnmarshalField(field)
                                && mapper.shouldSerializeMember(field.getDeclaringClass(), fieldName))) {
                            field = reflectionProvider.getFieldOrNull(field.getDeclaringClass().getSuperclass(),
                                fieldName);
                        }
                    }
                    if (field != null
                        && (fieldAlreadyChecked
                            || shouldUnmarshalField(field)
                                && mapper.shouldSerializeMember(field.getDeclaringClass(), fieldName))) {

                        final String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper);
                        if (classAttribute != null) {
                            type = mapper.realClass(classAttribute);
                        } else {
                            type = mapper.defaultImplementationOf(field.getType());
                        }
                        // TODO the reflection provider should already return the proper field
                        value = unmarshallField(context, result, type, field);
                        final Class<?> definedType = field.getType();
                        if (!definedType.isPrimitive()) {
                            type = definedType;
                        }
                    } else {
                        value = null;
                    }
                }
            } else {
                // we have an implicit collection with defined names
                implicitFieldName = implicitCollectionMapping.getFieldName();
                type = implicitCollectionMapping.getItemType();
                if (type == null) {
                    final String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper);
                    type = mapper.realClass(classAttribute != null ? classAttribute : originalNodeName);
                }
                value = context.convertAnother(result, type);
            }

            if (value != null && !type.isAssignableFrom(value.getClass())) {
                throw new ConversionException("Cannot convert type "
                    + value.getClass().getName()
                    + " to type "
                    + type.getName());
            }

            if (field != null) {
                reflectionProvider.writeField(result, fieldName, value, field.getDeclaringClass());
                seenFields.add(new FastField(field.getDeclaringClass(), fieldName));
            } else if (type != null) {
                if (implicitFieldName == null) {
                    // look for implicit field
                    implicitFieldName = mapper.getFieldNameForItemTypeAndName(fieldDeclaringClass, value != null
                        ? value.getClass()
                        : Mapper.Null.class, originalNodeName);
                }
                if (implicitCollectionsForCurrentObject == null) {
                    implicitCollectionsForCurrentObject = new HashMap<>();
                }
                writeValueToImplicitCollection(value, implicitCollectionsForCurrentObject, result, new FieldLocation(
                    implicitFieldName, fieldDeclaringClass));
            }

            reader.moveUp();
        }

        if (implicitCollectionsForCurrentObject != null) {
            for (final Map.Entry<FieldLocation, Collection<? super Object>> entry : implicitCollectionsForCurrentObject
                .entrySet()) {
                final Object value = entry.getValue();
                if (value instanceof ArraysList) {
                    final Object array = ((ArraysList)value).toPhysicalArray();
                    final FieldLocation fieldLocation = entry.getKey();
                    final Field field = reflectionProvider.getFieldOrNull(fieldLocation.definedIn,
                        fieldLocation.fieldName);
                    reflectionProvider.writeField(result, fieldLocation.fieldName, array, field != null
                        ? field.getDeclaringClass()
                        : null);
                }
            }
        }

        return result;
    }