Package com.jaxws.json.codec.decode

Source Code of com.jaxws.json.codec.decode.WSJSONPopulator

package com.jaxws.json.codec.decode;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.activation.DataHandler;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.transform.stream.StreamSource;

import org.jvnet.mimepull.MIMEPart;

import com.jaxws.json.codec.BeanAware;
import com.jaxws.json.codec.DateFormat;
import com.jaxws.json.codec.DebugTrace;
import com.jaxws.json.codec.JSONCodec;
import com.jaxws.json.codec.PublicFieldPropertyDescriptor;
import com.jaxws.json.feature.JSONWebService;
import com.jaxws.json.serializer.JSONObjectCustomizer;
import com.sun.xml.messaging.saaj.packaging.mime.internet.ContentDisposition;
import com.sun.xml.messaging.saaj.packaging.mime.internet.MimeBodyPart;
import com.sun.xml.messaging.saaj.packaging.mime.internet.MimePartDataSource;

/**
* @author Sundaramurthi Saminathan
* @version 2.0
*
* WSJSONPopulator create java object from input JSON map. Uses bean inspector and populate all object.
*
* Population customized using annotations.
*
* @see XmlElement
* @see XmlAttribute
* @see JSONWebService
*
*/
public class WSJSONPopulator extends BeanAware {
  /**
   * Static string value for JSON/Default null.
   */
  private static final String NULL     = "\u0000";
 
  /**
   * List of property name can be serialized to MAP
   */
  @SuppressWarnings("unused")
  private Pattern listMapKey;
 
  /**
   * List of value property names used in MAP
   */
  @SuppressWarnings("unused")
  private Pattern listMapValue;
 
  /**
   * JSON user specified object customizer.
   */
  final Map<Class<? extends Object>,JSONObjectCustomizer> objectCustomizers;
 
  /**
   * Date format used to deserialize JSON
   */
  private DateFormat dateFormat = DateFormat.RFC3339;
 
  /**
   * MIME attachments
   */
  private List<MIMEPart>   attachments  = null;

  //private JAXBContext context;
 
  /**
   *
   */
  private boolean traceEnabled = false;
 
  /**
   * Trace information.
   */
  private DebugTrace       traceLog;

  /**
   * @param listMapKey
   * @param listMapValue
   * @param dateFormat
   * @param objectCustomizers
   */
  public WSJSONPopulator(Pattern listMapKey,Pattern listMapValue,
      DateFormat dateFormat,Map<Class<? extends Object>,
      JSONObjectCustomizer> objectCustomizers,
      DebugTrace traceLog) {
    this.listMapKey     = listMapKey;
    this.listMapValue    = listMapValue;
    if(dateFormat != null){
      this.dateFormat    = dateFormat;
    }
   
    if(objectCustomizers != null){
      this.objectCustomizers  = objectCustomizers;
    }else{
      this.objectCustomizers  = new HashMap<Class<? extends Object>, JSONObjectCustomizer>();
    }
    this.traceEnabled   = traceLog != null;
    this.traceLog    = traceLog;
  }


  /**
   * Entry method
   * @param clazz
   * @param type
   * @param value
   * @param customizeInfo
   * @param method
   * @return
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   * @throws InstantiationException
   * @throws NoSuchMethodException
   * @throws IntrospectionException
   */
  @SuppressWarnings("unchecked")
  public Object convert(Class<?> clazz, Type type, Object value,
      JSONWebService customizeInfo, Method method) throws Exception {
    if(this.objectCustomizers.containsKey(clazz)){
      // Case 1: is it handled by Customizer?
      return this.objectCustomizers.get(clazz).decode(value);
    }else if (isJSONPrimitive(clazz))
      // Case 2: primitive class
            return convertPrimitive(clazz, value, customizeInfo, method);
    else if (Collection.class.isAssignableFrom(clazz))
      // Case 3: Collection deserialization
            return convertToCollection(clazz, type, value, customizeInfo, method);
        else if (clazz.isArray())
          // Case 4: Array conversion
            return convertToArray(clazz, type, value, customizeInfo, method);
        else if (Map.class.isAssignableFrom(clazz))
          // Case 5: Map conversion
            return convertToMap(clazz, type, value, customizeInfo, method);
        else if (value instanceof Map) {
          // Case 6: Object inside object conversion
            return this.populateObject(getNewInstance(clazz), (Map<String,Object>) value, customizeInfo);
        } else if(clazz.equals(JAXBElement.class)){
      // Case 7: is it JAXBElement bound with object?
          return convertJAXBElement(clazz, method);
    } else{
      if(traceEnabled){
        traceLog.warn("Input json value can't be handled by custom handler," +
            " and value not a primitive, collection, array, map, or jaxb elemnt. Please send only supported type or consider to write custom serializer");
      }
      return null;
    }
  }

