private final Object parseValue()

in google-http-client/src/main/java/com/google/api/client/json/JsonParser.java [697:882]


  private final Object parseValue(Field fieldContext,
      Type valueType,
      ArrayList<Type> context,
      Object destination,
      CustomizeJsonParser customizeParser,
      boolean handlePolymorphic) throws IOException {

    valueType = Data.resolveWildcardTypeOrTypeVariable(context, valueType);
    // resolve a parameterized type to a class
    Class<?> valueClass = valueType instanceof Class<?> ? (Class<?>) valueType : null;
    if (valueType instanceof ParameterizedType) {
      valueClass = Types.getRawClass((ParameterizedType) valueType);
    }
    // Void means skip
    if (valueClass == Void.class) {
      skipChildren();
      return null;
    }
    // value type is now null, class, parameterized type, or generic array type
    JsonToken token = getCurrentToken();
    try {
      switch (getCurrentToken()) {
        case START_ARRAY:
        case END_ARRAY:
          boolean isArray = Types.isArray(valueType);
          Preconditions.checkArgument(valueType == null || isArray || valueClass != null
              && Types.isAssignableToOrFrom(valueClass, Collection.class),
              "expected collection or array type but got %s", valueType);
          Collection<Object> collectionValue = null;
          if (customizeParser != null && fieldContext != null) {
            collectionValue = customizeParser.newInstanceForArray(destination, fieldContext);
          }
          if (collectionValue == null) {
            collectionValue = Data.newCollectionInstance(valueType);
          }
          Type subType = null;
          if (isArray) {
            subType = Types.getArrayComponentType(valueType);
          } else if (valueClass != null && Iterable.class.isAssignableFrom(valueClass)) {
            subType = Types.getIterableParameter(valueType);
          }
          subType = Data.resolveWildcardTypeOrTypeVariable(context, subType);
          parseArray(fieldContext, collectionValue, subType, context, customizeParser);
          if (isArray) {
            return Types.toArray(collectionValue, Types.getRawArrayComponentType(context, subType));
          }
          return collectionValue;
        case FIELD_NAME:
        case START_OBJECT:
        case END_OBJECT:
          Preconditions.checkArgument(
              !Types.isArray(valueType), "expected object or map type but got %s", valueType);
          // Check if we're parsing into a polymorphic datatype.
          Field typemapField = handlePolymorphic ? getCachedTypemapFieldFor(valueClass) : null;
          Object newInstance = null;
          if (valueClass != null && customizeParser != null) {
            newInstance = customizeParser.newInstanceForObject(destination, valueClass);
          }
          boolean isMap = valueClass != null && Types.isAssignableToOrFrom(valueClass, Map.class);
          if (typemapField != null) {
            newInstance = new GenericJson();
          } else if (newInstance == null) {
            // check if it is a map to avoid ClassCastException to Map
            if (isMap || valueClass == null) {
              newInstance = Data.newMapInstance(valueClass);
            } else {
              newInstance = Types.newInstance(valueClass);
            }
          }
          int contextSize = context.size();
          if (valueType != null) {
            context.add(valueType);
          }
          if (isMap && !GenericData.class.isAssignableFrom(valueClass)) {
            Type subValueType = Map.class.isAssignableFrom(valueClass)
                ? Types.getMapValueParameter(valueType) : null;
            if (subValueType != null) {
              @SuppressWarnings("unchecked")
              Map<String, Object> destinationMap = (Map<String, Object>) newInstance;
              parseMap(fieldContext, destinationMap, subValueType, context, customizeParser);
              return newInstance;
            }
          }
          parse(context, newInstance, customizeParser);
          if (valueType != null) {
            context.remove(contextSize);
          }
          if (typemapField == null) {
            return newInstance;
          }

          // Get the correct type out of the naively parsed data.
          Object typeValueObject = ((GenericJson) newInstance).get(typemapField.getName());
          Preconditions.checkArgument(
              typeValueObject != null, "No value specified for @JsonPolymorphicTypeMap field");
          String typeValue = typeValueObject.toString();
          JsonPolymorphicTypeMap typeMap = typemapField.getAnnotation(JsonPolymorphicTypeMap.class);
          Class<?> typeClass = null;
          for (TypeDef typeDefinition : typeMap.typeDefinitions()) {
            if (typeDefinition.key().equals(typeValue)) {
              typeClass = typeDefinition.ref();
              break;
            }
          }
          Preconditions.checkArgument(
              typeClass != null, "No TypeDef annotation found with key: " + typeValue);
          JsonFactory factory = getFactory();
          // TODO(ngmiceli): Avoid having to parse JSON content twice. Optimize when type is first.
          JsonParser parser = factory.createJsonParser(factory.toString(newInstance));
          parser.startParsing();
          return parser.parseValue(fieldContext, typeClass, context, null, null, false);
        case VALUE_TRUE:
        case VALUE_FALSE:
          Preconditions.checkArgument(valueType == null || valueClass == boolean.class
              || valueClass != null && valueClass.isAssignableFrom(Boolean.class),
              "expected type Boolean or boolean but got %s", valueType);
          return token == JsonToken.VALUE_TRUE ? Boolean.TRUE : Boolean.FALSE;
        case VALUE_NUMBER_FLOAT:
        case VALUE_NUMBER_INT:
          Preconditions.checkArgument(
              fieldContext == null || fieldContext.getAnnotation(JsonString.class) == null,
              "number type formatted as a JSON number cannot use @JsonString annotation");
          if (valueClass == null || valueClass.isAssignableFrom(BigDecimal.class)) {
            return getDecimalValue();
          }
          if (valueClass == BigInteger.class) {
            return getBigIntegerValue();
          }
          if (valueClass == Double.class || valueClass == double.class) {
            return getDoubleValue();
          }
          if (valueClass == Long.class || valueClass == long.class) {
            return getLongValue();
          }
          if (valueClass == Float.class || valueClass == float.class) {
            return getFloatValue();
          }
          if (valueClass == Integer.class || valueClass == int.class) {
            return getIntValue();
          }
          if (valueClass == Short.class || valueClass == short.class) {
            return getShortValue();
          }
          if (valueClass == Byte.class || valueClass == byte.class) {
            return getByteValue();
          }
          throw new IllegalArgumentException("expected numeric type but got " + valueType);
        case VALUE_STRING:
          Preconditions.checkArgument(valueClass == null
              || !Number.class.isAssignableFrom(valueClass) || fieldContext != null
              && fieldContext.getAnnotation(JsonString.class) != null,
              "number field formatted as a JSON string must use the @JsonString annotation");
          // TODO(yanivi): "special" values like Double.POSITIVE_INFINITY?
          return Data.parsePrimitiveValue(valueType, getText());
        case VALUE_NULL:
          Preconditions.checkArgument(valueClass == null || !valueClass.isPrimitive(),
              "primitive number field but found a JSON null");
          if (valueClass != null
              && 0 != (valueClass.getModifiers() & (Modifier.ABSTRACT | Modifier.INTERFACE))) {
            if (Types.isAssignableToOrFrom(valueClass, Collection.class)) {
              return Data.nullOf(Data.newCollectionInstance(valueType).getClass());
            }
            if (Types.isAssignableToOrFrom(valueClass, Map.class)) {
              return Data.nullOf(Data.newMapInstance(valueClass).getClass());
            }
          }
          return Data.nullOf(Types.getRawArrayComponentType(context, valueType));
        default:
          throw new IllegalArgumentException("unexpected JSON node type: " + token);
      }
    } catch (IllegalArgumentException e) {
      // build context string
      StringBuilder contextStringBuilder = new StringBuilder();
      String currentName = getCurrentName();
      if (currentName != null) {
        contextStringBuilder.append("key ").append(currentName);
      }
      if (fieldContext != null) {
        if (currentName != null) {
          contextStringBuilder.append(", ");
        }
        contextStringBuilder.append("field ").append(fieldContext);
      }
      throw new IllegalArgumentException(contextStringBuilder.toString(), e);
    }
  }