Package play.modules.siena

Source Code of play.modules.siena.SienaModelUtils

/**
*
*/
package play.modules.siena;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import play.Logger;
import play.Play;
import play.data.binding.BeanWrapper;
import play.data.binding.Binder;
import play.data.binding.RootParamNode;
import play.data.validation.Validation;
import play.db.Model;
import play.db.Model.Property;
import play.exceptions.UnexpectedException;
import siena.ClassInfo;
import siena.Filter;
import siena.Json;
import siena.PersistenceManager;
import siena.Query;
import siena.SienaException;
import siena.embed.Embedded;

import com.google.gson.JsonParseException;
import com.mysql.jdbc.Util;

/**
* Static functions to create/edit a Siena model
*
* @author Pascal Voitot <pascal.voitot@mandubian.org>
*
*/
public class SienaModelUtils {
  private static final long serialVersionUID = 949918995355310821L;

  public static <T> T create(Class<T> type, String name,
      Map<String, String[]> params) {
    return (T) create(type, name, params, new Annotation[0]);
  }
 
  public static <T> T create(Class<T> type, String name,
      Map<String, String[]> params, Annotation[] annotations) {
    T model = siena.Util.createObjectInstance(type);
    return (T) edit(model, name, params, annotations);
  }
 
 
  public static Field keyField(Class<?> clazz) {
        return ClassInfo.getClassInfo(clazz).getIdField();
    }
 
  public static Class<?> keyType(Class<?> clazz) {
    Field f = keyField(clazz);
        return (f == null) ? null : f.getType();
    }
 
  public static String keyName(Class<?> clazz) {
        Field f = keyField(clazz);
        return (f == null) ? null : ClassInfo.getSimplestColumnName(f);
    }
 
  public static Object keyValue(Object obj) {
    if(obj == null) return null;
        Field k = keyField(obj.getClass());
        return null != k ? siena.Util.readField(obj, k) : null;
    }
 
