private static boolean parseElementInternal()

in google-http-client-xml/src/main/java/com/google/api/client/xml/Xml.java [230:511]


  private static boolean parseElementInternal(XmlPullParser parser,
      ArrayList<Type> context,
      Object destination,
      Type valueType,
      XmlNamespaceDictionary namespaceDictionary,
      CustomizeParser customizeParser) throws IOException, XmlPullParserException {
    // TODO(yanivi): method is too long; needs to be broken down into smaller methods and comment
    // better
    GenericXml genericXml = destination instanceof GenericXml ? (GenericXml) destination : null;
    @SuppressWarnings("unchecked")
    Map<String, Object> destinationMap =
        genericXml == null && destination instanceof Map<?, ?> ? Map.class.cast(destination) : null;
    ClassInfo classInfo =
        destinationMap != null || destination == null ? null : ClassInfo.of(destination.getClass());
    if (parser.getEventType() == XmlPullParser.START_DOCUMENT) {
      parser.next();
    }
    parseNamespacesForElement(parser, namespaceDictionary);
    // generic XML
    if (genericXml != null) {
      genericXml.namespaceDictionary = namespaceDictionary;
      String name = parser.getName();
      String namespace = parser.getNamespace();
      String alias = namespaceDictionary.getNamespaceAliasForUriErrorOnUnknown(namespace);
      genericXml.name = alias.length() == 0 ? name : alias + ":" + name;
    }
    // attributes
    if (destination != null) {
      int attributeCount = parser.getAttributeCount();
      for (int i = 0; i < attributeCount; i++) {
        // TODO(yanivi): can have repeating attribute values, e.g. "@a=value1 @a=value2"?
        String attributeName = parser.getAttributeName(i);
        String attributeNamespace = parser.getAttributeNamespace(i);
        String attributeAlias = attributeNamespace.length() == 0
            ? "" : namespaceDictionary.getNamespaceAliasForUriErrorOnUnknown(attributeNamespace);
        String fieldName = getFieldName(true, attributeAlias, attributeNamespace, attributeName);
        Field field = classInfo == null ? null : classInfo.getField(fieldName);
        parseAttributeOrTextContent(parser.getAttributeValue(i),
            field,
            valueType,
            context,
            destination,
            genericXml,
            destinationMap,
            fieldName);
      }
    }
    Field field;
    ArrayValueMap arrayValueMap = new ArrayValueMap(destination);
    boolean isStopped = false;
    // TODO(yanivi): support Void type as "ignore" element/attribute
    main: while (true) {
      int event = parser.next();
      switch (event) {
        case XmlPullParser.END_DOCUMENT:
          isStopped = true;
          break main;
        case XmlPullParser.END_TAG:
          isStopped = customizeParser != null
              && customizeParser.stopAfterEndTag(parser.getNamespace(), parser.getName());
          break main;
        case XmlPullParser.TEXT:
          // parse text content
          if (destination != null) {
            field = classInfo == null ? null : classInfo.getField(TEXT_CONTENT);
            parseAttributeOrTextContent(parser.getText(),
                field,
                valueType,
                context,
                destination,
                genericXml,
                destinationMap,
                TEXT_CONTENT);
          }
          break;
        case XmlPullParser.START_TAG:
          if (customizeParser != null
              && customizeParser.stopBeforeStartTag(parser.getNamespace(), parser.getName())) {
            isStopped = true;
            break main;
          }
          if (destination == null) {
            parseTextContentForElement(parser, context, true, null);
          } else {
            // element
            parseNamespacesForElement(parser, namespaceDictionary);
            String namespace = parser.getNamespace();
            String alias = namespaceDictionary.getNamespaceAliasForUriErrorOnUnknown(namespace);
            String fieldName = getFieldName(false, alias, namespace, parser.getName());
            field = classInfo == null ? null : classInfo.getField(fieldName);
            Type fieldType = field == null ? valueType : field.getGenericType();
            fieldType = Data.resolveWildcardTypeOrTypeVariable(context, fieldType);
            // field type is now class, parameterized type, or generic array type
            // resolve a parameterized type to a class
            Class<?> fieldClass = fieldType instanceof Class<?> ? (Class<?>) fieldType : null;
            if (fieldType instanceof ParameterizedType) {
              fieldClass = Types.getRawClass((ParameterizedType) fieldType);
            }
            boolean isArray = Types.isArray(fieldType);
            // text content
            boolean ignore = field == null && destinationMap == null && genericXml == null;
            if (ignore || Data.isPrimitive(fieldType)) {
              int level = 1;
              while (level != 0) {
                switch (parser.next()) {
                  case XmlPullParser.END_DOCUMENT:
                    isStopped = true;
                    break main;
                  case XmlPullParser.START_TAG:
                    level++;
                    break;
                  case XmlPullParser.END_TAG:
                    level--;
                    break;
                  case XmlPullParser.TEXT:
                    if (!ignore && level == 1) {
                      parseAttributeOrTextContent(parser.getText(),
                          field,
                          valueType,
                          context,
                          destination,
                          genericXml,
                          destinationMap,
                          fieldName);
                    }
                    break;
                  default:
                    break;
                }
              }
            } else if (fieldType == null || fieldClass != null
                && Types.isAssignableToOrFrom(fieldClass, Map.class)) {
              // store the element as a map
              Map<String, Object> mapValue = Data.newMapInstance(fieldClass);
              int contextSize = context.size();
              if (fieldType != null) {
                context.add(fieldType);
              }
              Type subValueType = fieldType != null && Map.class.isAssignableFrom(fieldClass)
                  ? Types.getMapValueParameter(fieldType) : null;
              subValueType = Data.resolveWildcardTypeOrTypeVariable(context, subValueType);
              isStopped = parseElementInternal(parser,
                  context,
                  mapValue,
                  subValueType,
                  namespaceDictionary,
                  customizeParser);
              if (fieldType != null) {
                context.remove(contextSize);
              }
              if (destinationMap != null) {
                // map but not GenericXml: store as ArrayList of elements
                @SuppressWarnings("unchecked")
                Collection<Object> list = (Collection<Object>) destinationMap.get(fieldName);
                if (list == null) {
                  list = new ArrayList<Object>(1);
                  destinationMap.put(fieldName, list);
                }
                list.add(mapValue);
              } else if (field != null) {
                // not a map: store in field value
                FieldInfo fieldInfo = FieldInfo.of(field);
                if (fieldClass == Object.class) {
                  // field is an Object: store as ArrayList of element maps
                  @SuppressWarnings("unchecked")
                  Collection<Object> list = (Collection<Object>) fieldInfo.getValue(destination);
                  if (list == null) {
                    list = new ArrayList<Object>(1);
                    fieldInfo.setValue(destination, list);
                  }
                  list.add(mapValue);
                } else {
                  // field is a Map: store as a single element map
                  fieldInfo.setValue(destination, mapValue);
                }
              } else {
                // GenericXml: store as ArrayList of elements
                GenericXml atom = (GenericXml) destination;
                @SuppressWarnings("unchecked")
                Collection<Object> list = (Collection<Object>) atom.get(fieldName);
                if (list == null) {
                  list = new ArrayList<Object>(1);
                  atom.set(fieldName, list);
                }
                list.add(mapValue);
              }
            } else if (isArray || Types.isAssignableToOrFrom(fieldClass, Collection.class)) {
              // TODO(yanivi): some duplicate code here; isolate into reusable methods
              FieldInfo fieldInfo = FieldInfo.of(field);
              Object elementValue = null;
              Type subFieldType = isArray
                  ? Types.getArrayComponentType(fieldType) : Types.getIterableParameter(fieldType);
              Class<?> rawArrayComponentType =
                  Types.getRawArrayComponentType(context, subFieldType);
              subFieldType = Data.resolveWildcardTypeOrTypeVariable(context, subFieldType);
              Class<?> subFieldClass =
                  subFieldType instanceof Class<?> ? (Class<?>) subFieldType : null;
              if (subFieldType instanceof ParameterizedType) {
                subFieldClass = Types.getRawClass((ParameterizedType) subFieldType);
              }
              if (Data.isPrimitive(subFieldType)) {
                elementValue = parseTextContentForElement(parser, context, false, subFieldType);
              } else if (subFieldType == null || subFieldClass != null
                  && Types.isAssignableToOrFrom(subFieldClass, Map.class)) {
                elementValue = Data.newMapInstance(subFieldClass);
                int contextSize = context.size();
                if (subFieldType != null) {
                  context.add(subFieldType);
                }
                Type subValueType = subFieldType != null
                    && Map.class.isAssignableFrom(subFieldClass) ? Types.getMapValueParameter(
                    subFieldType) : null;
                subValueType = Data.resolveWildcardTypeOrTypeVariable(context, subValueType);
                isStopped = parseElementInternal(parser,
                    context,
                    elementValue,
                    subValueType,
                    namespaceDictionary,
                    customizeParser);
                if (subFieldType != null) {
                  context.remove(contextSize);
                }
              } else {
                elementValue = Types.newInstance(rawArrayComponentType);
                int contextSize = context.size();
                context.add(fieldType);
                isStopped = parseElementInternal(parser,
                    context,
                    elementValue,
                    null,
                    namespaceDictionary,
                    customizeParser);
                context.remove(contextSize);
              }
              if (isArray) {
                // array field: add new element to array value map
                if (field == null) {
                  arrayValueMap.put(fieldName, rawArrayComponentType, elementValue);
                } else {
                  arrayValueMap.put(field, rawArrayComponentType, elementValue);
                }
              } else {
                // collection: add new element to collection
                @SuppressWarnings("unchecked")
                Collection<Object> collectionValue = (Collection<Object>) (field == null
                    ? destinationMap.get(fieldName) : fieldInfo.getValue(destination));
                if (collectionValue == null) {
                  collectionValue = Data.newCollectionInstance(fieldType);
                  setValue(collectionValue,
                      field,
                      destination,
                      genericXml,
                      destinationMap,
                      fieldName);
                }
                collectionValue.add(elementValue);
              }
            } else {
              // not an array/iterable or a map, but we do have a field
              Object value = Types.newInstance(fieldClass);
              int contextSize = context.size();
              context.add(fieldType);
              isStopped = parseElementInternal(parser,
                  context,
                  value,
                  null,
                  namespaceDictionary,
                  customizeParser);
              context.remove(contextSize);
              setValue(value, field, destination, genericXml, destinationMap, fieldName);
            }
          }
          if (isStopped || parser.getEventType() == XmlPullParser.END_DOCUMENT) {
            isStopped = true;
            break main;
          }
          break;
      }
    }
    arrayValueMap.setValues();
    return isStopped;
  }