Package org.opentides.util

Source Code of org.opentides.util.CrudUtil

/*
   Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.   
*/

package org.opentides.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.opentides.InvalidImplementationException;
import org.opentides.bean.Auditable;
import org.opentides.bean.AuditableField;
import org.opentides.bean.BaseEntity;
import org.opentides.bean.Searchable;
import org.opentides.bean.SystemCodes;
import org.opentides.bean.user.BaseUser;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

/**
* @author allanctan
*
*/
public class CrudUtil {
 
    private static Logger _log = Logger.getLogger(CrudUtil.class);
   
  private static final String SQL_PARAM = ":([^\\s]+)";
  private static final Pattern SQL_PARAM_PATTERN = Pattern.compile(
      SQL_PARAM, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
 
    /**
     * Creates the logging message for new audit logs
     * @param obj
     * @return
     */
    public static String buildCreateMessage(Auditable obj) {
      StringBuffer message = new StringBuffer("Added ");
      String friendlyName = CrudUtil.getReadableName(obj);
      String primary = obj.getPrimaryField().getFieldName();
      message.append(friendlyName// class name
        .append(" ")
        .append(obj.getPrimaryField().getTitle())
        .append(":");
      Object value = retrieveNullableObjectValue(obj, obj.getPrimaryField().getFieldName());
      if (value!=null)
        message.append(value.toString())
        .append(" - ");
      // loop through the fields list
    List<AuditableField> auditFields = CacheUtil.getAuditable(obj);
    int count = 0;
    for (AuditableField property:auditFields) {
      Object ret = retrieveNullableObjectValue(obj, property.getFieldName());
      if (ret!=null && ret.toString().trim().length()>0 && !primary.equals(property.getFieldName())) {
        if (ret.getClass().isAssignableFrom(Date.class)) {
          if (!DateUtil.hasTime((Date) ret))
            ret = DateUtil.dateToString((Date)ret, "MM/dd/yyyy");
        }
        if (count > 0)
          message.append(" and ");
        message.append(property.getTitle())
          .append("=")
          .append(ret.toString());
        count++;
      }
    }
     
      return message.toString();     
    }
   
    /**
     * Creates the friendly message for auditable obj
     * @param obj
     * @return
     */
    public static String buildFriendlyCreateMessage(Auditable obj){
      StringBuffer message = new StringBuffer("Added new ");
      String friendlyName = CrudUtil.getReadableName(obj);
      message.append(friendlyName);
      AuditableField primaryField = obj.getPrimaryField();
      if (primaryField != null){
        Object value = retrieveNullableObjectValue(obj, obj.getPrimaryField().getFieldName());
          if (value!=null)
            message.append(": " + value.toString());
      }
      return message.toString();
    }
   
    /**
     * Creates the logging message for update audit logs
     * @param obj
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
  public static String buildUpdateMessage(Auditable oldObject, Auditable newObject) {
     
      StringBuffer message = new StringBuffer("Changed ");
      String friendlyName = CrudUtil.getReadableName(oldObject);
      message.append(friendlyName// class name
        .append(" ")
        .append(oldObject.getPrimaryField().getTitle())
        .append(":");
      Object value = retrieveNullableObjectValue(oldObject, oldObject.getPrimaryField().getFieldName());
      if (value!=null)
        message.append(value.toString())
        .append(" - ");
      // loop through the fields list
    List<AuditableField> auditFields = CacheUtil.getAuditable(oldObject);
    int count = 0;
    for (AuditableField property:auditFields) {
      Object oldValue = retrieveNullableObjectValue(oldObject, property.getFieldName());
      Object newValue = retrieveNullableObjectValue(newObject, property.getFieldName());
      if (oldValue == null) oldValue = "";
      if (newValue == null) newValue = "";
      if (oldValue instanceof Date) {
        // convert date to timestamp due to since hibernate stores
        // date as timestamp
        oldValue = new Timestamp(((Date) oldValue).getTime());
      }
      if (newValue instanceof Date) {
        // convert date to timestamp due to since hibernate stores
        // date as timestamp
        newValue = new Timestamp(((Date) newValue).getTime());
      }
      if (!oldValue.equals(newValue)) {
        if (count > 0)
          message.append(" and ");
        if("".equals(newValue)) {
          message.append(" removed ")
            .append(property.getTitle());
        } else {
          if (Collection.class.isAssignableFrom(oldValue.getClass()) &&
              Collection.class.isAssignableFrom(newValue.getClass()) ) {
            List addedList = new ArrayList();
            addedList.addAll((List) newValue);
            addedList.removeAll((List) oldValue);
            List removedList = new ArrayList();
            removedList.addAll((List) oldValue);
            removedList.removeAll((List) newValue)
            if (!addedList.isEmpty()) {
              message.append(" added ")
                  .append(property.getTitle())
                  .append(" ")
                  .append(addedList);
            }          
            if (!removedList.isEmpty()) {
              if (!addedList.isEmpty())
                message.append(" and");
              message.append(" removed ")
                  .append(property.getTitle())
                  .append(" ")
                  .append(removedList);           
            }
          } else {
            message.append(property.getTitle());
            // TODO: formatting of date below is not locale specific
            if (oldValue instanceof Timestamp) {
              if (!DateUtil.hasTime((Timestamp) oldValue))
                oldValue = DateUtil.dateToString((Timestamp) oldValue, "MM/dd/yyyy");
            }
            if (newValue instanceof Timestamp) {
              if (!DateUtil.hasTime((Timestamp) newValue))
                newValue = DateUtil.dateToString((Timestamp) newValue, "MM/dd/yyyy");
            }
            if (!StringUtil.isEmpty(oldValue.toString()))
              message.append(" from '")
                  .append(oldValue.toString())
                  .append("'");         
            message.append(" to '")
              .append(newValue.toString())
              .append("'");
          }       
        }
        count++;
      }
    }
      if (count==0)
        return "";
      else
        return message.toString();     
    }
   
    /**
     * Creates the friendly message for update logs
     * @param obj
     * @return
     */
    public static String buildFriendlyUpdateMessage(Auditable obj){
      StringBuffer message = new StringBuffer("Updated ");
      String friendlyName = CrudUtil.getReadableName(obj);
      message.append(friendlyName);
      AuditableField primaryField = obj.getPrimaryField();
      if (primaryField != null){
        Object value = retrieveNullableObjectValue(obj, obj.getPrimaryField().getFieldName());
          if (value!=null)
            message.append(": " + value.toString());
      }
      return message.toString();
    }
   
    /**
     * Creates the logging message for new audit logs
     * @param obj
     * @return
     */
    public static String buildDeleteMessage(Auditable obj) {
      StringBuffer message = new StringBuffer("Deleted ");
      String friendlyName = CrudUtil.getReadableName(obj);
      message.append(friendlyName// class name
        .append(" ")
        .append(obj.getPrimaryField().getTitle())
        .append(":");
      Object value = retrieveNullableObjectValue(obj, obj.getPrimaryField().getFieldName());
      if (value!=null)
        message.append(value.toString());
      return message.toString();     
    }
   
    /**
     *
     * @param obj
     * @return
     */
    public static String buildFriendlyDeleteMessage(Auditable obj){
      StringBuffer message = new StringBuffer("Deleted ");
      String friendlyName = CrudUtil.getReadableName(obj);
      message.append(friendlyName);
      AuditableField primaryField = obj.getPrimaryField();
      if (primaryField != null){
        Object value = retrieveNullableObjectValue(obj, obj.getPrimaryField().getFieldName());
          if (value!=null)
            message.append(": " + value.toString());
      }
      return message.toString();
    }

    /**
     * Builds the query string appended to queryByExample
     * @param example
     * @param exactMatch
     * @return
     */
    @SuppressWarnings("rawtypes")
  public static String buildJpaQueryString(Searchable example, boolean exactMatch) {
    int count = 0;
    StringBuffer clause = new StringBuffer(" where ");
    List<String> exampleFields = CacheUtil.getSearchable(example);
    for (String property:exampleFields) {
      // get the value
      Object ret = retrieveObjectValue(example, property);
      // append the alias
      property = "obj." + property;
      if (ret!=null && ret.toString().trim().length()>0) {
        if (count > 0)
          clause.append(" and ");
        if (String.class.isAssignableFrom(ret.getClass()) && !exactMatch) {
          clause.append(property)
            .append(" like '%")
            .append(StringUtil.escapeSql(ret.toString(), true))
            .append("%' ")
            .append(" escape '\\'")
            ;
        } else if(SystemCodes.class.isAssignableFrom(ret.getClass())) {
          SystemCodes sc = (SystemCodes) ret;
          clause.append(property)
          .append(".key")
          .append(" = '")
          .append(sc.getKey()+"'");
        } else if(BaseEntity.class.isAssignableFrom(ret.getClass())) {
          BaseEntity be = (BaseEntity) ret;
          clause.append(property)
          .append(".id")
          .append(" = ")
          .append(be.getId());
        } else if (Integer.class.isAssignableFrom(ret.getClass()) ||
               Float.class.isAssignableFrom(ret.getClass()) ||
               Long.class.isAssignableFrom(ret.getClass()) ||
               Double.class.isAssignableFrom(ret.getClass()) ||
               BigDecimal.class.isAssignableFrom(ret.getClass()) ||
               Boolean.class.isAssignableFrom(ret.getClass()) ) {
          // numeric types doesn't need to be enclosed in single quotes
          clause.append(property)
            .append(" = ")
            .append(ret.toString());
        } else if (Date.class.isAssignableFrom(ret.getClass())) {
          clause.append(getDateRangeClause(example, property));
        } else if (Class.class.isAssignableFrom(ret.getClass())){
          Class clazz = (Class) ret;
          clause.append(property)
          .append(" = '")
          .append(clazz.getName())
          .append("'");
        } else {
          clause.append(property)
            .append(" = '")
            .append(StringUtil.escapeSql(ret.toString(), false))
            .append("'");
        }
        count++;
      }
    }
      if (count > 0)
        return clause.toString();
      else
        return "";
    }

    public static String getDateRangeClause(Searchable example, String property) {
      StringBuffer clause = new StringBuffer();
      SimpleDateFormat defaultDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      property = property.substring(4); // remove the "obj." part
     
      //try to retrieve dateTo
    try {
      Object dateTo = retrieveObjectValue(example, property + "To");
      if(dateTo != null) {
        clause.append("obj.").append(property)
        .append(" <= '")
        .append(StringUtil.escapeSql(defaultDateFormat.format(dateTo), false))
        .append("'");
      }
    } catch (Exception e) {}
    //try to retrieve dateFrom
    try {
      Object dateFrom = retrieveObjectValue(example, property + "From");
      if(dateFrom != null) {
        if(!clause.toString().isEmpty()) {
          clause.append(" and ");
        }
        clause.append("obj.").append(property)
        .append(" >= '")
        .append(StringUtil.escapeSql(defaultDateFormat.format(dateFrom), false))
        .append("'");
      }
    } catch (Exception e) {}
   
    if(clause.toString().isEmpty()) {
      Object date = retrieveObjectValue(example, property);
      if(date != null) {
        clause.append("obj.").append(property)
        .append(" = '")
        .append(StringUtil.escapeSql(defaultDateFormat.format(date), false))
        .append("'");
      }
    }
    return clause.toString();
  }

  /**
     * Retrieves the property name for a method name.
     * (e.g. getName will return name)
     * @param methodName
     * @return
     */
    public static String getPropertyName(String methodName) {
      if (StringUtil.isEmpty(methodName) || methodName.length()<=3)
        return null;
      if (methodName.startsWith("get") || methodName.startsWith("set")) {
        String prop = methodName.substring(4);
        char c = Character.toLowerCase(methodName.charAt(3));
        return c+prop;
      } else
        return null;
    }
    /**
     * Retrieves the getter method name for a given property.
     * (e.g. name will return getName)
     * @param propertyName
     * @return
     */
    public static String getGetterMethodName(String propertyName) {
      if (StringUtil.isEmpty(propertyName) || propertyName.length()<=0)
        return null;
      char c = Character.toUpperCase(propertyName.charAt(0));
      return "get"+c+propertyName.substring(1);
    }
   
    /**
     * Retrieves the setter method name for a given property.
     * (e.g. name will return setName)
     * @param propertyName
     * @return
     */
    public static String getSetterMethodName(String propertyName) {
      if (StringUtil.isEmpty(propertyName) || propertyName.length()<=0)
        return null;
      char c = Character.toUpperCase(propertyName.charAt(0));
      return "set"+c+propertyName.substring(1);
    }

  /**
   * This method retrieves the object value that corresponds to the property specified.
   * This method can recurse inner classes until specified property is reached.
   *
   * For example:
   * obj.firstName
   * obj.address.Zipcode
   *
   * @param obj
   * @param property
   * @return
   */
  @SuppressWarnings("rawtypes")
  public static Object retrieveObjectValue(Object obj, String property) {
    if (property.contains(".")) {
      // we need to recurse down to final object
      String props[] = property.split("\\.");
      try {
        Object ivalue = null;
        if (Map.class.isAssignableFrom(obj.getClass())) {
          Map map = (Map) obj;
          ivalue = map.get(props[0]);         
        } else {
          Method method = obj.getClass().getMethod(getGetterMethodName(props[0]));
          ivalue = method.invoke(obj);
        }
        if (ivalue==null)
          return null;
        // traverse collection objects
        if (Collection.class.isAssignableFrom(ivalue.getClass())) {
          Iterator iter = ((Collection)ivalue).iterator();
          List<Object> ret = new ArrayList<Object>();
          String prop = property.substring(props[0].length()+1);
          while (iter.hasNext()) {
            Object lvalue = iter.next();
            if (lvalue!=null) {
              ret.add(retrieveObjectValue(lvalue,prop));
            }
          }
          return ret;
        }
        return retrieveObjectValue(ivalue,property.substring(props[0].length()+1));
      } catch (Exception e) {
        throw new InvalidImplementationException("Failed to retrieve value for "+property, e);
      }
    } else {
      // let's get the object value directly
      try {
        if (Map.class.isAssignableFrom(obj.getClass())) {
          Map map = (Map) obj;
          return map.get(property);         
        } else {
          Method method = obj.getClass().getMethod(getGetterMethodName(property));
          return method.invoke(obj);         
        }
      } catch (Exception e) {
        throw new InvalidImplementationException("Failed to retrieve value for "+property, e);
      }
    }
  }
  /**
   *
   * This method retrieves the object value that corresponds to the property specified.
   * This method supports nullable fields.
   *
   * @see retrieveObjectValue
   * @param obj
   * @param property
   * @return
   */
  public static Object retrieveNullableObjectValue(Object obj, String property) {
    try {
      return retrieveObjectValue(obj, property);
    } catch (Exception e) {
      return null;
    }
  }


  /**
   * This method retrieves the object type that corresponds to the property specified.
   * This method can recurse inner classes until specified property is reached.
   *
   * For example:
   * obj.firstName
   * obj.address.Zipcode
   *
   * @param obj
   * @param property
   * @return
   */
  public static Class<?> retrieveObjectType(Object obj, String property) {
    if (property.contains(".")) {
      // we need to recurse down to final object
      String props[] = property.split("\\.");
      try {
        Method method = obj.getClass().getMethod(getGetterMethodName(props[0]));
        Object ivalue = method.invoke(obj);
        return retrieveObjectType(ivalue,property.substring(props[0].length()+1));
      } catch (Exception e) {
        throw new InvalidImplementationException("Failed to retrieve value for "+property, e);
      }
    } else {
      // let's get the object value directly
      try {
        Method method = obj.getClass().getMethod(getGetterMethodName(property));
        return method.getReturnType();
      } catch (Exception e) {
        throw new InvalidImplementationException("Failed to retrieve value for "+property, e);
      }
    }
  }
 
  /**
   * This method evaluates the given expression from the object.
   * This method now uses Spring Expression Language (SpEL).
   *
   * @param obj
   * @param expression
   * @return
   */
  public static Boolean evaluateExpression(Object obj, String expression) {
    if (StringUtil.isEmpty(expression))
      return false;
    try {
      ExpressionParser parser = new SpelExpressionParser();
      Expression exp = parser.parseExpression(expression);
      return exp.getValue(obj, Boolean.class);    
    } catch (Exception e) {
      _log.debug("Failed to evaluate expression ["+expression+"] for object ["+obj.getClass()+"].");
      _log.debug(e.getMessage());
      return false;
    }
  }
 
  /**
   * This method will replace SQL parameters with
   * respective values from the object.
   * @param sql
   * @param obj
   * @return
   */
  @SuppressWarnings("unchecked")
  public static String replaceSQLParameters(String sql, Object obj) {
    // let's get all sql parameter by expression
    Matcher sqlMatcher =  CrudUtil.SQL_PARAM_PATTERN.matcher(sql);
    while (sqlMatcher.find()) {
      String param = sqlMatcher.group(1);
      Object valueObject = CrudUtil.retrieveNullableObjectValue(obj, param);
      if (valueObject==null) {
        sql = sql.replace(sqlMatcher.group(), "null");       
      } else if (String.class.isAssignableFrom(valueObject.getClass())) {
        sql = sql.replace(sqlMatcher.group(), "'"+valueObject.toString()+"'");
      } else if (Collection.class.isAssignableFrom(valueObject.getClass())) {
        Collection<Object> list = (Collection<Object>) valueObject;
        int ctr=0;
        StringBuffer buff = new StringBuffer();
        for (Object item:list) {
          if (ctr++>0)
            buff.append(", ");
          if (SystemCodes.class.isAssignableFrom(item.getClass())) {
            SystemCodes entity  = (SystemCodes) item;
            // use id
            buff.append("'")
              .append(entity.getKey())
              .append("'");
          } else
          if (BaseEntity.class.isAssignableFrom(item.getClass())) {
            BaseEntity entity  = (BaseEntity) item;
            // use id
            buff.append(entity.getId());
          } else
            buff.append("'")
            .append(item.toString())
            .append("'");
        }
        sql = sql.replace(sqlMatcher.group(), buff.toString());       
      } else
        sql = sql.replace(sqlMatcher.group(), valueObject.toString());
    }
    return sql;
  }

  /**
   * Helper method to retrieve all fields of a class including
   * fields declared in its superclass.
   * @param clazz
   * @return
   */
  @SuppressWarnings({ "rawtypes" })
  public static List<Field> getAllFields(Class clazz) {
    List<Field> fields = new ArrayList<Field>();
    if (BaseEntity.class.isAssignableFrom(clazz))
      fields.addAll(getAllFields(clazz.getSuperclass()));
    for (Field field:clazz.getDeclaredFields())
      fields.add(field);
    return fields;
  }
 
  /**
   * Helper method to retrieve a readable name for a given class.
   * This method tries to access static method named readableName and returns its value if exist.
   * Otherwise, the method tries to convert the class to a more readable form.
   * (e.g. InboundDocument becomes Inbound Document);
   * @param entityClass
   * @return
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  public static String getReadableName(Auditable object) {
    String entityClass = object.getClass().getName();     
    try {
      Class clazz = Class.forName(entityClass);
      try {
        Method method = clazz.getMethod("readableName");
                 return method.invoke(object).getClass().getSimpleName();
      } catch (Exception e) {
        String name = clazz.getSimpleName();
        return name.replaceAll("([A-Z])", " $1").trim();
      }
    } catch (ClassNotFoundException e) {
      _log.warn("Attempt to get readableName for unknown class ["+entityClass+"]");
      return "";
    }
  }
 
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public static String getReadableName(Class clazz) {
    try {
      Method method = clazz.getMethod("readableName");
      return method.invoke(null).toString().trim();
    } catch (Exception e) {
      String name = clazz.getSimpleName();
      return name.replaceAll("([A-Z])", " $1").trim();
    }
  }
 
  public static void setObjectValue(Object obj, String property, Object value) {
    if (property.contains(".")) {
      // we need to recurse down to final object
      String props[] = property.split("\\.");
      try {
        Method method = obj.getClass().getMethod(getGetterMethodName(props[0]));
        Object ivalue = method.invoke(obj);
        if(ivalue == null) {
          ivalue = retrieveObjectType(obj, props[0]).newInstance();
        }
        setObjectValue(obj, props[0], ivalue);
        setObjectValue(ivalue, property.substring(props[0].length()+1), value);
      } catch (Exception e) {
        throw new InvalidImplementationException("Failed to set value for "+property, e);
      }
    } else {
      // let's get the object value directly
      try {
        Method method = obj.getClass().getMethod(getSetterMethodName(property), retrieveObjectType(obj, property));
        method.invoke(obj, value);
      } catch (Exception e) {
        throw new InvalidImplementationException("Failed to retrieve value for "+property, e);
      }
    }
  }
 
  public static void main(String[] args) {
    BaseUser bu = new BaseUser();
    CrudUtil.setObjectValue(bu, "credential.username", "username");
    System.out.println(bu.getCredential().getUsername());
  }
}
TOP

Related Classes of org.opentides.util.CrudUtil

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.