content: package com.taobao.yugong.common.db;
  
  import java.lang.reflect.Array;
  import java.math.BigDecimal;
  import java.text.MessageFormat;
  import java.util.Date;
  import java.util.List;
  
  import org.apache.commons.lang.ObjectUtils;
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.lang.SystemUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.taobao.yugong.common.audit.RecordDumper;
  import com.taobao.yugong.common.db.meta.ColumnValue;
  import com.taobao.yugong.common.model.record.Record;
  import com.taobao.yugong.exception.YuGongException;
  
  /**
   * 记录对比
   *
   * @author agapple 2013-9-29 下午2:56:39
   */
  public class RecordDiffer {
  
      private static final String SEP           = SystemUtils.LINE_SEPARATOR;
      private static String       record_format = null;
      private static final Logger diffLogger    = LoggerFactory.getLogger("check");
  
      static {
          record_format = SEP + "-----------------" + SEP;
          record_format += "- Schema: {0} , Table: {1}" + SEP;
          record_format += "-----------------" + SEP;
          record_format += "---Pks" + SEP;
          record_format += "{2}" + SEP;
          record_format += "---diff" + SEP;
          record_format += "\t{3}" + SEP;
      }
  
      public static void diff(Record record1, Record record2) {
          if (record2 == null) {
              diffLogger.info(diffMessage(record1.getSchemaName(),
                  record1.getTableName(),
                  record1.getPrimaryKeys(),
                  "record not found"));
              return;
          }
  
          if (record1.getColumns().size() > record2.getColumns().size()) {
              diffLogger.info(diffMessage(record1.getSchemaName(),
                  record1.getTableName(),
                  record1.getPrimaryKeys(),
                  "column size is great than target column size"));
  
              return;
          }
  
          StringBuilder diff = new StringBuilder();
          boolean same = true;
          int size = record1.getColumns().size();
  
          for (int i = 0; i < size; i++) {
              ColumnValue column = record1.getColumns().get(i);
              same &= campareOneColumn(column, getColumn(record2, column.getColumn().getName()), diff);
          }
  
          if (!same) {
              diffLogger.info(diffMessage(record1.getSchemaName(),
                  record2.getTableName(),
                  record1.getPrimaryKeys(),
                  diff.toString()));
          }
      }
  
      private static boolean campareOneColumn(ColumnValue column1, ColumnValue column2, StringBuilder diff) {
          if (!column1.isCheck() || !column2.isCheck()) {
              return true;// 可能不需要做对比，忽略
          }
  
          Object value1 = column1.getValue();
          Object value2 = column2.getValue();
          if (value1 == null && value2 == null) {
              return true;
          }
  
          StringBuilder message = new StringBuilder();
          message.append(column1.getColumn())
              .append(" , values : [")
              .append(ObjectUtils.toString(value1))
              .append("] vs [")
              .append(ObjectUtils.toString(value2))
              .append("]\n\t");
  
          if ((value1 == null && value2 != null) || (value1 != null && value2 == null)) {
              diff.append(message);
              return false;
          }
  
          if (!value1.equals(value2)) {
              // custom define
              if (value1 instanceof java.util.Date && value2 instanceof java.util.Date) {
                  boolean result = dateValueEquals((java.util.Date) value1, (java.util.Date) value2);
                  if (!result) {
                      String v1 = value1.toString();
                      String v2 = value2.toString();
                      // 2012-02-02 02:02:02 与 2012-02-02 肯定是一种包含关系
                      if (v1.contains(v2) || v2.contains(v1)) {
                          return true;
                      }
                  } else {
                      return true;
                  }
              } else if (Number.class.isAssignableFrom(value1.getClass())
                         && Number.class.isAssignableFrom(value2.getClass())) {
  
                  String v1 = null;
                  String v2 = null;
                  v1 = getNumberString(value1);
                  v2 = getNumberString(value2);
                  boolean result = v1.equals(v2);
                  if (result) {
                      return true;
                  }
              } else if (value1.getClass().isArray() && value2.getClass().isArray()) {
                  boolean result = true;
                  if (Array.getLength(value1) == Array.getLength(value2)) {
                      int length = Array.getLength(value1);
                      for (int i = 0; i < length; i++) {
                          result &= ObjectUtils.equals(Array.get(value1, i), Array.get(value2, i));
                      }
  
                      if (result) {
                          return true;
                      }
                  }
              }
  
              // 其他情况为false
              diff.append(message);
              return false;
          } else {
              return true;
          }
      }
  
      private static String getNumberString(Object value1) {
          String v1;
          if (value1 instanceof BigDecimal) {
              v1 = ((BigDecimal) value1).toPlainString();
              if (StringUtils.indexOf(v1, ".") != -1) {
                  v1 = StringUtils.stripEnd(v1, "0");// 如果0是末尾，则删除之
                  v1 = StringUtils.stripEnd(v1, ".");// 如果.是末尾，则删除之
              }
          } else {
              v1 = value1.toString();
          }
          return v1;
      }
  
      private static boolean dateValueEquals(Date source, Date target) {
          return source.getTime() == target.getTime();
      }
  
      private static ColumnValue getColumn(Record record, String columnName) {
          for (ColumnValue column : record.getColumns()) {
              if (StringUtils.equalsIgnoreCase(columnName, column.getColumn().getName())) {
                  return column;
              }
          }
  
          throw new YuGongException("column[" + columnName + "] is not found.");
      }
  
      private static String diffMessage(String schemaName, String tableName, List<ColumnValue> primaryKeys, String message) {
          return MessageFormat.format(record_format,
              schemaName,
              tableName,
              RecordDumper.dumpRecordColumns(primaryKeys),
              message);
      }
  }
  