  /**
   * Case 7: JAXBElement conversion
   * @param clazz
   * @param method
   * @return
   */
  private JAXBElement<?> convertJAXBElement(Class<?> clazz, Method method){
    if(clazz.getGenericInterfaces().length == 1
        && clazz.getGenericInterfaces()[0].equals(Serializable.class)){
      XmlElementRef elmRef = method.getAnnotation(XmlElementRef.class);
      String elementName = null;
      if(elmRef == null || elmRef.name() == null){
        // Not a referred element
        if(method.getName().startsWith("set")){
          String charStart = ""+method.getName().charAt(3);
          elementName = charStart.toLowerCase()+method.getName().substring(4);
        }else{
          elementName = method.getName();
        }
      }else{
        elementName = elmRef !=null ? elmRef.name() : null;
      }
      if(elementName != null){
        throw new RuntimeException("FIXME JAXBElement");
        // FIXME JaxBeanInfo beanInfo = context.getBeanInfo(elementName);
        //return new JAXBElement(beanInfo.getTypeName(elementName), beanInfo.jaxbType, value);
      }
    }
    return null;
  }
 
  /**
   * Case 6: Object inside object conversion
   * @param object
   * @param elements
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   * @throws NoSuchMethodException
   * @throws IntrospectionException
   * @throws IllegalArgumentException
   * @throws JSONException
   * @throws InstantiationException
   */
    public Object populateObject(Object object, Map<String,Object> elements, JSONWebService customizeInfo, List<MIMEPart> attachments)
        throws Exception {
    this.attachments  = attachments;
    return this.populateObject(object, elements, customizeInfo);
  }
 
