公用toString方法

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.ServletRequest;

/**
 * 公用toString方法<br>
 * 将对象的以get为前缀的无参方法转化为字符串<br>
 * 主要用于输出日志信息<br>
 * 示例, 将User对象转换为字符串, 但地址不输出, 密码以*号输出<br>
 * new ToString(user).exclude("address").secrete("password").execute();
 * @author zhaohuihua
 * @version 2012-9-26
 * @since 1.0
 */
public class ToString {

    /**
     * 调试日志
     */
    private static Log log = new Log();

    /**
     * 默认最大缩进层次
     */
    private static final int MAX_INDENT = 3;
    /**
     * get方法正则表达式
     */
    private static final Pattern PTN_GETTER = Pattern.compile("^(?:get|is).+$");
    /**
     * 缩进符的正则表达式
     */
    private static final Pattern PTN_INDENT = Pattern
            .compile("([\\r\\n]+)(\\t*)");
	/**
     * 日期格式字符串
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * 方法比较器, 用于方法按名称排序
     */
    private static final MethodComparator<Method> COMPARATOR = new MethodComparator<Method>();

    /**
     * 有一些特殊字符会影响日志的显示格式, 需要替换为文本形式, 在REPLACE中配置
     */
    private static final Map<Pattern, String> REPLACE = new HashMap<Pattern, String>();
    static {
        // 将回车符 换行符 制表符等替换为文本形式
        REPLACE.put(Pattern.compile("\\f"), "\\\\f");
        REPLACE.put(Pattern.compile("\\r"), "\\\\r");
        REPLACE.put(Pattern.compile("\\n"), "\\\\n");
        REPLACE.put(Pattern.compile("\\t"), "\\\\t");
    }
    /**
     * "getClass" 方法
     */
    private static Method getClass = null;
    static {
        try {
            getClass = Object.class.getMethod("getClass");
        } catch (Exception e) {
            log.warn("init Object.getClass() method error", e);
        }
    }

    /**
     * 待转化的对象
     */
    private Object target;
    /**
     * 缩进层次
     */
    private int indent;
    /**
     * 最大缩进层次
     */
    private int maxIndent;
    /**
     * 有一些字段不希望被输出, 在exclusion中配置
     */
    private List<String> exclusion;
    /**
     * 有一些内容需要保密, 在secrecy中配置
     */
    private List<String> secrecy;

    /**
     * 构造函数
     * @param object待转化的对象
     */
    public ToString(Object object) {
        this(object, MAX_INDENT);
    }

    /**
     * 构造函数
     * @param target 待转化的对象
     * @param indent 最大缩进层次
     */
    public ToString(Object target, int indent) {
        this.target = target;
        this.maxIndent = indent;
        this.exclusion = new ArrayList<String>();
        this.secrecy = new ArrayList<String>();
    }

    /**
     * 有一些字段不希望被输出, 通过这个方法排除
     * @param fields 字段名, 用字段名可匹配方法名和Map中的KEY<br>
     *        如address, 会排除getAddress()方法, 以及Map中KEY=address的值<br>
     *        注意: 这里并不会真正的去取字段名, 只是简单的将首字母大写再加上get|is
     * @return 返回当前对象, 用于连续调用
     */
    public ToString exclude(String... fields) {
        for (String getter : fields) {
            exclusion.add(getter);
        }
        return this;
    }

    /**
     * 有一些内容需要保密, 通过这个方法设置, 设置后将以*号代替内容
     * @param fields 字段名, 用字段名可匹配方法名和Map中的KEY<br>
     *        如password, 会保密getPassword()方法, 以及Map中KEY=password的值<br>
     *        注意: 这里并不会真正的去取字段名, 只是简单的将首字母大写再加上get|is
     * @return 返回当前对象, 用于连续调用
     */
    public ToString secrete(String... fields) {
        for (String getter : fields) {
            secrecy.add(getter);
        }
        return this;
    }

    /**
     * 将对象的以get为前缀的无参方法转化为字符串<br>
     * 主要用于输出日志信息<br>
     * @return String
     */
    public String execute() {
        if (target == null) {
            return null;
        }
        try {
            return toString(target);
        } catch (Exception e) {
            log.warn("To string error", e);
            return ((Object) target).toString();
        }
    }

