com.wl4g.infra.common.lang.TableFormatters Maven / Gradle / Ivy
/*
* Copyright 2017 ~ 2025 the original author or authors. James Wong
*
* Licensed 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 com.wl4g.infra.common.lang;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Table grid formatter.
*
* @author Wangl.sir
* @version v1.0 2019年7月31日
* @since
*/
public class TableFormatters {
public static final int ALIGN_LEFT = 1;// 左对齐
public static final int ALIGN_RIGHT = 2;// 右对齐
public static final int ALIGN_CENTER = 3;// 居中对齐
private int align = ALIGN_CENTER;// 默认居中对齐
private boolean equilong = false;// 默认不等宽
private int padding = 1;// 左右边距默认为1
private char h = '-';// 默认水平分隔符
private char v = '|';// 默认竖直分隔符
private char o = '+';// 默认交叉分隔符
private char s = ' ';// 默认空白填充符
private List data;// 数据
private TableFormatters() {
}
public static TableFormatters build(String[][] data) {
TableFormatters self = new TableFormatters();
self.data = new ArrayList<>(Arrays.asList(data));
return self;
}
/**
* Chain call entry method, T can be String [], List < String >, any entity
* class can not be overloaded due to different Java generics, so here we
* write if instanceof for type determination.
*
* @param data
* @param
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static TableFormatters build(List data) {
TableFormatters self = new TableFormatters();
self.data = new ArrayList<>();
if (data.size() <= 0)
throw new RuntimeException("数据源至少得有一行吧");
Object obj = data.get(0);
if (obj instanceof String[]) {// 如果泛型为String数组,则直接设置
self.data = (List) data;
} else if (obj instanceof List) {// 如果泛型为List,则把list中的item依次转为String[],再设置
int length = ((List) obj).size();
for (Object item : data) {
List col = (List) item;
if (col.size() != length)
throw new RuntimeException("数据源每列长度必须一致");
self.data.add(col.toArray(new String[length]));
}
} else {// 如果泛型为实体类,则利用反射获取get方法列表,从而推算出属性列表。
// 根据反射得来的属性列表设置表格第一行thead
List colList = getColList(obj);
String[] header = new String[colList.size()];
for (int i = 0; i < colList.size(); i++) {
header[i] = colList.get(i).colName;
}
self.data.add(header);
// 利用反射调用相应get方法获取属性值来设置表格tbody
for (int i = 0; i < data.size(); i++) {
String[] item = new String[colList.size()];
for (int j = 0; j < colList.size(); j++) {
String value = null;
try {
Object invoke = obj.getClass().getMethod(colList.get(j).getMethodName).invoke(data.get(i));
if (null == invoke) {
continue;
}
value = null == invoke ? "" : invoke.toString();
// value =
// obj.getClass().getMethod(colList.get(j).getMethodName).invoke(data.get(i)).toString();
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
item[j] = value == null ? "null" : value;
}
self.data.add(item);
}
}
return self;
}
private static class Col {
private String colName;// 列名
private String getMethodName;// get方法名
}
/**
* Getting get method name and attribute name by reflection
*
* @return
*/
private static List getColList(Object obj) {
List colList = new ArrayList<>();
Method[] methods = obj.getClass().getMethods();
for (Method m : methods) {
StringBuilder getMethodName = new StringBuilder(m.getName());
if (getMethodName.substring(0, 3).equals("get") && !m.getName().equals("getClass")) {
Col col = new Col();
col.getMethodName = getMethodName.toString();
char first = Character.toLowerCase(getMethodName.delete(0, 3).charAt(0));
getMethodName.delete(0, 1).insert(0, first);
col.colName = getMethodName.toString();
colList.add(col);
}
}
return colList;
}
/**
* Gets the number of characters occupied by a string
*
* @param str
* @return
*/
private int getStringCharLength(String str) {
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");// 利用正则找到中文
if (StringUtils.isBlank(str)) {
return 4;
}
Matcher m = p.matcher(str);
int count = 0;
while (m.find()) {
count++;
}
return str.length() + count;
}
/**
* Longitudinal traversal to obtain the length of each column of data
*
* @return
*/
private int[] getColLengths() {
int[] result = new int[data.get(0).length];
for (int x = 0; x < result.length; x++) {
int max = 0;
for (int y = 0; y < data.size(); y++) {
int len = getStringCharLength(data.get(y)[x]);
if (len > max) {
max = len;
}
}
result[x] = max;
}
if (equilong) {// 如果等宽表格
int max = 0;
for (int len : result) {
if (len > max)
max = len;
}
for (int i = 0; i < result.length; i++) {
result[i] = max;
}
}
return result;
}
/**
* Get the table string
*
* @return
*/
public String getTableString() {
StringBuilder sb = new StringBuilder();
int[] colLengths = getColLengths();// 获取每列文字宽度
StringBuilder line = new StringBuilder();// 表格横向分隔线
line.append(o);
for (int len : colLengths) {
int allLen = len + padding * 2;// 还需要加上边距和分隔符的长度
for (int i = 0; i < allLen; i++) {
line.append(h);
}
line.append(o);
}
sb.append(line).append("\r\n");
for (int y = 0; y < data.size(); y++) {
sb.append(v);
for (int x = 0; x < data.get(y).length; x++) {
String cell = data.get(y)[x];
switch (align) {
case ALIGN_LEFT:
for (int i = 0; i < padding; i++)
sb.append(s);
sb.append(cell);
for (int i = 0; i < colLengths[x] - getStringCharLength(cell) + padding; i++)
sb.append(s);
break;
case ALIGN_RIGHT:
for (int i = 0; i < colLengths[x] - getStringCharLength(cell) + padding; i++)
sb.append(s);
sb.append(cell);
for (int i = 0; i < padding; i++)
sb.append(s);
break;
case ALIGN_CENTER:
int space = colLengths[x] - getStringCharLength(cell);
int left = space / 2;
int right = space - left;
for (int i = 0; i < left + padding; i++)
sb.append(s);
sb.append(cell);
for (int i = 0; i < right + padding; i++)
sb.append(s);
break;
}
sb.append(v);
}
sb.append("\r\n");
sb.append(line).append("\r\n");
}
return sb.toString();
}
/**
* Print tabular data.
*/
public void print() {
System.out.println(getTableString());
}
public TableFormatters setAlign(int align) {
this.align = align;
return this;
}
public TableFormatters setEquilong(boolean equilong) {
this.equilong = equilong;
return this;
}
public TableFormatters setPadding(int padding) {
this.padding = padding;
return this;
}
public TableFormatters setH(char h) {
this.h = h;
return this;
}
public TableFormatters setV(char v) {
this.v = v;
return this;
}
public TableFormatters setO(char o) {
this.o = o;
return this;
}
public TableFormatters setS(char s) {
this.s = s;
return this;
}
static class User {
String username;
String password;
String name;
User(String username, String password, String name) {
this.username = username;
this.password = password;
this.name = name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
List data1 = new ArrayList<>();
data1.add(new String[] { "用户名", "密码", "姓名" });
data1.add(new String[] { "xiaoming", "xm123", "小明" });
data1.add(new String[] { "xiaohong", "xh123", "小红" });
TableFormatters.build(data1).print();
List> data2 = new ArrayList<>();
data2.add(new ArrayList<>());
data2.add(new ArrayList<>());
data2.add(new ArrayList<>());
data2.get(0).add("用户名");
data2.get(0).add("密码");
data2.get(0).add("姓名");
data2.get(1).add("xiaoming");
data2.get(1).add("xm123");
data2.get(1).add("小明");
data2.get(2).add("xiaohong");
data2.get(2).add("xh123");
data2.get(2).add("小红");
TableFormatters.build(data2).setAlign(TableFormatters.ALIGN_LEFT).setPadding(5).setEquilong(true).print();
List data3 = new ArrayList<>();
data3.add(new User("xiaoming", "xm123", "小明"));
data3.add(new User("xiaohong", "xh123", "小红"));
TableFormatters.build(data3).setH('=').setV('!').print();
}
}