  /**
   * Case 6: Object inside object conversion
   * @param object
   * @param elements
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   * @throws NoSuchMethodException
   * @throws IntrospectionException
   * @throws IllegalArgumentException
   * @throws JSONException
   * @throws InstantiationException
   */
    @SuppressWarnings({ "unchecked", "rawtypes" })
  private Object populateObject(Object object, Map<String,Object> elements, JSONWebService customizeInfo)
        throws Exception {
      if(elements == null){
        return null;
      }
      Class<?>        clazz  = object.getClass();
      // xsdAny type generate Object java TYPE. In JSON current way to identifiy class using class property.
      if(clazz.equals(Object.class)){
        if(!elements.containsKey("class")){
          // If no class specifed but no class property return null.
          if(traceEnabled){
              traceLog.warn("Any Object elemnt. with out class property. Ignoring it");
              }
          return null;
        } else {
          // remove class property to avoid messing with Object class
          String clazzName = elements.remove("class").toString();
          try{
            clazz  = Class.forName(clazzName);
            if(!object.getClass().equals(clazz)){
              object  = getNewInstance(clazz);
            }
          }catch(Throwable th){
            if(traceEnabled){
                  traceLog.warn("Any Object elemnt. unable to load class " + clazzName);
                  }
            return null;
          }
        }
      }
    PropertyDescriptor[]   props   = getBeanProperties(clazz);

    //iterate over class fields
    for (PropertyDescriptor prop : props) {
      Method         writeMethod     = prop.getWriteMethod();
      Class<?>       propertyType     = prop.getPropertyType();
        JSONWebService     writeMethodConfig   = writeMethod != null ? writeMethod.getAnnotation(JSONWebService.class) : null;
        String         expectedJSONPropName= (writeMethodConfig != null && !writeMethodConfig.name().isEmpty()) ? writeMethodConfig.name() : prop.getName();
        // JOSN input contains specified property.
        if (elements.containsKey(expectedJSONPropName)) {
          Object value   = elements.get(expectedJSONPropName);
            if (writeMethod != null) {
                if (writeMethodConfig != null
                    && !writeMethodConfig.deserialize()) {
                  if(traceEnabled){
                    traceLog.info(String.format("Ignoring property %s due to deserialize set to false", expectedJSONPropName));
                  }
                  continue;
                }
                //use only public setters Bean property describer get only accessable setter.
                  // Bean getter always works on single property get. if (paramTypes.length == 1) {
                  try{
                    Class<?>[]   paramTypes     = writeMethod.getParameterTypes();
                      Type[]     genericTypes   = writeMethod.getGenericParameterTypes();
                     
                    Object convertedValue = this.convert(paramTypes[0], genericTypes[0], value, writeMethodConfig, writeMethod);
                    writeMethod.invoke(object, convertedValue);
                  }catch(Throwable exp){
                    if(prop instanceof PublicFieldPropertyDescriptor){
                      ((PublicFieldPropertyDescriptor)prop).setValue(object, value);
                      }
                    if(traceEnabled){
                      traceLog.warn(String.format("Exception while writing property \"%s\". Input %s. Expected type %s",
                          expectedJSONPropName, value, propertyType.getSimpleName()));
                  }
                  }
                 // }
            } else if (prop.getReadMethod() != null && Collection.class.isAssignableFrom(propertyType)) {
          try {
            Method         readMethod       = prop.getReadMethod();
            JSONWebService     readMethodConfig   = readMethod.getAnnotation(JSONWebService.class);
            if(readMethodConfig == null || readMethodConfig.deserialize()){
              //  add configuration
              Collection<Object> objectList = (Collection<Object>) readMethod.invoke(object);
              if(objectList != null){
                if(traceEnabled){
                  traceLog.info(String.format("Only list read method found for property %s adding new values to existing collection. " +
                            "Old list size: %d", expectedJSONPropName, objectList.size()));
                      }
                java.lang.reflect.Field f = getDeclaredField(clazz,prop.getName());
                if(f != null && f.isAnnotationPresent(XmlElements.class)){
                  XmlElements xmlElements = f.getAnnotation(XmlElements.class);
                  // Field is choice. name1OrName2OrName3. But user passing as
                  // "name1OrName2OrName3" single field name.
                  if(value instanceof Collection) {
                    Collection<Object> values = (Collection<Object>)value;
                    for(Object ob : values){
                      if(ob instanceof Map){
                        Map<String,Object> choiceOb = (Map<String,Object>)ob;
                        for(XmlElement xmlElement : xmlElements.value()){
                          for(Entry<String, Object> entry : choiceOb.entrySet()){
                            if(entry.getKey().equals(xmlElement.name())){
                              objectList.add(this.convert(xmlElement.type(),
                                  xmlElement.type(), entry.getValue(), null, null));
                            }
                          }
                        }
                      }
                    }
                  } else if(traceEnabled) {
                          traceLog.warn(String.format("Property %s is invalid. %s is a choice list. For more above choice read endpoint document.",
                        expectedJSONPropName,expectedJSONPropName));
                        }
                  continue;
                }
                Object convertedValue = this.convert(readMethod.getReturnType(), readMethod.getGenericReturnType(), value,
                    readMethodConfig, readMethod);
                objectList.addAll((Collection) convertedValue);
              }
            }
          } catch (Exception e) {
            if(traceEnabled){
              traceLog.warn(String.format("Failed to add list to existing collection for property %s in class %s. message %s",
                  expectedJSONPropName, clazz.getSimpleName(),e.getMessage()));
            }
          }
                } else if(Boolean.TYPE.equals(propertyType)){
                  // PROBLEM with JAXB boolean attribute with default value generate invalid setter method
                  try{
                    writeMethod = clazz.getMethod("set" + expectedJSONPropName.substring(0, 1).toUpperCase() +
                         expectedJSONPropName.substring(1),Boolean.class);
                    Object convertedValue = this.convert(Boolean.class, null, value, writeMethodConfig, writeMethod);
                    writeMethod.invoke(object, convertedValue);
                   }catch(Throwable th){
                     if(traceEnabled){
                        traceLog.warn(String.format("Ignoring property %s in class %s. message %s",
                    expectedJSONPropName, clazz.getSimpleName(),""));
              }
                   }
                } else {
                  if(traceEnabled){
                    traceLog.warn(String.format("Ignoring property %s in class %s. message %s",
                expectedJSONPropName, clazz.getSimpleName(),""));
          }
                }
        } else {
           // JOSN input DON'T contains specified property. May come from customized json name or XMLElement definition.
          java.lang.reflect.Field f = getDeclaredField(clazz,prop.getName());
          if(f != null){
          if(f.isAnnotationPresent(XmlElements.class)){
            // Handle XSD choice element.
            XmlElements   ann     = f.getAnnotation(XmlElements.class);
            XmlElement[]   xmlElements = ann.value();
            for(XmlElement elm : xmlElements){
              if(elements.containsKey(elm.name())){
                try{
                  Object jsonValue = elements.get(elm.name());
                  List<Map<String,Object>> objects = new ArrayList<Map<String,Object>>();
                  if(jsonValue instanceof Map){// Single object XSD choice with maxOccur 1
                    objects.add((Map<String,Object>)jsonValue);
                  }else if(jsonValue instanceof List){// List of object XSD choice with maxOccur greater than 1
                    objects.addAll((List)jsonValue);
                  }else if(traceEnabled){
                    traceLog.warn(String.format("Object choice found. But input JSON is not object or LIST ignoring %s",
                        prop.getName()));
                  }
                  List<Object> populatedObjects = new ArrayList<Object>();
                  for(Map v :  objects){
                    populatedObjects.add(populateObject(getNewInstance(elm.type()), v, writeMethodConfig));
                  }
                  Method readMethod = prop.getReadMethod();
                  if(writeMethod != null && readMethod != null && readMethod.invoke(object) == null){
                    // init list if read method returns null
                    writeMethod.invoke(object, new ArrayList<Object>());
                  }
                  if (readMethod != null){
                    Collection objectList = (Collection) readMethod.invoke(object);
                    if(objectList != null){
                      objectList.addAll(populatedObjects);
                    }else{
                      if(traceEnabled)
                        traceLog.warn("List property dont have write method. Also read method returned null. " +
                            "Ignoring json value for list " + prop.getName());
                    }
                  }
                } catch (Throwable th){
                  if(traceEnabled)
                    traceLog.warn(String.format("Failed to populate choice element list. property %s in clazz %s",prop.getName(),
                        clazz.getSimpleName()));
                }
              }
            }
          } else if(f.isAnnotationPresent(XmlMimeType.class) && writeMethod != null){
            // Process Attachments
            XmlMimeType   ann     = f.getAnnotation(XmlMimeType.class);
            writeMethod.invoke(object, new Object[]{handleMimeAttachement(f.getType(),ann,expectedJSONPropName)});
          } else if(f.isAnnotationPresent(XmlElement.class)){
            // Handle default value
            XmlElement     element   = f.getAnnotation(XmlElement.class);
            // JSON property name is same as in XML annotation, but class property name is different.
            if(!element.name().equals(NULL) && elements.containsKey(element.name())){
              writeMethod.invoke(object,
                  convert(propertyType, f.getType(), elements.get(element.name()),
                      writeMethod.getAnnotation(JSONWebService.class), writeMethod));
            } else if (!element.defaultValue().equals(NULL) && isJSONPrimitive(propertyType)){
              if(traceEnabled)
                traceLog.info(String.format("Input do not have %s. Populating default value: %s. flag to populate: %b",
                    expectedJSONPropName, element.defaultValue(), createDefaultOnNonNullable));
              writeMethod.invoke(object,
                  convert(propertyType, f.getType(), element.defaultValue(),
                      writeMethodConfig, writeMethod));
            } else if (!element.nillable() && createDefaultOnNonNullable) {
              // there is no default value and non nullable.
              if(!isJSONPrimitive(propertyType)){
                if(traceEnabled)
                  traceLog.warn("Non nillable object(\""+expectedJSONPropName +
                    "\") with nill value, populating default.");
                try{
                  writeMethod.invoke(object,
                      convert(propertyType,f.getType(), new HashMap<String, Object>(),
                          writeMethodConfig, writeMethod));
                }catch(Throwable th){
                  if(traceEnabled)
                    traceLog.error("Non nillable object(\""+expectedJSONPropName +
                      "\") with nill value, failed to create instance.");
                }
              } else {
                // Primitive don't have any default WHAT TODO . E.g. Integer object can't be instantiated.
                if(traceEnabled)
                  traceLog.error(String.format("Non nillable primitive \"%s\", also don't have default value." +
                    " Implementation may may fail to hanlde your request.  ", expectedJSONPropName));
              }
            }
          }
          }
            }
    }
    return  object;
    }
 
