/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/json/JavaDeserializer.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.json;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import java.lang.reflect.*;

public class JavaDeserializer implements JsonDeserializer {
  private static final Logger log
    = Logger.getLogger(JavaDeserializer.class.getName());

  private Class _type;
  private Constructor _ctor;
  private HashMap<String,JsonField> _fieldMap
    = new HashMap<String,JsonField>();

  JavaDeserializer(Class type)
  {
    try {
      _type = type;
      _ctor = introspectConstructor(type);
      _ctor.setAccessible(true);

      introspect();
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  void introspect()
  {
    introspectConstructor(_type);
    introspectFields(_type);
  }

  private Constructor introspectConstructor(Class type)
  {
    for (Constructor ctor : type.getDeclaredConstructors()) {
      if (ctor.getParameterTypes().length == 0)
        return ctor;
    }

    throw new IllegalStateException(type + " needs a zero-arg constructor");
  }

  private void introspectFields(Class type)
  {
    if (type == null)
      return;

    introspectFields(type.getSuperclass());

    for (Field field : type.getDeclaredFields()) {
      if (Modifier.isTransient(field.getModifiers()))
        continue;
      if (Modifier.isStatic(field.getModifiers()))
        continue;

      field.setAccessible(true);

      Class fieldType = field.getType();

      if (fieldType == int.class || fieldType == Integer.class)
        _fieldMap.put(field.getName(), new JsonIntField(field));
      else if (fieldType == long.class || fieldType == Long.class)
        _fieldMap.put(field.getName(), new JsonLongField(field));
      else if (fieldType == double.class || fieldType == Double.class)
        _fieldMap.put(field.getName(), new JsonDoubleField(field));
      else
        _fieldMap.put(field.getName(), new JsonObjectField(field));
    }
  }

  public Object read(JsonInput in)
    throws IOException
  {
    Object bean = create();

    in.parseBeanMap(bean, this);

    return bean;
  }

  public Object create()
  {
    try {
      return _ctor.newInstance();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void readField(JsonInput in, Object bean, String fieldName)
    throws IOException
  {
    JsonField jsonField = _fieldMap.get(fieldName);

    if (jsonField != null)
      jsonField.read(in, bean);
    else {
      // skip
      in.readObject();
    }
  }

  public Object complete(Object bean)
  {
    return bean;
  }

  public String toString()
  {
    return getClass().getSimpleName() + "[" + _type + "]";
  }

  abstract static class JsonField {
    abstract void read(JsonInput in, Object bean)
      throws IOException;
  }

  static class JsonIntField extends JsonField {
    private final Field _field;

    JsonIntField(Field field)
    {
      _field = field;
    }

    void read(JsonInput in, Object bean)
      throws IOException
    {
      long value = in.readLong();

      try {
        _field.setInt(bean, (int) value);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  static class JsonLongField extends JsonField {
    private final Field _field;

    JsonLongField(Field field)
    {
      _field = field;
    }

    void read(JsonInput in, Object bean)
      throws IOException
    {
      long value = in.readLong();

      try {
        _field.setLong(bean, value);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  static class JsonDoubleField extends JsonField {
    private final Field _field;

    JsonDoubleField(Field field)
    {
      _field = field;
    }

    void read(JsonInput in, Object bean)
      throws IOException
    {
      double value = in.readDouble();

      try {
        _field.setDouble(bean, value);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  static class JsonObjectField extends JsonField {
    private final Field _field;

    JsonObjectField(Field field)
    {
      _field = field;
    }

    void read(JsonInput in, Object bean)
      throws IOException
    {
      Object value = in.readObject(_field.getType());

      if (value == null)
        return;

      if (! _field.getType().isAssignableFrom(value.getClass()))
        throw new IOException(value.getClass() + " is an illegal value for " + _field);

      try {
        _field.set(bean, value);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }
}