    /**
     * 将对象的以get为前缀的无参方法转化为字符串
     * @param object 待转化的对象
     * @return String
     */
    @SuppressWarnings("unchecked")
    private String toString(Object object) {
        if (object == null) {
            return null;
        }

        Class<?> clazz = object.getClass();
        if (clazz.isArray()) { // 数组
            return arrayToString(object);
        } else if (CharSequence.class.isAssignableFrom(clazz)
                || clazz == Character.class) { // 字符类型或字符串类型
            return replace(object);
        } else if (clazz == Integer.class || clazz == Long.class
                || clazz == Short.class || clazz == Double.class
                || clazz == Float.class || clazz == Boolean.class
                || clazz == Byte.class) { // 基本类型及其包装类型
            return object.toString();
        } else if (clazz == Class.class) { // Class类型
            return getClassName(object);
        } else if (Date.class.isAssignableFrom(clazz)) { // 日期类型
            DateFormat formater = new SimpleDateFormat(DATE_FORMAT);
            return formater.format((Date) object);
        } else if (Collection.class.isAssignableFrom(clazz)) { // Collection类型
            return toString((Collection<?>) object);
        } else if (object != target && Enabled.class.isAssignableFrom(clazz)) {
            // 已经实现了ToString接口的类型
            String str = object.toString();
            // 增加缩进
            return PTN_INDENT.matcher(str).replaceAll("$1\t$2");
        }

        // 以下为复杂类型, 需要增加缩进了
        if (indent + 1 > maxIndent) { // 递归次数太多则不再转化
            return replace(object.toString());
        }
        indent++;
        String string;
        if (Map.class.isAssignableFrom(clazz)) { // Map类型
            string = toString((Map<Object, Object>) object);
        } else if (ServletRequest.class.isAssignableFrom(clazz)) {
            // ServletRequest类型
            Map map = ((ServletRequest) object).getParameterMap();
            string = toString(map);
        } else {
            // 其他类型
            // 将该对象的类及其父类的所有字段转换为字符串
            // 并将字段值按照对象的处理方式作递归转换
            List<Method> methods = getGetterList(object);
            string = toString(object, methods);
        }
        indent--;
        return string;
    }
    /**
     * 将对象的以get为前缀的无参方法转化为字符串<br>
     * @param object 待转化的对象
     * @param methods 指定的方法列表
     * @return String
     */
    private String toString(Object object, List<Method> methods) {
        if (object == null) {
            return null;
        }
        StringBuilder buffer = new StringBuilder();
        StringBuilder message = new StringBuilder();
        // 遍历方法列表, 依次调用对象的指定方法, 获取返回值
        for (Method method : methods) {
            if (method == null) {
                continue;
            }
            // 排除Static修饰符的方法和非公有的方法
            int modifier = method.getModifiers();
            if (Modifier.isStatic(modifier) || !Modifier.isPublic(modifier)) {
                continue;
            }
            String methodName = method.getName();
            if (method.getReturnType() == void.class // 没有返回结果
                    || method.getParameterTypes().length > 0 // 有参数
                    || !PTN_GETTER.matcher(methodName).matches()) { // 不是get方法
                continue;
            }
            try {
                if (buffer.length() > 0) {
                    buffer.append(", \r\n");
                }
                int index = methodName.startsWith("get") ? 3 : 2;
                // 打印缩进
                for (int i = 0; i < indent; i++) {
                    buffer.append("\t");
                }
                buffer.append(methodName.substring(index)).append(" = ");

                // 调用相应的方法获取返回结果
                if (isSecretMethod(methodName)) { // 保密的方法, 不再求值
                    buffer.append(toPassword(method.getReturnType()));
                    continue;
                }

                Object result = method.invoke(object, new Object[0]);
                Class<?> rclass = result == null ? null : result.getClass();
                // 获取被调用方法的返回值
                if (result == null) {
                    buffer.append("null");
                } else if (Class.class.isAssignableFrom(rclass)) { // Class类型
                    buffer.append(getClassName(object));
                } else if (CharSequence.class.isAssignableFrom(rclass)) {
                    buffer.append(replace(result));
                } else {
                    // 递归调用
                    buffer.append(toString(result));
                }
            } catch (Exception e) {
                if (message.length() > 0) {
                    message.append(", ");
                }
                message.append("Method:[").append(methodName).append("], ")
                        .append(getClassName(e))
                        .append(":[").append(e.getMessage()).append("]");
            }
        }
        if (message.length() > 0) { // 有失败的部分
            message.insert(0, "Get value is failed, ")
                .append("Class:[").append(getClassName(object)).append("], ");
            log.warn(message.toString());
        }
        // 打印换行, 缩进
        buffer.append("\r\n");
        for (int i = 0; i < indent - 1; i++) {
            buffer.append("\t");
        }
        return buffer.insert(0, "{\r\n").append("}").toString();
    }

