public void add(
     Class definedIn,
     String fieldName,
     String itemFieldName,
     Class itemType,
     String keyFieldName) {
   Field field = null;
   Class declaredIn = definedIn;
   while (declaredIn != Object.class) {
     try {
       field = declaredIn.getDeclaredField(fieldName);
       break;
     } catch (SecurityException e) {
       throw new InitializationException("Access denied for field with implicit collection", e);
     } catch (NoSuchFieldException e) {
       declaredIn = declaredIn.getSuperclass();
     }
   }
   if (field == null) {
     throw new InitializationException("No field \"" + fieldName + "\" for implicit collection");
   } else if (Map.class.isAssignableFrom(field.getType())) {
     if (itemFieldName == null && keyFieldName == null) {
       itemType = Map.Entry.class;
     }
   } else if (!Collection.class.isAssignableFrom(field.getType())) {
     Class fieldType = field.getType();
     if (!fieldType.isArray()) {
       throw new InitializationException(
           "Field \"" + fieldName + "\" declares no collection or array");
     } else {
       Class componentType = fieldType.getComponentType();
       componentType = componentType.isPrimitive() ? Primitives.box(componentType) : componentType;
       if (itemType == null) {
         itemType = componentType;
       } else {
         itemType = itemType.isPrimitive() ? Primitives.box(itemType) : itemType;
         if (!componentType.isAssignableFrom(itemType)) {
           throw new InitializationException(
               "Field \""
                   + fieldName
                   + "\" declares an array, but the array type is not compatible with "
                   + itemType.getName());
         }
       }
     }
   }
   ImplicitCollectionMapperForClass mapper = getOrCreateMapper(definedIn);
   mapper.add(new ImplicitCollectionMappingImpl(fieldName, itemType, itemFieldName, keyFieldName));
 }
  public Object doUnmarshal(
      final Object result,
      final HierarchicalStreamReader reader,
      final UnmarshallingContext context) {
    final SeenFields seenFields = new SeenFields();
    Iterator it = reader.getAttributeNames();
    // Remember outermost Saveable encountered, for reporting below
    if (result instanceof Saveable && context.get("Saveable") == null)
      context.put("Saveable", result);

    // Process attributes before recursing into child elements.
    while (it.hasNext()) {
      String attrAlias = (String) it.next();
      String attrName = mapper.attributeForAlias(attrAlias);
      Class classDefiningField = determineWhichClassDefinesField(reader);
      boolean fieldExistsInClass = fieldDefinedInClass(result, attrName);
      if (fieldExistsInClass) {
        Field field = reflectionProvider.getField(result.getClass(), attrName);
        SingleValueConverter converter =
            mapper.getConverterFromAttribute(field.getDeclaringClass(), attrName, field.getType());
        Class type = field.getType();
        if (converter == null) {
          converter = mapper.getConverterFromItemType(type);
        }
        if (converter != null) {
          Object value = converter.fromString(reader.getAttribute(attrAlias));
          if (type.isPrimitive()) {
            type = Primitives.box(type);
          }
          if (value != null && !type.isAssignableFrom(value.getClass())) {
            throw new ConversionException(
                "Cannot convert type " + value.getClass().getName() + " to type " + type.getName());
          }
          reflectionProvider.writeField(result, attrName, value, classDefiningField);
          seenFields.add(classDefiningField, attrName);
        }
      }
    }

    Map implicitCollectionsForCurrentObject = null;
    while (reader.hasMoreChildren()) {
      reader.moveDown();

      try {
        String fieldName = mapper.realMember(result.getClass(), reader.getNodeName());
        boolean implicitCollectionHasSameName =
            mapper.getImplicitCollectionDefForFieldName(result.getClass(), reader.getNodeName())
                != null;

        Class classDefiningField = determineWhichClassDefinesField(reader);
        boolean fieldExistsInClass =
            !implicitCollectionHasSameName && fieldDefinedInClass(result, fieldName);

        Class type =
            determineType(reader, fieldExistsInClass, result, fieldName, classDefiningField);
        final Object value;
        if (fieldExistsInClass) {
          Field field = reflectionProvider.getField(result.getClass(), fieldName);
          value = unmarshalField(context, result, type, field);
          // TODO the reflection provider should have returned the proper field in first place ....
          Class definedType =
              reflectionProvider.getFieldType(result, fieldName, classDefiningField);
          if (!definedType.isPrimitive()) {
            type = definedType;
          }
        } else {
          value = context.convertAnother(result, type);
        }

        if (value != null && !type.isAssignableFrom(value.getClass())) {
          LOGGER.warning(
              "Cannot convert type " + value.getClass().getName() + " to type " + type.getName());
          // behave as if we didn't see this element
        } else {
          if (fieldExistsInClass) {
            reflectionProvider.writeField(result, fieldName, value, classDefiningField);
            seenFields.add(classDefiningField, fieldName);
          } else {
            implicitCollectionsForCurrentObject =
                writeValueToImplicitCollection(
                    context, value, implicitCollectionsForCurrentObject, result, fieldName);
          }
        }
      } catch (NonExistentFieldException e) {
        LOGGER.log(WARNING, "Skipping a non-existent field " + e.getFieldName(), e);
        addErrorInContext(context, e);
      } catch (CannotResolveClassException e) {
        LOGGER.log(WARNING, "Skipping a non-existent type", e);
        addErrorInContext(context, e);
      } catch (LinkageError e) {
        LOGGER.log(WARNING, "Failed to resolve a type", e);
        addErrorInContext(context, e);
      }

      reader.moveUp();
    }

    // Report any class/field errors in Saveable objects
    if (context.get("ReadError") != null && context.get("Saveable") == result) {
      OldDataMonitor.report((Saveable) result, (ArrayList<Throwable>) context.get("ReadError"));
      context.put("ReadError", null);
    }
    return result;
  }