All Downloads are FREE. Search and download functionalities are using the official Maven repository.

play.data.binding.Unbinder Maven / Gradle / Ivy

There is a newer version: 2.6.2
Show newest version
package play.data.binding;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import play.libs.I18N;

/** Try to unbind an object to a Map<String,String> */
public class Unbinder {

  public static void unBind(
      Map result, Object src, String name, Annotation[] annotations) {
    if (src == null) {
      return;
    }
    if (src instanceof Class) {
      return;
    }
    unBind(result, src, src.getClass(), name, annotations);
  }

  private static void directUnbind(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    if (!result.containsKey(name)) {
      result.put(name, (src != null ? src.toString() : null));
    }
  }

  private static void unbindArray(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    if (src == null) {
      directUnbind(result, src, srcClazz, name, annotations);
    } else {
      Class clazz = src.getClass().getComponentType();
      int size = Array.getLength(src);
      for (int i = 0; i < size; i++) {
        unBind(result, Array.get(src, i), clazz, name + "[" + i + "]", annotations);
      }
    }
  }

  private static void unbindCollection(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    if (src == null) {
      directUnbind(result, src, srcClazz, name, annotations);
    } else {
      Collection c = (Collection) src;
      if (Map.class.isAssignableFrom(src.getClass())) {
        throw new UnsupportedOperationException("Unbind won't work with maps yet");
      } else {
        int i = 0;
        // We cannot convert it to array, as the class of the array will be object instead of the real object class
        // Moreover the list could contains different classes (all elements extends from a parent class)
        for (Object object : c) {
          unBind(result, object, object.getClass(), name + "[" + (i++) + "]", annotations);
        }
      }
    }
  }

  private static void unbindMap(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    if (src == null) {
      directUnbind(result, null, srcClazz, name, annotations);
    } else {
      Map map = (Map) src;

      for (Map.Entry entry : map.entrySet()) {
        Object key = entry.getKey();
        if (!isDirect(key.getClass())) {
          throw new UnsupportedOperationException("Unbind won't work with indirect map keys yet");
        }
        String paramKey = name + '.' + key.toString();
        Unbinder.unBind(result, entry.getValue(), paramKey, annotations);
      }
    }
  }

  private static void unbindDate(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    // Get the date format from the controller
    boolean isAsAnnotation = false;
    try {
      if (annotations != null) {
        for (Annotation annotation : annotations) {
          if (annotation.annotationType().equals(As.class)) {
            if (Calendar.class.isAssignableFrom(src.getClass())) {
              result.put(
                  name,
                  new SimpleDateFormat(((As) annotation).value()[0])
                      .format(((Calendar) src).getTime()));
            } else {
              result.put(
                  name, new SimpleDateFormat(((As) annotation).value()[0]).format((Date) src));
            }
            isAsAnnotation = true;
          }
        }
      }

    } catch (Exception e) {
      // Ignore
    }

    if (!isAsAnnotation) {
      // We want to use that one so when redirecting it looks ok. We could as well use the DateBinder.ISO8601 but the url looks terrible
      if (Calendar.class.isAssignableFrom(src.getClass())) {
        result.put(
            name, new SimpleDateFormat(I18N.getDateFormat()).format(((Calendar) src).getTime()));
      } else {
        result.put(name, new SimpleDateFormat(I18N.getDateFormat()).format((Date) src));
      }
    }
  }

  private static void internalUnbind(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    // Check for TypeBinder
    boolean isExtendedTypeBinder = false;
    try {
      if (annotations != null) {
        for (Annotation annotation : annotations) {
          if (annotation.annotationType().equals(As.class)) {
            // Check the unbinder param first
            Class> toInstantiate =
                (Class>) ((As) annotation).unbinder();
            if (!(toInstantiate.equals(As.DEFAULT.class))) {
              TypeUnbinder myInstance = toInstantiate.getDeclaredConstructor().newInstance();
              isExtendedTypeBinder = myInstance.unBind(result, src, srcClazz, name, annotations);
            } else {
              // unbinder is default, test if binder handle the unbinder too
              Class> toInstantiateBinder = ((As) annotation).binder();
              if (!(toInstantiateBinder.equals(As.DEFAULT.class))
                  && TypeUnbinder.class.isAssignableFrom(toInstantiateBinder)) {
                TypeUnbinder myInstance =
                    (TypeUnbinder) toInstantiateBinder.getDeclaredConstructor().newInstance();
                isExtendedTypeBinder = myInstance.unBind(result, src, srcClazz, name, annotations);
              }
            }
          }
        }
      }

      if (!isExtendedTypeBinder) {
        unBind(result, src, srcClazz, name, annotations);
      }
    } catch (Exception e) {
      throw new RuntimeException("Object " + srcClazz + " won't unbind field " + name, e);
    }
  }

  private static void unBind(
      Map result,
      Object src,
      Class srcClazz,
      String name,
      Annotation[] annotations) {
    if (isDirect(srcClazz) || src == null) {
      directUnbind(result, src, srcClazz, name, annotations);
    } else if (src.getClass().isArray()) {
      unbindArray(result, src, src.getClass(), name, annotations);
    } else if (Collection.class.isAssignableFrom(src.getClass())) {
      unbindCollection(result, src, src.getClass(), name, annotations);
    } else if (Map.class.isAssignableFrom(src.getClass())) {
      unbindMap(result, src, src.getClass(), name, annotations);
    } else if (Date.class.isAssignableFrom(src.getClass())
        || Calendar.class.isAssignableFrom(src.getClass())) {
      unbindDate(result, src, src.getClass(), name, annotations);
    } else {
      Field[] fields = src.getClass().getDeclaredFields();
      for (Field field : fields) {
        if ((field.getModifiers() & BeanWrapper.notwritableField) != 0) {
          // skip fields that cannot be bound by BeanWrapper
          continue;
        }

        String newName = name + "." + field.getName();
        boolean oldAcc = field.isAccessible();
        field.setAccessible(true);

        // first we try with annotations resolved from property
        List allAnnotations = new ArrayList<>();
        if (annotations != null && annotations.length > 0) {
          allAnnotations.addAll(Arrays.asList(annotations));
        }

        // Add entity field annotation
        Annotation[] propBindingAnnotations = field.getAnnotations();
        if (propBindingAnnotations != null && propBindingAnnotations.length > 0) {
          allAnnotations.addAll(Arrays.asList(propBindingAnnotations));
        }

        try {
          internalUnbind(
              result,
              field.get(src),
              field.getType(),
              newName,
              allAnnotations.toArray(new Annotation[0]));
        } catch (IllegalArgumentException | IllegalAccessException e) {
          throw new RuntimeException(
              "Object " + field.getType() + " won't unbind field " + newName, e);
        } finally {
          field.setAccessible(oldAcc);
        }
      }
    }
  }

  public static boolean isDirect(Class clazz) {
    return clazz.equals(String.class)
        || clazz.equals(Integer.class)
        || Enum.class.isAssignableFrom(clazz)
        || clazz.equals(Boolean.class)
        || clazz.equals(Long.class)
        || clazz.equals(Double.class)
        || clazz.equals(Float.class)
        || clazz.equals(Short.class)
        || clazz.equals(BigDecimal.class)
        || clazz.isPrimitive()
        || clazz.equals(Class.class);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy