content: package com.alibaba.fastjson.parser.deserializer;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Type;
  import java.util.HashMap;
  import java.util.Map;
  
  import com.alibaba.fastjson.JSONException;
  import com.alibaba.fastjson.parser.DefaultJSONParser;
  import com.alibaba.fastjson.parser.Feature;
  import com.alibaba.fastjson.parser.JSONScanner;
  import com.alibaba.fastjson.parser.JSONToken;
  import com.alibaba.fastjson.parser.ParserConfig;
  import com.alibaba.fastjson.util.TypeUtils;
  
  public class ThrowableDeserializer extends JavaBeanDeserializer {
  
      public ThrowableDeserializer(ParserConfig mapping, Class<?> clazz){
          super(mapping, clazz);
      }
  
      @SuppressWarnings("unchecked")
      public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
          JSONScanner lexer = (JSONScanner) parser.getLexer();
  
          if (parser.getResolveStatus() == DefaultJSONParser.TypeNameRedirect) {
              parser.setResolveStatus(DefaultJSONParser.NONE);
          } else {
              if (lexer.token() != JSONToken.LBRACE) {
                  throw new JSONException("syntax error");
              }
          }
  
          Throwable cause = null;
          Class<?> exClass = null;
          
          if (type != null && type instanceof Class) {
          	Class<?> clazz = (Class<?>) type;
          	if (Throwable.class.isAssignableFrom(clazz)) {
          		exClass = clazz;
          	}
          }
          
          String message = null;
          StackTraceElement[] stackTrace = null;
          Map<String, Object> otherValues = new HashMap<String, Object>();
  
          for (;;) {
              // lexer.scanSymbol
              String key = lexer.scanSymbol(parser.getSymbolTable());
  
              if (key == null) {
                  if (lexer.token() == JSONToken.RBRACE) {
                      lexer.nextToken(JSONToken.COMMA);
                      break;
                  }
                  if (lexer.token() == JSONToken.COMMA) {
                      if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
                          continue;
                      }
                  }
              }
  
              lexer.nextTokenWithColon(JSONToken.LITERAL_STRING);
  
              if ("@type".equals(key)) {
                  if (lexer.token() == JSONToken.LITERAL_STRING) {
                      String exClassName = lexer.stringVal();
                      exClass = TypeUtils.loadClass(exClassName);
                  } else {
                      throw new JSONException("syntax error");
                  }
                  lexer.nextToken(JSONToken.COMMA);
              } else if ("message".equals(key)) {
                  if (lexer.token() == JSONToken.NULL) {
                      message = null;
                  } else if (lexer.token() == JSONToken.LITERAL_STRING) {
                      message = lexer.stringVal();
                  } else {
                      throw new JSONException("syntax error");
                  }
                  lexer.nextToken();
              } else if ("cause".equals(key)) {
                  cause = deserialze(parser, null, "cause");
              } else if ("stackTrace".equals(key)) {
                  stackTrace = parser.parseObject(StackTraceElement[].class);
              } else {
                  // TODO
                  otherValues.put(key, parser.parse());
              }
  
              if (lexer.token() == JSONToken.COMMA) {
                  continue;
              }
  
              if (lexer.token() == JSONToken.RBRACE) {
                  lexer.nextToken(JSONToken.COMMA);
                  break;
              }
          }
  
          Throwable ex = null;
          if (exClass == null) {
              ex = new Exception(message, cause);
          } else {
              try {
                  ex = createException(message, cause, exClass);
                  if (ex == null) {
                      ex = new Exception(message, cause);
                  }
              } catch (Exception e) {
                  throw new JSONException("create instance error", e);
              }
          }
  
          if (stackTrace != null) {
              ex.setStackTrace(stackTrace);
          }
  
          return (T) ex;
      }
  
      private Throwable createException(String message, Throwable cause, Class<?> exClass) throws Exception {
          Constructor<?> defaultConstructor = null;
          Constructor<?> messageConstructor = null;
          Constructor<?> causeConstructor = null;
          for (Constructor<?> constructor : exClass.getConstructors()) {
              if (constructor.getParameterTypes().length == 0) {
                  defaultConstructor = constructor;
                  continue;
              }
  
              if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == String.class) {
                  messageConstructor = constructor;
                  continue;
              }
  
              if (constructor.getParameterTypes().length == 2 && constructor.getParameterTypes()[0] == String.class
                  && constructor.getParameterTypes()[1] == Throwable.class) {
                  causeConstructor = constructor;
                  continue;
              }
          }
  
          if (causeConstructor != null) {
              return (Throwable) causeConstructor.newInstance(message, cause);
          }
  
          if (messageConstructor != null) {
              return (Throwable) messageConstructor.newInstance(message);
          }
  
          if (defaultConstructor != null) {
              return (Throwable) defaultConstructor.newInstance();
          }
  
          return null;
      }
  
      public int getFastMatchToken() {
          return JSONToken.LBRACE;
      }
  }
  