    private String toString(final Collection<?> list) {
        if (list == null) {
            return "null";
        }
        int length = list.size();
        if (length == 0) {
            return "[]";
        }
        int i = 0;
        StringBuilder buffer = new StringBuilder();
        for (Object item : list) {
            if (i++ >= 20) { // 太长则截断
                buffer.append(", ... (").append(length).append(") ");
                break;
            }
            if (buffer.length() > 0) {
                buffer.append(", ");
            }
            buffer.append(toString(item));
        }
        return buffer.insert(0, "[").append("]").toString();
    }

    private String toString(final Map<Object, Object> map) {
        if (map == null) {
            return "null";
        }
        if (map.size() == 0) {
            return "[]";
        }
        StringBuilder buffer = new StringBuilder();
        Set<Map.Entry<Object, Object>> set = map.entrySet();
        for (Map.Entry<Object, Object> entry : set) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (key == null) {
                continue;
            }
            if (isExcludeKey(key)) { // 被排除的KEY
                continue;
            }
            if (buffer.length() > 0) {
                buffer.append(", \r\n");
            }
            // 打印缩进
            for (int i = 0; i < indent; i++) {
                buffer.append("\t");
            }
            buffer.append(key).append(" = ");
            if (isSecretKey(key)) { // 保密的KEY
                buffer.append(toPassword(value)); // 输出*号
            } else {
                buffer.append(toString(value));
            }
        }
        // 打印换行, 缩进
        buffer.append("\r\n");
        for (int i = 0; i < indent - 1; i++) {
            buffer.append("\t");
        }
        return buffer.insert(0, "{\r\n").append("}").toString();
    }

    /**
     * 数组转换为字符串
     * @param array 数组
     * @return 字符串
     */
    private String arrayToString(final Object array) {
        if (array == null) {
            return "null";
        }
        if (!array.getClass().isArray()) {
            return toString(array);
        }
        int length = Array.getLength(array);
        if (length == 0) {
            return "[]";
        }
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < length; i++) {
            if (i >= 20) { // 太长则截断
                buffer.append(", ... (").append(length).append(") ");
                break;
            }
            if (buffer.length() > 0) {
                buffer.append(", ");
            }
            buffer.append(toString(Array.get(array, i)));
        }
        return buffer.insert(0, "[").append("]").toString();
    }

    /**
     * 输出保密字符串
     * @param src 字符串
     * @return 保密字符串
     */
    private String toPassword(Object src) {
        if (src == null) return null;
        return toPassword(src.getClass());
    }

    /**
     * 输出保密字符串
     * @param clazz Class
     * @return 保密字符串
     */
    private String toPassword(Class<?> clazz) {
        if (clazz == null) return null;
        if (clazz.isArray()) { // 是数组
            return "[******]";
        } else {
            return "******";
        }
    }

    /**
     * 判断KEY是否为保密字段
     * @param key 字段
     * @return 是否为保密字段
     */
    private boolean isSecretKey(Object key) {
        return isSpecialKey(secrecy, key);
    }

    /**
     * 判断对象的方法是否为保密方法
     * @param method 方法名称
     * @return 是否为保密方法
     */
    private boolean isSecretMethod(Object method) {
        return isSpecialMethod(secrecy, method);
    }

    /**
     * 判断KEY是否为被排除字段
     * @param key 字段
     * @return 是否为被排除字段
     */
    private boolean isExcludeKey(Object key) {
        return isSpecialKey(exclusion, key);
    }

    /**
     * 判断对象的方法是否为被排除方法
     * @param method 方法名称
     * @return 是否为被排除方法
     */
    private boolean isExcludeMethod(Object method) {
        return isSpecialMethod(exclusion, method);
    }

    /**
     * 判断KEY是否为被排除或保密字段
     * @param list List
     * @param key 字段
     * @return 是否为被排除或保密字段
     */
    private boolean isSpecialKey(List<String> list, Object key) {
        if (key == null) return false;
        // 判断KEY是否为被排除或保密字段
        for (String name : list) {
            if (key.equals(name)) {
                return true; // 排除
            }
        }
        return false;
    }

    /**
     * 判断对象的方法是否为被排除或保密方法
     * @param list List
     * @param method 方法名称
     * @return 是否为被排除或保密方法
     */
    private boolean isSpecialMethod(List<String> list, Object method) {
        if (method == null)
            return false;
        // 判断方法名是否为被排除或保密字段
        for (String getter : list) {
            String name = getter;
            char initial = getter.charAt(0);
            if (initial >= 'a' && initial <= 'z') { // 首字母是小写字母转化为大写
                String lower = String.valueOf(initial);
                String upper = lower.toUpperCase();
                name = name.replaceFirst(lower, upper);
            }
            if (method.equals("get" + name)) {
                return true; // 排除或保密
            }
            if (method.equals("is" + name)) {
                return true; // 排除或保密
            }
        }
        return false;
    }

    /**
     * 将特殊字符替换为文本形式
     * @param obj 待转换的对象
     * @return 转换后的字符串
     */
    private String replace(Object obj) {
        if (obj == null) {
            return null;
        }
        String string = obj.toString();
        // 将特殊字符替换为文本形式
        Iterator<Map.Entry<Pattern, String>> iterator =
            REPLACE.entrySet().iterator();
        while (iterator.hasNext()) { // 遍历Map
            Map.Entry<Pattern, String> entry = iterator.next();
            Pattern pattern = entry.getKey();
            String replacement = entry.getValue();
            string = pattern.matcher(string).replaceAll(replacement);
        }
        if (string.length() > 60) { // 大于60则截取前50个字符
            string = new StringBuilder()
                .append(string.substring(0, 50)).append("... ")
                .append("(").append(string.length()).append(")")
                .toString();
        }
        return string;
    }

    /**
     * 获取指定对象的get方法列表
     * @param object Object
     * @return List<Method>
     */
    private List<Method> getGetterList(Object object) {
        if (object == null) return null;

        // 获取实例的类
        Class<?> clazz = object.getClass();
        // 类的所有方法
        Method[] methodAll = clazz.getMethods();
        // 筛选后的方法
        List<Method> list = new ArrayList<Method>();
        // 遍历所有方法, 只取有返回结果,无参数的get方法
        // 并剔除在被排除方法之列的
        for (Method method : methodAll) {
            if (method == null) continue;
            String methodName = method.getName();
            if (getClass.getName().equals(methodName)) continue;
            if (method.getReturnType() == void.class // 没有返回结果
                    || method.getParameterTypes().length > 0 // 有参数
                    || !PTN_GETTER.matcher(methodName).matches()) { // 不是get方法
                continue;
            }
            if (isExcludeMethod(methodName)) continue;
            list.add(method);
        }
        // 以方法名排序
        Collections.sort(list, COMPARATOR);
        list.add(0, getClass);
        return list;
    }

    private String getClassName(Object obj) {
        if (obj == null) return "null";
        StringBuilder array = new StringBuilder();
        Class<?> clazz;
        if (obj.getClass() == Class.class) {
            clazz = (Class<?>) obj;
        } else {
            clazz = obj.getClass();
        }
        while (clazz.isArray()) {
            clazz = clazz.getComponentType();
            array.append("[]");
        }
        String className = clazz.getName();
        int index = className.lastIndexOf('.') + 1;
        return className.substring(index) + array.toString();
    }

    /**
     * 将对象的以get为前缀的无参方法转化为字符串<br>
     * 主要用于输出日志信息<br>
     * @return String
     */
    public String toString() {
        return this.execute();
    }

    /**
     * 方法比较器, 按照方法名称比较
     */
    private static class MethodComparator<T> implements Comparator<T> {

        public int compare(T o1, T o2) {
            if (o1 instanceof Method && o2 instanceof Method) {
                return ((Method)o1).getName().compareTo(((Method)o2).getName());
            }
            return 0;
        }
    }

    private static class Log {
        public void warn(String msg) {
            System.out.println("[WARN]" + msg);
        }
        public void warn(String msg, Throwable e) {
            System.out.println("[WARN]" + msg);
            e.printStackTrace();
        }
    }

    /**
     * ToString的标记接口<br>
     * 如果xxx已经实现该接口, 则new ToString(xxx)时直接调用xxx.toString()方法
     * @author  zhaohuihua
     * @version  2012-10-23
     */
    public interface Enabled { }

    /**
     * 测试方法
     * @param args 输入参数
     */
    public static void main(String[] args) {
        System.out.println(new ToString(new char[] {'a', '=', '\\', '\n'}));

        System.out.println();

        Map<String, Color> map = new HashMap<String, Color>();
        map.put("black", Color.BLACK);
        map.put("red", Color.RED);
        map.put("blue", Color.BLUE);
        map.put("green", Color.GREEN);
        String string = new ToString(map, 4)
            .exclude("black").secrete("matrix").execute();
        System.out.println(string);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值