    /**
     * Case 6: Mime attachment
     * @param ann
     */
    private Object handleMimeAttachement(Class<?> clazz, XmlMimeType ann,String propertyName) {
    if(this.attachments != null){
      for(MIMEPart mimePart : this.attachments){
        List<String> contentDisposition = mimePart.getHeader(JSONCodec.CONTENT_DISPOSITION_HEADER);
        if(contentDisposition != null && contentDisposition.size() > 0){
          try {
            ContentDisposition disp = new ContentDisposition(contentDisposition.get(0));
            if(disp.getParameter("name") != null && disp.getParameter("name").equals(propertyName)){
              if(clazz.isAssignableFrom(DataHandler.class))
                return new DataHandler(new MimePartDataSource(new MimeBodyPart(mimePart.read())));
              else if(clazz.isAssignableFrom(javax.xml.transform.Source.class))
                return new StreamSource(mimePart.read());
            }
          } catch (Exception e) {
            if(this.traceEnabled){
              traceLog.error(String.format("Error while handling attachment name:\"%s\". message: \"%s\"",
                  propertyName,e.getMessage()));
            }
          }
        }
      }
    }
    if(this.traceEnabled){
      traceLog.warn(String.format("attachment name:\"%s\" Not found in request.", propertyName));
    }
    return null;
  }


