content: package com.alibaba.fastjson.support.spring;
  
  import com.alibaba.fastjson.JSON;
  import com.alibaba.fastjson.JSONException;
  import com.alibaba.fastjson.support.config.FastJsonConfig;
  import com.alibaba.fastjson2.JSONFactory;
  import com.alibaba.fastjson2.JSONPObject;
  import com.alibaba.fastjson2.JSONReader;
  import org.springframework.core.ResolvableType;
  import org.springframework.http.HttpHeaders;
  import org.springframework.http.HttpInputMessage;
  import org.springframework.http.HttpOutputMessage;
  import org.springframework.http.MediaType;
  import org.springframework.http.converter.AbstractHttpMessageConverter;
  import org.springframework.http.converter.GenericHttpMessageConverter;
  import org.springframework.http.converter.HttpMessageNotReadableException;
  import org.springframework.http.converter.HttpMessageNotWritableException;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.lang.reflect.TypeVariable;
  
  import static com.alibaba.fastjson.JSON.DEFAULT_PARSER_FEATURE;
  
  /**
   * Fastjson for Spring MVC Converter.
   * <p>
   * Compatible fastjson 1.2.x
   *
   * @author VictorZeng
   * @see AbstractHttpMessageConverter
   * @see GenericHttpMessageConverter
   * @since 2.0.2
   */
  
  public class FastJsonHttpMessageConverter
          extends AbstractHttpMessageConverter<Object>
          implements GenericHttpMessageConverter<Object> {
      public static final MediaType APPLICATION_JAVASCRIPT = new MediaType("application", "javascript");
  
      /**
       * with fastJson config
       */
      private FastJsonConfig fastJsonConfig = new FastJsonConfig();
  
      /**
       * Can serialize/deserialize all types.
       */
      public FastJsonHttpMessageConverter() {
          super(MediaType.ALL);
          setDefaultCharset(com.alibaba.fastjson.util.IOUtils.UTF8);
      }
  
      /**
       * @return the fastJsonConfig.
       */
      public FastJsonConfig getFastJsonConfig() {
          return fastJsonConfig;
      }
  
      /**
       * @param fastJsonConfig the fastJsonConfig to set.
       */
      public void setFastJsonConfig(FastJsonConfig fastJsonConfig) {
          this.fastJsonConfig = fastJsonConfig;
      }
  
      @Override
      protected boolean supports(Class<?> clazz) {
          return true;
      }
  
      @Override
      public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
          return super.canRead(contextClass, mediaType);
      }
  
      @Override
      public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
          return super.canWrite(clazz, mediaType);
      }
  
      @Override
      public Object read(
              Type type,
              Class<?> contextClass,
              HttpInputMessage inputMessage
      ) throws IOException, HttpMessageNotReadableException {
          return readType(getType(type, contextClass), inputMessage);
      }
  
      @Override
      public void write(
              Object o,
              Type type,
              MediaType contentType,
              HttpOutputMessage outputMessage
      ) throws IOException, HttpMessageNotWritableException {
          // support StreamingHttpOutputMessage in spring4.0+
          super.write(o, contentType, outputMessage);
      }
  
      @Override
      protected Object readInternal(
              Class<?> clazz,
              HttpInputMessage inputMessage
      ) throws IOException, HttpMessageNotReadableException {
          return readType(getType(clazz, null), inputMessage);
      }
  
      private Object readType(Type type, HttpInputMessage inputMessage) {
          try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
              InputStream in = inputMessage.getBody();
  
              byte[] buf = new byte[1024 * 64];
              for (; ; ) {
                  int len = in.read(buf);
                  if (len == -1) {
                      break;
                  }
  
                  if (len > 0) {
                      baos.write(buf, 0, len);
                  }
              }
              byte[] bytes = baos.toByteArray();
  
              JSONReader.Context context = JSON.createReadContext(
                      JSONFactory.getDefaultObjectReaderProvider(),
                      DEFAULT_PARSER_FEATURE,
                      fastJsonConfig.getFeatures()
              );
  
              String dateFormat = fastJsonConfig.getDateFormat();
              if (dateFormat != null) {
                  context.setDateFormat(dateFormat);
              }
  
              return JSON.parseObject(bytes, type, context);
          } catch (JSONException ex) {
              throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex);
          } catch (IOException ex) {
              throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
          }
      }
  
      @Override
      protected void writeInternal(
              Object object,
              HttpOutputMessage outputMessage
      ) throws IOException, HttpMessageNotWritableException {
          try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
              HttpHeaders headers = outputMessage.getHeaders();
  
              int contentLength;
              if (object instanceof String && com.alibaba.fastjson2.JSON.isValidObject((String) object)) {
                  byte[] strBytes = ((String) object).getBytes(fastJsonConfig.getCharset());
                  contentLength = strBytes.length;
                  outputMessage.getBody().write(strBytes, 0, strBytes.length);
              } else if (object instanceof byte[] && com.alibaba.fastjson2.JSON.isValid((byte[]) object)) {
                  byte[] strBytes = (byte[]) object;
                  contentLength = strBytes.length;
                  outputMessage.getBody().write(strBytes, 0, strBytes.length);
              } else {
                  if (object instanceof JSONPObject) {
                      headers.setContentType(APPLICATION_JAVASCRIPT);
                  }
  
                  contentLength = JSON.writeJSONString(
                          baos, object,
                          fastJsonConfig.getDateFormat(),
                          fastJsonConfig.getSerializeFilters(),
                          fastJsonConfig.getSerializerFeatures()
                  );
              }
  
              if (headers.getContentLength() < 0 && fastJsonConfig.isWriteContentLength()) {
                  headers.setContentLength(contentLength);
              }
  
              baos.writeTo(outputMessage.getBody());
          } catch (JSONException ex) {
              throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
          } catch (IOException ex) {
              throw new HttpMessageNotWritableException("I/O error while writing output message", ex);
          }
      }
  
      protected Type getType(Type type, Class<?> contextClass) {
          if (Spring4TypeResolvableHelper.isSupport()) {
              return Spring4TypeResolvableHelper.getType(type, contextClass);
          }
          return type;
      }
  
      private static class Spring4TypeResolvableHelper {
          private static boolean hasClazzResolvableType;
  
          static {
              try {
                  Class.forName("org.springframework.core.ResolvableType");
                  hasClazzResolvableType = true;
              } catch (ClassNotFoundException e) {
                  hasClazzResolvableType = false;
              }
          }
  
          private static boolean isSupport() {
              return hasClazzResolvableType;
          }
  
          private static Type getType(Type type, Class<?> contextClass) {
              if (contextClass != null) {
                  ResolvableType resolvedType = ResolvableType.forType(type);
                  if (type instanceof TypeVariable) {
                      ResolvableType resolvedTypeVariable = resolveVariable(
                              (TypeVariable) type,
                              ResolvableType.forClass(contextClass)
                      );
                      if (resolvedTypeVariable != ResolvableType.NONE) {
                          return resolvedTypeVariable.resolve();
                      }
                  } else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) {
                      ParameterizedType parameterizedType = (ParameterizedType) type;
                      Class<?>[] generics = new Class[parameterizedType.getActualTypeArguments().length];
                      Type[] typeArguments = parameterizedType.getActualTypeArguments();
  
                      for (int i = 0; i < typeArguments.length; ++i) {
                          Type typeArgument = typeArguments[i];
                          if (typeArgument instanceof TypeVariable) {
                              ResolvableType resolvedTypeArgument = resolveVariable(
                                      (TypeVariable) typeArgument,
                                      ResolvableType.forClass(contextClass)
                              );
                              if (resolvedTypeArgument != ResolvableType.NONE) {
                                  generics[i] = resolvedTypeArgument.resolve();
                              } else {
                                  generics[i] = ResolvableType.forType(typeArgument).resolve();
                              }
                          } else {
                              generics[i] = ResolvableType.forType(typeArgument).resolve();
                          }
                      }
  
                      return ResolvableType.forClassWithGenerics(resolvedType.getRawClass(), generics).getType();
                  }
              }
  
              return type;
          }
  
          private static ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) {
              ResolvableType resolvedType;
              if (contextType.hasGenerics()) {
                  resolvedType = ResolvableType.forType(typeVariable, contextType);
                  if (resolvedType.resolve() != null) {
                      return resolvedType;
                  }
              }
  
              ResolvableType superType = contextType.getSuperType();
              if (superType != ResolvableType.NONE) {
                  resolvedType = resolveVariable(typeVariable, superType);
                  if (resolvedType.resolve() != null) {
                      return resolvedType;
                  }
              }
              for (ResolvableType ifc : contextType.getInterfaces()) {
                  resolvedType = resolveVariable(typeVariable, ifc);
                  if (resolvedType.resolve() != null) {
                      return resolvedType;
                  }
              }
              return ResolvableType.NONE;
          }
      }
  }
  