mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-08-18 20:38:02 +08:00
Merge branch 'v5-dev' of https://github.com/totalo/hutool into v5-dev
This commit is contained in:
@@ -252,8 +252,8 @@ public class ListUtil {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((pageNo * pageSize) > resultSize) {
|
||||
// 相乘可能会导致越界 临时用long
|
||||
if (((long)pageNo * pageSize) > resultSize) {
|
||||
// 越界直接返回空
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public class ChineseDate {
|
||||
* @return 是否为闰月
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean isLeapMonth(){
|
||||
public boolean isLeapMonth() {
|
||||
return ChineseMonth.isLeapMonth(this.year, this.month);
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ public class ChineseDate {
|
||||
* @return 获得农历节日
|
||||
*/
|
||||
public String getFestivals() {
|
||||
return StrUtil.join(",", LunarFestival.getFestivals(this.month, this.day));
|
||||
return StrUtil.join(",", LunarFestival.getFestivals(this.year, this.month, day));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +258,7 @@ public class ChineseDate {
|
||||
* @return 获得天干地支的年月日信息
|
||||
*/
|
||||
public String getCyclicalYMD() {
|
||||
if (gyear >= LunarInfo.BASE_YEAR && gmonth > 0 && gday > 0){
|
||||
if (gyear >= LunarInfo.BASE_YEAR && gmonth > 0 && gday > 0) {
|
||||
return (cyclicalm(gyear, gmonth, gday));
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -17,6 +17,7 @@ public class LunarFestival {
|
||||
// 来自:https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD%E4%BC%A0%E7%BB%9F%E8%8A%82%E6%97%A5/396100
|
||||
private static final TableMap<Pair<Integer, Integer>, String> L_FTV = new TableMap<>(16);
|
||||
static{
|
||||
// 节日
|
||||
L_FTV.put(new Pair<>(1, 1), "春节");
|
||||
L_FTV.put(new Pair<>(1, 2), "犬日");
|
||||
L_FTV.put(new Pair<>(1, 3), "猪日");
|
||||
@@ -78,7 +79,6 @@ public class LunarFestival {
|
||||
L_FTV.put(new Pair<>(12, 8), "腊八节");
|
||||
L_FTV.put(new Pair<>(12, 16), "尾牙");
|
||||
L_FTV.put(new Pair<>(12, 23), "小年");
|
||||
L_FTV.put(new Pair<>(12, 29), "除夕");
|
||||
L_FTV.put(new Pair<>(12, 30), "除夕");
|
||||
}
|
||||
|
||||
@@ -88,6 +88,24 @@ public class LunarFestival {
|
||||
* @param month 月
|
||||
* @param day 日
|
||||
* @return 获得农历节日
|
||||
* @since 5.4.5
|
||||
*/
|
||||
public static List<String> getFestivals(int year, int month, int day) {
|
||||
// 春节判断,如果12月是小月,则29为除夕,否则30为除夕
|
||||
if(12 == month && 29 == day){
|
||||
if(29 == LunarInfo.monthDays(year, month)){
|
||||
day++;
|
||||
}
|
||||
}
|
||||
return getFestivals(month, day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得节日列表,此方法无法判断月是否为大月或小月
|
||||
*
|
||||
* @param month 月
|
||||
* @param day 日
|
||||
* @return 获得农历节日
|
||||
*/
|
||||
public static List<String> getFestivals(int month, int day) {
|
||||
return L_FTV.getValues(new Pair<>(month, day));
|
||||
|
||||
151
hutool-core/src/main/java/cn/hutool/core/lang/ConsoleTable.java
Normal file
151
hutool-core/src/main/java/cn/hutool/core/lang/ConsoleTable.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package cn.hutool.core.lang;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 控制台打印表格工具
|
||||
*
|
||||
* @author 孙宇
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public class ConsoleTable {
|
||||
|
||||
private static final char ROW_LINE = '-';
|
||||
private static final char COLUMN_LINE = '|';
|
||||
private static final char CORNER = '+';
|
||||
private static final char SPACE = '\u3000';
|
||||
private static final char LF = CharUtil.LF;
|
||||
|
||||
/**
|
||||
* 表格头信息
|
||||
*/
|
||||
private final List<List<String>> HEADER_LIST = new ArrayList<>();
|
||||
/**
|
||||
* 表格体信息
|
||||
*/
|
||||
private final List<List<String>> BODY_LIST = new ArrayList<>();
|
||||
/**
|
||||
* 每列最大字符个数
|
||||
*/
|
||||
private List<Integer> columnCharNumber;
|
||||
|
||||
/**
|
||||
* 添加头信息
|
||||
*
|
||||
* @param titles 列名
|
||||
* @return 自身对象
|
||||
*/
|
||||
public ConsoleTable addHeader(String... titles) {
|
||||
if (columnCharNumber == null) {
|
||||
columnCharNumber = new ArrayList<>(Collections.nCopies(titles.length, 0));
|
||||
}
|
||||
List<String> l = new ArrayList<>();
|
||||
fillColumns(l, titles);
|
||||
HEADER_LIST.add(l);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加体信息
|
||||
*
|
||||
* @param values 列值
|
||||
* @return 自身对象
|
||||
*/
|
||||
public ConsoleTable addBody(String... values) {
|
||||
List<String> l = new ArrayList<>();
|
||||
BODY_LIST.add(l);
|
||||
fillColumns(l, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充表格头或者体
|
||||
*
|
||||
* @param l 被填充列表
|
||||
* @param columns 填充内容
|
||||
*/
|
||||
private void fillColumns(List<String> l, String[] columns) {
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
String column = columns[i];
|
||||
String col = Convert.toSBC(column);
|
||||
l.add(col);
|
||||
int width = col.length();
|
||||
if (width > columnCharNumber.get(i)) {
|
||||
columnCharNumber.set(i, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表格字符串
|
||||
*
|
||||
* @return 表格字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
fillBorder(sb);
|
||||
fillRow(sb, HEADER_LIST);
|
||||
fillBorder(sb);
|
||||
fillRow(sb, BODY_LIST);
|
||||
fillBorder(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充表头或者表体信息
|
||||
*
|
||||
* @param sb
|
||||
* @param list 表头列表或者表体列表
|
||||
*/
|
||||
private void fillRow(StringBuilder sb, List<List<String>> list) {
|
||||
for (List<String> r : list) {
|
||||
for (int i = 0; i < r.size(); i++) {
|
||||
if (i == 0) {
|
||||
sb.append(COLUMN_LINE);
|
||||
}
|
||||
String header = r.get(i);
|
||||
sb.append(SPACE);
|
||||
sb.append(header);
|
||||
sb.append(SPACE);
|
||||
int l = header.length();
|
||||
int lw = columnCharNumber.get(i);
|
||||
if (lw > l) {
|
||||
for (int j = 0; j < (lw - l); j++) {
|
||||
sb.append(SPACE);
|
||||
}
|
||||
}
|
||||
sb.append(COLUMN_LINE);
|
||||
}
|
||||
sb.append(LF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼装边框
|
||||
*
|
||||
* @param sb StringBuilder
|
||||
*/
|
||||
private void fillBorder(StringBuilder sb) {
|
||||
sb.append(CORNER);
|
||||
for (Integer width : columnCharNumber) {
|
||||
sb.append(Convert.toSBC(StrUtil.fillAfter("", ROW_LINE, width + 2)));
|
||||
sb.append(CORNER);
|
||||
}
|
||||
sb.append(LF);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印到控制台
|
||||
*/
|
||||
public void print() {
|
||||
Console.print(toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
package cn.hutool.core.lang;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 控制台打印表格工具
|
||||
*
|
||||
* @author 孙宇
|
||||
*/
|
||||
public class ConsoleTableUtil {
|
||||
/**
|
||||
* 表格头信息
|
||||
*/
|
||||
private final List<List<String>> HEADER_LIST = new ArrayList<>();
|
||||
/**
|
||||
* 表格体信息
|
||||
*/
|
||||
private final List<List<String>> BODY_LIST = new ArrayList<>();
|
||||
/**
|
||||
* 每列最大字符个数
|
||||
*/
|
||||
private List<Integer> columnCharNumber;
|
||||
|
||||
/**
|
||||
* 测试
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
ConsoleTableUtil t = new ConsoleTableUtil();
|
||||
t.addHeader("姓名", "年龄");
|
||||
t.addBody("张三", "15");
|
||||
t.addBody("李四", "29");
|
||||
t.addBody("王二麻子", "37");
|
||||
t.print();
|
||||
|
||||
t = new ConsoleTableUtil();
|
||||
t.addHeader("体温", "占比");
|
||||
t.addHeader("℃", "%");
|
||||
t.addBody("36.8", "10");
|
||||
t.addBody("37", "5");
|
||||
t.print();
|
||||
|
||||
t = new ConsoleTableUtil();
|
||||
t.addHeader("标题1", "标题2");
|
||||
t.addBody("12345", "混合321654asdfcSDF");
|
||||
t.addBody("sd e3ee ff22", "ff值");
|
||||
t.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加头信息
|
||||
*
|
||||
* @param titles 列名
|
||||
* @return 自身对象
|
||||
*/
|
||||
public ConsoleTableUtil addHeader(String... titles) {
|
||||
if (columnCharNumber == null) {
|
||||
columnCharNumber = new ArrayList<>(Collections.nCopies(titles.length, 0));
|
||||
}
|
||||
List<String> l = new ArrayList<>();
|
||||
HEADER_LIST.add(l);
|
||||
fillColumns(l, titles);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加体信息
|
||||
*
|
||||
* @param values 列值
|
||||
* @return 自身对象
|
||||
*/
|
||||
public ConsoleTableUtil addBody(String... values) {
|
||||
List<String> l = new ArrayList<>();
|
||||
BODY_LIST.add(l);
|
||||
fillColumns(l, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充表格头或者体
|
||||
*
|
||||
* @param l
|
||||
* @param columns
|
||||
*/
|
||||
private void fillColumns(List<String> l, String[] columns) {
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
String column = columns[i];
|
||||
String col = Convert.toSBC(column);
|
||||
l.add(col);
|
||||
int width = col.length();
|
||||
if (width > columnCharNumber.get(i)) {
|
||||
columnCharNumber.set(i, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表格字符串
|
||||
*
|
||||
* @return 表格字符串
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
fillBorder(sb);
|
||||
for (List<String> headers : HEADER_LIST) {
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
if (i == 0) {
|
||||
sb.append('|');
|
||||
}
|
||||
String header = headers.get(i);
|
||||
sb.append(Convert.toSBC(" "));
|
||||
sb.append(header);
|
||||
sb.append(Convert.toSBC(" "));
|
||||
int l = header.length();
|
||||
int lw = columnCharNumber.get(i);
|
||||
if (lw > l) {
|
||||
for (int j = 0; j < (lw - l); j++) {
|
||||
sb.append(Convert.toSBC(" "));
|
||||
}
|
||||
}
|
||||
sb.append('|');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
fillBorder(sb);
|
||||
for (List<String> bodys : BODY_LIST) {
|
||||
for (int i = 0; i < bodys.size(); i++) {
|
||||
if (i == 0) {
|
||||
sb.append('|');
|
||||
}
|
||||
String body = bodys.get(i);
|
||||
sb.append(Convert.toSBC(" "));
|
||||
sb.append(body);
|
||||
sb.append(Convert.toSBC(" "));
|
||||
int l = body.length();
|
||||
int lw = columnCharNumber.get(i);
|
||||
if (lw > l) {
|
||||
for (int j = 0; j < (lw - l); j++) {
|
||||
sb.append(Convert.toSBC(" "));
|
||||
}
|
||||
}
|
||||
sb.append('|');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
fillBorder(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼装边框
|
||||
*
|
||||
* @param sb
|
||||
*/
|
||||
private void fillBorder(StringBuilder sb) {
|
||||
sb.append('*');
|
||||
for (Integer width : columnCharNumber) {
|
||||
sb.append(Convert.toSBC(StrUtil.fillAfter("", '-', width + 2)));
|
||||
sb.append('*');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印到控制台
|
||||
*/
|
||||
public void print() {
|
||||
Console.print(toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1613,7 +1613,7 @@ public class ArrayUtil {
|
||||
public static int indexOf(double[] array, double value) {
|
||||
if (null != array) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (value == array[i]) {
|
||||
if (NumberUtil.equals(value, array[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -1632,7 +1632,7 @@ public class ArrayUtil {
|
||||
public static int lastIndexOf(double[] array, double value) {
|
||||
if (null != array) {
|
||||
for (int i = array.length - 1; i >= 0; i--) {
|
||||
if (value == array[i]) {
|
||||
if (NumberUtil.equals(value, array[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -1663,7 +1663,7 @@ public class ArrayUtil {
|
||||
public static int indexOf(float[] array, float value) {
|
||||
if (null != array) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (value == array[i]) {
|
||||
if (NumberUtil.equals(value, array[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -1682,7 +1682,7 @@ public class ArrayUtil {
|
||||
public static int lastIndexOf(float[] array, float value) {
|
||||
if (null != array) {
|
||||
for (int i = array.length - 1; i >= 0; i--) {
|
||||
if (value == array[i]) {
|
||||
if (NumberUtil.equals(value, array[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -1777,7 +1777,7 @@ public class ArrayUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装类数组转为原始类型数组
|
||||
* 包装类数组转为原始类型数组,null转为0
|
||||
*
|
||||
* @param values 包装类型数组
|
||||
* @return 原始类型数组
|
||||
@@ -1793,7 +1793,7 @@ public class ArrayUtil {
|
||||
|
||||
final int[] array = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = values[i];
|
||||
array[i] = ObjectUtil.defaultIfNull(values[i], 0);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
@@ -1837,7 +1837,7 @@ public class ArrayUtil {
|
||||
|
||||
final long[] array = new long[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = values[i];
|
||||
array[i] = ObjectUtil.defaultIfNull(values[i], 0L);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
@@ -1881,7 +1881,7 @@ public class ArrayUtil {
|
||||
|
||||
char[] array = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = values[i];
|
||||
array[i] = ObjectUtil.defaultIfNull(values[i], Character.MIN_VALUE);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ public class CreditCodeUtil {
|
||||
}
|
||||
for (int i = 2; i < 8; i++) {
|
||||
int num = RandomUtil.randomInt(10);
|
||||
buf.append(Character.toUpperCase(BASE_CODE_ARRAY[num]));
|
||||
buf.append(BASE_CODE_ARRAY[num]);
|
||||
}
|
||||
for (int i = 8; i < 17; i++) {
|
||||
int num = RandomUtil.randomInt(BASE_CODE_ARRAY.length - 1);
|
||||
buf.append(Character.toUpperCase(BASE_CODE_ARRAY[num]));
|
||||
buf.append(BASE_CODE_ARRAY[num]);
|
||||
}
|
||||
|
||||
final String code = buf.toString();
|
||||
|
||||
@@ -1712,7 +1712,7 @@ public class NumberUtil {
|
||||
|
||||
/**
|
||||
* 比较大小,值相等 返回true<br>
|
||||
* 此方法通过调用{@link BigDecimal#compareTo(BigDecimal)}方法来判断是否相等<br>
|
||||
* 此方法通过调用{@link Double#doubleToLongBits(double)}方法来判断是否相等<br>
|
||||
* 此方法判断值相等时忽略精度的,即0.00 == 0
|
||||
*
|
||||
* @param num1 数字1
|
||||
@@ -1721,7 +1721,21 @@ public class NumberUtil {
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public static boolean equals(double num1, double num2) {
|
||||
return equals(toBigDecimal(num1), toBigDecimal(num2));
|
||||
return Double.doubleToLongBits(num1) == Double.doubleToLongBits(num2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较大小,值相等 返回true<br>
|
||||
* 此方法通过调用{@link Double#doubleToLongBits(double)}方法来判断是否相等<br>
|
||||
* 此方法判断值相等时忽略精度的,即0.00 == 0
|
||||
*
|
||||
* @param num1 数字1
|
||||
* @param num2 数字2
|
||||
* @return 是否相等
|
||||
* @since 5.4.5
|
||||
*/
|
||||
public static boolean equals(float num1, float num2) {
|
||||
return Float.floatToIntBits(num1) == Float.floatToIntBits(num2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,8 +48,8 @@ public class RandomUtil {
|
||||
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
|
||||
*
|
||||
* <p>
|
||||
* 注意:此方法返回的{@link ThreadLocalRandom}不可以在多线程环境下共享对象,否则有重复随机数问题。
|
||||
* 见:https://www.jianshu.com/p/89dfe990295c
|
||||
* 注意:此方法返回的{@link ThreadLocalRandom}不可以在多线程环境下共享对象,否则有重复随机数问题。
|
||||
* 见:https://www.jianshu.com/p/89dfe990295c
|
||||
* </p>
|
||||
*
|
||||
* @return {@link ThreadLocalRandom}
|
||||
@@ -522,9 +522,10 @@ public class RandomUtil {
|
||||
*
|
||||
* @return 随机颜色
|
||||
* @since 4.1.5
|
||||
* @deprecated 使用{@link ImagUtil#randomColor()}
|
||||
* @deprecated 使用ImgUtil.randomColor()
|
||||
*/
|
||||
public static Color randomColor() {
|
||||
@Deprecated
|
||||
public static Color randomColor() {
|
||||
final Random random = getRandom();
|
||||
return new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
|
||||
}
|
||||
|
||||
@@ -34,53 +34,95 @@ public class StrUtil {
|
||||
|
||||
public static final int INDEX_NOT_FOUND = -1;
|
||||
|
||||
/** 字符常量:空格符 ' ' */
|
||||
/**
|
||||
* 字符常量:空格符 ' '
|
||||
*/
|
||||
public static final char C_SPACE = CharUtil.SPACE;
|
||||
/** 字符常量:制表符 \t */
|
||||
/**
|
||||
* 字符常量:制表符 \t
|
||||
*/
|
||||
public static final char C_TAB = CharUtil.TAB;
|
||||
/** 字符常量:点 . */
|
||||
/**
|
||||
* 字符常量:点 .
|
||||
*/
|
||||
public static final char C_DOT = CharUtil.DOT;
|
||||
/** 字符常量:斜杠 / */
|
||||
/**
|
||||
* 字符常量:斜杠 /
|
||||
*/
|
||||
public static final char C_SLASH = CharUtil.SLASH;
|
||||
/** 字符常量:反斜杠 \ */
|
||||
/**
|
||||
* 字符常量:反斜杠 \
|
||||
*/
|
||||
public static final char C_BACKSLASH = CharUtil.BACKSLASH;
|
||||
/** 字符常量:回车符 \r */
|
||||
/**
|
||||
* 字符常量:回车符 \r
|
||||
*/
|
||||
public static final char C_CR = CharUtil.CR;
|
||||
/** 字符常量:换行符 \n */
|
||||
/**
|
||||
* 字符常量:换行符 \n
|
||||
*/
|
||||
public static final char C_LF = CharUtil.LF;
|
||||
/** 字符常量:下划线 _ */
|
||||
/**
|
||||
* 字符常量:下划线 _
|
||||
*/
|
||||
public static final char C_UNDERLINE = CharUtil.UNDERLINE;
|
||||
/** 字符常量:逗号 , */
|
||||
/**
|
||||
* 字符常量:逗号 ,
|
||||
*/
|
||||
public static final char C_COMMA = CharUtil.COMMA;
|
||||
/** 字符常量:花括号(左) { */
|
||||
/**
|
||||
* 字符常量:花括号(左) {
|
||||
*/
|
||||
public static final char C_DELIM_START = CharUtil.DELIM_START;
|
||||
/** 字符常量:花括号(右) } */
|
||||
/**
|
||||
* 字符常量:花括号(右) }
|
||||
*/
|
||||
public static final char C_DELIM_END = CharUtil.DELIM_END;
|
||||
/** 字符常量:中括号(左) [ */
|
||||
/**
|
||||
* 字符常量:中括号(左) [
|
||||
*/
|
||||
public static final char C_BRACKET_START = CharUtil.BRACKET_START;
|
||||
/** 字符常量:中括号(右) ] */
|
||||
/**
|
||||
* 字符常量:中括号(右) ]
|
||||
*/
|
||||
public static final char C_BRACKET_END = CharUtil.BRACKET_END;
|
||||
/** 字符常量:冒号 : */
|
||||
/**
|
||||
* 字符常量:冒号 :
|
||||
*/
|
||||
public static final char C_COLON = CharUtil.COLON;
|
||||
/** 字符常量:艾特 @ */
|
||||
/**
|
||||
* 字符常量:艾特 @
|
||||
*/
|
||||
public static final char C_AT = CharUtil.AT;
|
||||
|
||||
/** 字符串常量:空格符 ' ' */
|
||||
/**
|
||||
* 字符串常量:空格符 ' '
|
||||
*/
|
||||
public static final String SPACE = " ";
|
||||
/** 字符串常量:制表符 \t */
|
||||
/**
|
||||
* 字符串常量:制表符 \t
|
||||
*/
|
||||
public static final String TAB = " ";
|
||||
/** 字符串常量:点 . */
|
||||
/**
|
||||
* 字符串常量:点 .
|
||||
*/
|
||||
public static final String DOT = ".";
|
||||
/**
|
||||
* 字符串常量:双点 ..
|
||||
* 用途:作为指向上级文件夹的路径 "../path"
|
||||
*/
|
||||
public static final String DOUBLE_DOT = "..";
|
||||
/** 字符串常量:斜杠 / */
|
||||
/**
|
||||
* 字符串常量:斜杠 /
|
||||
*/
|
||||
public static final String SLASH = "/";
|
||||
/** 字符串常量:反斜杠 \ */
|
||||
/**
|
||||
* 字符串常量:反斜杠 \
|
||||
*/
|
||||
public static final String BACKSLASH = "\\";
|
||||
/** 字符串常量:空字符串 "" */
|
||||
/**
|
||||
* 字符串常量:空字符串 ""
|
||||
*/
|
||||
public static final String EMPTY = "";
|
||||
/**
|
||||
* 字符串常量:"null"
|
||||
@@ -92,45 +134,79 @@ public class StrUtil {
|
||||
* 解释:该字符常用于表示 Linux 系统和 MacOS 系统下的文本换行
|
||||
*/
|
||||
public static final String CR = "\r";
|
||||
/** 字符串常量:换行符 \n */
|
||||
/**
|
||||
* 字符串常量:换行符 \n
|
||||
*/
|
||||
public static final String LF = "\n";
|
||||
/**
|
||||
* 字符串常量:Windows 换行 \r\n
|
||||
* 解释:该字符串常用于表示 Windows 系统下的文本换行
|
||||
*/
|
||||
public static final String CRLF = "\r\n";
|
||||
/** 字符串常量:下划线 _ */
|
||||
/**
|
||||
* 字符串常量:下划线 _
|
||||
*/
|
||||
public static final String UNDERLINE = "_";
|
||||
/** 字符串常量:减号(中划线) - */
|
||||
/**
|
||||
* 字符串常量:减号(中划线) -
|
||||
*/
|
||||
public static final String DASHED = "-";
|
||||
/** 字符串常量:逗号 , */
|
||||
/**
|
||||
* 字符串常量:逗号 ,
|
||||
*/
|
||||
public static final String COMMA = ",";
|
||||
/** 字符串常量:花括号(左) { */
|
||||
/**
|
||||
* 字符串常量:花括号(左) {
|
||||
*/
|
||||
public static final String DELIM_START = "{";
|
||||
/** 字符串常量:花括号(右) } */
|
||||
/**
|
||||
* 字符串常量:花括号(右) }
|
||||
*/
|
||||
public static final String DELIM_END = "}";
|
||||
/** 字符串常量:中括号(左) [ */
|
||||
/**
|
||||
* 字符串常量:中括号(左) [
|
||||
*/
|
||||
public static final String BRACKET_START = "[";
|
||||
/** 字符串常量:中括号(右) ] */
|
||||
/**
|
||||
* 字符串常量:中括号(右) ]
|
||||
*/
|
||||
public static final String BRACKET_END = "]";
|
||||
/** 字符串常量:冒号 : */
|
||||
/**
|
||||
* 字符串常量:冒号 :
|
||||
*/
|
||||
public static final String COLON = ":";
|
||||
/** 字符串常量:艾特 @ */
|
||||
/**
|
||||
* 字符串常量:艾特 @
|
||||
*/
|
||||
public static final String AT = "@";
|
||||
|
||||
/** 字符串常量:HTML 空格转义 */
|
||||
/**
|
||||
* 字符串常量:HTML 空格转义
|
||||
*/
|
||||
public static final String HTML_NBSP = " ";
|
||||
/** 字符串常量:HTML And 符转义 & */
|
||||
/**
|
||||
* 字符串常量:HTML And 符转义 &
|
||||
*/
|
||||
public static final String HTML_AMP = "&";
|
||||
/** 字符串常量:HTML 双引号转义 " */
|
||||
/**
|
||||
* 字符串常量:HTML 双引号转义 "
|
||||
*/
|
||||
public static final String HTML_QUOTE = """;
|
||||
/** 字符串常量:HTML 单引号转义 ' */
|
||||
/**
|
||||
* 字符串常量:HTML 单引号转义 '
|
||||
*/
|
||||
public static final String HTML_APOS = "'";
|
||||
/** 字符串常量:HTML 小于号转义 < */
|
||||
/**
|
||||
* 字符串常量:HTML 小于号转义 <
|
||||
*/
|
||||
public static final String HTML_LT = "<";
|
||||
/** 字符串常量:HTML 大于号转义 > */
|
||||
/**
|
||||
* 字符串常量:HTML 大于号转义 >
|
||||
*/
|
||||
public static final String HTML_GT = ">";
|
||||
/** 字符串常量:空 JSON "{}" */
|
||||
/**
|
||||
* 字符串常量:空 JSON "{}"
|
||||
*/
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
|
||||
// ------------------------------------------------------------------------ Blank
|
||||
@@ -189,8 +265,7 @@ public class StrUtil {
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 如果为字符串是否为空串
|
||||
*
|
||||
* @see StrUtil#isBlank(CharSequence)
|
||||
* @see StrUtil#isBlank(CharSequence)
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static boolean isBlankIfStr(Object obj) {
|
||||
@@ -667,6 +742,9 @@ public class StrUtil {
|
||||
* @return 是否开始
|
||||
*/
|
||||
public static boolean startWith(CharSequence str, char c) {
|
||||
if (isEmpty(str)) {
|
||||
return false;
|
||||
}
|
||||
return c == str.charAt(0);
|
||||
}
|
||||
|
||||
@@ -674,8 +752,8 @@ public class StrUtil {
|
||||
* 是否以指定字符串开头<br>
|
||||
* 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
|
||||
*
|
||||
* @param str 被监测字符串
|
||||
* @param prefix 开头字符串
|
||||
* @param str 被监测字符串
|
||||
* @param prefix 开头字符串
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @return 是否以指定字符串开头
|
||||
* @since 5.4.3
|
||||
@@ -690,14 +768,14 @@ public class StrUtil {
|
||||
*
|
||||
* @param str 被监测字符串
|
||||
* @param prefix 开头字符串
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @param ignoreEquals 是否忽略字符串相等的情况
|
||||
* @return 是否以指定字符串开头
|
||||
* @since 5.4.3
|
||||
*/
|
||||
public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase, boolean ignoreEquals) {
|
||||
if (null == str || null == prefix) {
|
||||
if(false == ignoreEquals){
|
||||
if (false == ignoreEquals) {
|
||||
return false;
|
||||
}
|
||||
return null == str && null == prefix;
|
||||
@@ -710,7 +788,7 @@ public class StrUtil {
|
||||
isStartWith = str.toString().startsWith(prefix.toString());
|
||||
}
|
||||
|
||||
if(isStartWith){
|
||||
if (isStartWith) {
|
||||
return (false == ignoreEquals) || (false == equals(str, prefix, ignoreCase));
|
||||
}
|
||||
return false;
|
||||
@@ -779,6 +857,9 @@ public class StrUtil {
|
||||
* @return 是否结尾
|
||||
*/
|
||||
public static boolean endWith(CharSequence str, char c) {
|
||||
if (isEmpty(str)) {
|
||||
return false;
|
||||
}
|
||||
return c == str.charAt(str.length() - 1);
|
||||
}
|
||||
|
||||
@@ -1073,7 +1154,8 @@ public class StrUtil {
|
||||
* @return 移除后的字符串
|
||||
*/
|
||||
public static String removeAll(CharSequence str, CharSequence strToRemove) {
|
||||
if (isEmpty(str)) {
|
||||
// strToRemove如果为空, 也不用继续后面的逻辑
|
||||
if (isEmpty(str) || isEmpty(strToRemove)) {
|
||||
return str(str);
|
||||
}
|
||||
return str.toString().replace(strToRemove, EMPTY);
|
||||
@@ -2125,7 +2207,6 @@ public class StrUtil {
|
||||
}
|
||||
|
||||
final List<String> result = new LinkedList<>();
|
||||
final String[] split = split(str, prefix);
|
||||
for (String fragment : split(str, prefix)) {
|
||||
int suffixIndex = fragment.indexOf(suffix.toString());
|
||||
if (suffixIndex > 0) {
|
||||
@@ -2205,10 +2286,10 @@ public class StrUtil {
|
||||
if (null == str) {
|
||||
return null;
|
||||
}
|
||||
if (count <= 0) {
|
||||
if (count <= 0 || str.length() == 0) {
|
||||
return EMPTY;
|
||||
}
|
||||
if (count == 1 || str.length() == 0) {
|
||||
if (count == 1) {
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@@ -3997,11 +4078,12 @@ public class StrUtil {
|
||||
return false;
|
||||
}
|
||||
int len = value.length();
|
||||
boolean isAllMatch = true;
|
||||
for (int i = 0; i < len; i++) {
|
||||
isAllMatch &= matcher.match(value.charAt(i));
|
||||
if (false == matcher.match(value.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return isAllMatch;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.BiMap;
|
||||
@@ -13,7 +14,11 @@ import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
@@ -21,6 +26,8 @@ import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.Source;
|
||||
@@ -62,6 +69,10 @@ public class XmlUtil {
|
||||
* 在XML中无效的字符 正则
|
||||
*/
|
||||
public static final String INVALID_REGEX = "[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]";
|
||||
/**
|
||||
* 在XML中注释的内容 正则
|
||||
*/
|
||||
public static final String COMMENT_REGEX = "(?s)<!--.+?-->";
|
||||
/**
|
||||
* XML格式化输出默认缩进量
|
||||
*/
|
||||
@@ -76,6 +87,10 @@ public class XmlUtil {
|
||||
* 是否打开命名空间支持
|
||||
*/
|
||||
private static boolean namespaceAware = true;
|
||||
/**
|
||||
* Sax读取器工厂缓存
|
||||
*/
|
||||
private static SAXParserFactory factory;
|
||||
|
||||
/**
|
||||
* 禁用默认的DocumentBuilderFactory,禁用后如果有第三方的实现(如oracle的xdb包中的xmlparse),将会自动加载实现。
|
||||
@@ -184,6 +199,92 @@ public class XmlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Sax方式读取指定的XML<br>
|
||||
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
|
||||
*
|
||||
* @param file XML源文件,使用后自动关闭
|
||||
* @param contentHandler XML流处理器,用于按照Element处理xml
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public static void readBySax(File file, ContentHandler contentHandler) {
|
||||
InputStream in = null;
|
||||
try{
|
||||
in = FileUtil.getInputStream(file);
|
||||
readBySax(new InputSource(in), contentHandler);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Sax方式读取指定的XML<br>
|
||||
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
|
||||
*
|
||||
* @param reader XML源Reader,使用后自动关闭
|
||||
* @param contentHandler XML流处理器,用于按照Element处理xml
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public static void readBySax(Reader reader, ContentHandler contentHandler) {
|
||||
try{
|
||||
readBySax(new InputSource(reader), contentHandler);
|
||||
} finally {
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Sax方式读取指定的XML<br>
|
||||
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
|
||||
*
|
||||
* @param source XML源流,使用后自动关闭
|
||||
* @param contentHandler XML流处理器,用于按照Element处理xml
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public static void readBySax(InputStream source, ContentHandler contentHandler) {
|
||||
try{
|
||||
readBySax(new InputSource(source), contentHandler);
|
||||
} finally {
|
||||
IoUtil.close(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Sax方式读取指定的XML<br>
|
||||
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
|
||||
*
|
||||
* @param source XML源,可以是文件、流、路径等
|
||||
* @param contentHandler XML流处理器,用于按照Element处理xml
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public static void readBySax(InputSource source, ContentHandler contentHandler) {
|
||||
// 1.获取解析工厂
|
||||
if (null == factory) {
|
||||
factory = SAXParserFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(namespaceAware);
|
||||
}
|
||||
// 2.从解析工厂获取解析器
|
||||
final SAXParser parse;
|
||||
XMLReader reader;
|
||||
try {
|
||||
parse = factory.newSAXParser();
|
||||
if (contentHandler instanceof DefaultHandler) {
|
||||
parse.parse(source, (DefaultHandler) contentHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3.得到解读器
|
||||
reader = parse.getXMLReader();
|
||||
reader.setContentHandler(contentHandler);
|
||||
reader.parse(source);
|
||||
} catch (ParserConfigurationException | SAXException e) {
|
||||
throw new UtilException(e);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String类型的XML转换为XML文档
|
||||
*
|
||||
@@ -574,6 +675,20 @@ public class XmlUtil {
|
||||
return xmlContent.replaceAll(INVALID_REGEX, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除XML文本中的注释内容
|
||||
*
|
||||
* @param xmlContent XML文本
|
||||
* @return 当传入为null时返回null
|
||||
* @since 5.4.5
|
||||
*/
|
||||
public static String cleanComment(String xmlContent) {
|
||||
if (xmlContent == null) {
|
||||
return null;
|
||||
}
|
||||
return xmlContent.replaceAll(COMMENT_REGEX, StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点名获得子节点列表
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -77,4 +78,11 @@ public class ChineseDateTest {
|
||||
chineseDate = new ChineseDate(2020,4,15);
|
||||
Assert.assertEquals("闰四月", chineseDate.getChineseMonth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFestivalsTest(){
|
||||
// issue#I1XHSF@Gitee,2023-01-20对应农历腊月29,非除夕
|
||||
ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("2023-01-20"));
|
||||
Assert.assertTrue(StrUtil.isEmpty(chineseDate.getFestivals()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ public class ImgUtilTest {
|
||||
@Test
|
||||
@Ignore
|
||||
public void scaleTest2() {
|
||||
ImgUtil.scale(FileUtil.file("e:/pic/test.jpg"), FileUtil.file("e:/pic/test_result.jpg"), 0.8f);
|
||||
ImgUtil.scale(
|
||||
FileUtil.file("d:/test/2.png"),
|
||||
FileUtil.file("d:/test/2_result.jpg"), 600, 337, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.hutool.core.lang;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConsoleTableTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void printTest() {
|
||||
ConsoleTable t = new ConsoleTable();
|
||||
t.addHeader("姓名", "年龄");
|
||||
t.addBody("张三", "15");
|
||||
t.addBody("李四", "29");
|
||||
t.addBody("王二麻子", "37");
|
||||
t.print();
|
||||
|
||||
Console.log();
|
||||
|
||||
t = new ConsoleTable();
|
||||
t.addHeader("体温", "占比");
|
||||
t.addHeader("℃", "%");
|
||||
t.addBody("36.8", "10");
|
||||
t.addBody("37", "5");
|
||||
t.print();
|
||||
|
||||
Console.log();
|
||||
|
||||
t = new ConsoleTable();
|
||||
t.addHeader("标题1", "标题2");
|
||||
t.addBody("12345", "混合321654asdfcSDF");
|
||||
t.addBody("sd e3ee ff22", "ff值");
|
||||
t.print();
|
||||
}
|
||||
}
|
||||
@@ -263,10 +263,10 @@ public class NumberUtilTest {
|
||||
|
||||
@Test
|
||||
public void isPowerOfTwoTest() {
|
||||
Assert.assertEquals(false, NumberUtil.isPowerOfTwo(-1));
|
||||
Assert.assertEquals(true, NumberUtil.isPowerOfTwo(16));
|
||||
Assert.assertEquals(true, NumberUtil.isPowerOfTwo(65536));
|
||||
Assert.assertEquals(true, NumberUtil.isPowerOfTwo(1));
|
||||
Assert.assertEquals(false, NumberUtil.isPowerOfTwo(17));
|
||||
Assert.assertFalse(NumberUtil.isPowerOfTwo(-1));
|
||||
Assert.assertTrue(NumberUtil.isPowerOfTwo(16));
|
||||
Assert.assertTrue(NumberUtil.isPowerOfTwo(65536));
|
||||
Assert.assertTrue(NumberUtil.isPowerOfTwo(1));
|
||||
Assert.assertFalse(NumberUtil.isPowerOfTwo(17));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link XmlUtil} 工具类
|
||||
@@ -148,6 +151,18 @@ public class XmlUtilTest {
|
||||
Assert.assertNotNull(doc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readBySaxTest(){
|
||||
final Set<String> eles = CollUtil.newHashSet(
|
||||
"returnsms", "returnstatus", "message", "remainpoint", "taskID", "successCounts");
|
||||
XmlUtil.readBySax(ResourceUtil.getStream("test.xml"), new DefaultHandler(){
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) {
|
||||
Assert.assertTrue(eles.contains(localName));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapToXmlTestWithOmitXmlDeclaration() {
|
||||
|
||||
@@ -196,6 +211,13 @@ public class XmlUtilTest {
|
||||
Assert.assertEquals(testBean.getBankCode(), testBean2.getBankCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanCommentTest() {
|
||||
final String xmlContent = "<info><title>hutool</title><!-- 这是注释 --><lang>java</lang></info>";
|
||||
final String ret = XmlUtil.cleanComment(xmlContent);
|
||||
Assert.assertEquals("<info><title>hutool</title><lang>java</lang></info>", ret);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TestBean {
|
||||
private String ReqCode;
|
||||
|
||||
Reference in New Issue
Block a user