  public static <T> T edit(T o, String name, Map<String, String[]> params, Annotation[] annotations) {
    try {
      BeanWrapper bw = new BeanWrapper(o.getClass());
      // Start with relations
      Class<?> spClazz = o.getClass();
      /*Set<Field> fields = new HashSet<Field>();
      while (spClazz!=null) {
        Collections.addAll(fields, spClazz.getDeclaredFields());
        spClazz = spClazz.getSuperclass();
      }*/
      for (Field field : ClassInfo.getClassInfo(spClazz).allExtendedFields) {
        boolean isEntity = false;
        boolean isJson = false;
        String relation = null;
        boolean multiple = false;
        String owner = null;
        Class<?> clazz = field.getType();

        // ONE TO MANY or ONE TO ONE association
        if(ClassInfo.isModel(clazz)) {
          isEntity = true;
          relation = clazz.getName();
        }

        // MANY TO ONE association
        // type QUERY<T> + annotation @Filter
        else if(siena.Query.class.isAssignableFrom(clazz)){
          isEntity = true;
          multiple = true;
          Class<?> fieldType =
            (Class<?>) ((ParameterizedType)
                field.getGenericType()).getActualTypeArguments()[0];
          relation = fieldType.getName();
          owner = field.getAnnotation(Filter.class).value();
          // by default, takes the type of the parent entity in lower case
          if(owner == null || "".equals(owner)){
            owner = o.getClass().getName().toLowerCase();
          }
        }
        else if(Json.class.isAssignableFrom(clazz)){
          isJson = true;
        }
        else if(field.isAnnotationPresent(Embedded.class)){
          if(List.class.isAssignableFrom(clazz)){
            multiple = true;
                }
          else if(Map.class.isAssignableFrom(clazz)){
            multiple = true;
                }
                else {
                  multiple = false;
                }
        }
        else if(byte[].class.isAssignableFrom(clazz)
            /*|| Blob.class.isAssignableFrom(field.getType())*/)
        {
          // if params is present but empty, resets the older value
          @SuppressWarnings("unused")
          String[] posted = params.get(name + "." + field.getName());
          // TODO
          @SuppressWarnings("unused")
          Object val = field.get(o)
          //params.put(name + "." + field.getName(), val);
        }
       
        if (isEntity) {
          // builds entity list for many to one
          if (multiple) {
            // retrieves list to synchronize new and removed objects
            Query<?> q = (Query<?>)siena.Util.readField(o, field);
            // no limitation for the time being
            List<?> relObjs = q.fetch();

            @SuppressWarnings("unchecked")
            Class<? extends siena.Model> relClass = (Class<? extends siena.Model>)Play.classloader.loadClass(relation);
            String idName = keyName(relClass);
            String[] ids = params.get(name + "." + field.getName() + "@"+idName);
            if(ids == null) {
              ids = params.get(name + "." + field.getName() + "."+idName);
            }

            if (ids != null) {             
              params.remove(name + "." + field.getName() + "."+idName);
              params.remove(name + "." + field.getName() + "@"+idName);
             
              Field ownerField = siena.Util.getField(relClass, owner);
              for (String _id : ids) {
                if (_id.equals("")) {
                  continue;
                }
                Object idVal = Binder.directBind(_id, keyType(relClass));
               
                // verifies the field is not already owned by the object
                // if yes, no need to resave it with this owner
                boolean b = false;
                for(Object relObj:relObjs){
                  Object keyRelObj = keyValue(relObj);
                  if(keyRelObj != null && keyRelObj.equals(idVal)){
                    relObjs.remove(relObj);
                    b = true;
                    break;
                  }
                }
                if(!b){
                  siena.Model res =
                    siena.Model.all(relClass)
                      .filter(idName, idVal)
                      .get();
                  if(res!=null){
                    // sets the object to the owner field into the relation entity
                   
                    if(ownerField == null) {
                      throw new UnexpectedException("In related Model "+relClass.getName()+" owner field '"+owner+"' not found");
                    }
                    siena.Util.setField(res, ownerField, o);
                    res.save();
                  }
                   
                  else Validation.addError(name+"."+field.getName(), "validation.notFound", _id);
                }
               
              }
              // now remaining objects have to be unowned
              for(Object relObj:relObjs){
                siena.Util.setField(relObj, ownerField, null);
                SienaPlugin.pm().save(relObj);
              }

              // can't set arraylist to Query<T>
              // bw.set(field.getName(), o, l);
            }
          }
          // builds simple entity for simple association
          else {
            @SuppressWarnings("unchecked")
            Class<? extends siena.Model> relClass = (Class<? extends siena.Model>)Play.classloader.loadClass(relation);
            String idName = keyName(relClass);
            String[] ids = params.get(name + "." + field.getName() + "@"+idName);
            if(ids == null) {
              ids = params.get(name + "." + field.getName() + "."+idName);
            }
            if (ids != null && ids.length > 0 && !ids[0].equals("")) {
              params.remove(name + "." + field.getName() + "."+idName);
              params.remove(name + "." + field.getName() + "@"+idName);

              siena.Model res =
                siena.Model.all(relClass)
                  .filter(idName, Binder.directBind(ids[0], keyType(relClass)))
                  .get();
              if(res!=null)
                bw.set(field.getName(), o, res);
              else Validation.addError(name+"."+field.getName(), "validation.notFound", ids[0]);

            } else if(ids != null && ids.length > 0 && ids[0].equals("")) {
              bw.set(field.getName(), o , null);
              params.remove(name + "." + field.getName() + "."+idName);
              params.remove(name + "." + field.getName() + "@"+idName);
            }
          }                   
        }
        else if(isJson){
          String[] jsonStr = params.get(name + "." + field.getName());
          if (jsonStr != null && jsonStr.length > 0 && !jsonStr[0].equals("")) {
            try {
              //com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
              //parser.parse(jsonStr[0]);
             
              Json json = Json.loads(jsonStr[0]);
              if(json!=null){
                bw.set(field.getName(), o, json);
                params.remove(name + "." + field.getName());
              }
              else Validation.addError(name+"."+field.getName(), "validation.notParsable");
            }catch(JsonParseException ex){
              ex.printStackTrace();
              Logger.error("json parserdelete exception:%s",
                  ex.getCause()!=null?ex.getCause().getMessage(): ex.getMessage());
              Validation.addError(
                  name+"."+field.getName(),
                  "validation.notParsable",
                  ex.getCause()!=null?ex.getCause().getMessage(): ex.getMessage());
            }catch(SienaException ex){
              ex.printStackTrace();
              Logger.error("json parserdelete exception:%s",
                  ex.getCause()!=null?ex.getCause().getMessage(): ex.getMessage());
              Validation.addError(
                  name+"."+field.getName(),
                  "validation.notParsable",
                  ex.getCause()!=null?ex.getCause().getMessage(): ex.getMessage());
            }
            catch(IllegalArgumentException ex){
              ex.printStackTrace();
              Logger.error("json parser exception:%s",
                  ex.getCause()!=null?ex.getCause().getMessage(): ex.getMessage());
              Validation.addError(
                  name+"."+field.getName(),
                  "validation.notParsable",
                  ex.getCause()!=null?ex.getCause().getMessage(): ex.getMessage());
            }
          }
        } 
      }
      // Then bind
      // all composites objects (simple entity, list and maps) are managed
      // by this function
      // v1.0.x code
      // bw.bind(name, o.getClass(), params, "", o);

            RootParamNode paramNode = RootParamNode.convert(params);
            // no http params to bind this object ? so we won't try to bind this object
            if(paramNode.getChild(name) == null) {
                return null;
            }
      // v1.1 compliant

            bw.bind(name, (Type)o.getClass(), params, "", o, o.getClass().getAnnotations());
     
      return (T) o;
    } catch (Exception e) {
      throw new UnexpectedException(e);
    }
  }

 
  @SuppressWarnings({ "rawtypes" })
  public static List<Class> getSienaClasses(){
    // we must at list try to find the siena classes...
        // the classes extending siena.Model
    List<Class> classes = Play.classloader.getAssignableClasses(siena.Model.class);
        // the classes having @Entity       
        List<Class> entityClasses = Play.classloader.getAnnotatedClasses(siena.Entity.class);
       
        // uses a set to prevent duplicate classes easily
        // adds only the classes not yet in the list (removes those who have both conditions)
       
        for(Class<?> cl: entityClasses){
          if(!classes.contains(cl)){
            classes.add(cl);
          }
        }
       
        return classes;
  }
 