  /**
     * Case 5: Map conversion
     *
     * @param clazz
     * @param type
     * @param value
     * @param accessor
     * @return
     * @throws JSONException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws NoSuchMethodException
     * @throws IntrospectionException
     */
    @SuppressWarnings("unchecked")
    private Object convertToMap(Class<?> clazz, Type type, Object value, JSONWebService customizeInfo,Method accessor)
            throws Exception {
        if (value == null)
            return null;
        else if (value instanceof Map) {
            Class<?> itemClass = Object.class;
            Type itemType = null;
            if (type != null && type instanceof ParameterizedType) {
                ParameterizedType ptype = (ParameterizedType) type;
                itemType = ptype.getActualTypeArguments()[1];
                if (itemType.getClass().equals(Class.class)) {
                    itemClass = (Class<?>) itemType;
                } else {
                    itemClass = (Class<?>) ((ParameterizedType) itemType).getRawType();
                }
            }
            Map<String,Object> values = (Map<String,Object>) value;

            Map<String,Object> newMap = (Map<String,Object>) getNewInstance(clazz);;

            //create an object for each element
            Iterator<Map.Entry<String,Object>> iter = values.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String,Object>   entry   = iter.next();
                String             key   = entry.getKey();
                Object             v     = entry.getValue();

                if (itemClass.equals(Object.class)) {
                    //String, Object
                    newMap.put(key, v);
                } else if (isJSONPrimitive(itemClass)) {
                    //primitive map
                    newMap.put(key, this.convertPrimitive(itemClass, v, customizeInfo,
                            accessor));
                } else if (Map.class.isAssignableFrom(itemClass)) {
                    newMap.put(key, convertToMap(itemClass, itemType, v, customizeInfo, accessor));
                } else if (List.class.isAssignableFrom(itemClass)) {
                    newMap.put(key, convertToCollection(itemClass, itemType, v, customizeInfo, accessor));
                } else if (v instanceof Map) {
                    //map of beans
                    newMap.put(key,
                        this.populateObject(getNewInstance(itemClass), (Map<String,Object>) v, null));
                } else if(traceEnabled){
                  traceLog.error(String.format("Incompatible types for property %s in class %s",
                      accessor.getName(),
                      accessor.getDeclaringClass().getSimpleName()));
                }
            }
            return newMap;
        } else if(traceEnabled){
          traceLog.error(String.format("Incompatible types for property %s in class %s",
              accessor.getName(),
              accessor.getDeclaringClass().getSimpleName()));
        }
        return null;
    }

    /**
     * Case 4: Array conversion
     * @param clazz
     * @param type
     * @param value
     * @param Method
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Object convertToArray(Class<?> clazz, Type type, Object value,
        JSONWebService customizeInfo, Method accessor) throws Exception {
        if (value == null)
            return null;
        else if (value instanceof List) {
            Class<?> arrayType = clazz.getComponentType();
            List values = (List) value;
            Object newArray = Array.newInstance(arrayType, values.size());

            //create an object for each element
            for (int j = 0; j < values.size(); j++) {
                Object listValue = values.get(j);

                if (arrayType.equals(Object.class)) {
                    //Object[]
                    Array.set(newArray, j, listValue);
                } else if (isJSONPrimitive(arrayType)) {
                    //primitive array
                    Array.set(newArray, j, this.convertPrimitive(arrayType, listValue, customizeInfo,
                            accessor));
                } else if (listValue instanceof Map) {
                    //array of other class
                    Object newObject = null;
                    if (Map.class.isAssignableFrom(arrayType)) {
                        newObject = convertToMap(arrayType, type, listValue, customizeInfo, accessor);
                    } else if (List.class.isAssignableFrom(arrayType)) {
                        newObject = convertToCollection(arrayType, type, listValue, customizeInfo, accessor);
                    } else {
                        newObject = populateObject(getNewInstance(arrayType), (Map<String,Object>) listValue, customizeInfo);
                    }

                    Array.set(newArray, j, newObject);
                } else if(traceEnabled){
                  traceLog.error(String.format("Incompatible types for property %s in class %s",
                      accessor.getName(),
                      accessor.getDeclaringClass().getSimpleName()));
                }
            }

            return newArray;
        } else if(traceEnabled){
          traceLog.error(String.format("Incompatible types for property %s in class %s",
              accessor.getName(),
              accessor.getDeclaringClass().getSimpleName()));
        }
        return null;
    }
    /**
     * Deserializer Case 3: collection class
     * @param clazz
     * @param type
     * @param value
     * @param accessor
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws NoSuchMethodException
     * @throws IntrospectionException
     */
    @SuppressWarnings("unchecked")
    private Object convertToCollection(Class<?> clazz, Type type, Object value,
        JSONWebService customizeInfo, Method accessor) throws Exception {
        if (value == null)
            return null;
       
        Class<?> itemClass = Object.class;
        Type itemType = null;
        if (type != null && type instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) type;
            itemType = ptype.getActualTypeArguments()[0];
            if (itemType.getClass().equals(Class.class)) {
                itemClass = (Class<?>) itemType;
            } else {
                itemClass = (Class<?>) ((ParameterizedType) itemType).getRawType();
            }
        }
       
        if (Collection.class.isAssignableFrom(value.getClass())) {
            Collection<?> values = (Collection<?>) value;
      @SuppressWarnings("rawtypes")
      Collection newCollection = (Collection<?>)getNewInstance(clazz);
            //create an object for each element
            if (itemClass.equals(Object.class)) {
                //Object[]
              newCollection.addAll(values);
                if(traceEnabled)
                  traceLog.warn(String.format("Unparameterazed list with object type found. accessor: \"%s\" class: %s",
                      accessor.getName(),accessor.getDeclaringClass()));
            } else if (isJSONPrimitive(itemClass)) {
                //primitive array
              for (Object listValue : values) {
                    newCollection.add(this.convertPrimitive(itemClass, listValue,
                        customizeInfo, accessor));
              }
            } else if (Map.class.isAssignableFrom(itemClass)) {
              for (Object listValue : values) {
                newCollection.add(convertToMap(itemClass, itemType, listValue, customizeInfo, accessor));
              }
            } else if (List.class.isAssignableFrom(itemClass)) {
              // List of list
              for (Object listValue : values) {
                newCollection.add(convertToCollection(itemClass, itemType, listValue, customizeInfo, accessor));
              }
            } else {
              for (Object listValue : values) {
                if(listValue instanceof Map){
                      newCollection.add(
                          populateObject(getNewInstance(itemClass),
                              (Map<String,Object>) listValue, customizeInfo));
                }
              }
            }
            return newCollection;
        } else if (value instanceof Map) {
          if(traceEnabled){
            traceLog.info(String.format("Expecting array, but found MAP. for property %s in class %s. Using values as List",
                accessor.getName(),
                accessor.getDeclaringClass().getSimpleName()));
          }
          /*if(listMapKey != null && !itemClass.equals(Object.class)){
            if(listMapValue != null){
              // TODO this is logical only value is primitive. Is it required to do this conversion?
            }
          }*/
          return convertToCollection(clazz, type,
              ((Map<String,Object>)value).values(), customizeInfo, accessor);
        } else{
          if(traceEnabled){
            traceLog.error(String.format("Incompatible types for property %s in class %s",
                accessor.getName(),
                accessor.getDeclaringClass().getSimpleName()));
          }
        }
        return null;
    }
   
    /**
     * Converts numbers to the desired class, if possible
     *
     * Deserializer Case 2: primitive class
     * @param method
     */
    @SuppressWarnings({"unchecked","rawtypes"})
  private Object convertPrimitive(final Class clazz,final  Object value,final JSONWebService customizeInfo,final Method method) {
        if (value == null) {
            if (Short.TYPE.equals(clazz))
                return (short) 0;
            else if (Byte.TYPE.equals(clazz))
                return (byte) 0;
            else if (Integer.TYPE.equals(clazz))
                return 0;
            else if (Long.TYPE.equals(clazz))
                return 0L;
            else if (Float.TYPE.equals(clazz))
                return 0f;
            else if (Double.TYPE.equals(clazz))
                return 0d;
            else if (Boolean.TYPE.equals(clazz))
                return Boolean.FALSE;
            else// Null in case of objects
                return null;
        } else if (value instanceof Number) {
            Number number = (Number) value;

            if (Short.TYPE.equals(clazz))
                return number.shortValue();
            else if (Integer.TYPE.equals(clazz)) // Periority 1
                return number.intValue();
            else if (Integer.class.equals(clazz))
                return new Integer(number.intValue());
            else if (Double.TYPE.equals(clazz)) // Periority 2
                return number.doubleValue();
            else if (Double.class.equals(clazz))
                return new Double(number.doubleValue());
            else if (Long.TYPE.equals(clazz))
                return number.longValue();
            else if (Long.class.equals(clazz))
                return new Long(number.longValue());
            else if (String.class.equals(clazz))
                return value.toString();
            else if(isDateTime(clazz))
              return handleAsDate(clazz,value,customizeInfo,method);
            else if (Short.class.equals(clazz))
                return new Short(number.shortValue());
            else if (Byte.TYPE.equals(clazz))
                return number.byteValue();
            else if (Byte.class.equals(clazz))
                return new Byte(number.byteValue());
            else if (Float.TYPE.equals(clazz))
                return number.floatValue();
            else if (Float.class.equals(clazz))
                return new Float(number.floatValue());
            else if (BigDecimal.class.equals(clazz))
                return new BigDecimal(number.doubleValue());
            else if (BigInteger.class.equals(clazz))
                return new BigInteger(number.toString());
           // else
            // TODO log warn
             
        } else if (isDateTime(clazz)) {
          return handleAsDate(clazz,value,customizeInfo,method);
        } else if (clazz.isEnum()) {
            String sValue = (value instanceof Map) ? ((String)((Map)value).get("_name")) : (String) value;
            try{
              return Enum.valueOf(clazz, sValue);
            }catch(Throwable th){
              // try in value fields
              for(Object conzt: clazz.getEnumConstants()){
            try{
              // If xml value annotation use it.
              if(clazz.getDeclaredField(((Enum<?>)conzt).name()).
                  getAnnotation(XmlEnumValue.class).value().equals(sValue)){
                return conzt;
              }
            }catch(Throwable t){};
          }
              // user error?
              return null;
            }
        } else if (value instanceof String) {
            String sValue = (String) value;
            if(sValue.trim().isEmpty() && Number.class.isAssignableFrom(clazz)){
              sValue = "0";
              if(traceEnabled)traceLog.warn("Empty string passed for number. Converting it to zero. Field : " + method.getName());
            }
            if (String.class.equals(clazz))
              return value;
            else if (Boolean.TYPE.equals(clazz))
                return Boolean.parseBoolean(sValue);
            else if (Boolean.class.equals(clazz))
                return Boolean.valueOf(sValue);
            else if (Short.TYPE.equals(clazz)   || Short.class.equals(clazz))
                return Short.decode(sValue);
            else if (Byte.TYPE.equals(clazz)   || Byte.class.equals(clazz))
                return Byte.decode(sValue);
            else if (Integer.TYPE.equals(clazz) || Integer.class.equals(clazz))
                return Integer.decode(sValue);
            else if (Long.TYPE.equals(clazz|| Long.class.equals(clazz))
                return Long.decode(sValue);
            else if (Float.TYPE.equals(clazz))
                return Float.parseFloat(sValue);
            else if (Float.class.equals(clazz))
                return Float.valueOf(sValue);
            else if (Double.TYPE.equals(clazz))
                return Double.parseDouble(sValue);
            else if (Double.class.equals(clazz))
                return Double.valueOf(sValue);
            else if (BigDecimal.class.equals(clazz))
                return new BigDecimal(sValue);
            else if (BigInteger.class.equals(clazz))
                return new BigInteger(sValue);
            else if (Character.TYPE.equals(clazz) || Character.class.equals(clazz)) {
                char charValue = 0;
                if (sValue.length() > 0) {
                    charValue = sValue.charAt(0);
                }
                if (Character.TYPE.equals(clazz))
                    return charValue;
                else
                    return new Character(charValue);
            } else if (clazz.equals(Locale.class)) {
                String[] components = sValue.split("_", 2);
                if (components.length == 2) {
                    return new Locale(components[0], components[1]);
                } else {
                    return new Locale(sValue);
                }
            } else if (Enum.class.isAssignableFrom(clazz)) {
                return Enum.valueOf(clazz, sValue);
            }
        } else if(clazz != null && !clazz.isPrimitive() /*Boolean reaches here*/ && !clazz.isAssignableFrom(value.getClass())){
          // TODO log. Value not assignable from the value. The ignore value. TODO add trace log
          return null;
        }
        return value;
    }
   
    /**
     * Utility methof to handle date
     * @param clazz
     * @param value
     * @param customizeInfo
     * @param method
     * @return
     */
    private Object handleAsDate(final Class<?> clazz,final  Object dateValue,final JSONWebService customizeInfo,final Method method){
      Date date = null;
      String dateStr = dateValue != null ? String.valueOf(dateValue) : null;
      if(this.dateFormat != DateFormat.PLAIN){
        String timePattern = (customizeInfo != null && customizeInfo.format().length() > 0) ?
                customizeInfo.format() : this.dateFormat.getFormat();
        // ISO, RFC3339 and CUSTOM format handled here.
            if(timePattern == null || timePattern.trim().isEmpty() ){
              // ISO format
              // select the time pattern to use:
              if (dateStr.length() == 10) {
                  timePattern = "yyyy-MM-dd";
              } else if (dateStr.length() == 15) {
                  timePattern = "yyyy-MM-ddHH:mm";
              } else if (dateStr.length() == 16) {
                  timePattern = "yyyy-MM-dd'T'HH:mm";
              } else if (dateStr.length() == 18) {
                  timePattern = "yyyy-MM-ddHH:mm:ss";
              } else if (dateStr.length() == 19) {
                  timePattern = "yyyy-MM-dd'T'HH:mm:ss";
              } else if (dateStr.length() > 10 && dateStr.charAt(10) == 'T') {
                if(dateStr.length() > 19 && (dateStr.charAt(19) == '+' || dateStr.charAt(19) == '-')){
                  timePattern = "yyyy-MM-dd'T'HH:mm:ssZ";// time
                }else{
                  timePattern = "yyyy-MM-dd'T'HH:mm:ssz";
                }
              } else {
                  timePattern = "yyyy-MM-ddHH:mm:ssz";
              }
              if(timePattern.endsWith("Z") && dateStr.length() > 22 && dateStr.charAt(22) == ':'){
                // time zone input follows ":" pattern
                StringBuffer buf = new StringBuffer(dateStr);
                dateStr = buf.replace(22, 23, "").toString();
              }
            }
            // Format the current time.
            SimpleDateFormat formatter = new SimpleDateFormat(timePattern);

            Date d = null;
            try {
                d = formatter.parse(dateStr, new ParsePosition(0));
            } catch (Exception e) {
              if(this.traceEnabled)
                traceLog.warn(
                    String.format("Failed to parse date string \"%s\" using pattern \"%s\". Cause: %s",dateStr,timePattern,e.getMessage()));
            }
            return d;
      }else{
        try{
          date = new Date(new Long(dateStr));
        }catch(Exception  e){
          if(traceEnabled)traceLog.warn(
              "PLAIN date format specifed. But input date string is not number.(time in milliseconds): " + method.getName());
        }
      }
      if(date == null){
        return null;
      }else if(clazz.equals(Timestamp.class)){
        return new Timestamp(date.getTime());
      }else if(clazz.equals(Calendar.class)){
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(date.getTime());
        return cal;
      }else if(clazz.equals(java.sql.Date.class)){
        return new java.sql.Date(date.getTime());
      }else{
        return date;
      }
    }
}
TOP

Related Classes of com.jaxws.json.codec.decode.WSJSONPopulator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.