  public static List<Property> listProperties(final PersistenceManager pm, final Class<?> clazz) {
    List<Model.Property> properties = new ArrayList<Model.Property>();
        Set<Field> fields = new LinkedHashSet<Field>();
        // can't use classInfo.allFields as we need also Query fields
        // TODO superclass fields?
        /*for(Field f:clazz.getDeclaredFields()){
          if(f.getType() == Class.class ||
              (f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT ||
              (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC ||
              f.isSynthetic())
          {        
            continue;
          }
          fields.add(f);
        }*/

        for (Field f : ClassInfo.getClassInfo(clazz).allExtendedFields) {
            Model.Property mp = buildProperty(f, pm);
            if (mp != null) {
                properties.add(mp);
            }
        }
        return properties;
  }
 
  public static Model.Property buildProperty(final Field field, final PersistenceManager pm) {
        Model.Property modelProperty = new Model.Property();
        final Class<?> clazz = field.getType();
       
        modelProperty.type = clazz;
        modelProperty.field = field;
        // ONE-TO-ONE / MANY-TO-ONE
        if (ClassInfo.isModel(clazz)) {
          modelProperty.isRelation = true;
            modelProperty.relationType = clazz;
            modelProperty.choices = new Model.Choices() {

                @SuppressWarnings("unchecked")
                public List<Object> list() {
                  return (List<Object>)pm.createQuery(clazz).fetch();
                }
            };
        }
        // AUTOMATIC QUERY
        // ONE-TO-MANY
        if (Query.class.isAssignableFrom(clazz)) {
            final Class<?> fieldType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
           
            modelProperty.isRelation = true;
            modelProperty.isMultiple = true;
            modelProperty.relationType = fieldType;
            modelProperty.choices = new Model.Choices() {
              @SuppressWarnings("unchecked")
              public List<Object> list() {
                return (List<Object>)pm.createQuery(fieldType).fetch();
              }
            };
        }
       
        // ENUM
        if (clazz.isEnum()) {
            modelProperty.choices = new Model.Choices() {
                @SuppressWarnings("unchecked")
                public List<Object> list() {
                    return (List<Object>) Arrays.asList(clazz.getEnumConstants());
                }
            };
        }
       
        // JSON
        if (Json.class.isAssignableFrom(clazz)) {
            modelProperty.type = String.class;
        }

        if (field.isAnnotationPresent(Embedded.class)) {
          if(List.class.isAssignableFrom(clazz)){
            final Class<?> fieldType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
           
            modelProperty.isRelation = true;
                modelProperty.isMultiple = true;
                modelProperty.relationType = fieldType;
          }
          else if(Map.class.isAssignableFrom(clazz)){
            // gets T2 for map<T1,T2>
            final Class<?> fieldType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1];
            modelProperty.isRelation = true;
                modelProperty.isMultiple = true;
                modelProperty.relationType = fieldType;
          }
          else {
            modelProperty.isRelation = true;
            modelProperty.isMultiple = false;
            modelProperty.relationType = clazz;
          }
        }
       
        modelProperty.name = field.getName();
        if (clazz.equals(String.class)) {
            modelProperty.isSearchable = true;
        }
        if(ClassInfo.isGenerated(field)){
          modelProperty.isGenerated = true;
        }
        return modelProperty;
    }
 
  public static <T> T findById(Class<T> clazz, Object id) {
    if (id == null) {
            return null;
        }
    try {
      // needs to manage UUID because directBind doesn't do it
      Class<?> kt = keyType(clazz);
      if(kt == UUID.class){
        return SienaPlugin.pm().getByKey(clazz, UUID.fromString(id.toString()));
      }else {
        return SienaPlugin.pm().getByKey(clazz,
                Binder.directBind(id.toString(), keyType(clazz)));
      }
        } catch (Exception e) {
            // Key is invalid, thus nothing was found
            return null;
        }
  }
 
  /*
     * code directly inspired from Morphia Play Plugin
     * https://github.com/greenlaw110/play-morphia
     *
     * Support the following syntax at the moment: property = 'val' property
     * in ('val1', 'val2' ...) prop1 ... and prop2 ...
     */
  public static <T> void processWhere(Query<T> q, String where) {
        if (null != where) {
            where = where.trim();
        } else {
            where = "";
        }
        if ("".equals(where) || "null".equalsIgnoreCase(where))
            return;
       
        String[] propValPairs = where.split("(and|&&)");
        for (String propVal : propValPairs) {
            if (propVal.contains("=")) {
                String[] sa = propVal.split("=");
                if (sa.length != 2) {
                    throw new IllegalArgumentException(
                            "invalid where clause: " + where);
                }
                String prop = sa[0];
                String val = sa[1];
                Logger.trace("where prop val pair found: %1$s = %2$s",
                        prop, val);
                prop = prop.replaceAll("[\"' ]", "");
                if (val.matches("[\"'].*[\"']")) {
                    // string value
                    val = val.replaceAll("[\"' ]", "");
                    q.filter(prop, val);
                } else {
                    // possible string, number or boolean value
                    if (val.matches("[-+]?\\d+\\.\\d+")) {
                        q.filter(prop, Float.parseFloat(val));
                    } else if (val.matches("[-+]?\\d+")) {
                        q.filter(prop, Integer.parseInt(val));
                    } else if (val
                            .matches("(false|true|FALSE|TRUE|False|True)")) {
                        q.filter(prop, Boolean.parseBoolean(val));
                    } else {
                        q.filter(prop, val);
                    }
                }
            } else if (propVal.contains(" in ")) {
                String[] sa = propVal.split(" in ");
                if (sa.length != 2) {
                    throw new IllegalArgumentException(
                            "invalid where clause: " + where);
                }
                String prop = sa[0].trim();
                String val0 = sa[1].trim();
                if (!val0.matches("\\(.*\\)")) {
                    throw new IllegalArgumentException(
                            "invalid where clause: " + where);
                }
                val0 = val0.replaceAll("[\\(\\)]", "");
                String[] vals = val0.split(",");
                List<Object> l = new ArrayList<Object>();
                for (String val : vals) {
                    // possible string, number or boolean value
                    if (val.matches("[-+]?\\d+\\.\\d+")) {
                        l.add(Float.parseFloat(val));
                    } else if (val.matches("[-+]?\\d+")) {
                        l.add(Integer.parseInt(val));
                    } else if (val
                            .matches("(false|true|FALSE|TRUE|False|True)")) {
                        l.add(Boolean.parseBoolean(val));
                    } else {
                        l.add(val);
                    }
                }
                q.filter(prop + " IN ", l);
            } else {
                throw new IllegalArgumentException("invalid where clause: "
                        + where);
            }
        }
    }
 
  public static <T> List<T> fetch(PersistenceManager pm, Class<T> clazz,
      int offset, int size,
      String orderBy,  String order,
      List<String> searchFields, String keywords, String where) {
    Query<T> q = pm.createQuery(clazz);   
    Field keyField = keyField(clazz);
    // ORDER
    if(orderBy == null) {
      if (order == null) {
        q.order(keyField.getName());
      }
      else {
        if(order.equals("+")){
          q.order(keyField.getName());
        }
        else if(order.equals("-")){
          q.order(order+keyField.getName());
        }
        else if(order.equals("ASC")){
          q.order(keyField.getName());
       
        else if(order.equals("DESC")){
          q.order("-"+keyField.getName());
        }
        else {
          q.order(keyField.getName());
        }
      }
    }
    else {
      if (order == null) {
        q.order(orderBy);
      }
      else {
        if(order.equals("+")){
          q.order(orderBy);
        }else if(order.equals("-")){
          q.order(order+orderBy);
        }
        else if(order.equals("ASC")){
          q.order(orderBy);
        }
        else if(order.equals("DESC")){
          q.order("-"+orderBy);
        }
        else {
          q.order(orderBy);
        }
      }
    }
   
    // SEARCH
    // TODO define the search strings
    if(keywords != null){
      if(searchFields != null && searchFields.size() != 0){
        q.search(keywords, (String[])searchFields.toArray());
      }else{
        ClassInfo ci = ClassInfo.getClassInfo(clazz);
        String[] strs = new String[ci.allFields.size()];
        int i=0;
        for(Field f : ClassInfo.getClassInfo(clazz).allFields){
          strs[i++] = f.getName();
        }
        q.search(keywords, strs);
      }
    }
   
    // WHERE
    processWhere(q, where);
   
    return q.fetch(size, offset);
  }

  public static <T> Long count(PersistenceManager pm, Class<T> clazz,
      List<String> searchFields,
      String keywords, String where) {
    Query<T> q = pm.createQuery(clazz);
   
    // SEARCH
    // TODO define the search strings
    if(keywords != null){
      if(searchFields != null && searchFields.size() != 0){
        q.search(keywords, (String[])searchFields.toArray());
      }else{
        ClassInfo ci = ClassInfo.getClassInfo(clazz);
        String[] strs = new String[ci.allFields.size()];
        int i=0;
        for(Field f : ClassInfo.getClassInfo(clazz).allFields){
          strs[i++] = f.getName();
        }
        q.search(keywords, strs);
      }
    }
   
    // WHERE
    processWhere(q, where);
   
    return new Long(q.count());
  }

  public static <T> void deleteAll(PersistenceManager pm, Class<T> clazz) {
    pm.createQuery(clazz).delete();
  }
}
TOP

Related Classes of play.modules.siena.SienaModelUtils

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.