好多东西添加
parent
7489f8d291
commit
8b16f9ca57
|
@ -535,7 +535,44 @@ $(() => {
|
||||||
callback()
|
callback()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
/* ******************* 流程明细字段包含某个字符串时给某个字段赋值为某个东西end ******************* */
|
|
||||||
|
/* ******************* 流程明细字段包含某个字符串时给某个字段赋值为某个东西end ******************* */
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************* 流程提交按钮触发流程抄送start ******************* */
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
let config = {
|
||||||
|
// 抄送人所在明细表编号,1-明细1 ; 2-明细2
|
||||||
|
detailNo: '1',
|
||||||
|
// 抄送人所在字段字段名
|
||||||
|
ccResourceField: '',
|
||||||
|
// 一级部门负责人字段名
|
||||||
|
firstLevelDepResponsibleField: ''
|
||||||
|
}
|
||||||
|
WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, function (callback) {
|
||||||
|
$.ajax("/api/aiyh/pcn/cc-workflow/trigger", {
|
||||||
|
type: "POST",
|
||||||
|
data: JSON.stringify({
|
||||||
|
detailNo: config.detailNo,
|
||||||
|
requestId: WfForm.getBaseInfo().requestid,
|
||||||
|
ccResourceField: config.ccResourceField,
|
||||||
|
firstLevelDepResponsibleField: config.firstLevelDepResponsibleField
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
complete: (res) => {
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
/* ******************* 流程提交按钮触发流程抄送end ******************* */
|
||||||
|
|
||||||
|
|
||||||
/* ******************* 提交外出流程 ******************* */
|
/* ******************* 提交外出流程 ******************* */
|
||||||
(function start() {
|
(function start() {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCa8TBiSrOPSgJER3nd+gW2eCoSjXHRVxEX54AdxzmtDe4Rn6OlvMVG1GLWfTnjdwbLTVeZUqjTpgZkCdGIp9njgjs6v6oQ7f8+DhQvByp6BSNXBHM+XH+YnmIohcQpnH7qvDT8l41Io2IVZbeNBWMlAtnz7laXWisYkYEDUm8HXOadKLkX+aqWdEYJHGy8zYN9ktNWH7P8BXBUAUd1vtTZhhE6BMd/O0WSqWhgpABbFZ8VKVT/5u4dlXUykKNbCKrsfXoiLE/7plbfHji02sUdaA1LAOF4QOaTePaGZu/55TWCYZPjWKrW2c0Gr8mS/1MQrg1zsWV2c59GqLNMWICtAgMBAAECggEAOS4thvi+j3DmqUAfj3YHybFLBZHBoVoaatH6jALMHDt50nMxt6aUv3D+EN4iEPoKPdkLLQA+Ye1xilW9SEt5s+aJ6UJ2hszuV35moHxqhqGCy0hPJ4KHbFF3NDE5lYm1pPdULqvXbiktt2vUc2y7jBsjSEx7FFwob2azUACKDRL+PkNGGcMmdMeCIHBcQf+j0CTY/VvsPGHqf53I/Ffe2KAE3Y0SugoIB5dqUE9VN5KGC01+X6hufaNUpPLYLbBiaI1eHVtMCHbGNmNnHuFgsEWT4ya4fw1rm9DLS8zJwDHkHmegE5ieqM7EzM2UIvKCN3TQJcy+Ssk3FX+KBFQqmQKBgQDaiC8QFqA7z8DY0K6lwj8PMiuNeu9APJuHLAhGdbDXjAKfdU1cNU4SHfsjjCU4Wymgp8QNrMD8WhardKHV+zr6B0mRMnUYOI6aoAg1+UcGoHYM2PEonZS+OtBHI68wDIsiKQ2Mybsl6GlxoLfo3YdlcclPFHNzXoVc8QFboN0TdwKBgQC1gerb6Anwlj6rJB9RCnoR/Jq5Lyh5wa1eL1C+gZSSM8Mwwxu3VpPohUMca7t1F1vmEDaf/Z2nsQ8IULnYkS94iGYooIcV2ffY5bagi/DkuTYfbf2QBWSZNTI1yHTWoaIXElRG0+svxgL6w98D3Y5lzIt0skz+/ZSGIdiLg8mt+wKBgQCBJCNzxXsxfWeAeWoMKMttJn/YXwLOGkLq0ZmeUeSMrH/MTdzGlfWp/S+xZRuFv1HNT/crAaEWQALPleAhfRLwOKg/9up9wsZ7GAFiLArOHrtEgluZXe5NsKHuuGbJ5U+/gzUvsvM2xq6xaIHmSiu+Rkzpv7MuRXhYYVAlHt4mpwKBgALOOEgf5Q9v8xYIH+fLxqlCg027ed+v67MZ/iCDtj0wSaMWUPZbgzvD246z55jevI/ozj9Y1zgBV58kSEsdq2MskI+uM4hV7yvOGS2QHDAc4MZJl/LC8pQfq2ADcjLjGrNKmDzkB62cXO1tW6Qep5XRPJKYMvJ6DvKn0UYOym5DAoGBALsEOPep1R8CBWCnni8igvbMudBwpzg5e9V6bu7p3MdADhGA/FMkCQgI2VH6fTufQsOA6rYi7YcdMS80r+p1/HMuVskOlqQfPDJ3OG0g0W8nwxf3XbIoEJcaAzLmqDOQMHfhmnoQsPlnCd7YRybbEEkEbKeRjyrDqhMSTSWC8lw4
|
|
@ -0,0 +1 @@
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmvEwYkqzj0oCREd53foFtngqEo1x0VcRF+eAHcc5rQ3uEZ+jpbzFRtRi1n0543cGy01XmVKo06YGZAnRiKfZ44I7Or+qEO3/Pg4ULwcqegUjVwRzPlx/mJ5iKIXEKZx+6rw0/JeNSKNiFWW3jQVjJQLZ8+5Wl1orGJGBA1JvB1zmnSi5F/mqlnRGCRxsvM2DfZLTVh+z/AVwVAFHdb7U2YYROgTHfztFkqloYKQAWxWfFSlU/+buHZV1MpCjWwiq7H16IixP+6ZW3x44tNrFHWgNSwDheEDmk3j2hmbv+eU1gmGT41iq1tnNBq/Jkv9TEK4Nc7FldnOfRqizTFiArQIDAQAB
|
|
@ -732,7 +732,7 @@ public class Util extends weaver.general.Util {
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
for (int i = 0, l = charArray.length; i < l; i++) {
|
for (int i = 0, l = charArray.length; i < l; i++) {
|
||||||
// 判断当前字符是否是"_",如果跳出本次循环
|
// 判断当前字符是否是"_",如果跳出本次循环
|
||||||
if (charArray[i] == 95) {
|
if (charArray[i] == '_') {
|
||||||
underlineBefore = true;
|
underlineBefore = true;
|
||||||
} else if (underlineBefore) {
|
} else if (underlineBefore) {
|
||||||
// 如果为true,代表上次的字符是"_",当前字符需要转成大写
|
// 如果为true,代表上次的字符是"_",当前字符需要转成大写
|
||||||
|
@ -763,10 +763,10 @@ public class Util extends weaver.general.Util {
|
||||||
for (int i = 0, l = charArray.length; i < l; i++) {
|
for (int i = 0, l = charArray.length; i < l; i++) {
|
||||||
if (charArray[i] >= 65 && charArray[i] <= 90) {
|
if (charArray[i] >= 65 && charArray[i] <= 90) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
buffer.append(charArray[i] += 32);
|
buffer.append(Character.toLowerCase(charArray[i]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
buffer.append("_").append(charArray[i] += 32);
|
buffer.append("_").append(Character.toLowerCase(charArray[i]));
|
||||||
} else {
|
} else {
|
||||||
buffer.append(charArray[i]);
|
buffer.append(charArray[i]);
|
||||||
}
|
}
|
||||||
|
@ -2100,7 +2100,12 @@ public class Util extends weaver.general.Util {
|
||||||
|
|
||||||
String s;
|
String s;
|
||||||
for (Iterator<T> item = coll.iterator(); item.hasNext(); sb.append(s)) {
|
for (Iterator<T> item = coll.iterator(); item.hasNext(); sb.append(s)) {
|
||||||
s = (String) item.next();
|
T value = item.next();
|
||||||
|
if (Objects.isNull(value)) {
|
||||||
|
s = "";
|
||||||
|
} else {
|
||||||
|
s = String.valueOf(value);
|
||||||
|
}
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2111,6 +2116,38 @@ public class Util extends weaver.general.Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* join方法
|
||||||
|
*
|
||||||
|
* @param coll
|
||||||
|
* @param split
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> String joinEach(T[] coll, String split) {
|
||||||
|
if (Objects.isNull(coll)) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean isFirst = true;
|
||||||
|
String s;
|
||||||
|
for (Iterator<T> item = Arrays.stream(coll).iterator(); item.hasNext(); sb.append(s)) {
|
||||||
|
T value = item.next();
|
||||||
|
if (Objects.isNull(value)) {
|
||||||
|
s = "";
|
||||||
|
} else {
|
||||||
|
s = String.valueOf(value);
|
||||||
|
}
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
} else {
|
||||||
|
sb.append(split);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将数组使用指定的分隔符链接为字符串
|
* 将数组使用指定的分隔符链接为字符串
|
||||||
*
|
*
|
||||||
|
@ -3736,7 +3773,35 @@ public class Util extends weaver.general.Util {
|
||||||
return Util.getValueByKeyStr("main." + fieldInfo.getFieldName(), workflowData);
|
return Util.getValueByKeyStr("main." + fieldInfo.getFieldName(), workflowData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <K, V> Map<K, V> createFunctionMap() {
|
public static String getIpAddress(HttpServletRequest request) throws IOException {
|
||||||
return null;
|
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
|
||||||
|
String ip = request.getHeader("X-Forwarded-For");
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
} else if (ip.length() > 15) {
|
||||||
|
String[] ips = ip.split(",");
|
||||||
|
for (int index = 0; index < ips.length; index++) {
|
||||||
|
String strIp = ips[index];
|
||||||
|
if (!("unknown".equalsIgnoreCase(strIp))) {
|
||||||
|
ip = strIp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ public @interface Association {
|
||||||
|
|
||||||
String column();
|
String column();
|
||||||
|
|
||||||
String select();
|
|
||||||
|
String select() default "";
|
||||||
|
|
||||||
Id id();
|
Id id();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>批量插入</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2022-08-09 17:40</p>
|
||||||
|
*
|
||||||
|
* @author aiyh EBU7-dev-1
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface BatchDeleteOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
// sql是否是在参数中
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>批量插入</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2022-08-09 17:40</p>
|
||||||
|
*
|
||||||
|
* @author aiyh EBU7-dev-1
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface BatchInsertOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
// sql是否是在参数中
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>批量插入</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2022-08-09 17:40</p>
|
||||||
|
*
|
||||||
|
* @author aiyh EBU7-dev-1
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface BatchUpdateOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
// sql是否是在参数中
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>可以为null</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2023/3/8 21:41</p>
|
||||||
|
*
|
||||||
|
* @author youHong.ai
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Documented
|
||||||
|
public @interface CanBeNull {
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ public @interface CollectionMapping {
|
||||||
String property();
|
String property();
|
||||||
|
|
||||||
/** 数据库字段名 */
|
/** 数据库字段名 */
|
||||||
String column();
|
String column() default "";
|
||||||
|
|
||||||
/** 查询方法全限定类名 */
|
/** 查询方法全限定类名 */
|
||||||
String select() default "";
|
String select() default "";
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>列名</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2023/3/10 10:18</p>
|
||||||
|
*
|
||||||
|
* @author youHong.ai
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.ANNOTATION_TYPE)
|
||||||
|
@Documented
|
||||||
|
public @interface Column {
|
||||||
|
String value();
|
||||||
|
|
||||||
|
String oracle() default "";
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author @author EBU7-dev1-ay
|
||||||
|
* create 2021/12/19 0019 15:08
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface DeleteOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
// sql是否是在参数中
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author @author EBU7-dev1-ay
|
||||||
|
* create 2021/12/19 0019 15:08
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface InsertOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
// sql是否是在参数中
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import java.lang.annotation.*;
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Select {
|
public @interface Select {
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
// sql是否是在参数中
|
// sql是否是在参数中
|
||||||
boolean custom() default false;
|
boolean custom() default false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author @author EBU7-dev1-ay
|
||||||
|
* create 2021/12/19 0019 15:08
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface SelectOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package aiyh.utils.annotation.recordset;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author @author EBU7-dev1-ay
|
||||||
|
* create 2021/12/19 0019 15:08
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface UpdateOracle {
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
// sql是否是在参数中
|
||||||
|
boolean custom() default false;
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package aiyh.utils.entity;
|
package aiyh.utils.entity;
|
||||||
|
|
||||||
|
import aiyh.utils.annotation.recordset.SqlOracleDbFieldAnn;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,11 +12,33 @@ import lombok.Data;
|
||||||
@Data
|
@Data
|
||||||
public class DocImageInfo {
|
public class DocImageInfo {
|
||||||
|
|
||||||
|
/** docId */
|
||||||
|
@SqlOracleDbFieldAnn("DOC_ID")
|
||||||
private Integer docId;
|
private Integer docId;
|
||||||
|
/** image id */
|
||||||
|
@SqlOracleDbFieldAnn("IMAGE_FILE_ID")
|
||||||
private Integer imageFileId;
|
private Integer imageFileId;
|
||||||
|
/** 文件名称 */
|
||||||
|
@SqlOracleDbFieldAnn("IMAGE_FILE_NAME")
|
||||||
private String imageFileName;
|
private String imageFileName;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
@SqlOracleDbFieldAnn("DOC_CREATE_TIME")
|
||||||
|
private String docCreateTime;
|
||||||
|
|
||||||
|
/** 创建日期 */
|
||||||
|
@SqlOracleDbFieldAnn("DOC_CREATE_DATE")
|
||||||
|
private String docCreateDate;
|
||||||
|
/** 明细数据id */
|
||||||
|
@SqlOracleDbFieldAnn("ID")
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
/** 其他id */
|
||||||
|
@SqlOracleDbFieldAnn("DETAIL_ID")
|
||||||
private Integer detailId;
|
private Integer detailId;
|
||||||
|
/** 文件大小 */
|
||||||
|
@SqlOracleDbFieldAnn("FILE_SIZE")
|
||||||
private Integer fileSize;
|
private Integer fileSize;
|
||||||
|
/** 文件类型 */
|
||||||
|
@SqlOracleDbFieldAnn("DOC_FILE_TYPE")
|
||||||
private Integer docFileType;
|
private Integer docFileType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package aiyh.utils.fileUtil;
|
package aiyh.utils.fileUtil;
|
||||||
|
|
||||||
import aiyh.utils.zwl.common.ToolUtil;
|
import aiyh.utils.zwl.common.ToolUtil;
|
||||||
import sun.font.FontDesignMetrics;
|
|
||||||
import weaver.conn.RecordSet;
|
import weaver.conn.RecordSet;
|
||||||
import weaver.file.FileUpload;
|
import weaver.file.FileUpload;
|
||||||
import weaver.file.ImageFileManager;
|
import weaver.file.ImageFileManager;
|
||||||
|
@ -76,7 +75,7 @@ public class WritWatermark {
|
||||||
}
|
}
|
||||||
String message = maxStr;
|
String message = maxStr;
|
||||||
Font defaultFont = new Font(fontName, fontStyle, fontSize);
|
Font defaultFont = new Font(fontName, fontStyle, fontSize);
|
||||||
int wordWidth = getWordWidth(defaultFont, message);
|
int wordWidth = getWordWidth(defaultFont, message, graphics);
|
||||||
// 对字体进行限制
|
// 对字体进行限制
|
||||||
if (wordWidth >= Math.min(width, height)) {
|
if (wordWidth >= Math.min(width, height)) {
|
||||||
fontSize = Math.min(width, height) / (pressText.length() + 6);
|
fontSize = Math.min(width, height) / (pressText.length() + 6);
|
||||||
|
@ -151,7 +150,7 @@ public class WritWatermark {
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||||
outputStreamTem.close();
|
outputStreamTem.close();
|
||||||
// 保存生成的水印图片到压缩包
|
// 保存生成的水印图片到压缩包
|
||||||
|
@ -277,7 +276,7 @@ public class WritWatermark {
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||||
outputStreamTem.close();
|
outputStreamTem.close();
|
||||||
// 保存生成的水印图片到压缩包
|
// 保存生成的水印图片到压缩包
|
||||||
|
@ -403,7 +402,7 @@ public class WritWatermark {
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||||
outputStreamTem.close();
|
outputStreamTem.close();
|
||||||
// 保存生成的水印图片到压缩包
|
// 保存生成的水印图片到压缩包
|
||||||
|
@ -540,7 +539,7 @@ public class WritWatermark {
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||||
outputStreamTem.close();
|
outputStreamTem.close();
|
||||||
// 保存生成的水印图片到压缩包
|
// 保存生成的水印图片到压缩包
|
||||||
|
@ -660,7 +659,7 @@ public class WritWatermark {
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||||
outputStreamTem.close();
|
outputStreamTem.close();
|
||||||
// 保存生成的水印图片到压缩包
|
// 保存生成的水印图片到压缩包
|
||||||
|
@ -780,7 +779,7 @@ public class WritWatermark {
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||||
outputStreamTem.close();
|
outputStreamTem.close();
|
||||||
// 保存生成的水印图片到压缩包
|
// 保存生成的水印图片到压缩包
|
||||||
|
@ -810,13 +809,14 @@ public class WritWatermark {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getWordWidth(Font font, String content) {
|
public static int getWordWidth(Font font, String content, Graphics2D graphics) {
|
||||||
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
|
// FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
|
||||||
int width = 0;
|
// int width = 0;
|
||||||
for (int i = 0; i < content.length(); i++) {
|
// for (int i = 0; i < content.length(); i++) {
|
||||||
width += metrics.charWidth(content.charAt(i));
|
// width += metrics.charWidth(content.charAt(i));
|
||||||
}
|
// }
|
||||||
return width;
|
// return width;
|
||||||
|
return graphics.getFontMetrics(font).stringWidth(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import weaver.file.ImageFileManager;
|
||||||
import weaver.system.SystemComInfo;
|
import weaver.system.SystemComInfo;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -136,8 +135,8 @@ public class PdfUtil {
|
||||||
// 输出到临时目录
|
// 输出到临时目录
|
||||||
FileOutputStream outputStreamTem;
|
FileOutputStream outputStreamTem;
|
||||||
try {
|
try {
|
||||||
outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
outputStreamTem = new FileOutputStream(tempPath);
|
||||||
} catch (FileNotFoundException | UnsupportedEncodingException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw new CustomerException("创建临时文件流和路径转换失败!", e);
|
throw new CustomerException("创建临时文件流和路径转换失败!", e);
|
||||||
}
|
}
|
||||||
PdfStamper pdfStamper = null;
|
PdfStamper pdfStamper = null;
|
||||||
|
@ -181,7 +180,7 @@ public class PdfUtil {
|
||||||
try {
|
try {
|
||||||
waterPdf.close();
|
waterPdf.close();
|
||||||
Files.deleteIfExists(Paths.get(tempPath));
|
Files.deleteIfExists(Paths.get(tempPath));
|
||||||
} catch (IOException e ){
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return imageFileId;
|
return imageFileId;
|
||||||
|
|
|
@ -4,7 +4,6 @@ package aiyh.utils.httpUtil;
|
||||||
import aiyh.utils.Util;
|
import aiyh.utils.Util;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONException;
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
@ -136,11 +135,11 @@ public class ResponeVo implements HttpResponse {
|
||||||
} catch (JsonProcessingException ignored) {
|
} catch (JsonProcessingException ignored) {
|
||||||
try {
|
try {
|
||||||
this.resultList = (List<Map>) JSONArray.parseArray(this.getEntityString(), Map.class);
|
this.resultList = (List<Map>) JSONArray.parseArray(this.getEntityString(), Map.class);
|
||||||
} catch (JSONException e) {
|
} catch (Exception e) {
|
||||||
Util.getLogger().error("Unable to convert the response result to array!" + Util.getErrString(e));
|
Util.getLogger().error("Unable to convert the response result to array!");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.getLogger().error("Unable to convert the response result to map or array!" + Util.getErrString(e));
|
Util.getLogger().error("Unable to convert the response result to map or array!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,17 +58,31 @@ public interface UtilMapper {
|
||||||
String selectWorkfowMainTable(@ParamMapper("workflowId") String workflowId);
|
String selectWorkfowMainTable(@ParamMapper("workflowId") String workflowId);
|
||||||
|
|
||||||
|
|
||||||
@Select("select id,docid doc_id,imagefileid image_file_id,imagefilename image_file_name from docimagefile where docid = #{docId}")
|
@Select("select di.id id,dc.id doc_id," +
|
||||||
|
"di.imagefileid image_file_id," +
|
||||||
|
"dc.DOCCREATEDATE doc_create_date,dc.DOCCREATETIME doc_create_time," +
|
||||||
|
"(case when imagefilename = '' or imagefilename is null then dc.DOCSUBJECT else imagefilename end) image_file_name " +
|
||||||
|
"from DocDetail dc " +
|
||||||
|
"left join docimagefile di on (dc.id = #{docId} and dc.id = di.docid) " +
|
||||||
|
"where dc.id = #{docId} ")
|
||||||
DocImageInfo selectDocImageInfo(@ParamMapper("docId") String docId);
|
DocImageInfo selectDocImageInfo(@ParamMapper("docId") String docId);
|
||||||
|
|
||||||
@Select("select id,docid doc_id," +
|
@Select("select di.id id,dc.id doc_id," +
|
||||||
"imagefileid image_file_id, docfiletype doc_file_type," +
|
"di.imagefileid image_file_id, di.docfiletype doc_file_type," +
|
||||||
"imagefilename image_file_name from docimagefile where docid in ($t{docIds})")
|
"dc.DOCCREATEDATE doc_create_date,dc.DOCCREATETIME doc_create_time," +
|
||||||
|
"(case when imagefilename = '' or imagefilename is null then dc.DOCSUBJECT else imagefilename end) image_file_name " +
|
||||||
|
"from DocDetail dc " +
|
||||||
|
"left join docimagefile di on (dc.id in ($t{docIds}) and dc.id = di.docid )" +
|
||||||
|
"where dc.id in ($t{docIds})")
|
||||||
List<DocImageInfo> selectDocImageInfos(@ParamMapper("docIds") String docIds);
|
List<DocImageInfo> selectDocImageInfos(@ParamMapper("docIds") String docIds);
|
||||||
|
|
||||||
@Select("select id,docid doc_id,imagefileid image_file_id," +
|
@Select("select di.id id,dc.id doc_id," +
|
||||||
"docfiletype doc_file_type," +
|
"di.imagefileid image_file_id, di.docfiletype doc_file_type," +
|
||||||
"imagefilename image_file_name from docimagefile where docid in (${docIds})")
|
"dc.DOCCREATEDATE doc_create_date,dc.DOCCREATETIME doc_create_time," +
|
||||||
|
"(case when imagefilename = '' or imagefilename is null then dc.DOCSUBJECT else imagefilename end) image_file_name " +
|
||||||
|
"from DocDetail dc " +
|
||||||
|
"left join docimagefile di on (dc.id in ($t{docIds}) and dc.id = di.docid )" +
|
||||||
|
"where dc.id in ($t{docIds})")
|
||||||
List<DocImageInfo> selectDocImageInfos(@ParamMapper("docIds") String[] docIds);
|
List<DocImageInfo> selectDocImageInfos(@ParamMapper("docIds") String[] docIds);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package aiyh.utils.recordset;
|
package aiyh.utils.recordset;
|
||||||
|
|
||||||
import aiyh.utils.Util;
|
import aiyh.utils.Util;
|
||||||
|
import aiyh.utils.annotation.recordset.CanBeNull;
|
||||||
import weaver.conn.RecordSet;
|
import weaver.conn.RecordSet;
|
||||||
import weaver.conn.RecordSetTrans;
|
import weaver.conn.RecordSetTrans;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>float处理</p>
|
* <p>float处理</p>
|
||||||
|
@ -18,8 +20,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
||||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
return Double.parseDouble(string);
|
return Double.parseDouble(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +34,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
||||||
String string = Util.null2String(rs.getString(index));
|
String string = Util.null2String(rs.getString(index));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
return Double.parseDouble(string);
|
return Double.parseDouble(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +48,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
||||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
return Double.parseDouble(string);
|
return Double.parseDouble(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +62,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
||||||
String string = Util.null2String(rs.getString(index));
|
String string = Util.null2String(rs.getString(index));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
return Double.parseDouble(string);
|
return Double.parseDouble(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package aiyh.utils.recordset;
|
package aiyh.utils.recordset;
|
||||||
|
|
||||||
import aiyh.utils.Util;
|
import aiyh.utils.Util;
|
||||||
|
import aiyh.utils.annotation.recordset.CanBeNull;
|
||||||
import weaver.conn.RecordSet;
|
import weaver.conn.RecordSet;
|
||||||
import weaver.conn.RecordSetTrans;
|
import weaver.conn.RecordSetTrans;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>float处理</p>
|
* <p>float处理</p>
|
||||||
|
@ -18,8 +20,13 @@ public class FloatTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
||||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
return Float.parseFloat(string);
|
return Float.parseFloat(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +34,13 @@ public class FloatTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
||||||
String string = Util.null2String(rs.getString(index), "0.0");
|
String string = Util.null2String(rs.getString(index), "0.0");
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
return Float.parseFloat(string);
|
return Float.parseFloat(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +48,13 @@ public class FloatTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
||||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
return Float.parseFloat(string);
|
return Float.parseFloat(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +62,13 @@ public class FloatTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
||||||
String string = Util.null2String(rs.getString(index));
|
String string = Util.null2String(rs.getString(index));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
return Float.parseFloat(string);
|
return Float.parseFloat(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package aiyh.utils.recordset;
|
package aiyh.utils.recordset;
|
||||||
|
|
||||||
import aiyh.utils.Util;
|
import aiyh.utils.Util;
|
||||||
|
import aiyh.utils.annotation.recordset.CanBeNull;
|
||||||
import weaver.conn.RecordSet;
|
import weaver.conn.RecordSet;
|
||||||
import weaver.conn.RecordSetTrans;
|
import weaver.conn.RecordSetTrans;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author EBU7-dev1-ayh create 2021/12/21 0021 13:10
|
* @author EBU7-dev1-ayh create 2021/12/21 0021 13:10
|
||||||
|
@ -16,8 +18,11 @@ public class IntegerTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
||||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (string.contains(".")) {
|
if (string.contains(".")) {
|
||||||
string = string.substring(0, string.indexOf("."));
|
string = string.substring(0, string.indexOf("."));
|
||||||
}
|
}
|
||||||
|
@ -28,8 +33,13 @@ public class IntegerTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
||||||
String string = Util.null2String(rs.getString(index));
|
String string = Util.null2String(rs.getString(index));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (string.contains(".")) {
|
if (string.contains(".")) {
|
||||||
string = string.substring(0, string.indexOf("."));
|
string = string.substring(0, string.indexOf("."));
|
||||||
}
|
}
|
||||||
|
@ -40,8 +50,13 @@ public class IntegerTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
||||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (string.contains(".")) {
|
if (string.contains(".")) {
|
||||||
string = string.substring(0, string.indexOf("."));
|
string = string.substring(0, string.indexOf("."));
|
||||||
}
|
}
|
||||||
|
@ -52,8 +67,13 @@ public class IntegerTypeHandler implements TypeHandler {
|
||||||
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
||||||
String string = Util.null2String(rs.getString(index));
|
String string = Util.null2String(rs.getString(index));
|
||||||
if ("".equals(string)) {
|
if ("".equals(string)) {
|
||||||
|
if (!Objects.isNull(declaredField)) {
|
||||||
|
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (string.contains(".")) {
|
if (string.contains(".")) {
|
||||||
string = string.substring(0, string.indexOf("."));
|
string = string.substring(0, string.indexOf("."));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import aiyh.utils.sqlUtil.sqlResult.impl.BatchSqlResultImpl;
|
||||||
import aiyh.utils.sqlUtil.sqlResult.impl.PrepSqlResultImpl;
|
import aiyh.utils.sqlUtil.sqlResult.impl.PrepSqlResultImpl;
|
||||||
import weaver.conn.RecordSet;
|
import weaver.conn.RecordSet;
|
||||||
import weaver.conn.RecordSetTrans;
|
import weaver.conn.RecordSetTrans;
|
||||||
|
import weaver.conn.constant.DBConstant;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -59,25 +60,12 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
return invokeRsTrans(proxy, method, args, name);
|
return invokeRsTrans(proxy, method, args, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object invokeDefault(int type, String sql, boolean custom, RecordSet rs, Method method, Object[] args) {
|
||||||
private Object invokeRs(Object proxy, Method method, Object[] args, String name) {
|
|
||||||
String mapperKey = method.getDeclaringClass().getName();
|
|
||||||
if (!"".equals(name) && null != name) {
|
|
||||||
mapperKey += "." + name;
|
|
||||||
}
|
|
||||||
RecordSet rs = rsManager.getRs(mapperKey);
|
|
||||||
if (rs == null) {
|
|
||||||
rsManager.setRecordSet(mapperKey);
|
|
||||||
rs = rsManager.getRs(mapperKey);
|
|
||||||
}
|
|
||||||
SqlHandler sqlHandler = new SqlHandler();
|
SqlHandler sqlHandler = new SqlHandler();
|
||||||
ResultMapper resultMapper = new ResultMapper();
|
ResultMapper resultMapper = new ResultMapper();
|
||||||
Select select = method.getAnnotation(Select.class);
|
|
||||||
if (select != null) {
|
|
||||||
// 查询
|
|
||||||
String sql = select.value();
|
|
||||||
boolean custom = select.custom();
|
|
||||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
||||||
|
switch (type) {
|
||||||
|
case 1: {
|
||||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("select ")) {
|
if (!handler.getSqlStr().trim().toLowerCase().startsWith("select ")) {
|
||||||
throw new CustomerException("The sql statement does not match, the @Select annotation can only execute the select statement, please check whether the sql statement matches!");
|
throw new CustomerException("The sql statement does not match, the @Select annotation can only execute the select statement, please check whether the sql statement matches!");
|
||||||
}
|
}
|
||||||
|
@ -89,12 +77,7 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
}
|
}
|
||||||
return resultMapper.mapperResult(rs, method, method.getReturnType(), this);
|
return resultMapper.mapperResult(rs, method, method.getReturnType(), this);
|
||||||
}
|
}
|
||||||
Update update = method.getAnnotation(Update.class);
|
case 2: {
|
||||||
if (update != null) {
|
|
||||||
// 查询
|
|
||||||
String sql = update.value();
|
|
||||||
boolean custom = update.custom();
|
|
||||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
|
||||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("update ")) {
|
if (!handler.getSqlStr().trim().toLowerCase().startsWith("update ")) {
|
||||||
throw new CustomerException("The sql statement does not match, the @Update annotation can only execute the update statement, please check whether the sql statement matches!");
|
throw new CustomerException("The sql statement does not match, the @Update annotation can only execute the update statement, please check whether the sql statement matches!");
|
||||||
}
|
}
|
||||||
|
@ -120,12 +103,7 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Insert insert = method.getAnnotation(Insert.class);
|
case 3: {
|
||||||
if (insert != null) {
|
|
||||||
// 查询
|
|
||||||
String sql = insert.value();
|
|
||||||
boolean custom = insert.custom();
|
|
||||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
|
||||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("insert ")) {
|
if (!handler.getSqlStr().trim().toLowerCase().startsWith("insert ")) {
|
||||||
throw new CustomerException("The sql statement does not match, the @Insert annotation can only execute the insert statement, please check whether the sql statement matches!");
|
throw new CustomerException("The sql statement does not match, the @Insert annotation can only execute the insert statement, please check whether the sql statement matches!");
|
||||||
}
|
}
|
||||||
|
@ -144,12 +122,7 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Delete delete = method.getAnnotation(Delete.class);
|
case 4: {
|
||||||
if (delete != null) {
|
|
||||||
// 查询
|
|
||||||
String sql = delete.value();
|
|
||||||
boolean custom = delete.custom();
|
|
||||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
|
||||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("delete ")) {
|
if (!handler.getSqlStr().trim().toLowerCase().startsWith("delete ")) {
|
||||||
throw new CustomerException("The sql statement does not match, the @Delete annotation can only execute the delete statement, please check whether the sql statement matches!");
|
throw new CustomerException("The sql statement does not match, the @Delete annotation can only execute the delete statement, please check whether the sql statement matches!");
|
||||||
}
|
}
|
||||||
|
@ -168,12 +141,8 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean hasBatchInsert = method.isAnnotationPresent(BatchInsert.class);
|
case 5: {
|
||||||
if (hasBatchInsert) {
|
|
||||||
BatchInsert batchInsert = method.getAnnotation(BatchInsert.class);
|
|
||||||
String sql = batchInsert.value();
|
|
||||||
Class<?> returnType = method.getReturnType();
|
Class<?> returnType = method.getReturnType();
|
||||||
boolean custom = batchInsert.custom();
|
|
||||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||||
|
@ -189,14 +158,9 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
boolean hasBatchUpdate = method.isAnnotationPresent(BatchUpdate.class);
|
case 6: {
|
||||||
if (hasBatchUpdate) {
|
|
||||||
BatchUpdate batchUpdate = method.getAnnotation(BatchUpdate.class);
|
|
||||||
String sql = batchUpdate.value();
|
|
||||||
Class<?> returnType = method.getReturnType();
|
Class<?> returnType = method.getReturnType();
|
||||||
boolean custom = batchUpdate.custom();
|
|
||||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||||
|
@ -212,14 +176,9 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
boolean hasBatchDelete = method.isAnnotationPresent(BatchDelete.class);
|
case 7: {
|
||||||
if (hasBatchDelete) {
|
|
||||||
BatchDelete batchDelete = method.getAnnotation(BatchDelete.class);
|
|
||||||
String sql = batchDelete.value();
|
|
||||||
Class<?> returnType = method.getReturnType();
|
Class<?> returnType = method.getReturnType();
|
||||||
boolean custom = batchDelete.custom();
|
|
||||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||||
|
@ -235,11 +194,114 @@ public class RecordsetUtil implements InvocationHandler {
|
||||||
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new CustomerException("不支持的sql注解类型: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invokeRs(Object proxy, Method method, Object[] args, String name) {
|
||||||
|
String mapperKey = method.getDeclaringClass().getName();
|
||||||
|
if (!"".equals(name) && null != name) {
|
||||||
|
mapperKey += "." + name;
|
||||||
|
}
|
||||||
|
RecordSet rs = rsManager.getRs(mapperKey);
|
||||||
|
if (rs == null) {
|
||||||
|
rsManager.setRecordSet(mapperKey);
|
||||||
|
rs = rsManager.getRs(mapperKey);
|
||||||
|
}
|
||||||
|
SqlHandler sqlHandler = new SqlHandler();
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
SelectOracle selectOracle = method.getAnnotation(SelectOracle.class);
|
||||||
|
if (selectOracle != null) {
|
||||||
|
String sql = selectOracle.value();
|
||||||
|
boolean custom = selectOracle.custom();
|
||||||
|
return invokeDefault(1, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateOracle updateOracle = method.getAnnotation(UpdateOracle.class);
|
||||||
|
if (updateOracle != null) {
|
||||||
|
String sql = updateOracle.value();
|
||||||
|
boolean custom = updateOracle.custom();
|
||||||
|
return invokeDefault(2, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
InsertOracle insertOracle = method.getAnnotation(InsertOracle.class);
|
||||||
|
if (insertOracle != null) {
|
||||||
|
String sql = insertOracle.value();
|
||||||
|
boolean custom = insertOracle.custom();
|
||||||
|
return invokeDefault(3, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
DeleteOracle deleteOracle = method.getAnnotation(DeleteOracle.class);
|
||||||
|
if (deleteOracle != null) {
|
||||||
|
String sql = deleteOracle.value();
|
||||||
|
boolean custom = deleteOracle.custom();
|
||||||
|
return invokeDefault(4, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
BatchInsertOracle batchInsertOracle = method.getAnnotation(BatchInsertOracle.class);
|
||||||
|
if (batchInsertOracle != null) {
|
||||||
|
String sql = batchInsertOracle.value();
|
||||||
|
boolean custom = batchInsertOracle.custom();
|
||||||
|
return invokeDefault(5, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
BatchUpdateOracle batchUpdateOracle = method.getAnnotation(BatchUpdateOracle.class);
|
||||||
|
if (batchUpdateOracle != null) {
|
||||||
|
String sql = batchUpdateOracle.value();
|
||||||
|
boolean custom = batchUpdateOracle.custom();
|
||||||
|
return invokeDefault(6, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
BatchDeleteOracle batchDeleteOracle = method.getAnnotation(BatchDeleteOracle.class);
|
||||||
|
if (batchDeleteOracle != null) {
|
||||||
|
String sql = batchDeleteOracle.value();
|
||||||
|
boolean custom = batchDeleteOracle.custom();
|
||||||
|
return invokeDefault(7, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Select select = method.getAnnotation(Select.class);
|
||||||
|
if (select != null) {
|
||||||
|
String sql = select.value();
|
||||||
|
boolean custom = select.custom();
|
||||||
|
return invokeDefault(1, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
Update update = method.getAnnotation(Update.class);
|
||||||
|
if (update != null) {
|
||||||
|
String sql = update.value();
|
||||||
|
boolean custom = update.custom();
|
||||||
|
return invokeDefault(2, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
Insert insert = method.getAnnotation(Insert.class);
|
||||||
|
if (insert != null) {
|
||||||
|
String sql = insert.value();
|
||||||
|
boolean custom = insert.custom();
|
||||||
|
return invokeDefault(3, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
Delete delete = method.getAnnotation(Delete.class);
|
||||||
|
if (delete != null) {
|
||||||
|
String sql = delete.value();
|
||||||
|
boolean custom = delete.custom();
|
||||||
|
return invokeDefault(4, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
BatchInsert batchInsert = method.getAnnotation(BatchInsert.class);
|
||||||
|
if (batchInsert != null) {
|
||||||
|
String sql = batchInsert.value();
|
||||||
|
boolean custom = batchInsert.custom();
|
||||||
|
return invokeDefault(5, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
BatchUpdate batchUpdate = method.getAnnotation(BatchUpdate.class);
|
||||||
|
if (batchUpdate != null) {
|
||||||
|
String sql = batchUpdate.value();
|
||||||
|
boolean custom = batchUpdate.custom();
|
||||||
|
return invokeDefault(6, sql, custom, rs, method, args);
|
||||||
|
}
|
||||||
|
BatchDelete batchDelete = method.getAnnotation(BatchDelete.class);
|
||||||
|
if (batchDelete != null) {
|
||||||
|
String sql = batchDelete.value();
|
||||||
|
boolean custom = batchDelete.custom();
|
||||||
|
return invokeDefault(7, sql, custom, rs, method, args);
|
||||||
}
|
}
|
||||||
throw new CustomerException("该方法没有添加注解!请检查是否正确添加注解!@Select、@Update、@Insert、@Delete、@BatchUpdate、@BatchInsert、@BatchDelete");
|
throw new CustomerException("该方法没有添加注解!请检查是否正确添加注解!@Select、@Update、@Insert、@Delete、@BatchUpdate、@BatchInsert、@BatchDelete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Object invokeRsTrans(Object proxy, Method method, Object[] args, String name) {
|
private Object invokeRsTrans(Object proxy, Method method, Object[] args, String name) {
|
||||||
String mapperKey = method.getDeclaringClass().getName();
|
String mapperKey = method.getDeclaringClass().getName();
|
||||||
if (!"".equals(name) && null != name) {
|
if (!"".equals(name) && null != name) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class ResultMapper {
|
||||||
typeHandler.put(Boolean.class, new BooleanTypeHandler());
|
typeHandler.put(Boolean.class, new BooleanTypeHandler());
|
||||||
typeHandler.put(boolean.class, new BooleanTypeHandler());
|
typeHandler.put(boolean.class, new BooleanTypeHandler());
|
||||||
typeHandler.put(Date.class, new DataTypeHandler());
|
typeHandler.put(Date.class, new DataTypeHandler());
|
||||||
typeHandler.put(Float.class, new FloatTypeHandler());
|
typeHandler.put(float.class, new FloatTypeHandler());
|
||||||
typeHandler.put(Float.class, new FloatTypeHandler());
|
typeHandler.put(Float.class, new FloatTypeHandler());
|
||||||
typeHandler.put(double.class, new DoubleTypeHandler());
|
typeHandler.put(double.class, new DoubleTypeHandler());
|
||||||
typeHandler.put(Double.class, new DoubleTypeHandler());
|
typeHandler.put(Double.class, new DoubleTypeHandler());
|
||||||
|
@ -319,8 +319,12 @@ public class ResultMapper {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||||
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getInt(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getInt(i + 1));
|
||||||
|
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getInt(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
||||||
|
@ -328,16 +332,24 @@ public class ResultMapper {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||||
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getFloat(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getFloat(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getFloat(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getFloat(i + 1));
|
||||||
|
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getFloat(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (enable) {
|
if (enable) {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||||
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
||||||
|
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getString(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
|
@ -386,8 +398,10 @@ public class ResultMapper {
|
||||||
} else {
|
} else {
|
||||||
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, fieldName, declaredField);
|
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, fieldName, declaredField);
|
||||||
}
|
}
|
||||||
|
if (!Objects.isNull(value)) {
|
||||||
propertyDescriptor.getWriteMethod().invoke(o, value);
|
propertyDescriptor.getWriteMethod().invoke(o, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.getLogger().error("报错了,写入数据到实体类报错!\n" + Util.getErrString(e));
|
Util.getLogger().error("报错了,写入数据到实体类报错!\n" + Util.getErrString(e));
|
||||||
|
@ -449,8 +463,12 @@ public class ResultMapper {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||||
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getInt(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getInt(i + 1));
|
||||||
|
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getInt(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
||||||
|
@ -458,8 +476,12 @@ public class ResultMapper {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||||
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getFloat(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getFloat(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getFloat(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getFloat(i + 1));
|
||||||
|
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getFloat(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (method.isAnnotationPresent(Associations.class)) {
|
if (method.isAnnotationPresent(Associations.class)) {
|
||||||
|
@ -488,8 +510,12 @@ public class ResultMapper {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||||
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
||||||
|
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getString(i + 1));
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
@ -528,6 +554,7 @@ public class ResultMapper {
|
||||||
Object value = null;
|
Object value = null;
|
||||||
String fieldName = propertyDescriptor.getName();
|
String fieldName = propertyDescriptor.getName();
|
||||||
|
|
||||||
|
Field declaredField = o.getClass().getDeclaredField(fieldName);
|
||||||
if (Strings.isNullOrEmpty(fieldName)) {
|
if (Strings.isNullOrEmpty(fieldName)) {
|
||||||
fieldName = propertyDescriptor.getDisplayName();
|
fieldName = propertyDescriptor.getDisplayName();
|
||||||
}
|
}
|
||||||
|
@ -536,7 +563,20 @@ public class ResultMapper {
|
||||||
if (association != null) {
|
if (association != null) {
|
||||||
if (association.property().equals(fieldName)) {
|
if (association.property().equals(fieldName)) {
|
||||||
Object cassociationValue = association(rs, association, method);
|
Object cassociationValue = association(rs, association, method);
|
||||||
|
if (cassociationValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (paramType.containsKey(declaredField.getType())) {
|
||||||
|
cassociationValue = paramType.get(declaredField.getType()).apply(String.valueOf(cassociationValue));
|
||||||
|
}
|
||||||
|
try {
|
||||||
propertyDescriptor.getWriteMethod().invoke(o, cassociationValue);
|
propertyDescriptor.getWriteMethod().invoke(o, cassociationValue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||||
|
if (value != null) {
|
||||||
|
Util.getLogger().error("查询数据类型: " + value.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,21 +586,37 @@ public class ResultMapper {
|
||||||
if (collectionMapping != null) {
|
if (collectionMapping != null) {
|
||||||
if (fieldName.equals(collectionMapping.property()) && !"".equals(collectionMapping.property())) {
|
if (fieldName.equals(collectionMapping.property()) && !"".equals(collectionMapping.property())) {
|
||||||
Object collection = collection(rs, collectionMapping, method);
|
Object collection = collection(rs, collectionMapping, method);
|
||||||
|
try {
|
||||||
propertyDescriptor.getWriteMethod().invoke(o, collection);
|
propertyDescriptor.getWriteMethod().invoke(o, collection);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||||
|
if (value != null) {
|
||||||
|
Util.getLogger().error("查询数据类型: " + value.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Field declaredField = o.getClass().getDeclaredField(fieldName);
|
TypeHandler typeHandler = ResultMapper.typeHandler.get(propertyType);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, Util.toUnderlineCase(fieldName), declaredField);
|
value = typeHandler == null ? null : typeHandler.getValue(rs, Util.toUnderlineCase(fieldName), declaredField);
|
||||||
} else {
|
} else {
|
||||||
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, fieldName, declaredField);
|
value = typeHandler == null ? null : typeHandler.getValue(rs, fieldName, declaredField);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
if (!Objects.isNull(value)) {
|
||||||
propertyDescriptor.getWriteMethod().invoke(o, value);
|
propertyDescriptor.getWriteMethod().invoke(o, value);
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||||
|
if (value != null) {
|
||||||
|
Util.getLogger().error("查询数据类型: " + value.getClass());
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.getLogger().error("报错了,写入数据到实体类报错!\n" + Util.getErrString(e));
|
Util.getLogger().error("报错了,写入数据到实体类报错!\n" + Util.getErrString(e));
|
||||||
throw new CustomerException(e.getMessage(), e);
|
throw new CustomerException(e.getMessage(), e);
|
||||||
|
@ -607,6 +663,9 @@ public class ResultMapper {
|
||||||
Id id = annotation.id();
|
Id id = annotation.id();
|
||||||
String column = annotation.column();
|
String column = annotation.column();
|
||||||
String columnValue = rs.getString(column);
|
String columnValue = rs.getString(column);
|
||||||
|
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||||
|
columnValue = rs.getString(column.toUpperCase());
|
||||||
|
}
|
||||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -665,6 +724,9 @@ public class ResultMapper {
|
||||||
Id id = annotation.id();
|
Id id = annotation.id();
|
||||||
String column = annotation.column();
|
String column = annotation.column();
|
||||||
String columnValue = rs.getString(column);
|
String columnValue = rs.getString(column);
|
||||||
|
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||||
|
columnValue = rs.getString(column.toUpperCase());
|
||||||
|
}
|
||||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.AnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AnnotationSynthesizer}的基本实现
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAnnotationSynthesizer<T> implements AnnotationSynthesizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解来源最初来源
|
||||||
|
*/
|
||||||
|
protected final T source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包含根注解以及其元注解在内的全部注解实例
|
||||||
|
*/
|
||||||
|
protected final Map<Class<? extends Annotation>, SynthesizedAnnotation> synthesizedAnnotationMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已经合成过的注解对象
|
||||||
|
*/
|
||||||
|
private final Map<Class<? extends Annotation>, Annotation> synthesizedProxyAnnotations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解选择器
|
||||||
|
*/
|
||||||
|
protected final SynthesizedAnnotationSelector annotationSelector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解属性处理器
|
||||||
|
*/
|
||||||
|
protected final Collection<SynthesizedAnnotationPostProcessor> postProcessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解扫描器
|
||||||
|
*/
|
||||||
|
protected final AnnotationScanner annotationScanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个注解合成器
|
||||||
|
*
|
||||||
|
* @param source 当前查找的注解对象
|
||||||
|
* @param annotationSelector 合成注解选择器
|
||||||
|
* @param annotationPostProcessors 注解后置处理器
|
||||||
|
* @param annotationScanner 注解扫描器,该扫描器需要支持扫描注解类
|
||||||
|
*/
|
||||||
|
protected AbstractAnnotationSynthesizer(
|
||||||
|
T source,
|
||||||
|
SynthesizedAnnotationSelector annotationSelector,
|
||||||
|
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
|
||||||
|
AnnotationScanner annotationScanner) {
|
||||||
|
Assert.notNull(source, "source must not null");
|
||||||
|
Assert.notNull(annotationSelector, "annotationSelector must not null");
|
||||||
|
Assert.notNull(annotationPostProcessors, "annotationPostProcessors must not null");
|
||||||
|
Assert.notNull(annotationPostProcessors, "annotationScanner must not null");
|
||||||
|
|
||||||
|
this.source = source;
|
||||||
|
this.annotationSelector = annotationSelector;
|
||||||
|
this.annotationScanner = annotationScanner;
|
||||||
|
this.postProcessors = CollUtil.unmodifiable(
|
||||||
|
CollUtil.sort(annotationPostProcessors, Comparator.comparing(SynthesizedAnnotationPostProcessor::order))
|
||||||
|
);
|
||||||
|
this.synthesizedProxyAnnotations = new LinkedHashMap<>();
|
||||||
|
this.synthesizedAnnotationMap = MapUtil.unmodifiable(loadAnnotations());
|
||||||
|
annotationPostProcessors.forEach(processor ->
|
||||||
|
synthesizedAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载合成注解的必要属性
|
||||||
|
*
|
||||||
|
* @return 合成注解
|
||||||
|
*/
|
||||||
|
protected abstract Map<Class<? extends Annotation>, SynthesizedAnnotation> loadAnnotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定的注解类型和对应注解对象,合成最终所需的合成注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param annotation 合成注解对象
|
||||||
|
* @param <A> 注解类型
|
||||||
|
* @return 最终所需的合成注解
|
||||||
|
*/
|
||||||
|
protected abstract <A extends Annotation> A synthesize(Class<A> annotationType, SynthesizedAnnotation annotation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解来源最初来源
|
||||||
|
*
|
||||||
|
* @return 合成注解来源最初来源
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解选择器
|
||||||
|
*
|
||||||
|
* @return 注解选择器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SynthesizedAnnotationSelector getAnnotationSelector() {
|
||||||
|
return annotationSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解后置处理器
|
||||||
|
*
|
||||||
|
* @return 合成注解后置处理器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
|
||||||
|
return postProcessors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已合成的注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 已合成的注解
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
|
||||||
|
return synthesizedAnnotationMap.get(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全部的合成注解
|
||||||
|
*
|
||||||
|
* @return 合成注解
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
|
||||||
|
return synthesizedAnnotationMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param <A> 注解类型
|
||||||
|
* @return 类型
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <A extends Annotation> A synthesize(Class<A> annotationType) {
|
||||||
|
return (A)synthesizedProxyAnnotations.computeIfAbsent(annotationType, type -> {
|
||||||
|
final SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType);
|
||||||
|
return ObjectUtil.isNull(synthesizedAnnotation) ?
|
||||||
|
null : synthesize(annotationType, synthesizedAnnotation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SynthesizedAnnotationPostProcessor}的基本实现,
|
||||||
|
* 用于处理注解中带有{@link Link}注解的属性。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see MirrorLinkAnnotationPostProcessor
|
||||||
|
* @see AliasLinkAnnotationPostProcessor
|
||||||
|
*/
|
||||||
|
public abstract class AbstractLinkAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若一个注解属性上存在{@link Link}注解,注解的{@link Link#type()}返回值在{@link #processTypes()}中存在,
|
||||||
|
* 且此{@link Link}指定的注解对象在当前的{@link SynthesizedAggregateAnnotation}中存在,
|
||||||
|
* 则从聚合器中获取类型对应的合成注解对象,与该对象中的指定属性,然后将全部关联数据交给
|
||||||
|
* {@link #processLinkedAttribute}处理。
|
||||||
|
*
|
||||||
|
* @param synthesizedAnnotation 合成的注解
|
||||||
|
* @param synthesizer 合成注解聚合器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) {
|
||||||
|
final Map<String, AnnotationAttribute> attributeMap = new HashMap<>(synthesizedAnnotation.getAttributes());
|
||||||
|
attributeMap.forEach((originalAttributeName, originalAttribute) -> {
|
||||||
|
// 获取注解
|
||||||
|
final Link link = getLinkAnnotation(originalAttribute, processTypes());
|
||||||
|
if (ObjectUtil.isNull(link)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取注解属性
|
||||||
|
final SynthesizedAnnotation linkedAnnotation = getLinkedAnnotation(link, synthesizer, synthesizedAnnotation.annotationType());
|
||||||
|
if (ObjectUtil.isNull(linkedAnnotation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final AnnotationAttribute linkedAttribute = linkedAnnotation.getAttributes().get(link.attribute());
|
||||||
|
// 处理
|
||||||
|
processLinkedAttribute(
|
||||||
|
synthesizer, link,
|
||||||
|
synthesizedAnnotation, synthesizedAnnotation.getAttributes().get(originalAttributeName),
|
||||||
|
linkedAnnotation, linkedAttribute
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== 抽象方法 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当属性上存在{@link Link}注解时,仅当{@link Link#type()}在本方法返回值内存在时才进行处理
|
||||||
|
*
|
||||||
|
* @return 支持处理的{@link RelationType}类型
|
||||||
|
*/
|
||||||
|
protected abstract RelationType[] processTypes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对关联的合成注解对象及其关联属性的处理
|
||||||
|
*
|
||||||
|
* @param synthesizer 注解合成器
|
||||||
|
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
|
||||||
|
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
|
||||||
|
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
|
||||||
|
* @param linkedAnnotation {@link Link}指向的关联注解对象
|
||||||
|
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
|
||||||
|
*/
|
||||||
|
protected abstract void processLinkedAttribute(
|
||||||
|
AnnotationSynthesizer synthesizer, Link annotation,
|
||||||
|
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
|
||||||
|
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute
|
||||||
|
);
|
||||||
|
|
||||||
|
// =========================== @Link注解的处理 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从注解属性上获取指定类型的{@link Link}注解
|
||||||
|
*
|
||||||
|
* @param attribute 注解属性
|
||||||
|
* @param relationTypes 类型
|
||||||
|
* @return 注解
|
||||||
|
*/
|
||||||
|
protected Link getLinkAnnotation(AnnotationAttribute attribute, RelationType... relationTypes) {
|
||||||
|
return Opt.ofNullable(attribute)
|
||||||
|
.map(t -> AnnotationUtil.getSynthesizedAnnotation(attribute.getAttribute(), Link.class))
|
||||||
|
.filter(a -> ArrayUtil.contains(relationTypes, a.type()))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从合成注解中获取{@link Link#type()}指定的注解对象
|
||||||
|
*
|
||||||
|
* @param annotation {@link Link}注解
|
||||||
|
* @param synthesizer 注解合成器
|
||||||
|
* @param defaultType 默认类型
|
||||||
|
* @return {@link SynthesizedAnnotation}
|
||||||
|
*/
|
||||||
|
protected SynthesizedAnnotation getLinkedAnnotation(Link annotation, AnnotationSynthesizer synthesizer, Class<? extends Annotation> defaultType) {
|
||||||
|
final Class<?> targetAnnotationType = getLinkedAnnotationType(annotation, defaultType);
|
||||||
|
return synthesizer.getSynthesizedAnnotation(targetAnnotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若{@link Link#annotation()}获取的类型{@code Annotation#getClass()},则返回{@code defaultType},
|
||||||
|
* 否则返回{@link Link#annotation()}指定的类型
|
||||||
|
*
|
||||||
|
* @param annotation {@link Link}注解
|
||||||
|
* @param defaultType 默认注解类型
|
||||||
|
* @return 注解类型
|
||||||
|
*/
|
||||||
|
protected Class<?> getLinkedAnnotationType(Link annotation, Class<?> defaultType) {
|
||||||
|
return ObjectUtil.equals(annotation.annotation(), Annotation.class) ?
|
||||||
|
defaultType : annotation.annotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== 注解属性的校验 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验两个注解属性的返回值类型是否一致
|
||||||
|
*
|
||||||
|
* @param original 原属性
|
||||||
|
* @param alias 别名属性
|
||||||
|
*/
|
||||||
|
protected void checkAttributeType(AnnotationAttribute original, AnnotationAttribute alias) {
|
||||||
|
Assert.equals(
|
||||||
|
original.getAttributeType(), alias.getAttributeType(),
|
||||||
|
"return type of the linked attribute [{}] is inconsistent with the original [{}]",
|
||||||
|
original.getAttribute(), alias.getAttribute()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查{@link Link}指向的注解属性是否就是本身
|
||||||
|
*
|
||||||
|
* @param original {@link Link}注解的属性
|
||||||
|
* @param linked {@link Link}指向的注解属性
|
||||||
|
*/
|
||||||
|
protected void checkLinkedSelf(AnnotationAttribute original, AnnotationAttribute linked) {
|
||||||
|
boolean linkSelf = (original == linked) || ObjectUtil.equals(original.getAttribute(), linked.getAttribute());
|
||||||
|
Assert.isFalse(linkSelf, "cannot link self [{}]", original.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查{@link Link}指向的注解属性是否存在
|
||||||
|
*
|
||||||
|
* @param original {@link Link}注解的属性
|
||||||
|
* @param linkedAttribute {@link Link}指向的注解属性
|
||||||
|
* @param annotation {@link Link}注解
|
||||||
|
*/
|
||||||
|
protected void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) {
|
||||||
|
Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]",
|
||||||
|
original.getAttribute(), annotation.attribute(),
|
||||||
|
getLinkedAnnotationType(annotation, original.getAnnotationType())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link WrappedAnnotationAttribute}的基本实现
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see ForceAliasedAnnotationAttribute
|
||||||
|
* @see AliasedAnnotationAttribute
|
||||||
|
* @see MirroredAnnotationAttribute
|
||||||
|
*/
|
||||||
|
public abstract class AbstractWrappedAnnotationAttribute implements WrappedAnnotationAttribute {
|
||||||
|
|
||||||
|
protected final AnnotationAttribute original;
|
||||||
|
protected final AnnotationAttribute linked;
|
||||||
|
|
||||||
|
protected AbstractWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) {
|
||||||
|
Assert.notNull(original, "target must not null");
|
||||||
|
Assert.notNull(linked, "linked must not null");
|
||||||
|
this.original = original;
|
||||||
|
this.linked = linked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationAttribute getOriginal() {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationAttribute getLinked() {
|
||||||
|
return linked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationAttribute getNonWrappedOriginal() {
|
||||||
|
AnnotationAttribute curr = null;
|
||||||
|
AnnotationAttribute next = original;
|
||||||
|
while (next != null) {
|
||||||
|
curr = next;
|
||||||
|
next = next.isWrapped() ? ((WrappedAnnotationAttribute)curr).getOriginal() : null;
|
||||||
|
}
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<AnnotationAttribute> getAllLinkedNonWrappedAttributes() {
|
||||||
|
List<AnnotationAttribute> leafAttributes = new ArrayList<>();
|
||||||
|
collectLeafAttribute(this, leafAttributes);
|
||||||
|
return leafAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectLeafAttribute(AnnotationAttribute curr, List<AnnotationAttribute> leafAttributes) {
|
||||||
|
if (ObjectUtil.isNull(curr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!curr.isWrapped()) {
|
||||||
|
leafAttributes.add(curr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WrappedAnnotationAttribute wrappedAttribute = (WrappedAnnotationAttribute)curr;
|
||||||
|
collectLeafAttribute(wrappedAttribute.getOriginal(), leafAttributes);
|
||||||
|
collectLeafAttribute(wrappedAttribute.getLinked(), leafAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示一组被聚合在一起的注解对象
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public interface AggregateAnnotation extends Annotation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在聚合中是否存在的指定类型注解对象
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
boolean isAnnotationPresent(Class<? extends Annotation> annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取聚合中的全部注解对象
|
||||||
|
*
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
Annotation[] getAnnotations();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 别名注解,使用此注解的字段、方法、参数等会有一个别名,用于Bean拷贝、Bean转Map等
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.1.1
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||||
|
public @interface Alias {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 别名值,即使用此注解要替换成的别名名称
|
||||||
|
*
|
||||||
|
* @return 别名值
|
||||||
|
*/
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.ForestMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.LinkedForestMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.TreeEntry;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>用于处理注解对象中带有{@link Alias}注解的属性。<br>
|
||||||
|
* 当该处理器执行完毕后,{@link Alias}注解指向的目标注解的属性将会被包装并替换为
|
||||||
|
* {@link ForceAliasedAnnotationAttribute}。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see Alias
|
||||||
|
* @see ForceAliasedAnnotationAttribute
|
||||||
|
*/
|
||||||
|
public class AliasAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int order() {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) {
|
||||||
|
final Map<String, AnnotationAttribute> attributeMap = synthesizedAnnotation.getAttributes();
|
||||||
|
|
||||||
|
// 记录别名与属性的关系
|
||||||
|
final ForestMap<String, AnnotationAttribute> attributeAliasMappings = new LinkedForestMap<>(false);
|
||||||
|
attributeMap.forEach((attributeName, attribute) -> {
|
||||||
|
final String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class))
|
||||||
|
.map(Alias::value)
|
||||||
|
.orElse(null);
|
||||||
|
if (ObjectUtil.isNull(alias)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final AnnotationAttribute aliasAttribute = attributeMap.get(alias);
|
||||||
|
Assert.notNull(aliasAttribute, "no method for alias: [{}]", alias);
|
||||||
|
attributeAliasMappings.putLinkedNodes(alias, aliasAttribute, attributeName, attribute);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理别名
|
||||||
|
attributeMap.forEach((attributeName, attribute) -> {
|
||||||
|
final AnnotationAttribute resolvedAttribute = Opt.ofNullable(attributeName)
|
||||||
|
.map(attributeAliasMappings::getRootNode)
|
||||||
|
.map(TreeEntry::getValue)
|
||||||
|
.orElse(attribute);
|
||||||
|
Assert.isTrue(
|
||||||
|
ObjectUtil.isNull(resolvedAttribute)
|
||||||
|
|| ClassUtil.isAssignable(attribute.getAttributeType(), resolvedAttribute.getAttributeType()),
|
||||||
|
"return type of the root alias method [{}] is inconsistent with the original [{}]",
|
||||||
|
resolvedAttribute.getClass(), attribute.getAttributeType()
|
||||||
|
);
|
||||||
|
if (attribute != resolvedAttribute) {
|
||||||
|
attributeMap.put(attributeName, new ForceAliasedAnnotationAttribute(attribute, resolvedAttribute));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
synthesizedAnnotation.setAttributes(attributeMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@link Link}的子注解。表示“原始属性”将作为“关联属性”的别名。
|
||||||
|
* <ul>
|
||||||
|
* <li>当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;</li>
|
||||||
|
* <li>当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;</li>
|
||||||
|
* </ul>
|
||||||
|
* <b>注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效</b>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see Link
|
||||||
|
* @see RelationType#ALIAS_FOR
|
||||||
|
*/
|
||||||
|
@Link(type = RelationType.ALIAS_FOR)
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||||
|
public @interface AliasFor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||||
|
*
|
||||||
|
* @return 注解类型
|
||||||
|
*/
|
||||||
|
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
Class<? extends Annotation> annotation() default Annotation.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #annotation()}指定注解中关联的属性
|
||||||
|
*
|
||||||
|
* @return 关联属性
|
||||||
|
*/
|
||||||
|
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
String attribute() default "";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为
|
||||||
|
* {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}的属性。<br>
|
||||||
|
* 当该处理器执行完毕后,{@link Link}注解指向的目标注解的属性将会被包装并替换为
|
||||||
|
* {@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see RelationType#ALIAS_FOR
|
||||||
|
* @see AliasedAnnotationAttribute
|
||||||
|
* @see RelationType#FORCE_ALIAS_FOR
|
||||||
|
* @see ForceAliasedAnnotationAttribute
|
||||||
|
*/
|
||||||
|
public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
|
||||||
|
|
||||||
|
private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR };
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int order() {
|
||||||
|
return Integer.MIN_VALUE + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该处理器只处理{@link Link#type()}类型为{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的注解属性
|
||||||
|
*
|
||||||
|
* @return 含有{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的数组
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected RelationType[] processTypes() {
|
||||||
|
return PROCESSED_RELATION_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取{@link Link}指向的目标注解属性,并根据{@link Link#type()}的类型是
|
||||||
|
* {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}
|
||||||
|
* 将目标注解属性包装为{@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute},
|
||||||
|
* 然后用包装后注解属性在对应的合成注解中替换原始的目标注解属性
|
||||||
|
*
|
||||||
|
* @param synthesizer 注解合成器
|
||||||
|
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
|
||||||
|
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
|
||||||
|
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
|
||||||
|
* @param linkedAnnotation {@link Link}指向的关联注解对象
|
||||||
|
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void processLinkedAttribute(
|
||||||
|
AnnotationSynthesizer synthesizer, Link annotation,
|
||||||
|
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
|
||||||
|
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
|
||||||
|
// 校验别名关系
|
||||||
|
checkAliasRelation(annotation, originalAttribute, linkedAttribute);
|
||||||
|
// 处理aliasFor类型的关系
|
||||||
|
if (RelationType.ALIAS_FOR.equals(annotation.type())) {
|
||||||
|
wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, AliasedAnnotationAttribute::new);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 处理forceAliasFor类型的关系
|
||||||
|
wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, ForceAliasedAnnotationAttribute::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装
|
||||||
|
*/
|
||||||
|
private void wrappingLinkedAttribute(
|
||||||
|
AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator<AnnotationAttribute> wrapping) {
|
||||||
|
// 不是包装属性
|
||||||
|
if (!aliasAttribute.isWrapped()) {
|
||||||
|
processAttribute(synthesizer, originalAttribute, aliasAttribute, wrapping);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 是包装属性
|
||||||
|
final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute;
|
||||||
|
wrapper.getAllLinkedNonWrappedAttributes().forEach(
|
||||||
|
t -> processAttribute(synthesizer, originalAttribute, t, wrapping)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解属性,然后将其再进行一层包装
|
||||||
|
*/
|
||||||
|
private void processAttribute(
|
||||||
|
AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute,
|
||||||
|
AnnotationAttribute target, BinaryOperator<AnnotationAttribute> wrapping) {
|
||||||
|
Opt.ofNullable(target.getAnnotationType())
|
||||||
|
.map(synthesizer::getSynthesizedAnnotation)
|
||||||
|
.ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基本校验
|
||||||
|
*/
|
||||||
|
private void checkAliasRelation(Link annotation, AnnotationAttribute originalAttribute, AnnotationAttribute linkedAttribute) {
|
||||||
|
checkLinkedAttributeNotNull(originalAttribute, linkedAttribute, annotation);
|
||||||
|
checkAttributeType(originalAttribute, linkedAttribute);
|
||||||
|
checkCircularDependency(originalAttribute, linkedAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查两个属性是否互为别名
|
||||||
|
*/
|
||||||
|
private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) {
|
||||||
|
checkLinkedSelf(original, alias);
|
||||||
|
Link annotation = getLinkAnnotation(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR);
|
||||||
|
if (ObjectUtil.isNull(annotation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Class<?> aliasAnnotationType = getLinkedAnnotationType(annotation, alias.getAnnotationType());
|
||||||
|
if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Assert.notEquals(
|
||||||
|
annotation.attribute(), original.getAttributeName(),
|
||||||
|
"circular reference between the alias attribute [{}] and the original attribute [{}]",
|
||||||
|
alias.getAttribute(), original.getAttribute()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示一个具有别名的属性。
|
||||||
|
* 当别名属性值为默认值时,优先返回原属性的值,当别名属性不为默认值时,优先返回别名属性的值
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see AliasLinkAnnotationPostProcessor
|
||||||
|
* @see RelationType#ALIAS_FOR
|
||||||
|
*/
|
||||||
|
public class AliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
|
||||||
|
|
||||||
|
protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
|
||||||
|
super(origin, linked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若{@link #linked}为默认值,则返回{@link #original}的值,否则返回{@link #linked}的值
|
||||||
|
*
|
||||||
|
* @return 属性值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
return linked.isValueEquivalentToDefaultValue() ? super.getValue() : linked.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
|
||||||
|
*
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isValueEquivalentToDefaultValue() {
|
||||||
|
return linked.isValueEquivalentToDefaultValue() && original.isValueEquivalentToDefaultValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示注解的某个属性,等同于绑定的调用对象的{@link Method}方法。<br>
|
||||||
|
* 在{@link SynthesizedAggregateAnnotation}的解析以及取值过程中,
|
||||||
|
* 可以通过设置{@link SynthesizedAnnotation}的注解属性,
|
||||||
|
* 从而使得可以从一个注解对象中属性获取另一个注解对象的属性值
|
||||||
|
*
|
||||||
|
* <p>一般情况下,注解属性的处理会发生在{@link SynthesizedAnnotationPostProcessor}调用时
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see SynthesizedAnnotationPostProcessor
|
||||||
|
* @see WrappedAnnotationAttribute
|
||||||
|
* @see CacheableAnnotationAttribute
|
||||||
|
* @see AbstractWrappedAnnotationAttribute
|
||||||
|
* @see ForceAliasedAnnotationAttribute
|
||||||
|
* @see AliasedAnnotationAttribute
|
||||||
|
* @see MirroredAnnotationAttribute
|
||||||
|
*/
|
||||||
|
public interface AnnotationAttribute {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解对象
|
||||||
|
*
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
Annotation getAnnotation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解属性对应的方法
|
||||||
|
*
|
||||||
|
* @return 注解属性对应的方法
|
||||||
|
*/
|
||||||
|
Method getAttribute();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取声明属性的注解类
|
||||||
|
*
|
||||||
|
* @return 声明注解的注解类
|
||||||
|
*/
|
||||||
|
default Class<?> getAnnotationType() {
|
||||||
|
return getAttribute().getDeclaringClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性名称
|
||||||
|
*
|
||||||
|
* @return 属性名称
|
||||||
|
*/
|
||||||
|
default String getAttributeName() {
|
||||||
|
return getAttribute().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解属性
|
||||||
|
*
|
||||||
|
* @return 注解属性
|
||||||
|
*/
|
||||||
|
default Object getValue() {
|
||||||
|
return ReflectUtil.invoke(getAnnotation(), getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该注解属性的值是否等于默认值
|
||||||
|
*
|
||||||
|
* @return 该注解属性的值是否等于默认值
|
||||||
|
*/
|
||||||
|
boolean isValueEquivalentToDefaultValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性类型
|
||||||
|
*
|
||||||
|
* @return 属性类型
|
||||||
|
*/
|
||||||
|
default Class<?> getAttributeType() {
|
||||||
|
return getAttribute().getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性上的注解
|
||||||
|
*
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||||
|
return getAttribute().getAnnotation(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
default boolean isWrapped() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示一个可以从当前接口的实现类中,获得特定的属性值
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface AnnotationAttributeValueProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @param attributeType 属性类型
|
||||||
|
* @return 注解属性值
|
||||||
|
*/
|
||||||
|
Object getAttributeValue(String attributeName, Class<?> attributeType);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解代理<br>
|
||||||
|
* 通过代理指定注解,可以自定义调用注解的方法逻辑,如支持{@link Alias} 注解
|
||||||
|
*
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public class AnnotationProxy<T extends Annotation> implements Annotation, InvocationHandler, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final T annotation;
|
||||||
|
private final Class<T> type;
|
||||||
|
private final Map<String, Object> attributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param annotation 注解
|
||||||
|
*/
|
||||||
|
public AnnotationProxy(T annotation) {
|
||||||
|
this.annotation = annotation;
|
||||||
|
//noinspection unchecked
|
||||||
|
this.type = (Class<T>) annotation.annotationType();
|
||||||
|
this.attributes = initAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Annotation> annotationType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
|
||||||
|
// 注解别名
|
||||||
|
Alias alias = method.getAnnotation(Alias.class);
|
||||||
|
if(null != alias){
|
||||||
|
final String name = alias.value();
|
||||||
|
if(StrUtil.isNotBlank(name)){
|
||||||
|
if(false == attributes.containsKey(name)){
|
||||||
|
throw new IllegalArgumentException(StrUtil.format("No method for alias: [{}]", name));
|
||||||
|
}
|
||||||
|
return attributes.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object value = attributes.get(method.getName());
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return method.invoke(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化注解的属性<br>
|
||||||
|
* 此方法预先调用所有注解的方法,将注解方法值缓存于attributes中
|
||||||
|
*
|
||||||
|
* @return 属性(方法结果)映射
|
||||||
|
*/
|
||||||
|
private Map<String, Object> initAttributes() {
|
||||||
|
final Method[] methods = ReflectUtil.getMethods(this.type);
|
||||||
|
final Map<String, Object> attributes = new HashMap<>(methods.length, 1);
|
||||||
|
|
||||||
|
for (Method method : methods) {
|
||||||
|
// 跳过匿名内部类自动生成的方法
|
||||||
|
if (method.isSynthetic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes.put(method.getName(), ReflectUtil.invoke(this.annotation, method));
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>注解合成器,用于处理一组给定的与{@link #getSource()}具有直接或间接联系的注解对象,
|
||||||
|
* 并返回与原始注解对象具有不同属性的“合成”注解。
|
||||||
|
*
|
||||||
|
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
|
||||||
|
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
|
||||||
|
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation},
|
||||||
|
* 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。<br>
|
||||||
|
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
|
||||||
|
* 自定义选择器以拦截原始注解被扫描的过程。
|
||||||
|
*
|
||||||
|
* <p>当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
|
||||||
|
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor},
|
||||||
|
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
|
||||||
|
* 该钩子一般用于根据{@link Link}注解对属性进行调整。<br>
|
||||||
|
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
|
||||||
|
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
|
||||||
|
*
|
||||||
|
* <p>使用{@link #synthesize(Class)}用于获取“合成”后的注解,
|
||||||
|
* 该注解对象的属性可能会与原始的对象属性不同。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public interface AnnotationSynthesizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解来源最初来源
|
||||||
|
*
|
||||||
|
* @return 合成注解来源最初来源
|
||||||
|
*/
|
||||||
|
Object getSource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解选择器
|
||||||
|
*
|
||||||
|
* @return 注解选择器
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotationSelector getAnnotationSelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解后置处理器
|
||||||
|
*
|
||||||
|
* @return 合成注解后置处理器
|
||||||
|
*/
|
||||||
|
Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已合成的注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 已合成的注解
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全部的合成注解
|
||||||
|
*
|
||||||
|
* @return 合成注解
|
||||||
|
*/
|
||||||
|
Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 类型
|
||||||
|
*/
|
||||||
|
<T extends Annotation> T synthesize(Class<T> annotationType);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,576 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.AnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.MethodAnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.TypeAnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.exceptions.UtilException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Func1;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.LambdaUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.*;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.lang.invoke.SerializedLambda;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解工具类<br>
|
||||||
|
* 快速获取注解对象、注解值等工具封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.0.9
|
||||||
|
*/
|
||||||
|
public class AnnotationUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 元注解
|
||||||
|
*/
|
||||||
|
static final Set<Class<? extends Annotation>> META_ANNOTATIONS = CollUtil.newHashSet(Target.class, //
|
||||||
|
Retention.class, //
|
||||||
|
Inherited.class, //
|
||||||
|
Documented.class, //
|
||||||
|
SuppressWarnings.class, //
|
||||||
|
Override.class, //
|
||||||
|
Deprecated.class//
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Jdk自带的元注解。<br>
|
||||||
|
* 包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Target}</li>
|
||||||
|
* <li>{@link Retention}</li>
|
||||||
|
* <li>{@link Inherited}</li>
|
||||||
|
* <li>{@link Documented}</li>
|
||||||
|
* <li>{@link SuppressWarnings}</li>
|
||||||
|
* <li>{@link Override}</li>
|
||||||
|
* <li>{@link Deprecated}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 是否为Jdk自带的元注解
|
||||||
|
*/
|
||||||
|
public static boolean isJdkMetaAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return META_ANNOTATIONS.contains(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否不为Jdk自带的元注解。<br>
|
||||||
|
* 包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Target}</li>
|
||||||
|
* <li>{@link Retention}</li>
|
||||||
|
* <li>{@link Inherited}</li>
|
||||||
|
* <li>{@link Documented}</li>
|
||||||
|
* <li>{@link SuppressWarnings}</li>
|
||||||
|
* <li>{@link Override}</li>
|
||||||
|
* <li>{@link Deprecated}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 是否为Jdk自带的元注解
|
||||||
|
*/
|
||||||
|
public static boolean isNotJdkMateAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return false == isJdkMetaAnnotation(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定的被注解的元素转换为组合注解元素
|
||||||
|
*
|
||||||
|
* @param annotationEle 注解元素
|
||||||
|
* @return 组合注解元素
|
||||||
|
*/
|
||||||
|
public static CombinationAnnotationElement toCombination(AnnotatedElement annotationEle) {
|
||||||
|
if (annotationEle instanceof CombinationAnnotationElement) {
|
||||||
|
return (CombinationAnnotationElement) annotationEle;
|
||||||
|
}
|
||||||
|
return new CombinationAnnotationElement(annotationEle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解
|
||||||
|
*
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param isToCombination 是否为转换为组合注解,组合注解可以递归获取注解的注解
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination) {
|
||||||
|
return getAnnotations(annotationEle, isToCombination, (Predicate<Annotation>) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组合注解
|
||||||
|
*
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 限定的
|
||||||
|
* @return 注解对象数组
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public static <T> T[] getCombinationAnnotations(AnnotatedElement annotationEle, Class<T> annotationType) {
|
||||||
|
return getAnnotations(annotationEle, true, annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解
|
||||||
|
*
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param isToCombination 是否为转换为组合注解,组合注解可以递归获取注解的注解
|
||||||
|
* @param annotationType 限定的
|
||||||
|
* @return 注解对象数组
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public static <T> T[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination, Class<T> annotationType) {
|
||||||
|
final Annotation[] annotations = getAnnotations(annotationEle, isToCombination,
|
||||||
|
(annotation -> null == annotationType || annotationType.isAssignableFrom(annotation.getClass())));
|
||||||
|
|
||||||
|
final T[] result = ArrayUtil.newArray(annotationType, annotations.length);
|
||||||
|
for (int i = 0; i < annotations.length; i++) {
|
||||||
|
//noinspection unchecked
|
||||||
|
result[i] = (T) annotations[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解
|
||||||
|
*
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param isToCombination 是否为转换为组合注解,组合注解可以递归获取注解的注解
|
||||||
|
* @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
|
||||||
|
* @return 注解对象,如果提供的{@link AnnotatedElement}为{@code null},返回{@code null}
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination, Predicate<Annotation> predicate) {
|
||||||
|
if (null == annotationEle) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isToCombination) {
|
||||||
|
if (null == predicate) {
|
||||||
|
return toCombination(annotationEle).getAnnotations();
|
||||||
|
}
|
||||||
|
return CombinationAnnotationElement.of(annotationEle, predicate).getAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Annotation[] result = annotationEle.getAnnotations();
|
||||||
|
if (null == predicate) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return ArrayUtil.filter(result, predicate::test);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解
|
||||||
|
*
|
||||||
|
* @param <A> 注解类型
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotationEle, Class<A> annotationType) {
|
||||||
|
return (null == annotationEle) ? null : toCombination(annotationEle).getAnnotation(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否包含指定注解指定注解
|
||||||
|
*
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 是否包含指定注解
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public static boolean hasAnnotation(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) {
|
||||||
|
return null != getAnnotation(annotationEle, annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解默认值<br>
|
||||||
|
* 如果无指定的属性方法返回null
|
||||||
|
*
|
||||||
|
* @param <T> 注解值类型
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
* @throws UtilException 调用注解中的方法时执行异常
|
||||||
|
*/
|
||||||
|
public static <T> T getAnnotationValue(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) throws UtilException {
|
||||||
|
return getAnnotationValue(annotationEle, annotationType, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解属性的值<br>
|
||||||
|
* 如果无指定的属性方法返回null
|
||||||
|
*
|
||||||
|
* @param <T> 注解值类型
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param propertyName 属性名,例如注解中定义了name()方法,则 此处传入name
|
||||||
|
* @return 注解对象
|
||||||
|
* @throws UtilException 调用注解中的方法时执行异常
|
||||||
|
*/
|
||||||
|
public static <T> T getAnnotationValue(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType, String propertyName) throws UtilException {
|
||||||
|
final Annotation annotation = getAnnotation(annotationEle, annotationType);
|
||||||
|
if (null == annotation) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Method method = ReflectUtil.getMethodOfObj(annotation, propertyName);
|
||||||
|
if (null == method) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ReflectUtil.invoke(annotation, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解属性的值<br>
|
||||||
|
* 如果无指定的属性方法返回null
|
||||||
|
*
|
||||||
|
* @param <A> 注解类型
|
||||||
|
* @param <R> 注解类型值
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param propertyName 属性名,例如注解中定义了name()方法,则 此处传入name
|
||||||
|
* @return 注解对象
|
||||||
|
* @throws UtilException 调用注解中的方法时执行异常
|
||||||
|
* @since 5.8.9
|
||||||
|
*/
|
||||||
|
public static <A extends Annotation, R> R getAnnotationValue(AnnotatedElement annotationEle, Func1<A, R> propertyName) {
|
||||||
|
if (propertyName == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
final SerializedLambda lambda = LambdaUtil.resolve(propertyName);
|
||||||
|
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
|
||||||
|
final Class<A> annotationClass = ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';')));
|
||||||
|
return getAnnotationValue(annotationEle, annotationClass, lambda.getImplMethodName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定注解中所有属性值<br>
|
||||||
|
* 如果无指定的属性方法返回null
|
||||||
|
*
|
||||||
|
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
* @throws UtilException 调用注解中的方法时执行异常
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> getAnnotationValueMap(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) throws UtilException {
|
||||||
|
final Annotation annotation = getAnnotation(annotationEle, annotationType);
|
||||||
|
if (null == annotation) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Method[] methods = ReflectUtil.getMethods(annotationType, t -> {
|
||||||
|
if (ArrayUtil.isEmpty(t.getParameterTypes())) {
|
||||||
|
// 只读取无参方法
|
||||||
|
final String name = t.getName();
|
||||||
|
// 跳过自有的几个方法
|
||||||
|
return (false == "hashCode".equals(name)) //
|
||||||
|
&& (false == "toString".equals(name)) //
|
||||||
|
&& (false == "annotationType".equals(name));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
final HashMap<String, Object> result = new HashMap<>(methods.length, 1);
|
||||||
|
for (Method method : methods) {
|
||||||
|
result.put(method.getName(), ReflectUtil.invoke(annotation, method));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解类的保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @return 保留时间枚举
|
||||||
|
*/
|
||||||
|
public static RetentionPolicy getRetentionPolicy(Class<? extends Annotation> annotationType) {
|
||||||
|
final Retention retention = annotationType.getAnnotation(Retention.class);
|
||||||
|
if (null == retention) {
|
||||||
|
return RetentionPolicy.CLASS;
|
||||||
|
}
|
||||||
|
return retention.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解类可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @return 注解修饰的程序元素数组
|
||||||
|
*/
|
||||||
|
public static ElementType[] getTargetType(Class<? extends Annotation> annotationType) {
|
||||||
|
final Target target = annotationType.getAnnotation(Target.class);
|
||||||
|
if (null == target) {
|
||||||
|
return new ElementType[]{ElementType.TYPE, //
|
||||||
|
ElementType.FIELD, //
|
||||||
|
ElementType.METHOD, //
|
||||||
|
ElementType.PARAMETER, //
|
||||||
|
ElementType.CONSTRUCTOR, //
|
||||||
|
ElementType.LOCAL_VARIABLE, //
|
||||||
|
ElementType.ANNOTATION_TYPE, //
|
||||||
|
ElementType.PACKAGE//
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return target.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否会保存到 Javadoc 文档中
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @return 是否会保存到 Javadoc 文档中
|
||||||
|
*/
|
||||||
|
public static boolean isDocumented(Class<? extends Annotation> annotationType) {
|
||||||
|
return annotationType.isAnnotationPresent(Documented.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否可以被继承,默认为 false
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @return 是否会保存到 Javadoc 文档中
|
||||||
|
*/
|
||||||
|
public static boolean isInherited(Class<? extends Annotation> annotationType) {
|
||||||
|
return annotationType.isAnnotationPresent(Inherited.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描注解类,以及注解类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外,
|
||||||
|
* 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合
|
||||||
|
*
|
||||||
|
* <p>比如:<br>
|
||||||
|
* 若{@code annotationType}为 A,且A存在元注解B,B又存在元注解C和D,则有:
|
||||||
|
* <pre>
|
||||||
|
* |-> C.class [@a, @b]
|
||||||
|
* A.class -> B.class [@a] -|
|
||||||
|
* |-> D.class [@a, @c]
|
||||||
|
* </pre>
|
||||||
|
* 扫描A,则该方法最终将返回 {@code [@a, @a, @b, @a, @c]}
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @return 注解对象集合
|
||||||
|
* @see MetaAnnotationScanner
|
||||||
|
*/
|
||||||
|
public static List<Annotation> scanMetaAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return AnnotationScanner.DIRECTLY_AND_META_ANNOTATION.getAnnotationsIfSupport(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>扫描类以及类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认元注解外,
|
||||||
|
* 全部类/接口的{@link Class#getAnnotations()}方法返回的注解对象。<br>
|
||||||
|
* 层级结构将按广度优先递归,遵循规则如下:
|
||||||
|
* <ul>
|
||||||
|
* <li>同一层级中,优先处理父类,然后再处理父接口;</li>
|
||||||
|
* <li>同一个接口在不同层级出现,优先选择层级距离{@code targetClass}更近的接口;</li>
|
||||||
|
* <li>同一个接口在相同层级出现,优先选择其子类/子接口被先解析的那个;</li>
|
||||||
|
* </ul>
|
||||||
|
* 注解根据其声明类/接口被扫描的顺序排序,若注解都在同一个{@link Class}中被声明,则还会遵循{@link Class#getAnnotations()}的顺序。
|
||||||
|
*
|
||||||
|
* <p>比如:<br>
|
||||||
|
* 若{@code targetClass}为{@code A.class},且{@code A.class}存在父类{@code B.class}、父接口{@code C.class},
|
||||||
|
* 三个类的注解声明情况如下:
|
||||||
|
* <pre>
|
||||||
|
* |-> B.class [@a, @b]
|
||||||
|
* A.class [@a] -|
|
||||||
|
* |-> C.class [@a, @c]
|
||||||
|
* </pre>
|
||||||
|
* 则该方法最终将返回 {@code [@a, @a, @b, @a, @c]}
|
||||||
|
*
|
||||||
|
* @param targetClass 类
|
||||||
|
* @return 注解对象集合
|
||||||
|
* @see TypeAnnotationScanner
|
||||||
|
*/
|
||||||
|
public static List<Annotation> scanClass(Class<?> targetClass) {
|
||||||
|
return AnnotationScanner.TYPE_HIERARCHY.getAnnotationsIfSupport(targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>扫描方法,以及该方法所在类的{@link Class}层级结构中的具有相同方法签名的方法,
|
||||||
|
* 将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认元注解外,
|
||||||
|
* 全部匹配方法上{@link Method#getAnnotations()}方法返回的注解对象。<br>
|
||||||
|
* 方法所在类的层级结构将按广度优先递归,遵循规则如下:
|
||||||
|
* <ul>
|
||||||
|
* <li>同一层级中,优先处理父类,然后再处理父接口;</li>
|
||||||
|
* <li>同一个接口在不同层级出现,优先选择层级距离{@code targetClass}更近的接口;</li>
|
||||||
|
* <li>同一个接口在相同层级出现,优先选择其子类/子接口被先解析的那个;</li>
|
||||||
|
* </ul>
|
||||||
|
* 方法上的注解根据方法的声明类/接口被扫描的顺序排序,若注解都在同一个类的同一个方法中被声明,则还会遵循{@link Method#getAnnotations()}的顺序。
|
||||||
|
*
|
||||||
|
* <p>比如:<br>
|
||||||
|
* 若方法X声明于{@code A.class},且重载/重写自父类{@code B.class},并且父类中的方法X由重写至其实现的接口{@code C.class},
|
||||||
|
* 三个类的注解声明情况如下:
|
||||||
|
* <pre>
|
||||||
|
* A#X()[@a] -> B#X()[@b] -> C#X()[@c]
|
||||||
|
* </pre>
|
||||||
|
* 则该方法最终将返回 {@code [@a, @b, @c]}
|
||||||
|
*
|
||||||
|
* @param method 方法
|
||||||
|
* @return 注解对象集合
|
||||||
|
* @see MethodAnnotationScanner
|
||||||
|
*/
|
||||||
|
public static List<Annotation> scanMethod(Method method) {
|
||||||
|
return AnnotationScanner.TYPE_HIERARCHY.getAnnotationsIfSupport(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置新的注解的属性(字段)值
|
||||||
|
*
|
||||||
|
* @param annotation 注解对象
|
||||||
|
* @param annotationField 注解属性(字段)名称
|
||||||
|
* @param value 要更新的属性值
|
||||||
|
* @since 5.5.2
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
public static void setValue(Annotation annotation, String annotationField, Object value) {
|
||||||
|
final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues");
|
||||||
|
memberValues.put(annotationField, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该注解对象是否为通过代理类生成的合成注解
|
||||||
|
*
|
||||||
|
* @param annotation 注解对象
|
||||||
|
* @return 是否
|
||||||
|
* @see SynthesizedAnnotationProxy#isProxyAnnotation(Class)
|
||||||
|
*/
|
||||||
|
public static boolean isSynthesizedAnnotation(Annotation annotation) {
|
||||||
|
return SynthesizedAnnotationProxy.isProxyAnnotation(annotation.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取别名支持后的注解
|
||||||
|
*
|
||||||
|
* @param annotationEle 被注解的类
|
||||||
|
* @param annotationType 注解类型Class
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 别名支持后的注解
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public static <T extends Annotation> T getAnnotationAlias(AnnotatedElement annotationEle, Class<T> annotationType) {
|
||||||
|
final T annotation = getAnnotation(annotationEle, annotationType);
|
||||||
|
return aggregatingFromAnnotation(annotation).synthesize(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定注解实例与其元注解转为合成注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @param annotations 注解对象
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 合成注解
|
||||||
|
* @see SynthesizedAggregateAnnotation
|
||||||
|
*/
|
||||||
|
public static <T extends Annotation> T getSynthesizedAnnotation(Class<T> annotationType, Annotation... annotations) {
|
||||||
|
// TODO 缓存合成注解信息,避免重复解析
|
||||||
|
return Opt.ofNullable(annotations)
|
||||||
|
.filter(ArrayUtil::isNotEmpty)
|
||||||
|
.map(AnnotationUtil::aggregatingFromAnnotationWithMeta)
|
||||||
|
.map(a -> a.synthesize(annotationType))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取元素上距离指定元素最接近的合成注解
|
||||||
|
* <ul>
|
||||||
|
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
|
||||||
|
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>注解合成规则如下:
|
||||||
|
* 若{@code AnnotatedEle}按顺序从上到下声明了A,B,C三个注解,且三注解存在元注解如下:
|
||||||
|
* <pre>
|
||||||
|
* A -> M3
|
||||||
|
* B -> M1 -> M2 -> M3
|
||||||
|
* C -> M2 -> M3
|
||||||
|
* </pre>
|
||||||
|
* 此时入参{@code annotationType}类型为{@code M2},则最终将优先返回基于根注解B合成的合成注解
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 合成注解
|
||||||
|
* @see SynthesizedAggregateAnnotation
|
||||||
|
*/
|
||||||
|
public static <T extends Annotation> T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class<T> annotationType) {
|
||||||
|
T target = annotatedEle.getAnnotation(annotationType);
|
||||||
|
if (ObjectUtil.isNotNull(target)) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
return AnnotationScanner.DIRECTLY
|
||||||
|
.getAnnotationsIfSupport(annotatedEle).stream()
|
||||||
|
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取元素上所有指定注解
|
||||||
|
* <ul>
|
||||||
|
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
|
||||||
|
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>注解合成规则如下:
|
||||||
|
* 若{@code AnnotatedEle}按顺序从上到下声明了A,B,C三个注解,且三注解存在元注解如下:
|
||||||
|
* <pre>
|
||||||
|
* A -> M1 -> M2
|
||||||
|
* B -> M3 -> M1 -> M2
|
||||||
|
* C -> M2
|
||||||
|
* </pre>
|
||||||
|
* 此时入参{@code annotationType}类型为{@code M1},则最终将返回基于根注解A与根注解B合成的合成注解。
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 合成注解
|
||||||
|
* @see SynthesizedAggregateAnnotation
|
||||||
|
*/
|
||||||
|
public static <T extends Annotation> List<T> getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class<T> annotationType) {
|
||||||
|
return AnnotationScanner.DIRECTLY
|
||||||
|
.getAnnotationsIfSupport(annotatedEle).stream()
|
||||||
|
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对指定注解对象进行聚合
|
||||||
|
*
|
||||||
|
* @param annotations 注解对象
|
||||||
|
* @return 聚合注解
|
||||||
|
*/
|
||||||
|
public static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation... annotations) {
|
||||||
|
return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.NOTHING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对指定注解对象及其元注解进行聚合
|
||||||
|
*
|
||||||
|
* @param annotations 注解对象
|
||||||
|
* @return 聚合注解
|
||||||
|
*/
|
||||||
|
public static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation... annotations) {
|
||||||
|
return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.DIRECTLY_AND_META_ANNOTATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法是否为注解属性方法。 <br>
|
||||||
|
* 方法无参数,且有返回值的方法认为是注解属性的方法。
|
||||||
|
*
|
||||||
|
* @param method 方法
|
||||||
|
*/
|
||||||
|
static boolean isAttributeMethod(Method method) {
|
||||||
|
return method.getParameterCount() == 0 && method.getReturnType() != void.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AnnotationAttribute}的基本实现
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class CacheableAnnotationAttribute implements AnnotationAttribute {
|
||||||
|
|
||||||
|
private boolean valueInvoked;
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
private boolean defaultValueInvoked;
|
||||||
|
private Object defaultValue;
|
||||||
|
|
||||||
|
private final Annotation annotation;
|
||||||
|
private final Method attribute;
|
||||||
|
|
||||||
|
public CacheableAnnotationAttribute(Annotation annotation, Method attribute) {
|
||||||
|
Assert.notNull(annotation, "annotation must not null");
|
||||||
|
Assert.notNull(attribute, "attribute must not null");
|
||||||
|
this.annotation = annotation;
|
||||||
|
this.attribute = attribute;
|
||||||
|
this.valueInvoked = false;
|
||||||
|
this.defaultValueInvoked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Annotation getAnnotation() {
|
||||||
|
return this.annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Method getAttribute() {
|
||||||
|
return this.attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
if (!valueInvoked) {
|
||||||
|
valueInvoked = true;
|
||||||
|
value = ReflectUtil.invoke(annotation, attribute);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValueEquivalentToDefaultValue() {
|
||||||
|
if (!defaultValueInvoked) {
|
||||||
|
defaultValue = attribute.getDefaultValue();
|
||||||
|
defaultValueInvoked = true;
|
||||||
|
}
|
||||||
|
return ObjectUtil.equals(getValue(), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.multi.RowKeyTable;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.multi.Table;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现,
|
||||||
|
* 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序,
|
||||||
|
* 然后选择具有所需属性的,排序最靠前的注解用于获取属性值
|
||||||
|
*
|
||||||
|
* <p>通过该处理器获取合成注解属性值时会出现隐式别名,
|
||||||
|
* 即子注解和元注解中同时存在类型和名称皆相同的属性时,元注解中属性总是会被该属性覆盖,
|
||||||
|
* 并且该覆盖关系并不会通过{@link Alias}或{@link Link}被传递到关联的属性中。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor {
|
||||||
|
|
||||||
|
private final Table<String, Class<?>, Object> valueCaches = new RowKeyTable<>();
|
||||||
|
private final Comparator<Hierarchical> annotationComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个带缓存的注解值选择器
|
||||||
|
*
|
||||||
|
* @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值
|
||||||
|
*/
|
||||||
|
public CacheableSynthesizedAnnotationAttributeProcessor(Comparator<Hierarchical> annotationComparator) {
|
||||||
|
Assert.notNull(annotationComparator, "annotationComparator must not null");
|
||||||
|
this.annotationComparator = annotationComparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个带缓存的注解值选择器,
|
||||||
|
* 默认按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序,
|
||||||
|
* 越靠前的越优先被取值。
|
||||||
|
*/
|
||||||
|
public CacheableSynthesizedAnnotationAttributeProcessor() {
|
||||||
|
this(Hierarchical.DEFAULT_HIERARCHICAL_COMPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> T getAttributeValue(String attributeName, Class<T> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations) {
|
||||||
|
Object value = valueCaches.get(attributeName, attributeType);
|
||||||
|
// 此处理论上不可能出现缓存值为nul的情况
|
||||||
|
if (ObjectUtil.isNotNull(value)) {
|
||||||
|
return (T)value;
|
||||||
|
}
|
||||||
|
value = synthesizedAnnotations.stream()
|
||||||
|
.filter(ma -> ma.hasAttribute(attributeName, attributeType))
|
||||||
|
.min(annotationComparator)
|
||||||
|
.map(ma -> ma.getAttributeValue(attributeName))
|
||||||
|
.orElse(null);
|
||||||
|
valueCaches.put(attributeName, attributeType, value);
|
||||||
|
return (T)value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.TableMap;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合注解 对JDK的原生注解机制做一个增强,支持类似Spring的组合注解。<br>
|
||||||
|
* 核心实现使用了递归获取指定元素上的注解以及注解的注解,以实现复合注解的获取。
|
||||||
|
*
|
||||||
|
* @author Succy, Looly
|
||||||
|
* @since 4.0.9
|
||||||
|
**/
|
||||||
|
|
||||||
|
public class CombinationAnnotationElement implements AnnotatedElement, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建CombinationAnnotationElement
|
||||||
|
*
|
||||||
|
* @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
|
||||||
|
* @return CombinationAnnotationElement
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public static CombinationAnnotationElement of(AnnotatedElement element, Predicate<Annotation> predicate) {
|
||||||
|
return new CombinationAnnotationElement(element, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解类型与注解对象对应表
|
||||||
|
*/
|
||||||
|
private Map<Class<? extends Annotation>, Annotation> annotationMap;
|
||||||
|
/**
|
||||||
|
* 直接注解类型与注解对象对应表
|
||||||
|
*/
|
||||||
|
private Map<Class<? extends Annotation>, Annotation> declaredAnnotationMap;
|
||||||
|
/**
|
||||||
|
* 过滤器
|
||||||
|
*/
|
||||||
|
private final Predicate<Annotation> predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
*/
|
||||||
|
public CombinationAnnotationElement(AnnotatedElement element) {
|
||||||
|
this(element, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public CombinationAnnotationElement(AnnotatedElement element, Predicate<Annotation> predicate) {
|
||||||
|
this.predicate = predicate;
|
||||||
|
init(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
|
||||||
|
return annotationMap.containsKey(annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||||
|
Annotation annotation = annotationMap.get(annotationClass);
|
||||||
|
return (annotation == null) ? null : (T) annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Annotation[] getAnnotations() {
|
||||||
|
final Collection<Annotation> annotations = this.annotationMap.values();
|
||||||
|
return annotations.toArray(new Annotation[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Annotation[] getDeclaredAnnotations() {
|
||||||
|
final Collection<Annotation> annotations = this.declaredAnnotationMap.values();
|
||||||
|
return annotations.toArray(new Annotation[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*
|
||||||
|
* @param element 元素
|
||||||
|
*/
|
||||||
|
private void init(AnnotatedElement element) {
|
||||||
|
final Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
|
||||||
|
this.declaredAnnotationMap = new TableMap<>();
|
||||||
|
parseDeclared(declaredAnnotations);
|
||||||
|
|
||||||
|
final Annotation[] annotations = element.getAnnotations();
|
||||||
|
if (Arrays.equals(declaredAnnotations, annotations)) {
|
||||||
|
this.annotationMap = this.declaredAnnotationMap;
|
||||||
|
} else {
|
||||||
|
this.annotationMap = new TableMap<>();
|
||||||
|
parse(annotations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进行递归解析注解,直到全部都是元注解为止
|
||||||
|
*
|
||||||
|
* @param annotations Class, Method, Field等
|
||||||
|
*/
|
||||||
|
private void parseDeclared(Annotation[] annotations) {
|
||||||
|
Class<? extends Annotation> annotationType;
|
||||||
|
// 直接注解
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
annotationType = annotation.annotationType();
|
||||||
|
// issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
|
||||||
|
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
|
||||||
|
&& false == declaredAnnotationMap.containsKey(annotationType)) {
|
||||||
|
if(test(annotation)){
|
||||||
|
declaredAnnotationMap.put(annotationType, annotation);
|
||||||
|
}
|
||||||
|
// 测试不通过的注解,不影响继续递归
|
||||||
|
parseDeclared(annotationType.getDeclaredAnnotations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进行递归解析注解,直到全部都是元注解为止
|
||||||
|
*
|
||||||
|
* @param annotations Class, Method, Field等
|
||||||
|
*/
|
||||||
|
private void parse(Annotation[] annotations) {
|
||||||
|
Class<? extends Annotation> annotationType;
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
annotationType = annotation.annotationType();
|
||||||
|
// issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
|
||||||
|
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
|
||||||
|
&& false == declaredAnnotationMap.containsKey(annotationType)) {
|
||||||
|
if(test(annotation)){
|
||||||
|
annotationMap.put(annotationType, annotation);
|
||||||
|
}
|
||||||
|
// 测试不通过的注解,不影响继续递归
|
||||||
|
parse(annotationType.getAnnotations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查给定的注解是否符合过滤条件
|
||||||
|
*
|
||||||
|
* @param annotation 注解对象
|
||||||
|
* @return 是否符合条件
|
||||||
|
*/
|
||||||
|
private boolean test(Annotation annotation) {
|
||||||
|
return null == this.predicate || this.predicate.test(annotation);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@link Link}的子注解。表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
|
||||||
|
* 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
|
||||||
|
* <b>注意,该注解与{@link Link}、{@link AliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效</b>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see Link
|
||||||
|
* @see RelationType#FORCE_ALIAS_FOR
|
||||||
|
*/
|
||||||
|
@Link(type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||||
|
public @interface ForceAliasFor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||||
|
*
|
||||||
|
* @return 关联注解类型
|
||||||
|
*/
|
||||||
|
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
Class<? extends Annotation> annotation() default Annotation.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #annotation()}指定注解中关联的属性
|
||||||
|
*
|
||||||
|
* @return 关联的属性
|
||||||
|
*/
|
||||||
|
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
String attribute() default "";
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示一个被指定了强制别名的注解属性。
|
||||||
|
* 当调用{@link #getValue()}时,总是返回{@link #linked}的值
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see AliasAnnotationPostProcessor
|
||||||
|
* @see AliasLinkAnnotationPostProcessor
|
||||||
|
* @see RelationType#ALIAS_FOR
|
||||||
|
* @see RelationType#FORCE_ALIAS_FOR
|
||||||
|
*/
|
||||||
|
public class ForceAliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
|
||||||
|
|
||||||
|
protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
|
||||||
|
super(origin, linked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总是返回{@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
|
||||||
|
*
|
||||||
|
* @return {@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
return linked.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总是返回{@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
|
||||||
|
*
|
||||||
|
* @return {@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isValueEquivalentToDefaultValue() {
|
||||||
|
return linked.isValueEquivalentToDefaultValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总是返回{@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
|
||||||
|
*
|
||||||
|
* @return {@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<?> getAttributeType() {
|
||||||
|
return linked.getAttributeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,318 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.AnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SynthesizedAggregateAnnotation}的基本实现,表示基于多个注解对象,
|
||||||
|
* 或多个根注解对象与他们的多层元注解对象的聚合得到的注解。
|
||||||
|
*
|
||||||
|
* <p>假设现有注解A,若指定的{@link #annotationScanner}支持扫描注解A的元注解,
|
||||||
|
* 且A上存在元注解B,B上存在元注解C,则对注解A进行解析,将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link GenericSynthesizedAggregateAnnotation}。
|
||||||
|
* 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素,
|
||||||
|
* 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。
|
||||||
|
*
|
||||||
|
* <p>在扫描指定根注解及其元注解时,若在不同的层级出现了类型相同的注解实例,
|
||||||
|
* 将会根据实例化时指定的{@link SynthesizedAnnotationSelector}选择最优的注解,
|
||||||
|
* 完成对根注解及其元注解的扫描后,合成注解中每种类型的注解对象都将有且仅有一个。<br>
|
||||||
|
* 默认情况下,将使用{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}作为选择器,
|
||||||
|
* 此时若出现扫描时得到了多个同类型的注解对象,有且仅有最接近根注解的注解对象会被作为有效注解。
|
||||||
|
*
|
||||||
|
* <p>当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后,
|
||||||
|
* 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAnnotationPostProcessor}
|
||||||
|
* 进行后置处理。<br>
|
||||||
|
* 默认情况下,将注册以下后置处理器以对{@link Alias}与{@link Link}和其扩展注解提供支持:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link AliasAnnotationPostProcessor};</li>
|
||||||
|
* <li>{@link MirrorLinkAnnotationPostProcessor};</li>
|
||||||
|
* <li>{@link AliasLinkAnnotationPostProcessor};</li>
|
||||||
|
* </ul>
|
||||||
|
* 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。
|
||||||
|
*
|
||||||
|
* <p>{@link GenericSynthesizedAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)},
|
||||||
|
* 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值,
|
||||||
|
* 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。
|
||||||
|
* 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。<br>
|
||||||
|
* 默认情况下,实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor},
|
||||||
|
* 该处理器将令元注解中与子注解类型与名称皆一致的属性被子注解的属性覆盖,并且缓存最终获取到的属性值。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see AnnotationUtil
|
||||||
|
* @see SynthesizedAnnotationProxy
|
||||||
|
* @see SynthesizedAnnotationSelector
|
||||||
|
* @see SynthesizedAnnotationAttributeProcessor
|
||||||
|
* @see SynthesizedAnnotationPostProcessor
|
||||||
|
* @see AnnotationSynthesizer
|
||||||
|
* @see AnnotationScanner
|
||||||
|
*/
|
||||||
|
public class GenericSynthesizedAggregateAnnotation
|
||||||
|
extends AbstractAnnotationSynthesizer<List<Annotation>>
|
||||||
|
implements SynthesizedAggregateAnnotation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根对象
|
||||||
|
*/
|
||||||
|
private final Object root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 距离根对象的垂直距离
|
||||||
|
*/
|
||||||
|
private final int verticalDistance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 距离根对象的水平距离
|
||||||
|
*/
|
||||||
|
private final int horizontalDistance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解属性处理器
|
||||||
|
*/
|
||||||
|
private final SynthesizedAnnotationAttributeProcessor attributeProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于指定根注解,为其与其元注解的层级结构中的全部注解构造一个合成注解。
|
||||||
|
* 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象,
|
||||||
|
* 当获取值时,同样遵循该规则。
|
||||||
|
*
|
||||||
|
* @param source 源注解
|
||||||
|
*/
|
||||||
|
public GenericSynthesizedAggregateAnnotation(Annotation... source) {
|
||||||
|
this(Arrays.asList(source), new MetaAnnotationScanner());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。
|
||||||
|
* 若扫描器支持对注解的层级结构进行扫描,则若层级结构中出现了相同的注解对象时,
|
||||||
|
* 将优先选择以距离根注解最近,且优先被扫描的注解对象,并且当获取注解属性值时同样遵循该规则。
|
||||||
|
*
|
||||||
|
* @param source 源注解
|
||||||
|
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
|
||||||
|
*/
|
||||||
|
public GenericSynthesizedAggregateAnnotation(List<Annotation> source, AnnotationScanner annotationScanner) {
|
||||||
|
this(
|
||||||
|
source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY,
|
||||||
|
new CacheableSynthesizedAnnotationAttributeProcessor(),
|
||||||
|
Arrays.asList(
|
||||||
|
SynthesizedAnnotationPostProcessor.ALIAS_ANNOTATION_POST_PROCESSOR,
|
||||||
|
SynthesizedAnnotationPostProcessor.MIRROR_LINK_ANNOTATION_POST_PROCESSOR,
|
||||||
|
SynthesizedAnnotationPostProcessor.ALIAS_LINK_ANNOTATION_POST_PROCESSOR
|
||||||
|
),
|
||||||
|
annotationScanner
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
|
||||||
|
*
|
||||||
|
* @param source 当前查找的注解对象
|
||||||
|
* @param annotationSelector 合成注解选择器
|
||||||
|
* @param attributeProcessor 注解属性处理器
|
||||||
|
* @param annotationPostProcessors 注解后置处理器
|
||||||
|
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
|
||||||
|
*/
|
||||||
|
public GenericSynthesizedAggregateAnnotation(
|
||||||
|
List<Annotation> source,
|
||||||
|
SynthesizedAnnotationSelector annotationSelector,
|
||||||
|
SynthesizedAnnotationAttributeProcessor attributeProcessor,
|
||||||
|
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
|
||||||
|
AnnotationScanner annotationScanner) {
|
||||||
|
this(
|
||||||
|
null, 0, 0,
|
||||||
|
source, annotationSelector, attributeProcessor, annotationPostProcessors, annotationScanner
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
|
||||||
|
*
|
||||||
|
* @param root 根对象
|
||||||
|
* @param verticalDistance 距离根对象的水平距离
|
||||||
|
* @param horizontalDistance 距离根对象的垂直距离
|
||||||
|
* @param source 当前查找的注解对象
|
||||||
|
* @param annotationSelector 合成注解选择器
|
||||||
|
* @param attributeProcessor 注解属性处理器
|
||||||
|
* @param annotationPostProcessors 注解后置处理器
|
||||||
|
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
|
||||||
|
*/
|
||||||
|
GenericSynthesizedAggregateAnnotation(
|
||||||
|
Object root, int verticalDistance, int horizontalDistance,
|
||||||
|
List<Annotation> source,
|
||||||
|
SynthesizedAnnotationSelector annotationSelector,
|
||||||
|
SynthesizedAnnotationAttributeProcessor attributeProcessor,
|
||||||
|
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
|
||||||
|
AnnotationScanner annotationScanner) {
|
||||||
|
super(source, annotationSelector, annotationPostProcessors, annotationScanner);
|
||||||
|
Assert.notNull(attributeProcessor, "attributeProcessor must not null");
|
||||||
|
|
||||||
|
this.root = ObjectUtil.defaultIfNull(root, this);
|
||||||
|
this.verticalDistance = verticalDistance;
|
||||||
|
this.horizontalDistance = horizontalDistance;
|
||||||
|
this.attributeProcessor = attributeProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取根对象
|
||||||
|
*
|
||||||
|
* @return 根对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取与根对象的垂直距离
|
||||||
|
*
|
||||||
|
* @return 与根对象的垂直距离
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getVerticalDistance() {
|
||||||
|
return verticalDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取与根对象的水平距离
|
||||||
|
*
|
||||||
|
* @return 获取与根对象的水平距离
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getHorizontalDistance() {
|
||||||
|
return horizontalDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按广度优先扫描{@link #source}上的元注解
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Map<Class<? extends Annotation>, SynthesizedAnnotation> loadAnnotations() {
|
||||||
|
Map<Class<? extends Annotation>, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// 根注解默认水平坐标为0,根注解的元注解坐标从1开始
|
||||||
|
for (int i = 0; i < source.size(); i++) {
|
||||||
|
final Annotation sourceAnnotation = source.get(i);
|
||||||
|
Assert.isFalse(AnnotationUtil.isSynthesizedAnnotation(sourceAnnotation), "source [{}] has been synthesized");
|
||||||
|
annotationMap.put(sourceAnnotation.annotationType(), new MetaAnnotation(sourceAnnotation, sourceAnnotation, 0, i));
|
||||||
|
Assert.isTrue(
|
||||||
|
annotationScanner.support(sourceAnnotation.annotationType()),
|
||||||
|
"annotation scanner [{}] cannot support scan [{}]",
|
||||||
|
annotationScanner, sourceAnnotation.annotationType()
|
||||||
|
);
|
||||||
|
annotationScanner.scan(
|
||||||
|
(index, annotation) -> {
|
||||||
|
SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType());
|
||||||
|
SynthesizedAnnotation newAnnotation = new MetaAnnotation(sourceAnnotation, annotation, index + 1, annotationMap.size());
|
||||||
|
if (ObjectUtil.isNull(oldAnnotation)) {
|
||||||
|
annotationMap.put(annotation.annotationType(), newAnnotation);
|
||||||
|
} else {
|
||||||
|
annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sourceAnnotation.annotationType(), null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return annotationMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解属性处理器
|
||||||
|
*
|
||||||
|
* @return 合成注解属性处理器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
|
||||||
|
return this.attributeProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
|
||||||
|
* <p>当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @param attributeType 属性类型
|
||||||
|
* @return 属性
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
|
||||||
|
return attributeProcessor.getAttributeValue(attributeName, attributeType, synthesizedAnnotationMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解中包含的指定注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||||
|
return Opt.ofNullable(annotationType)
|
||||||
|
.map(synthesizedAnnotationMap::get)
|
||||||
|
.map(SynthesizedAnnotation::getAnnotation)
|
||||||
|
.map(annotationType::cast)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前合成注解中是否存在指定元注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
|
||||||
|
return synthesizedAnnotationMap.containsKey(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解中包含的全部注解
|
||||||
|
*
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Annotation[] getAnnotations() {
|
||||||
|
return synthesizedAnnotationMap.values().stream()
|
||||||
|
.map(SynthesizedAnnotation::getAnnotation)
|
||||||
|
.toArray(Annotation[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 合成注解对象
|
||||||
|
* @see SynthesizedAnnotationProxy#create(Class, AnnotationAttributeValueProvider, SynthesizedAnnotation)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T extends Annotation> T synthesize(Class<T> annotationType, SynthesizedAnnotation annotation) {
|
||||||
|
return SynthesizedAnnotationProxy.create(annotationType, this, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解包装类,表示{@link #source}以及{@link #source}所属层级结构中的全部关联注解对象
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public static class MetaAnnotation extends GenericSynthesizedAnnotation<Annotation, Annotation> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个合成注解
|
||||||
|
*
|
||||||
|
* @param root 根对象
|
||||||
|
* @param annotation 被合成的注解对象
|
||||||
|
* @param verticalDistance 距离根对象的水平距离
|
||||||
|
* @param horizontalDistance 距离根对象的垂直距离
|
||||||
|
*/
|
||||||
|
protected MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
|
||||||
|
super(root, annotation, verticalDistance, horizontalDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SynthesizedAnnotation}的基本实现
|
||||||
|
*
|
||||||
|
* @param <R> 根对象类型
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class GenericSynthesizedAnnotation<R, T extends Annotation> implements SynthesizedAnnotation {
|
||||||
|
|
||||||
|
private final R root;
|
||||||
|
private final T annotation;
|
||||||
|
private final Map<String, AnnotationAttribute> attributeMethodCaches;
|
||||||
|
private final int verticalDistance;
|
||||||
|
private final int horizontalDistance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个合成注解
|
||||||
|
*
|
||||||
|
* @param root 根对象
|
||||||
|
* @param annotation 被合成的注解对象
|
||||||
|
* @param verticalDistance 距离根对象的水平距离
|
||||||
|
* @param horizontalDistance 距离根对象的垂直距离
|
||||||
|
*/
|
||||||
|
protected GenericSynthesizedAnnotation(
|
||||||
|
R root, T annotation, int verticalDistance, int horizontalDistance) {
|
||||||
|
this.root = root;
|
||||||
|
this.annotation = annotation;
|
||||||
|
this.verticalDistance = verticalDistance;
|
||||||
|
this.horizontalDistance = horizontalDistance;
|
||||||
|
this.attributeMethodCaches = new HashMap<>();
|
||||||
|
this.attributeMethodCaches.putAll(loadAttributeMethods());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载注解属性
|
||||||
|
*
|
||||||
|
* @return 注解属性
|
||||||
|
*/
|
||||||
|
protected Map<String, AnnotationAttribute> loadAttributeMethods() {
|
||||||
|
return Stream.of(ClassUtil.getDeclaredMethods(annotation.annotationType()))
|
||||||
|
.filter(AnnotationUtil::isAttributeMethod)
|
||||||
|
.collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 元注解是否存在该属性
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @return 是否存在该属性
|
||||||
|
*/
|
||||||
|
public boolean hasAttribute(String attributeName) {
|
||||||
|
return attributeMethodCaches.containsKey(attributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 元注解是否存在该属性,且该属性的值类型是指定类型或其子类
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @param returnType 返回值类型
|
||||||
|
* @return 是否存在该属性
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasAttribute(String attributeName, Class<?> returnType) {
|
||||||
|
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
|
||||||
|
.filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType()))
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该注解的全部属性
|
||||||
|
*
|
||||||
|
* @return 注解属性
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<String, AnnotationAttribute> getAttributes() {
|
||||||
|
return this.attributeMethodCaches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @param attribute 注解属性
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
|
||||||
|
attributeMethodCaches.put(attributeName, attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @param operator 替换操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
|
||||||
|
AnnotationAttribute old = attributeMethodCaches.get(attributeName);
|
||||||
|
if (ObjectUtil.isNotNull(old)) {
|
||||||
|
attributeMethodCaches.put(attributeName, operator.apply(old));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @return 属性值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getAttributeValue(String attributeName) {
|
||||||
|
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
|
||||||
|
.map(AnnotationAttribute::getValue)
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该合成注解对应的根节点
|
||||||
|
*
|
||||||
|
* @return 合成注解对应的根节点
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被合成的注解对象
|
||||||
|
*
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该合成注解与根对象的垂直距离。
|
||||||
|
* 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
|
||||||
|
*
|
||||||
|
* @return 合成注解与根对象的垂直距离
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getVerticalDistance() {
|
||||||
|
return verticalDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该合成注解与根对象的水平距离。
|
||||||
|
* 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
|
||||||
|
*
|
||||||
|
* @return 合成注解与根对象的水平距离
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getHorizontalDistance() {
|
||||||
|
return horizontalDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被合成的注解类型
|
||||||
|
*
|
||||||
|
* @return 被合成的注解类型
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<? extends Annotation> annotationType() {
|
||||||
|
return annotation.annotationType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @param attributeType 属性类型
|
||||||
|
* @return 注解属性值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
|
||||||
|
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
|
||||||
|
.filter(method -> ClassUtil.isAssignable(attributeType, method.getAttributeType()))
|
||||||
|
.map(AnnotationAttribute::getValue)
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>描述以一个参照物为对象,存在于该参照物的层级结构中的对象。
|
||||||
|
*
|
||||||
|
* <p>该对象可通过{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}
|
||||||
|
* 描述其在以参照物为基点的坐标坐标系中的位置。<br>
|
||||||
|
* 在需要对该接口的实现类进行按优先级排序时,距离{@link #getRoot()}对象越近,则该实现类的优先级越高。
|
||||||
|
* 默认提供了{@link #DEFAULT_HIERARCHICAL_COMPARATOR}用于实现该比较规则。<br>
|
||||||
|
* 一般情况下,{@link #getRoot()}返回值相同的对象之间的比较才有意义。
|
||||||
|
*
|
||||||
|
* <p>此外,还提供了{@link Selector}接口用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象,
|
||||||
|
* 默认提供了四个实现类:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Selector#NEAREST_AND_OLDEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回旧对象;</li>
|
||||||
|
* <li>{@link Selector#NEAREST_AND_NEWEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回新对象;</li>
|
||||||
|
* <li>{@link Selector#FARTHEST_AND_OLDEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回旧对象;</li>
|
||||||
|
* <li>{@link Selector#FARTHEST_AND_NEWEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回新对象;</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public interface Hierarchical extends Comparable<Hierarchical> {
|
||||||
|
|
||||||
|
// ====================== compare ======================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认{@link #getHorizontalDistance()}与{@link #getVerticalDistance()}排序的比较器
|
||||||
|
*/
|
||||||
|
Comparator<Hierarchical> DEFAULT_HIERARCHICAL_COMPARATOR = Comparator
|
||||||
|
.comparing(Hierarchical::getVerticalDistance)
|
||||||
|
.thenComparing(Hierarchical::getHorizontalDistance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}排序
|
||||||
|
*
|
||||||
|
* @param o {@link SynthesizedAnnotation}对象
|
||||||
|
* @return 比较值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default int compareTo(Hierarchical o) {
|
||||||
|
return DEFAULT_HIERARCHICAL_COMPARATOR.compare(this, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== hierarchical ======================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参照物,即坐标为{@code (0, 0)}的对象。
|
||||||
|
* 当对象本身即为参照物时,该方法应当返回其本身
|
||||||
|
*
|
||||||
|
* @return 参照物
|
||||||
|
*/
|
||||||
|
Object getRoot();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该对象与参照物的垂直距离。
|
||||||
|
* 默认情况下,该距离即为当前对象与参照物之间相隔的层级数。
|
||||||
|
*
|
||||||
|
* @return 合成注解与根对象的垂直距离
|
||||||
|
*/
|
||||||
|
int getVerticalDistance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该对象与参照物的水平距离。
|
||||||
|
* 默认情况下,该距离即为当前对象在与参照物{@link #getVerticalDistance()}相同的情况下条,
|
||||||
|
* 该对象被扫描到的顺序。
|
||||||
|
*
|
||||||
|
* @return 合成注解与根对象的水平距离
|
||||||
|
*/
|
||||||
|
int getHorizontalDistance();
|
||||||
|
|
||||||
|
// ====================== selector ======================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Hierarchical}选择器,用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
interface Selector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的对象,当距离一样时优先返回旧对象
|
||||||
|
*/
|
||||||
|
Selector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的对象,当距离一样时优先返回新对象
|
||||||
|
*/
|
||||||
|
Selector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的对象,当距离一样时优先返回旧对象
|
||||||
|
*/
|
||||||
|
Selector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的对象,当距离一样时优先返回新对象
|
||||||
|
*/
|
||||||
|
Selector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比较两个被合成的对象,选择其中的一个并返回
|
||||||
|
*
|
||||||
|
* @param <T> 复合注解类型
|
||||||
|
* @param prev 上一对象,该参数不允许为空
|
||||||
|
* @param next 下一对象,该参数不允许为空
|
||||||
|
* @return 对象
|
||||||
|
*/
|
||||||
|
<T extends Hierarchical> T choose(T prev, T next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
|
||||||
|
*/
|
||||||
|
class NearestAndOldestPrioritySelector implements Selector {
|
||||||
|
@Override
|
||||||
|
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
|
||||||
|
*/
|
||||||
|
class NearestAndNewestPrioritySelector implements Selector {
|
||||||
|
@Override
|
||||||
|
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
|
||||||
|
*/
|
||||||
|
class FarthestAndOldestPrioritySelector implements Selector {
|
||||||
|
@Override
|
||||||
|
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
|
||||||
|
*/
|
||||||
|
class FarthestAndNewestPrioritySelector implements Selector {
|
||||||
|
@Override
|
||||||
|
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。
|
||||||
|
* 在通过{@link SynthesizedAggregateAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。<br>
|
||||||
|
*
|
||||||
|
* <p>该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor},
|
||||||
|
* 使用三个子注解等同于{@link Link}。但是需要注意的是,
|
||||||
|
* 当注解中的属性同时存在多个{@link Link}或基于{@link Link}的子注解时,
|
||||||
|
* 仅有声明在被注解的属性最上方的注解会生效,其余注解都将被忽略。
|
||||||
|
*
|
||||||
|
* <b>注意:该注解的优先级低于{@link Alias}</b>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see SynthesizedAggregateAnnotation
|
||||||
|
* @see RelationType
|
||||||
|
* @see AliasFor
|
||||||
|
* @see MirrorFor
|
||||||
|
* @see ForceAliasFor
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||||
|
public @interface Link {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||||
|
*
|
||||||
|
* @return 关联的注解类型
|
||||||
|
*/
|
||||||
|
Class<? extends Annotation> annotation() default Annotation.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #annotation()}指定注解中关联的属性
|
||||||
|
*
|
||||||
|
* @return 属性名
|
||||||
|
*/
|
||||||
|
String attribute() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #attribute()}指定属性与当前注解的属性建的关联关系类型
|
||||||
|
*
|
||||||
|
* @return 关系类型
|
||||||
|
*/
|
||||||
|
RelationType type() default RelationType.MIRROR_FOR;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@link Link}的子注解。表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。<br>
|
||||||
|
* 它们遵循下述规则:
|
||||||
|
* <ul>
|
||||||
|
* <li>互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;</li>
|
||||||
|
* <li>互为镜像的两个属性,类型必须一致;</li>
|
||||||
|
* <li>互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;</li>
|
||||||
|
* <li>互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;</li>
|
||||||
|
* </ul>
|
||||||
|
* <b>注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效</b>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see Link
|
||||||
|
* @see RelationType#MIRROR_FOR
|
||||||
|
*/
|
||||||
|
@Link(type = RelationType.MIRROR_FOR)
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||||
|
public @interface MirrorFor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||||
|
*
|
||||||
|
* @return 关联的注解类型
|
||||||
|
*/
|
||||||
|
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
Class<? extends Annotation> annotation() default Annotation.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #annotation()}指定注解中关联的属性
|
||||||
|
*
|
||||||
|
* @return 属性名
|
||||||
|
*/
|
||||||
|
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
|
||||||
|
String attribute() default "";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。<br>
|
||||||
|
* 当该处理器执行完毕后,原始合成注解中被{@link Link}注解的属性与{@link Link}注解指向的目标注解的属性,
|
||||||
|
* 都将会被被包装并替换为{@link MirroredAnnotationAttribute}。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see RelationType#MIRROR_FOR
|
||||||
|
* @see MirroredAnnotationAttribute
|
||||||
|
*/
|
||||||
|
public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
|
||||||
|
|
||||||
|
private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.MIRROR_FOR };
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int order() {
|
||||||
|
return Integer.MIN_VALUE + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该处理器只处理{@link Link#type()}类型为{@link RelationType#MIRROR_FOR}的注解属性
|
||||||
|
*
|
||||||
|
* @return 仅有{@link RelationType#MIRROR_FOR}数组
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected RelationType[] processTypes() {
|
||||||
|
return PROCESSED_RELATION_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将存在镜像关系的合成注解属性分别包装为{@link MirroredAnnotationAttribute}对象,
|
||||||
|
* 并使用包装后{@link MirroredAnnotationAttribute}替换在它们对应合成注解实例中的{@link AnnotationAttribute}
|
||||||
|
*
|
||||||
|
* @param synthesizer 注解合成器
|
||||||
|
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
|
||||||
|
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
|
||||||
|
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
|
||||||
|
* @param linkedAnnotation {@link Link}指向的关联注解对象
|
||||||
|
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void processLinkedAttribute(
|
||||||
|
AnnotationSynthesizer synthesizer, Link annotation,
|
||||||
|
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
|
||||||
|
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
|
||||||
|
|
||||||
|
// 镜像属性必然成对出现,因此此处必定存在三种情况:
|
||||||
|
// 1.两属性都不为镜像属性,此时继续进行后续处理;
|
||||||
|
// 2.两属性都为镜像属性,并且指向对方,此时无需后续处理;
|
||||||
|
// 3.两属性仅有任意一属性为镜像属性,此时镜像属性必然未指向当前原始属性,此时应该抛出异常;
|
||||||
|
if (originalAttribute instanceof MirroredAnnotationAttribute
|
||||||
|
|| linkedAttribute instanceof MirroredAnnotationAttribute) {
|
||||||
|
checkMirrored(originalAttribute, linkedAttribute);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验镜像关系
|
||||||
|
checkMirrorRelation(annotation, originalAttribute, linkedAttribute);
|
||||||
|
// 包装这一对镜像属性,并替换原注解中的对应属性
|
||||||
|
final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, linkedAttribute);
|
||||||
|
originalAnnotation.setAttribute(originalAttribute.getAttributeName(), mirroredOriginalAttribute);
|
||||||
|
final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(linkedAttribute, originalAttribute);
|
||||||
|
linkedAnnotation.setAttribute(annotation.attribute(), mirroredTargetAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查映射关系是否正确
|
||||||
|
*/
|
||||||
|
private void checkMirrored(AnnotationAttribute original, AnnotationAttribute mirror) {
|
||||||
|
final boolean originalAttributeMirrored = original instanceof MirroredAnnotationAttribute;
|
||||||
|
final boolean mirrorAttributeMirrored = mirror instanceof MirroredAnnotationAttribute;
|
||||||
|
|
||||||
|
// 校验通过
|
||||||
|
final boolean passed = originalAttributeMirrored && mirrorAttributeMirrored
|
||||||
|
&& ObjectUtil.equals(((MirroredAnnotationAttribute)original).getLinked(), ((MirroredAnnotationAttribute)mirror).getOriginal());
|
||||||
|
if (passed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验失败,拼装异常信息用于抛出异常
|
||||||
|
String errorMsg;
|
||||||
|
// 原始字段已经跟其他字段形成镜像
|
||||||
|
if (originalAttributeMirrored && !mirrorAttributeMirrored) {
|
||||||
|
errorMsg = CharSequenceUtil.format(
|
||||||
|
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
|
||||||
|
original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 镜像字段已经跟其他字段形成镜像
|
||||||
|
else if (!originalAttributeMirrored && mirrorAttributeMirrored) {
|
||||||
|
errorMsg = CharSequenceUtil.format(
|
||||||
|
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
|
||||||
|
mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况
|
||||||
|
else {
|
||||||
|
errorMsg = CharSequenceUtil.format(
|
||||||
|
"attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]",
|
||||||
|
mirror.getAttribute(), original.getAttribute(),
|
||||||
|
mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(),
|
||||||
|
original.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基本校验
|
||||||
|
*/
|
||||||
|
private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) {
|
||||||
|
// 镜像属性必须存在
|
||||||
|
checkLinkedAttributeNotNull(original, mirror, annotation);
|
||||||
|
// 镜像属性返回值必须一致
|
||||||
|
checkAttributeType(original, mirror);
|
||||||
|
// 镜像属性上必须存在对应的注解
|
||||||
|
final Link mirrorAttributeAnnotation = getLinkAnnotation(mirror, RelationType.MIRROR_FOR);
|
||||||
|
Assert.isTrue(
|
||||||
|
ObjectUtil.isNotNull(mirrorAttributeAnnotation) && RelationType.MIRROR_FOR.equals(mirrorAttributeAnnotation.type()),
|
||||||
|
"mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]",
|
||||||
|
mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR
|
||||||
|
);
|
||||||
|
checkLinkedSelf(original, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示存在对应镜像属性的注解属性,当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see MirrorLinkAnnotationPostProcessor
|
||||||
|
* @see RelationType#MIRROR_FOR
|
||||||
|
*/
|
||||||
|
public class MirroredAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
|
||||||
|
|
||||||
|
public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
|
||||||
|
super(origin, linked);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
final boolean originIsDefault = original.isValueEquivalentToDefaultValue();
|
||||||
|
final boolean targetIsDefault = linked.isValueEquivalentToDefaultValue();
|
||||||
|
final Object originValue = original.getValue();
|
||||||
|
final Object targetValue = linked.getValue();
|
||||||
|
|
||||||
|
// 都为默认值,或都为非默认值时,两方法的返回值必须相等
|
||||||
|
if (originIsDefault == targetIsDefault) {
|
||||||
|
Assert.equals(
|
||||||
|
originValue, targetValue,
|
||||||
|
"the values of attributes [{}] and [{}] that mirror each other are different: [{}] <==> [{}]",
|
||||||
|
original.getAttribute(), linked.getAttribute(), originValue, targetValue
|
||||||
|
);
|
||||||
|
return originValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 两者有一者不为默认值时,优先返回非默认值
|
||||||
|
return originIsDefault ? targetValue : originValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
|
||||||
|
*
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isValueEquivalentToDefaultValue() {
|
||||||
|
return original.isValueEquivalentToDefaultValue() && linked.isValueEquivalentToDefaultValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性忽略注解,使用此注解的字段等会被忽略,主要用于Bean拷贝、Bean转Map等<br>
|
||||||
|
* 此注解应用于字段时,忽略读取和设置属性值,应用于setXXX方法忽略设置值,应用于getXXX忽略读取值
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||||
|
public @interface PropIgnore {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>注解属性的关系类型 <br>
|
||||||
|
* 若将被{@link Link}注解的属性称为“原始属性”,而在{@link Link}注解中指向的注解属性称为“关联属性”,
|
||||||
|
* 则该枚举用于描述“原始属性”与“关联属性”在{@link SynthesizedAggregateAnnotation}处理过程中的作用关系。<br>
|
||||||
|
* 根据在{@link Link#type()}中指定的关系类型的不同,通过{@link SynthesizedAggregateAnnotation}合成的注解的属性值也将有所变化。
|
||||||
|
*
|
||||||
|
* <p>当一个注解中的所有属性同时具备多种关系时,将依次按下述顺序处理:
|
||||||
|
* <ol>
|
||||||
|
* <li>属性上的{@link Alias}注解;</li>
|
||||||
|
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #MIRROR_FOR};</li>
|
||||||
|
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #FORCE_ALIAS_FOR};</li>
|
||||||
|
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #ALIAS_FOR};</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see SynthesizedAggregateAnnotation
|
||||||
|
* @see Link
|
||||||
|
*/
|
||||||
|
public enum RelationType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。<br>
|
||||||
|
* 它们遵循下述规则:
|
||||||
|
* <ul>
|
||||||
|
* <li>互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;</li>
|
||||||
|
* <li>互为镜像的两个属性,类型必须一致;</li>
|
||||||
|
* <li>互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;</li>
|
||||||
|
* <li>互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
MIRROR_FOR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示“原始属性”将作为“关联属性”的别名。
|
||||||
|
* <ul>
|
||||||
|
* <li>当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;</li>
|
||||||
|
* <li>当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
ALIAS_FOR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
|
||||||
|
* 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
|
||||||
|
*/
|
||||||
|
FORCE_ALIAS_FOR;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示基于特定规则聚合,将一组注解聚合而来的注解对象,
|
||||||
|
* 该注解对象允许根据一定规则“合成”一些跟原始注解属性不一样合成注解。
|
||||||
|
*
|
||||||
|
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
|
||||||
|
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
|
||||||
|
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation},
|
||||||
|
* 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。<br>
|
||||||
|
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
|
||||||
|
* 自定义选择器以拦截原始注解被扫描的过程。
|
||||||
|
*
|
||||||
|
* <p>当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
|
||||||
|
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor},
|
||||||
|
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
|
||||||
|
* 该钩子一般用于根据{@link Link}注解对属性进行调整。<br>
|
||||||
|
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
|
||||||
|
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
|
||||||
|
*
|
||||||
|
* <p>合成注解允许通过{@link #synthesize(Class)}合成一个指定的注解对象,
|
||||||
|
* 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成,
|
||||||
|
* 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
|
||||||
|
* 处理后的、用于合成当前实例的全部关联注解的相关属性。<br>
|
||||||
|
* {@link SynthesizedAnnotationAttributeProcessor}是合成注解生命周期中的第三个钩子,
|
||||||
|
* 自定义属性处理器以拦截合成注解的取值过程。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see AnnotationSynthesizer
|
||||||
|
* @see SynthesizedAnnotation
|
||||||
|
* @see SynthesizedAnnotationSelector
|
||||||
|
* @see SynthesizedAnnotationAttributeProcessor
|
||||||
|
* @see SynthesizedAnnotationPostProcessor
|
||||||
|
* @see GenericSynthesizedAggregateAnnotation
|
||||||
|
*/
|
||||||
|
public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer, AnnotationAttributeValueProvider {
|
||||||
|
|
||||||
|
// ================== hierarchical ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 距离{@link #getRoot()}返回值的垂直距离,
|
||||||
|
* 默认聚合注解即为根对象,因此返回0
|
||||||
|
*
|
||||||
|
* @return 距离{@link #getRoot()}返回值的水平距离,
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default int getVerticalDistance() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 距离{@link #getRoot()}返回值的水平距离,
|
||||||
|
* 默认聚合注解即为根对象,因此返回0
|
||||||
|
*
|
||||||
|
* @return 距离{@link #getRoot()}返回值的水平距离,
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default int getHorizontalDistance() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================== synthesize ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取在聚合中存在的指定注解对象
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
<T extends Annotation> T getAnnotation(Class<T> annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合成注解属性处理器
|
||||||
|
*
|
||||||
|
* @return 合成注解属性处理器
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的注解类型
|
||||||
|
*
|
||||||
|
* @return 注解类型
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default Class<? extends Annotation> annotationType() {
|
||||||
|
return this.getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从聚合中获取指定类型的属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @param attributeType 属性类型
|
||||||
|
* @return 属性值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
Object getAttributeValue(String attributeName, Class<?> attributeType);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>用于在{@link SynthesizedAggregateAnnotation}中表示一个处于合成状态的注解对象。<br>
|
||||||
|
* 当对多个合成注解排序时,默认使用{@link #DEFAULT_HIERARCHICAL_COMPARATOR}进行排序,
|
||||||
|
* 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序,
|
||||||
|
* 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see SynthesizedAggregateAnnotation
|
||||||
|
*/
|
||||||
|
public interface SynthesizedAnnotation extends Annotation, Hierarchical, AnnotationAttributeValueProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被合成的注解对象
|
||||||
|
*
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
Annotation getAnnotation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该合成注解与根对象的垂直距离。
|
||||||
|
* 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
|
||||||
|
*
|
||||||
|
* @return 合成注解与根对象的垂直距离
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
int getVerticalDistance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该合成注解与根对象的水平距离。
|
||||||
|
* 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
|
||||||
|
*
|
||||||
|
* @return 合成注解与根对象的水平距离
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
int getHorizontalDistance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解是否存在该名称相同,且类型一致的属性
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @param returnType 返回值类型
|
||||||
|
* @return 是否存在该属性
|
||||||
|
*/
|
||||||
|
boolean hasAttribute(String attributeName, Class<?> returnType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该注解的全部属性
|
||||||
|
*
|
||||||
|
* @return 注解属性
|
||||||
|
*/
|
||||||
|
Map<String, AnnotationAttribute> getAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置该注解的全部属性
|
||||||
|
*
|
||||||
|
* @param attributes 注解属性
|
||||||
|
*/
|
||||||
|
default void setAttributes(Map<String, AnnotationAttribute> attributes) {
|
||||||
|
if (CollUtil.isNotEmpty(attributes)) {
|
||||||
|
attributes.forEach(this::setAttribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @param attribute 注解属性
|
||||||
|
*/
|
||||||
|
void setAttribute(String attributeName, AnnotationAttribute attribute);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @param operator 替换操作
|
||||||
|
*/
|
||||||
|
void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名
|
||||||
|
* @return 属性值
|
||||||
|
*/
|
||||||
|
Object getAttributeValue(String attributeName);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解属性选择器。用于在{@link SynthesizedAggregateAnnotation}中从指定类型的合成注解里获取到对应的属性值
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SynthesizedAnnotationAttributeProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从一批被合成注解中,获取指定名称与类型的属性值
|
||||||
|
*
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @param attributeType 属性类型
|
||||||
|
* @param synthesizedAnnotations 被合成的注解
|
||||||
|
* @param <R> 属性类型
|
||||||
|
* @return 属性值
|
||||||
|
*/
|
||||||
|
<R> R getAttributeValue(String attributeName, Class<R> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.comparator.CompareUtil;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>被合成注解后置处理器,用于在{@link SynthesizedAggregateAnnotation}加载完所有待合成注解后,
|
||||||
|
* 再对加载好的{@link SynthesizedAnnotation}进行后置处理。<br>
|
||||||
|
* 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序,
|
||||||
|
* 该值更小的处理器将被优先执行。
|
||||||
|
*
|
||||||
|
* <p>该接口存在多个实现类,调用者应当保证在任何时候,对一批后置处理器的调用顺序都符合:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link AliasAnnotationPostProcessor};</li>
|
||||||
|
* <li>{@link MirrorLinkAnnotationPostProcessor};</li>
|
||||||
|
* <li>{@link AliasLinkAnnotationPostProcessor};</li>
|
||||||
|
* <li>其他后置处理器;</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see AliasAnnotationPostProcessor
|
||||||
|
* @see MirrorLinkAnnotationPostProcessor
|
||||||
|
* @see AliasLinkAnnotationPostProcessor
|
||||||
|
*/
|
||||||
|
public interface SynthesizedAnnotationPostProcessor extends Comparable<SynthesizedAnnotationPostProcessor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性上带有{@link Alias}的注解对象的后置处理器
|
||||||
|
*/
|
||||||
|
AliasAnnotationPostProcessor ALIAS_ANNOTATION_POST_PROCESSOR = new AliasAnnotationPostProcessor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性上带有{@link Link},且与其他注解的属性存在镜像关系的注解对象的后置处理器
|
||||||
|
*/
|
||||||
|
MirrorLinkAnnotationPostProcessor MIRROR_LINK_ANNOTATION_POST_PROCESSOR = new MirrorLinkAnnotationPostProcessor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性上带有{@link Link},且与其他注解的属性存在别名关系的注解对象的后置处理器
|
||||||
|
*/
|
||||||
|
AliasLinkAnnotationPostProcessor ALIAS_LINK_ANNOTATION_POST_PROCESSOR = new AliasLinkAnnotationPostProcessor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在一组后置处理器中被调用的顺序,越小越靠前
|
||||||
|
*
|
||||||
|
* @return 排序值
|
||||||
|
*/
|
||||||
|
default int order() {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比较两个后置处理器的{@link #order()}返回值
|
||||||
|
*
|
||||||
|
* @param o 比较对象
|
||||||
|
* @return 大小
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default int compareTo(SynthesizedAnnotationPostProcessor o) {
|
||||||
|
return CompareUtil.compare(this, o, Comparator.comparing(SynthesizedAnnotationPostProcessor::order));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给定指定被合成注解与其所属的合成注解聚合器实例,经过处理后返回最终
|
||||||
|
*
|
||||||
|
* @param synthesizedAnnotation 合成的注解
|
||||||
|
* @param synthesizer 注解合成器
|
||||||
|
*/
|
||||||
|
void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合成注解代理类,用于为{@link SynthesizedAnnotation}生成对应的合成注解代理对象
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see SynthesizedAnnotation
|
||||||
|
* @see AnnotationAttributeValueProvider
|
||||||
|
*/
|
||||||
|
public class SynthesizedAnnotationProxy implements InvocationHandler {
|
||||||
|
|
||||||
|
private final AnnotationAttributeValueProvider annotationAttributeValueProvider;
|
||||||
|
private final SynthesizedAnnotation annotation;
|
||||||
|
private final Map<String, BiFunction<Method, Object[], Object>> methods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
|
||||||
|
*
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param annotationAttributeValueProvider 注解属性值获取器
|
||||||
|
* @param annotation 合成注解
|
||||||
|
* @return 代理注解
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Annotation> T create(
|
||||||
|
Class<T> annotationType,
|
||||||
|
AnnotationAttributeValueProvider annotationAttributeValueProvider,
|
||||||
|
SynthesizedAnnotation annotation) {
|
||||||
|
if (ObjectUtil.isNull(annotation)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final SynthesizedAnnotationProxy proxyHandler = new SynthesizedAnnotationProxy(annotationAttributeValueProvider, annotation);
|
||||||
|
if (ObjectUtil.isNull(annotation)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (T) Proxy.newProxyInstance(
|
||||||
|
annotationType.getClassLoader(),
|
||||||
|
new Class[]{annotationType, SyntheticProxyAnnotation.class},
|
||||||
|
proxyHandler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
|
||||||
|
*
|
||||||
|
* @param <T> 注解类型
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @param annotation 合成注解
|
||||||
|
* @return 代理注解
|
||||||
|
*/
|
||||||
|
public static <T extends Annotation> T create(
|
||||||
|
Class<T> annotationType, SynthesizedAnnotation annotation) {
|
||||||
|
return create(annotationType, annotation, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该类是否为通过{@code SynthesizedAnnotationProxy}生成的代理类
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public static boolean isProxyAnnotation(Class<?> annotationType) {
|
||||||
|
return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
SynthesizedAnnotationProxy(AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) {
|
||||||
|
Assert.notNull(annotationAttributeValueProvider, "annotationAttributeValueProvider must not null");
|
||||||
|
Assert.notNull(annotation, "annotation must not null");
|
||||||
|
this.annotationAttributeValueProvider = annotationAttributeValueProvider;
|
||||||
|
this.annotation = annotation;
|
||||||
|
this.methods = new HashMap<>(9);
|
||||||
|
loadMethods();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
return Opt.ofNullable(methods.get(method.getName()))
|
||||||
|
.map(m -> m.apply(method, args))
|
||||||
|
.orElseGet(() -> ReflectUtil.invoke(this, method, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================= 代理方法 =========================
|
||||||
|
|
||||||
|
void loadMethods() {
|
||||||
|
methods.put("toString", (method, args) -> proxyToString());
|
||||||
|
methods.put("hashCode", (method, args) -> proxyHashCode());
|
||||||
|
methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
|
||||||
|
methods.put("getRoot", (method, args) -> annotation.getRoot());
|
||||||
|
methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
|
||||||
|
methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
|
||||||
|
methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String) args[0], (Class<?>) args[1]));
|
||||||
|
methods.put("getAttributes", (method, args) -> annotation.getAttributes());
|
||||||
|
methods.put("setAttribute", (method, args) -> {
|
||||||
|
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
|
||||||
|
});
|
||||||
|
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String) args[0]));
|
||||||
|
methods.put("annotationType", (method, args) -> annotation.annotationType());
|
||||||
|
for (final Method declaredMethod : ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) {
|
||||||
|
methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String proxyToString() {
|
||||||
|
final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
|
||||||
|
.filter(AnnotationUtil::isAttributeMethod)
|
||||||
|
.map(method -> CharSequenceUtil.format(
|
||||||
|
"{}={}", method.getName(), proxyAttributeValue(method))
|
||||||
|
)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int proxyHashCode() {
|
||||||
|
return Objects.hash(annotationAttributeValueProvider, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object proxyGetSynthesizedAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object proxyAttributeValue(Method attributeMethod) {
|
||||||
|
return annotationAttributeValueProvider.getAttributeValue(attributeMethod.getName(), attributeMethod.getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过代理类生成的合成注解
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
interface SyntheticProxyAnnotation extends SynthesizedAnnotation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该代理注解对应的已合成注解
|
||||||
|
*
|
||||||
|
* @return 理注解对应的已合成注解
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotation getSynthesizedAnnotation();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解选择器,指定两个注解,选择其中一个返回。<br>
|
||||||
|
* 该接口用于在{@link SynthesizedAggregateAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SynthesizedAnnotationSelector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotationSelector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotationSelector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotationSelector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
|
||||||
|
*/
|
||||||
|
SynthesizedAnnotationSelector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比较两个被合成的注解,选择其中的一个并返回
|
||||||
|
*
|
||||||
|
* @param <T> 复合注解类型
|
||||||
|
* @param oldAnnotation 已存在的注解,该参数不允许为空
|
||||||
|
* @param newAnnotation 新获取的注解,该参数不允许为空
|
||||||
|
* @return 被合成的注解
|
||||||
|
*/
|
||||||
|
<T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
|
||||||
|
*/
|
||||||
|
class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||||
|
@Override
|
||||||
|
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return Hierarchical.Selector.NEAREST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
|
||||||
|
*/
|
||||||
|
class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||||
|
@Override
|
||||||
|
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return Hierarchical.Selector.NEAREST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
|
||||||
|
*/
|
||||||
|
class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||||
|
@Override
|
||||||
|
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return Hierarchical.Selector.FARTHEST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
|
||||||
|
*/
|
||||||
|
class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||||
|
@Override
|
||||||
|
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||||
|
return Hierarchical.Selector.FARTHEST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>表示一个被包装过的{@link AnnotationAttribute},
|
||||||
|
* 该实例中的一些方法可能会被代理到另一个注解属性对象中,
|
||||||
|
* 从而使得通过原始的注解属性的方法获取到另一注解属性的值。<br>
|
||||||
|
* 除了{@link #getValue()}以外,其他方法的返回值应当尽可能与{@link #getOriginal()}
|
||||||
|
* 返回的{@link AnnotationAttribute}对象的方法返回值一致。
|
||||||
|
*
|
||||||
|
* <p>当包装类被包装了多层后,则规则生效优先级按包装的先后顺序倒序排序,
|
||||||
|
* 比如a、b互为镜像,此时a、b两属性应当都被{@link MirroredAnnotationAttribute}包装,
|
||||||
|
* 若再指定c为a的别名字段,则c、a、b都要在原基础上再次包装一层{@link AliasedAnnotationAttribute}。<br>
|
||||||
|
* 此时a、b同时被包装了两层,则执行时,优先执行{@link AliasedAnnotationAttribute}的逻辑,
|
||||||
|
* 当该规则不生效时,比如c只有默认值,此时上一次的{@link MirroredAnnotationAttribute}的逻辑才会生效。
|
||||||
|
*
|
||||||
|
* <p>被包装的{@link AnnotationAttribute}实际结构为一颗二叉树,
|
||||||
|
* 当包装类再次被包装时,实际上等于又添加了一个新的根节点,
|
||||||
|
* 此时需要同时更新树的全部关联叶子节点。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see AnnotationAttribute
|
||||||
|
* @see ForceAliasedAnnotationAttribute
|
||||||
|
* @see AliasedAnnotationAttribute
|
||||||
|
* @see MirroredAnnotationAttribute
|
||||||
|
*/
|
||||||
|
public interface WrappedAnnotationAttribute extends AnnotationAttribute {
|
||||||
|
|
||||||
|
// =========================== 新增方法 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被包装的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
|
||||||
|
*
|
||||||
|
* @return 被包装的{@link AnnotationAttribute}对象
|
||||||
|
*/
|
||||||
|
AnnotationAttribute getOriginal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最初的被包装的{@link AnnotationAttribute}
|
||||||
|
*
|
||||||
|
* @return 最初的被包装的{@link AnnotationAttribute}
|
||||||
|
*/
|
||||||
|
AnnotationAttribute getNonWrappedOriginal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取包装{@link #getOriginal()}的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
|
||||||
|
*
|
||||||
|
* @return 包装对象
|
||||||
|
*/
|
||||||
|
AnnotationAttribute getLinked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 遍历以当前实例为根节点的树结构,获取所有未被包装的属性
|
||||||
|
*
|
||||||
|
* @return 叶子节点
|
||||||
|
*/
|
||||||
|
Collection<AnnotationAttribute> getAllLinkedNonWrappedAttributes();
|
||||||
|
|
||||||
|
// =========================== 代理实现 ===========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解对象
|
||||||
|
*
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default Annotation getAnnotation() {
|
||||||
|
return getOriginal().getAnnotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解属性对应的方法
|
||||||
|
*
|
||||||
|
* @return 注解属性对应的方法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default Method getAttribute() {
|
||||||
|
return getOriginal().getAttribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该注解属性的值是否等于默认值 <br>
|
||||||
|
* 默认仅当{@link #getOriginal()}与{@link #getLinked()}返回的注解属性
|
||||||
|
* 都为默认值时,才返回{@code true}
|
||||||
|
*
|
||||||
|
* @return 该注解属性的值是否等于默认值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
boolean isValueEquivalentToDefaultValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性类型
|
||||||
|
*
|
||||||
|
* @return 属性类型
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default Class<?> getAttributeType() {
|
||||||
|
return getOriginal().getAttributeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性上的注解
|
||||||
|
*
|
||||||
|
* @param annotationType 注解类型
|
||||||
|
* @return 注解对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||||
|
return getOriginal().getAnnotation(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default boolean isWrapped() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* 注解包,提供增强型注解和注解工具类
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation;
|
|
@ -0,0 +1,288 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为需要从类的层级结构中获取注解的{@link AnnotationScanner}提供基本实现
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnotationScanner<T>> implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父类
|
||||||
|
*/
|
||||||
|
private boolean includeSuperClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父接口
|
||||||
|
*/
|
||||||
|
private boolean includeInterfaces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
|
||||||
|
*/
|
||||||
|
private Predicate<Class<?>> filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排除的类型,以上类型及其树结构将直接不被查找
|
||||||
|
*/
|
||||||
|
private final Set<Class<?>> excludeTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换器
|
||||||
|
*/
|
||||||
|
private final List<UnaryOperator<Class<?>>> converters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有转换器
|
||||||
|
*/
|
||||||
|
private boolean hasConverters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前实例
|
||||||
|
*/
|
||||||
|
private final T typedThis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个类注解扫描器
|
||||||
|
*
|
||||||
|
* @param includeSuperClass 是否允许扫描父类
|
||||||
|
* @param includeInterfaces 是否允许扫描父接口
|
||||||
|
* @param filter 过滤器
|
||||||
|
* @param excludeTypes 不包含的类型
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected AbstractTypeAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||||
|
Assert.notNull(filter, "filter must not null");
|
||||||
|
Assert.notNull(excludeTypes, "excludeTypes must not null");
|
||||||
|
this.includeSuperClass = includeSuperClass;
|
||||||
|
this.includeInterfaces = includeInterfaces;
|
||||||
|
this.filter = filter;
|
||||||
|
this.excludeTypes = excludeTypes;
|
||||||
|
this.converters = new ArrayList<>();
|
||||||
|
this.typedThis = (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父类
|
||||||
|
*
|
||||||
|
* @return 是否允许扫描父类
|
||||||
|
*/
|
||||||
|
public boolean isIncludeSuperClass() {
|
||||||
|
return includeSuperClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父接口
|
||||||
|
*
|
||||||
|
* @return 是否允许扫描父接口
|
||||||
|
*/
|
||||||
|
public boolean isIncludeInterfaces() {
|
||||||
|
return includeInterfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
|
||||||
|
*
|
||||||
|
* @param filter 过滤器
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
public T setFilter(Predicate<Class<?>> filter) {
|
||||||
|
Assert.notNull(filter, "filter must not null");
|
||||||
|
this.filter = filter;
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加不扫描的类型,该类型及其树结构将直接不被查找
|
||||||
|
*
|
||||||
|
* @param excludeTypes 不扫描的类型
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
public T addExcludeTypes(Class<?>... excludeTypes) {
|
||||||
|
CollUtil.addAll(this.excludeTypes, excludeTypes);
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加转换器
|
||||||
|
*
|
||||||
|
* @param converter 转换器
|
||||||
|
* @return 当前实例
|
||||||
|
* @see JdkProxyClassConverter
|
||||||
|
*/
|
||||||
|
public T addConverters(UnaryOperator<Class<?>> converter) {
|
||||||
|
Assert.notNull(converter, "converter must not null");
|
||||||
|
this.converters.add(converter);
|
||||||
|
if (!this.hasConverters) {
|
||||||
|
this.hasConverters = CollUtil.isNotEmpty(this.converters);
|
||||||
|
}
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父类
|
||||||
|
*
|
||||||
|
* @param includeSuperClass 是否
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
protected T setIncludeSuperClass(boolean includeSuperClass) {
|
||||||
|
this.includeSuperClass = includeSuperClass;
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父接口
|
||||||
|
*
|
||||||
|
* @param includeInterfaces 是否
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
protected T setIncludeInterfaces(boolean includeInterfaces) {
|
||||||
|
this.includeInterfaces = includeInterfaces;
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 则根据广度优先递归扫描类的层级结构,并对层级结构中类/接口声明的层级索引和它们声明的注解对象进行处理
|
||||||
|
*
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle 注解元素
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
filter = ObjectUtil.defaultIfNull(filter, a -> annotation -> true);
|
||||||
|
final Class<?> sourceClass = getClassFormAnnotatedElement(annotatedEle);
|
||||||
|
final Deque<List<Class<?>>> classDeque = CollUtil.newLinkedList(CollUtil.newArrayList(sourceClass));
|
||||||
|
final Set<Class<?>> accessedTypes = new LinkedHashSet<>();
|
||||||
|
int index = 0;
|
||||||
|
while (!classDeque.isEmpty()) {
|
||||||
|
final List<Class<?>> currClassQueue = classDeque.removeFirst();
|
||||||
|
final List<Class<?>> nextClassQueue = new ArrayList<>();
|
||||||
|
for (Class<?> targetClass : currClassQueue) {
|
||||||
|
targetClass = convert(targetClass);
|
||||||
|
// 过滤不需要处理的类
|
||||||
|
if (isNotNeedProcess(accessedTypes, targetClass)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
accessedTypes.add(targetClass);
|
||||||
|
// 扫描父类
|
||||||
|
scanSuperClassIfNecessary(nextClassQueue, targetClass);
|
||||||
|
// 扫描接口
|
||||||
|
scanInterfaceIfNecessary(nextClassQueue, targetClass);
|
||||||
|
// 处理层级索引和注解
|
||||||
|
final Annotation[] targetAnnotations = getAnnotationsFromTargetClass(annotatedEle, index, targetClass);
|
||||||
|
for (final Annotation annotation : targetAnnotations) {
|
||||||
|
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||||
|
consumer.accept(index, annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (CollUtil.isNotEmpty(nextClassQueue)) {
|
||||||
|
classDeque.addLast(nextClassQueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从要搜索的注解元素上获得要递归的类型
|
||||||
|
*
|
||||||
|
* @param annotatedElement 注解元素
|
||||||
|
* @return 要递归的类型
|
||||||
|
*/
|
||||||
|
protected abstract Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从类上获取最终所需的目标注解
|
||||||
|
*
|
||||||
|
* @param source 最初的注解元素
|
||||||
|
* @param index 类的层级索引
|
||||||
|
* @param targetClass 类
|
||||||
|
* @return 最终所需的目标注解
|
||||||
|
*/
|
||||||
|
protected abstract Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前类是否不需要处理
|
||||||
|
*
|
||||||
|
* @param accessedTypes 访问类型
|
||||||
|
* @param targetClass 目标类型
|
||||||
|
* @return 是否不需要处理
|
||||||
|
*/
|
||||||
|
protected boolean isNotNeedProcess(Set<Class<?>> accessedTypes, Class<?> targetClass) {
|
||||||
|
return ObjectUtil.isNull(targetClass)
|
||||||
|
|| accessedTypes.contains(targetClass)
|
||||||
|
|| excludeTypes.contains(targetClass)
|
||||||
|
|| filter.negate().test(targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若{@link #includeInterfaces}为{@code true},则将目标类的父接口也添加到nextClasses
|
||||||
|
*
|
||||||
|
* @param nextClasses 下一个类集合
|
||||||
|
* @param targetClass 目标类型
|
||||||
|
*/
|
||||||
|
protected void scanInterfaceIfNecessary(List<Class<?>> nextClasses, Class<?> targetClass) {
|
||||||
|
if (includeInterfaces) {
|
||||||
|
final Class<?>[] interfaces = targetClass.getInterfaces();
|
||||||
|
if (ArrayUtil.isNotEmpty(interfaces)) {
|
||||||
|
CollUtil.addAll(nextClasses, interfaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若{@link #includeSuperClass}为{@code true},则将目标类的父类也添加到nextClasses
|
||||||
|
*
|
||||||
|
* @param nextClassQueue 下一个类队列
|
||||||
|
* @param targetClass 目标类型
|
||||||
|
*/
|
||||||
|
protected void scanSuperClassIfNecessary(List<Class<?>> nextClassQueue, Class<?> targetClass) {
|
||||||
|
if (includeSuperClass) {
|
||||||
|
final Class<?> superClass = targetClass.getSuperclass();
|
||||||
|
if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
|
||||||
|
nextClassQueue.add(superClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若存在转换器,则使用转换器对目标类进行转换
|
||||||
|
*
|
||||||
|
* @param target 目标类
|
||||||
|
* @return 转换后的类
|
||||||
|
*/
|
||||||
|
protected Class<?> convert(Class<?> target) {
|
||||||
|
if (hasConverters) {
|
||||||
|
for (final UnaryOperator<Class<?>> converter : converters) {
|
||||||
|
target = converter.apply(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若类型为jdk代理类,则尝试转换为原始被代理类
|
||||||
|
*/
|
||||||
|
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
|
||||||
|
@Override
|
||||||
|
public Class<?> apply(Class<?> sourceClass) {
|
||||||
|
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>注解扫描器,用于从支持的可注解元素上获取所需注解
|
||||||
|
*
|
||||||
|
* <p>默认提供了以下扫描方式:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #NOTHING}:什么都不做,什么注解都不扫描;</li>
|
||||||
|
* <li>{@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;</li>
|
||||||
|
* <li>
|
||||||
|
* {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,
|
||||||
|
* 以及这些注解的元注解;
|
||||||
|
* </li>
|
||||||
|
* <li>{@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;</li>
|
||||||
|
* <li>{@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||||
|
* <li>{@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;</li>
|
||||||
|
* <li>{@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||||
|
* <li>{@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;</li>
|
||||||
|
* <li>{@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see TypeAnnotationScanner
|
||||||
|
* @see MethodAnnotationScanner
|
||||||
|
* @see FieldAnnotationScanner
|
||||||
|
* @see MetaAnnotationScanner
|
||||||
|
* @see ElementAnnotationScanner
|
||||||
|
* @see GenericAnnotationScanner
|
||||||
|
*/
|
||||||
|
public interface AnnotationScanner {
|
||||||
|
|
||||||
|
// ============================ 预置的扫描器实例 ============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不扫描任何注解
|
||||||
|
*/
|
||||||
|
AnnotationScanner NOTHING = new EmptyAnnotationScanner();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner DIRECTLY = new GenericAnnotationScanner(false, false, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,以及这些注解的元注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner DIRECTLY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身以及父类的层级结构中声明的注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner SUPERCLASS = new GenericAnnotationScanner(false, true, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner SUPERCLASS_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身以及父接口的层级结构中声明的注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner INTERFACE = new GenericAnnotationScanner(false, false, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner INTERFACE_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身以及父类、父接口的层级结构中声明的注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner TYPE_HIERARCHY = new GenericAnnotationScanner(false, true, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
|
||||||
|
*/
|
||||||
|
AnnotationScanner TYPE_HIERARCHY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, true);
|
||||||
|
|
||||||
|
// ============================ 静态方法 ============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param scanners 注解扫描器
|
||||||
|
* @return 注解
|
||||||
|
*/
|
||||||
|
static List<Annotation> scanByAnySupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
|
||||||
|
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Stream.of(scanners)
|
||||||
|
.filter(scanner -> scanner.support(annotatedEle))
|
||||||
|
.findFirst()
|
||||||
|
.map(scanner -> scanner.getAnnotations(annotatedEle))
|
||||||
|
.orElseGet(Collections::emptyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定的扫描器,扫描元素上可能存在的注解
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param scanners 注解扫描器
|
||||||
|
* @return 注解
|
||||||
|
*/
|
||||||
|
static List<Annotation> scanByAllSupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
|
||||||
|
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Stream.of(scanners)
|
||||||
|
.map(scanner -> scanner.getAnnotationsIfSupport(annotatedEle))
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================ 抽象方法 ============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持扫描该注解元素
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
default boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 注解
|
||||||
|
*/
|
||||||
|
default List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
|
||||||
|
final List<Annotation> annotations = new ArrayList<>();
|
||||||
|
scan((index, annotation) -> annotations.add(annotation), annotatedEle, null);
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若{@link #support(AnnotatedElement)}返回{@code true},
|
||||||
|
* 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果,
|
||||||
|
* 否则返回{@link Collections#emptyList()}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 注解
|
||||||
|
*/
|
||||||
|
default List<Annotation> getAnnotationsIfSupport(AnnotatedElement annotatedEle) {
|
||||||
|
return support(annotatedEle) ? getAnnotations(annotatedEle) : Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理。
|
||||||
|
* 调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||||
|
*
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
default void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
filter = ObjectUtil.defaultIfNull(filter, (a)->annotation -> true);
|
||||||
|
for (final Annotation annotation : annotatedEle.getAnnotations()) {
|
||||||
|
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||||
|
consumer.accept(0, annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若{@link #support(AnnotatedElement)}返回{@code true},则调用{@link #scan(BiConsumer, AnnotatedElement, Predicate)}
|
||||||
|
*
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
default void scanIfSupport(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
if (support(annotatedEle)) {
|
||||||
|
scan(consumer, annotatedEle, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描{@link AnnotatedElement}上的注解,不支持处理层级对象
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class ElementAnnotationScanner implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持扫描该注解元素,仅当注解元素不为空时返回{@code true}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return ObjectUtil.isNotNull(annotatedEle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描{@link AnnotatedElement}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||||
|
*
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
filter = ObjectUtil.defaultIfNull(filter,a-> t -> true);
|
||||||
|
Stream.of(annotatedEle.getAnnotations())
|
||||||
|
.filter(filter)
|
||||||
|
.forEach(annotation -> consumer.accept(0, annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认不扫描任何元素的扫描器
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class EmptyAnnotationScanner implements AnnotationScanner {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描{@link Field}上的注解
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class FieldAnnotationScanner implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Field}时返回{@code true}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return annotatedEle instanceof Field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描{@link Field}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||||
|
*
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
filter = ObjectUtil.defaultIfNull(filter, a -> annotation -> true);
|
||||||
|
for (final Annotation annotation : annotatedEle.getAnnotations()) {
|
||||||
|
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||||
|
consumer.accept(0, annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.multi.ListValueMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。
|
||||||
|
*
|
||||||
|
* <p>当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构,
|
||||||
|
* 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描;
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构,
|
||||||
|
* 扫描器将扫描层级结构中类、接口声明的注解;
|
||||||
|
* </li>
|
||||||
|
* <li>当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;</li>
|
||||||
|
* </ul>
|
||||||
|
* 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see TypeAnnotationScanner
|
||||||
|
* @see MethodAnnotationScanner
|
||||||
|
* @see MetaAnnotationScanner
|
||||||
|
* @see ElementAnnotationScanner
|
||||||
|
*/
|
||||||
|
public class GenericAnnotationScanner implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型扫描器
|
||||||
|
*/
|
||||||
|
private final AnnotationScanner typeScanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法扫描器
|
||||||
|
*/
|
||||||
|
private final AnnotationScanner methodScanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 元注解扫描器
|
||||||
|
*/
|
||||||
|
private final AnnotationScanner metaScanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通元素扫描器
|
||||||
|
*/
|
||||||
|
private final AnnotationScanner elementScanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用注解扫描器支持扫描所有类型的{@link AnnotatedElement}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个通用注解扫描器
|
||||||
|
*
|
||||||
|
* @param enableScanMetaAnnotation 是否扫描注解上的元注解
|
||||||
|
* @param enableScanSupperClass 是否扫描父类
|
||||||
|
* @param enableScanSupperInterface 是否扫描父接口
|
||||||
|
*/
|
||||||
|
public GenericAnnotationScanner(
|
||||||
|
boolean enableScanMetaAnnotation,
|
||||||
|
boolean enableScanSupperClass,
|
||||||
|
boolean enableScanSupperInterface) {
|
||||||
|
|
||||||
|
this.metaScanner = enableScanMetaAnnotation ? new MetaAnnotationScanner() : new EmptyAnnotationScanner();
|
||||||
|
this.typeScanner = new TypeAnnotationScanner(
|
||||||
|
enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet()
|
||||||
|
);
|
||||||
|
this.methodScanner = new MethodAnnotationScanner(
|
||||||
|
enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet()
|
||||||
|
);
|
||||||
|
this.elementScanner = new ElementAnnotationScanner();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
|
||||||
|
*
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
|
||||||
|
if (ObjectUtil.isNull(annotatedEle)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 注解元素是类
|
||||||
|
if (annotatedEle instanceof Class) {
|
||||||
|
scanElements(typeScanner, consumer, annotatedEle, filter);
|
||||||
|
}
|
||||||
|
// 注解元素是方法
|
||||||
|
else if (annotatedEle instanceof Method) {
|
||||||
|
scanElements(methodScanner, consumer, annotatedEle, filter);
|
||||||
|
}
|
||||||
|
// 注解元素是其他类型
|
||||||
|
else {
|
||||||
|
scanElements(elementScanner, consumer, annotatedEle, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描注解类的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
|
||||||
|
*
|
||||||
|
* @param scanner 使用的扫描器
|
||||||
|
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||||
|
*/
|
||||||
|
private void scanElements(
|
||||||
|
AnnotationScanner scanner,
|
||||||
|
BiConsumer<Integer, Annotation> consumer,
|
||||||
|
AnnotatedElement annotatedEle,
|
||||||
|
Predicate<Annotation> filter) {
|
||||||
|
// 扫描类上注解
|
||||||
|
final ListValueMap<Integer, Annotation> classAnnotations = new ListValueMap<>(new LinkedHashMap<>());
|
||||||
|
scanner.scan((index, annotation) -> {
|
||||||
|
if (filter.test(annotation)) {
|
||||||
|
classAnnotations.putValue(index, annotation);
|
||||||
|
}
|
||||||
|
}, annotatedEle, filter);
|
||||||
|
|
||||||
|
// 扫描元注解
|
||||||
|
classAnnotations.forEach((index, annotations) ->
|
||||||
|
annotations.forEach(annotation -> {
|
||||||
|
consumer.accept(index, annotation);
|
||||||
|
metaScanner.scan(consumer, annotation.annotationType(), filter);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描注解类上存在的注解,支持处理枚举实例或枚举类型
|
||||||
|
* 需要注意,当待解析是枚举类时,有可能与{@link TypeAnnotationScanner}冲突
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see TypeAnnotationScanner
|
||||||
|
*/
|
||||||
|
public class MetaAnnotationScanner implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
|
||||||
|
*/
|
||||||
|
private final boolean includeSupperMetaAnnotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个元注解扫描器
|
||||||
|
*
|
||||||
|
* @param includeSupperMetaAnnotation 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
|
||||||
|
*/
|
||||||
|
public MetaAnnotationScanner(boolean includeSupperMetaAnnotation) {
|
||||||
|
this.includeSupperMetaAnnotation = includeSupperMetaAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个元注解扫描器,默认在扫描当前注解上的元注解后,并继续递归扫描元注解
|
||||||
|
*/
|
||||||
|
public MetaAnnotationScanner() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Annotation}接口的子类{@link Class}时返回{@code true}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return (annotatedEle instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class<?>) annotatedEle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 注解
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
|
||||||
|
final List<Annotation> annotations = new ArrayList<>();
|
||||||
|
scan(
|
||||||
|
(index, annotation) -> annotations.add(annotation), annotatedEle,
|
||||||
|
annotation -> ObjectUtil.notEqual(annotation, annotatedEle)
|
||||||
|
);
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按广度优先扫描指定注解上的元注解,对扫描到的注解与层级索引进行操作
|
||||||
|
*
|
||||||
|
* @param consumer 当前层级索引与操作
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @param filter 过滤器
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||||
|
filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
|
||||||
|
Set<Class<? extends Annotation>> accessed = new HashSet<>();
|
||||||
|
final Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList((Class<? extends Annotation>) annotatedEle));
|
||||||
|
int distance = 0;
|
||||||
|
do {
|
||||||
|
final List<Class<? extends Annotation>> annotationTypes = deque.removeFirst();
|
||||||
|
for (final Class<? extends Annotation> type : annotationTypes) {
|
||||||
|
final List<Annotation> metaAnnotations = Stream.of(type.getAnnotations())
|
||||||
|
.filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
|
||||||
|
.filter(filter)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (final Annotation metaAnnotation : metaAnnotations) {
|
||||||
|
consumer.accept(distance, metaAnnotation);
|
||||||
|
}
|
||||||
|
accessed.add(type);
|
||||||
|
List<Class<? extends Annotation>> next = metaAnnotations.stream()
|
||||||
|
.map(Annotation::annotationType)
|
||||||
|
.filter(t -> !accessed.contains(t))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollUtil.isNotEmpty(next)) {
|
||||||
|
deque.addLast(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
distance++;
|
||||||
|
} while (includeSupperMetaAnnotation && !deque.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描{@link Method}上的注解
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<MethodAnnotationScanner> implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个类注解扫描器,仅扫描该方法上直接声明的注解
|
||||||
|
*/
|
||||||
|
public MethodAnnotationScanner() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个类注解扫描器
|
||||||
|
*
|
||||||
|
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
|
||||||
|
*/
|
||||||
|
public MethodAnnotationScanner(boolean scanSameSignatureMethod) {
|
||||||
|
this(scanSameSignatureMethod, targetClass -> true, CollUtil.newLinkedHashSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个方法注解扫描器
|
||||||
|
*
|
||||||
|
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
|
||||||
|
* @param filter 过滤器
|
||||||
|
* @param excludeTypes 不包含的类型
|
||||||
|
*/
|
||||||
|
public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||||
|
super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个方法注解扫描器
|
||||||
|
*
|
||||||
|
* @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法
|
||||||
|
* @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法
|
||||||
|
* @param filter 过滤器
|
||||||
|
* @param excludeTypes 不包含的类型
|
||||||
|
*/
|
||||||
|
public MethodAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||||
|
super(includeSuperClass, includeInterfaces, filter, excludeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Method}时返回{@code true}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return boolean 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return annotatedEle instanceof Method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取声明该方法的类
|
||||||
|
*
|
||||||
|
* @param annotatedElement 注解元素
|
||||||
|
* @return 要递归的类型
|
||||||
|
* @see Method#getDeclaringClass()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement) {
|
||||||
|
return ((Method)annotatedElement).getDeclaringClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若父类/父接口中方法具有相同的方法签名,则返回该方法上的注解
|
||||||
|
*
|
||||||
|
* @param source 原始方法
|
||||||
|
* @param index 类的层级索引
|
||||||
|
* @param targetClass 类
|
||||||
|
* @return 最终所需的目标注解
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
|
||||||
|
final Method sourceMethod = (Method) source;
|
||||||
|
return Stream.of(ClassUtil.getDeclaredMethods(targetClass))
|
||||||
|
.filter(superMethod -> !superMethod.isBridge())
|
||||||
|
.filter(superMethod -> hasSameSignature(sourceMethod, superMethod))
|
||||||
|
.map(AnnotatedElement::getAnnotations)
|
||||||
|
.flatMap(Stream::of)
|
||||||
|
.toArray(Annotation[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否扫描类层级结构中具有相同方法签名的方法
|
||||||
|
*
|
||||||
|
* @param scanSuperMethodIfOverride 是否扫描类层级结构中具有相同方法签名的方法
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
public MethodAnnotationScanner setScanSameSignatureMethod(boolean scanSuperMethodIfOverride) {
|
||||||
|
setIncludeInterfaces(scanSuperMethodIfOverride);
|
||||||
|
setIncludeSuperClass(scanSuperMethodIfOverride);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该方法是否具备与扫描的方法相同的方法签名
|
||||||
|
*/
|
||||||
|
private boolean hasSameSignature(Method sourceMethod, Method superMethod) {
|
||||||
|
if (false == StrUtil.equals(sourceMethod.getName(), superMethod.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Class<?>[] sourceParameterTypes = sourceMethod.getParameterTypes();
|
||||||
|
final Class<?>[] targetParameterTypes = superMethod.getParameterTypes();
|
||||||
|
if (sourceParameterTypes.length != targetParameterTypes.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ArrayUtil.containsAll(sourceParameterTypes, targetParameterTypes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ClassUtil.isAssignable(superMethod.getReturnType(), sourceMethod.getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描{@link Class}上的注解
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class TypeAnnotationScanner extends AbstractTypeAnnotationScanner<TypeAnnotationScanner> implements AnnotationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个类注解扫描器
|
||||||
|
*
|
||||||
|
* @param includeSupperClass 是否允许扫描父类
|
||||||
|
* @param includeInterfaces 是否允许扫描父接口
|
||||||
|
* @param filter 过滤器
|
||||||
|
* @param excludeTypes 不包含的类型
|
||||||
|
*/
|
||||||
|
public TypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||||
|
super(includeSupperClass, includeInterfaces, filter, excludeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建一个类注解扫描器,默认允许扫描指定元素的父类以及父接口
|
||||||
|
*/
|
||||||
|
public TypeAnnotationScanner() {
|
||||||
|
this(true, true, t -> true, CollUtil.newLinkedHashSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Class}接时返回{@code true}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 是否支持扫描该注解元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean support(AnnotatedElement annotatedEle) {
|
||||||
|
return annotatedEle instanceof Class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将注解元素转为{@link Class}
|
||||||
|
*
|
||||||
|
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||||
|
* @return 要递归的类型
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedEle) {
|
||||||
|
return (Class<?>)annotatedEle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取{@link Class#getAnnotations()}
|
||||||
|
*
|
||||||
|
* @param source 最初的注解元素
|
||||||
|
* @param index 类的层级索引
|
||||||
|
* @param targetClass 类
|
||||||
|
* @return 类上直接声明的注解
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
|
||||||
|
return targetClass.getAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父类
|
||||||
|
*
|
||||||
|
* @param includeSuperClass 是否允许扫描父类
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TypeAnnotationScanner setIncludeSuperClass(boolean includeSuperClass) {
|
||||||
|
return super.setIncludeSuperClass(includeSuperClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许扫描父接口
|
||||||
|
*
|
||||||
|
* @param includeInterfaces 是否允许扫描父类
|
||||||
|
* @return 当前实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TypeAnnotationScanner setIncludeInterfaces(boolean includeInterfaces) {
|
||||||
|
return super.setIncludeInterfaces(includeInterfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若类型为jdk代理类,则尝试转换为原始被代理类
|
||||||
|
*/
|
||||||
|
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
|
||||||
|
@Override
|
||||||
|
public Class<?> apply(Class<?> sourceClass) {
|
||||||
|
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* 注解包扫描封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
|
@ -0,0 +1,324 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.BooleanUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ModifierUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述<br>
|
||||||
|
* 查找Getter和Setter方法时会:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 忽略字段和方法名的大小写
|
||||||
|
* 2. Getter查找getXXX、isXXX、getIsXXX
|
||||||
|
* 3. Setter查找setXXX、setIsXXX
|
||||||
|
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 3.1.2
|
||||||
|
*/
|
||||||
|
public class BeanDesc implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean类
|
||||||
|
*/
|
||||||
|
private final Class<?> beanClass;
|
||||||
|
/**
|
||||||
|
* 属性Map
|
||||||
|
*/
|
||||||
|
private final Map<String, PropDesc> propMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param beanClass Bean类
|
||||||
|
*/
|
||||||
|
public BeanDesc(Class<?> beanClass) {
|
||||||
|
Assert.notNull(beanClass);
|
||||||
|
this.beanClass = beanClass;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Bean的全类名
|
||||||
|
*
|
||||||
|
* @return Bean的类名
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return this.beanClass.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Bean的简单类名
|
||||||
|
*
|
||||||
|
* @return Bean的类名
|
||||||
|
*/
|
||||||
|
public String getSimpleName() {
|
||||||
|
return this.beanClass.getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段名-字段属性Map
|
||||||
|
*
|
||||||
|
* @param ignoreCase 是否忽略大小写,true为忽略,false不忽略
|
||||||
|
* @return 字段名-字段属性Map
|
||||||
|
*/
|
||||||
|
public Map<String, PropDesc> getPropMap(boolean ignoreCase) {
|
||||||
|
return ignoreCase ? new CaseInsensitiveMap<>(1, this.propMap) : this.propMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段属性列表
|
||||||
|
*
|
||||||
|
* @return {@link PropDesc} 列表
|
||||||
|
*/
|
||||||
|
public Collection<PropDesc> getProps() {
|
||||||
|
return this.propMap.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性,如果不存在返回null
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return {@link PropDesc}
|
||||||
|
*/
|
||||||
|
public PropDesc getProp(String fieldName) {
|
||||||
|
return this.propMap.get(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字段名对应的字段对象,如果不存在返回null
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 字段值
|
||||||
|
*/
|
||||||
|
public Field getField(String fieldName) {
|
||||||
|
final PropDesc desc = this.propMap.get(fieldName);
|
||||||
|
return null == desc ? null : desc.getField();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Getter方法,如果不存在返回null
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return Getter方法
|
||||||
|
*/
|
||||||
|
public Method getGetter(String fieldName) {
|
||||||
|
final PropDesc desc = this.propMap.get(fieldName);
|
||||||
|
return null == desc ? null : desc.getGetter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Setter方法,如果不存在返回null
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return Setter方法
|
||||||
|
*/
|
||||||
|
public Method getSetter(String fieldName) {
|
||||||
|
final PropDesc desc = this.propMap.get(fieldName);
|
||||||
|
return null == desc ? null : desc.getSetter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------------ Private method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化<br>
|
||||||
|
* 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
private BeanDesc init() {
|
||||||
|
final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase);
|
||||||
|
PropDesc prop;
|
||||||
|
for (Field field : ReflectUtil.getFields(this.beanClass)) {
|
||||||
|
// 排除静态属性和对象子类
|
||||||
|
if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) {
|
||||||
|
prop = createProp(field, gettersAndSetters);
|
||||||
|
// 只有不存在时才放入,防止父类属性覆盖子类属性
|
||||||
|
this.propMap.putIfAbsent(prop.getFieldName(), prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字段创建属性描述<br>
|
||||||
|
* 查找Getter和Setter方法时会:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 忽略字段和方法名的大小写
|
||||||
|
* 2. Getter查找getXXX、isXXX、getIsXXX
|
||||||
|
* 3. Setter查找setXXX、setIsXXX
|
||||||
|
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @param methods 类中所有的方法
|
||||||
|
* @return {@link PropDesc}
|
||||||
|
* @since 4.0.2
|
||||||
|
*/
|
||||||
|
private PropDesc createProp(Field field, Method[] methods) {
|
||||||
|
final PropDesc prop = findProp(field, methods, false);
|
||||||
|
// 忽略大小写重新匹配一次
|
||||||
|
if (null == prop.getter || null == prop.setter) {
|
||||||
|
final PropDesc propIgnoreCase = findProp(field, methods, true);
|
||||||
|
if (null == prop.getter) {
|
||||||
|
prop.getter = propIgnoreCase.getter;
|
||||||
|
}
|
||||||
|
if (null == prop.setter) {
|
||||||
|
prop.setter = propIgnoreCase.setter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找字段对应的Getter和Setter方法
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @param gettersOrSetters 类中所有的Getter或Setter方法
|
||||||
|
* @param ignoreCase 是否忽略大小写匹配
|
||||||
|
* @return PropDesc
|
||||||
|
*/
|
||||||
|
private PropDesc findProp(Field field, Method[] gettersOrSetters, boolean ignoreCase) {
|
||||||
|
final String fieldName = field.getName();
|
||||||
|
final Class<?> fieldType = field.getType();
|
||||||
|
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
|
||||||
|
|
||||||
|
Method getter = null;
|
||||||
|
Method setter = null;
|
||||||
|
String methodName;
|
||||||
|
for (Method method : gettersOrSetters) {
|
||||||
|
methodName = method.getName();
|
||||||
|
if (method.getParameterCount() == 0) {
|
||||||
|
// 无参数,可能为Getter方法
|
||||||
|
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||||
|
// 方法名与字段名匹配,则为Getter方法
|
||||||
|
getter = method;
|
||||||
|
}
|
||||||
|
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||||
|
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
|
||||||
|
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
|
||||||
|
setter = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (null != getter && null != setter) {
|
||||||
|
// 如果Getter和Setter方法都找到了,不再继续寻找
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PropDesc(field, getter, setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法是否为Getter方法<br>
|
||||||
|
* 匹配规则如下(忽略大小写):
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 字段名 -》 方法名
|
||||||
|
* isName -》 isName
|
||||||
|
* isName -》 isIsName
|
||||||
|
* isName -》 getIsName
|
||||||
|
* name -》 isName
|
||||||
|
* name -》 getName
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param methodName 方法名
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param isBooleanField 是否为Boolean类型字段
|
||||||
|
* @param ignoreCase 匹配是否忽略大小写
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
private boolean isMatchGetter(String methodName, String fieldName, boolean isBooleanField, boolean ignoreCase) {
|
||||||
|
final String handledFieldName;
|
||||||
|
if (ignoreCase) {
|
||||||
|
// 全部转为小写,忽略大小写比较
|
||||||
|
methodName = methodName.toLowerCase();
|
||||||
|
handledFieldName = fieldName.toLowerCase();
|
||||||
|
fieldName = handledFieldName;
|
||||||
|
} else {
|
||||||
|
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 针对Boolean类型特殊检查
|
||||||
|
if (isBooleanField) {
|
||||||
|
if (fieldName.startsWith("is")) {
|
||||||
|
// 字段已经是is开头
|
||||||
|
if (methodName.equals(fieldName) // isName -》 isName
|
||||||
|
|| ("get" + handledFieldName).equals(methodName)// isName -》 getIsName
|
||||||
|
|| ("is" + handledFieldName).equals(methodName)// isName -》 isIsName
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (("is" + handledFieldName).equals(methodName)) {
|
||||||
|
// 字段非is开头, name -》 isName
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包括boolean的任何类型只有一种匹配情况:name -》 getName
|
||||||
|
return ("get" + handledFieldName).equals(methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法是否为Setter方法<br>
|
||||||
|
* 匹配规则如下(忽略大小写):
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 字段名 -》 方法名
|
||||||
|
* isName -》 setName
|
||||||
|
* isName -》 setIsName
|
||||||
|
* name -》 setName
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param methodName 方法名
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param isBooleanField 是否为Boolean类型字段
|
||||||
|
* @param ignoreCase 匹配是否忽略大小写
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
private boolean isMatchSetter(String methodName, String fieldName, boolean isBooleanField, boolean ignoreCase) {
|
||||||
|
final String handledFieldName;
|
||||||
|
if (ignoreCase) {
|
||||||
|
// 全部转为小写,忽略大小写比较
|
||||||
|
methodName = methodName.toLowerCase();
|
||||||
|
handledFieldName = fieldName.toLowerCase();
|
||||||
|
fieldName = handledFieldName;
|
||||||
|
} else {
|
||||||
|
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非标准Setter方法跳过
|
||||||
|
if (false == methodName.startsWith("set")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 针对Boolean类型特殊检查
|
||||||
|
if (isBooleanField && fieldName.startsWith("is")) {
|
||||||
|
// 字段是is开头
|
||||||
|
if (("set" + StrUtil.removePrefix(fieldName, "is")).equals(methodName)// isName -》 setName
|
||||||
|
|| ("set" + handledFieldName).equals(methodName)// isName -》 setIsName
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包括boolean的任何类型只有一种匹配情况:name -》 setName
|
||||||
|
return ("set" + handledFieldName).equals(methodName);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------------------------------------ Private method end
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Func0;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.WeakConcurrentMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean属性缓存<br>
|
||||||
|
* 缓存用于防止多次反射造成的性能问题
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public enum BeanDescCache {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得属性名和{@link BeanDesc}Map映射
|
||||||
|
*
|
||||||
|
* @param beanClass Bean的类
|
||||||
|
* @param supplier 对象不存在时创建对象的函数
|
||||||
|
* @return 属性名和{@link BeanDesc}映射
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) {
|
||||||
|
return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空全局的Bean属性缓存
|
||||||
|
*
|
||||||
|
* @since 5.7.21
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
this.bdCache.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.exceptions.ExceptionUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean异常
|
||||||
|
* @author xiaoleilu
|
||||||
|
*/
|
||||||
|
public class BeanException extends RuntimeException{
|
||||||
|
private static final long serialVersionUID = -8096998667745023423L;
|
||||||
|
|
||||||
|
public BeanException(Throwable e) {
|
||||||
|
super(ExceptionUtil.getMessage(e), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanException(String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanException(String message, Throwable throwable) {
|
||||||
|
super(message, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanException(Throwable throwable, String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params), throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Func0;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.ReferenceConcurrentMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.WeakConcurrentMap;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean属性缓存<br>
|
||||||
|
* 缓存用于防止多次反射造成的性能问题
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public enum BeanInfoCache {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>();
|
||||||
|
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得属性名和{@link PropertyDescriptor}Map映射
|
||||||
|
*
|
||||||
|
* @param beanClass Bean的类
|
||||||
|
* @param ignoreCase 是否忽略大小写
|
||||||
|
* @return 属性名和{@link PropertyDescriptor}Map映射
|
||||||
|
*/
|
||||||
|
public Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> beanClass, boolean ignoreCase) {
|
||||||
|
return getCache(ignoreCase).get(beanClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得属性名和{@link PropertyDescriptor}Map映射
|
||||||
|
*
|
||||||
|
* @param beanClass Bean的类
|
||||||
|
* @param ignoreCase 是否忽略大小写
|
||||||
|
* @param supplier 缓存对象产生函数
|
||||||
|
* @return 属性名和{@link PropertyDescriptor}Map映射
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public Map<String, PropertyDescriptor> getPropertyDescriptorMap(
|
||||||
|
Class<?> beanClass,
|
||||||
|
boolean ignoreCase,
|
||||||
|
Func0<Map<String, PropertyDescriptor>> supplier) {
|
||||||
|
return getCache(ignoreCase).computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入缓存
|
||||||
|
*
|
||||||
|
* @param beanClass Bean的类
|
||||||
|
* @param fieldNamePropertyDescriptorMap 属性名和{@link PropertyDescriptor}Map映射
|
||||||
|
* @param ignoreCase 是否忽略大小写
|
||||||
|
*/
|
||||||
|
public void putPropertyDescriptorMap(Class<?> beanClass, Map<String, PropertyDescriptor> fieldNamePropertyDescriptorMap, boolean ignoreCase) {
|
||||||
|
getCache(ignoreCase).put(beanClass, fieldNamePropertyDescriptorMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空缓存
|
||||||
|
*
|
||||||
|
* @since 5.7.21
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
this.pdCache.clear();
|
||||||
|
this.ignoreCasePdCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据是否忽略字段名的大小写,返回不用Cache对象
|
||||||
|
*
|
||||||
|
* @param ignoreCase 是否忽略大小写
|
||||||
|
* @return {@link ReferenceConcurrentMap}
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
private ReferenceConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> getCache(boolean ignoreCase) {
|
||||||
|
return ignoreCase ? ignoreCasePdCache : pdCache;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,324 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.ListUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.NumberUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
|
||||||
|
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
|
||||||
|
* <ol>
|
||||||
|
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||||
|
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||||
|
* </ol>
|
||||||
|
* <p>
|
||||||
|
* 表达式栗子:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* persion
|
||||||
|
* persion.name
|
||||||
|
* persons[3]
|
||||||
|
* person.friends[5].name
|
||||||
|
* ['person']['friends'][5]['name']
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 4.0.6
|
||||||
|
*/
|
||||||
|
public class BeanPath implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表达式边界符号数组
|
||||||
|
*/
|
||||||
|
private static final char[] EXP_CHARS = {CharUtil.DOT, CharUtil.BRACKET_START, CharUtil.BRACKET_END};
|
||||||
|
|
||||||
|
private boolean isStartWith = false;
|
||||||
|
protected List<String> patternParts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析Bean路径表达式为Bean模式<br>
|
||||||
|
* Bean表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
|
||||||
|
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
|
||||||
|
* <ol>
|
||||||
|
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||||
|
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||||
|
* </ol>
|
||||||
|
* <p>
|
||||||
|
* 表达式栗子:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* persion
|
||||||
|
* persion.name
|
||||||
|
* persons[3]
|
||||||
|
* person.friends[5].name
|
||||||
|
* ['person']['friends'][5]['name']
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param expression 表达式
|
||||||
|
* @return BeanPath
|
||||||
|
*/
|
||||||
|
public static BeanPath create(final String expression) {
|
||||||
|
return new BeanPath(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param expression 表达式
|
||||||
|
*/
|
||||||
|
public BeanPath(final String expression) {
|
||||||
|
init(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取表达式解析后的分段列表
|
||||||
|
*
|
||||||
|
* @return 表达式分段列表
|
||||||
|
*/
|
||||||
|
public List<String> getPatternParts() {
|
||||||
|
return this.patternParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Bean中对应表达式的值
|
||||||
|
*
|
||||||
|
* @param bean Bean对象或Map或List等
|
||||||
|
* @return 值,如果对应值不存在,则返回null
|
||||||
|
*/
|
||||||
|
public Object get(final Object bean) {
|
||||||
|
return get(this.patternParts, bean, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置表达式指定位置(或filed对应)的值<br>
|
||||||
|
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
|
||||||
|
* 注意:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
|
||||||
|
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param bean Bean、Map或List
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
public void set(final Object bean, final Object value) {
|
||||||
|
set(bean, this.patternParts, lastIsNumber(this.patternParts), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.patternParts.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//region Private Methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置表达式指定位置(或filed对应)的值<br>
|
||||||
|
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
|
||||||
|
* 注意:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
|
||||||
|
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param bean Bean、Map或List
|
||||||
|
* @param patternParts 表达式块列表
|
||||||
|
* @param value 值
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
private void set(Object bean, List<String> patternParts, boolean nextNumberPart, Object value) {
|
||||||
|
Object subBean = this.get(patternParts, bean, true);
|
||||||
|
if (null == subBean) {
|
||||||
|
final List<String> parentParts = getParentParts(patternParts);
|
||||||
|
this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>());
|
||||||
|
//set中有可能做过转换,因此此处重新获取bean
|
||||||
|
subBean = this.get(patternParts, bean, true);
|
||||||
|
}
|
||||||
|
BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断path列表中末尾的标记是否为数字
|
||||||
|
*
|
||||||
|
* @param patternParts path列表
|
||||||
|
* @return 是否为数字
|
||||||
|
*/
|
||||||
|
private static boolean lastIsNumber(List<String> patternParts) {
|
||||||
|
return NumberUtil.isInteger(patternParts.get(patternParts.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取父级路径列表
|
||||||
|
*
|
||||||
|
* @param patternParts 路径列表
|
||||||
|
* @return 父级路径列表
|
||||||
|
*/
|
||||||
|
private static List<String> getParentParts(List<String> patternParts) {
|
||||||
|
return patternParts.subList(0, patternParts.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Bean中对应表达式的值
|
||||||
|
*
|
||||||
|
* @param patternParts 表达式分段列表
|
||||||
|
* @param bean Bean对象或Map或List等
|
||||||
|
* @param ignoreLast 是否忽略最后一个值,忽略最后一个值则用于set,否则用于read
|
||||||
|
* @return 值,如果对应值不存在,则返回null
|
||||||
|
*/
|
||||||
|
private Object get(final List<String> patternParts, final Object bean, final boolean ignoreLast) {
|
||||||
|
int length = patternParts.size();
|
||||||
|
if (ignoreLast) {
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
Object subBean = bean;
|
||||||
|
boolean isFirst = true;
|
||||||
|
String patternPart;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
patternPart = patternParts.get(i);
|
||||||
|
subBean = getFieldValue(subBean, patternPart);
|
||||||
|
if (null == subBean) {
|
||||||
|
// 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作)
|
||||||
|
if (isFirst && false == this.isStartWith && BeanUtil.isMatchName(bean, patternPart, true)) {
|
||||||
|
subBean = bean;
|
||||||
|
isFirst = false;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Object getFieldValue(final Object bean, final String expression) {
|
||||||
|
if (StrUtil.isBlank(expression)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StrUtil.contains(expression, ':')) {
|
||||||
|
// [start:end:step] 模式
|
||||||
|
final List<String> parts = StrUtil.splitTrim(expression, ':');
|
||||||
|
final int start = Integer.parseInt(parts.get(0));
|
||||||
|
final int end = Integer.parseInt(parts.get(1));
|
||||||
|
int step = 1;
|
||||||
|
if (3 == parts.size()) {
|
||||||
|
step = Integer.parseInt(parts.get(2));
|
||||||
|
}
|
||||||
|
if (bean instanceof Collection) {
|
||||||
|
return CollUtil.sub((Collection<?>) bean, start, end, step);
|
||||||
|
} else if (ArrayUtil.isArray(bean)) {
|
||||||
|
return ArrayUtil.sub(bean, start, end, step);
|
||||||
|
}
|
||||||
|
} else if (StrUtil.contains(expression, ',')) {
|
||||||
|
// [num0,num1,num2...]模式或者['key0','key1']模式
|
||||||
|
final List<String> keys = StrUtil.splitTrim(expression, ',');
|
||||||
|
if (bean instanceof Collection) {
|
||||||
|
return CollUtil.getAny((Collection<?>) bean, Convert.convert(int[].class, keys));
|
||||||
|
} else if (ArrayUtil.isArray(bean)) {
|
||||||
|
return ArrayUtil.getAny(bean, Convert.convert(int[].class, keys));
|
||||||
|
} else {
|
||||||
|
final String[] unWrappedKeys = new String[keys.size()];
|
||||||
|
for (int i = 0; i < unWrappedKeys.length; i++) {
|
||||||
|
unWrappedKeys[i] = StrUtil.unWrap(keys.get(i), '\'');
|
||||||
|
}
|
||||||
|
if (bean instanceof Map) {
|
||||||
|
// 只支持String为key的Map
|
||||||
|
return MapUtil.getAny((Map<String, ?>) bean, unWrappedKeys);
|
||||||
|
} else {
|
||||||
|
final Map<String, Object> map = BeanUtil.beanToMap(bean);
|
||||||
|
return MapUtil.getAny(map, unWrappedKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 数字或普通字符串
|
||||||
|
return BeanUtil.getFieldValue(bean, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*
|
||||||
|
* @param expression 表达式
|
||||||
|
*/
|
||||||
|
private void init(final String expression) {
|
||||||
|
final List<String> localPatternParts = new ArrayList<>();
|
||||||
|
final int length = expression.length();
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
char c;
|
||||||
|
boolean isNumStart = false;// 下标标识符开始
|
||||||
|
boolean isInWrap = false; //标识是否在引号内
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
c = expression.charAt(i);
|
||||||
|
if (0 == i && '$' == c) {
|
||||||
|
// 忽略开头的$符,表示当前对象
|
||||||
|
isStartWith = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('\'' == c) {
|
||||||
|
// 结束
|
||||||
|
isInWrap = (false == isInWrap);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == isInWrap && ArrayUtil.contains(EXP_CHARS, c)) {
|
||||||
|
// 处理边界符号
|
||||||
|
if (CharUtil.BRACKET_END == c) {
|
||||||
|
// 中括号(数字下标)结束
|
||||||
|
if (false == isNumStart) {
|
||||||
|
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find ']' but no '[' !", expression, i));
|
||||||
|
}
|
||||||
|
isNumStart = false;
|
||||||
|
// 中括号结束加入下标
|
||||||
|
} else {
|
||||||
|
if (isNumStart) {
|
||||||
|
// 非结束中括号情况下发现起始中括号报错(中括号未关闭)
|
||||||
|
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find '[' but no ']' !", expression, i));
|
||||||
|
} else if (CharUtil.BRACKET_START == c) {
|
||||||
|
// 数字下标开始
|
||||||
|
isNumStart = true;
|
||||||
|
}
|
||||||
|
// 每一个边界符之前的表达式是一个完整的KEY,开始处理KEY
|
||||||
|
}
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
localPatternParts.add(builder.toString());
|
||||||
|
}
|
||||||
|
builder.setLength(0);
|
||||||
|
} else {
|
||||||
|
// 非边界符号,追加字符
|
||||||
|
builder.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 末尾边界符检查
|
||||||
|
if (isNumStart) {
|
||||||
|
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find '[' but no ']' !", expression, length - 1));
|
||||||
|
} else {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
localPatternParts.add(builder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不可变List
|
||||||
|
this.patternParts = ListUtil.unmodifiable(localPatternParts);
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,226 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.clone.CloneSupport;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态Bean,通过反射对Bean的相关方法做操作<br>
|
||||||
|
* 支持Map和普通Bean
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 3.0.7
|
||||||
|
*/
|
||||||
|
public class DynaBean extends CloneSupport<DynaBean> implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Class<?> beanClass;
|
||||||
|
private final Object bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个DynaBean
|
||||||
|
*
|
||||||
|
* @param bean 普通Bean
|
||||||
|
* @return DynaBean
|
||||||
|
*/
|
||||||
|
public static DynaBean create(Object bean) {
|
||||||
|
return new DynaBean(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个DynaBean
|
||||||
|
*
|
||||||
|
* @param beanClass Bean类
|
||||||
|
* @return DynaBean
|
||||||
|
*/
|
||||||
|
public static DynaBean create(Class<?> beanClass) {
|
||||||
|
return new DynaBean(beanClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个DynaBean
|
||||||
|
*
|
||||||
|
* @param beanClass Bean类
|
||||||
|
* @param params 构造Bean所需要的参数
|
||||||
|
* @return DynaBean
|
||||||
|
*/
|
||||||
|
public static DynaBean create(Class<?> beanClass, Object... params) {
|
||||||
|
return new DynaBean(beanClass, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------ Constructor start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param beanClass Bean类
|
||||||
|
* @param params 构造Bean所需要的参数
|
||||||
|
*/
|
||||||
|
public DynaBean(Class<?> beanClass, Object... params) {
|
||||||
|
this(ReflectUtil.newInstance(beanClass, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param beanClass Bean类
|
||||||
|
*/
|
||||||
|
public DynaBean(Class<?> beanClass) {
|
||||||
|
this(ReflectUtil.newInstance(beanClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bean 原始Bean
|
||||||
|
*/
|
||||||
|
public DynaBean(Object bean) {
|
||||||
|
Assert.notNull(bean);
|
||||||
|
if (bean instanceof DynaBean) {
|
||||||
|
bean = ((DynaBean) bean).getBean();
|
||||||
|
}
|
||||||
|
this.bean = bean;
|
||||||
|
this.beanClass = ClassUtil.getClass(bean);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------ Constructor end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字段对应值
|
||||||
|
*
|
||||||
|
* @param <T> 属性值类型
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 字段值
|
||||||
|
* @throws BeanException 反射获取属性值或字段值导致的异常
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(String fieldName) throws BeanException {
|
||||||
|
if (Map.class.isAssignableFrom(beanClass)) {
|
||||||
|
return (T) ((Map<?, ?>) bean).get(fieldName);
|
||||||
|
} else {
|
||||||
|
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||||
|
if (null == prop) {
|
||||||
|
throw new BeanException("No public field or get method for {}", fieldName);
|
||||||
|
}
|
||||||
|
return (T) prop.getValue(bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有指定名称的bean属性
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 是否有bean属性
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public boolean containsProp(String fieldName) {
|
||||||
|
if (Map.class.isAssignableFrom(beanClass)) {
|
||||||
|
return ((Map<?, ?>) bean).containsKey(fieldName);
|
||||||
|
} else{
|
||||||
|
return null != BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字段对应值,获取异常返回{@code null}
|
||||||
|
*
|
||||||
|
* @param <T> 属性值类型
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 字段值
|
||||||
|
* @since 3.1.1
|
||||||
|
*/
|
||||||
|
public <T> T safeGet(String fieldName) {
|
||||||
|
try {
|
||||||
|
return get(fieldName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字段值
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param value 字段值
|
||||||
|
* @throws BeanException 反射获取属性值或字段值导致的异常
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public void set(String fieldName, Object value) throws BeanException {
|
||||||
|
if (Map.class.isAssignableFrom(beanClass)) {
|
||||||
|
((Map) bean).put(fieldName, value);
|
||||||
|
} else {
|
||||||
|
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||||
|
if (null == prop) {
|
||||||
|
throw new BeanException("No public field or set method for {}", fieldName);
|
||||||
|
}
|
||||||
|
prop.setValue(bean, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行原始Bean中的方法
|
||||||
|
*
|
||||||
|
* @param methodName 方法名
|
||||||
|
* @param params 参数
|
||||||
|
* @return 执行结果,可能为null
|
||||||
|
*/
|
||||||
|
public Object invoke(String methodName, Object... params) {
|
||||||
|
return ReflectUtil.invoke(this.bean, methodName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得原始Bean
|
||||||
|
*
|
||||||
|
* @param <T> Bean类型
|
||||||
|
* @return bean
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getBean() {
|
||||||
|
return (T) this.bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Bean的类型
|
||||||
|
*
|
||||||
|
* @param <T> Bean类型
|
||||||
|
* @return Bean类型
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> Class<T> getBeanClass() {
|
||||||
|
return (Class<T>) this.beanClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((bean == null) ? 0 : bean.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final DynaBean other = (DynaBean) obj;
|
||||||
|
if (bean == null) {
|
||||||
|
return other.bean == null;
|
||||||
|
} else return bean.equals(other.bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.bean.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为了解决反射过程中,需要传递null参数,但是会丢失参数类型而设立的包装类
|
||||||
|
*
|
||||||
|
* @param <T> Null值对应的类型
|
||||||
|
* @author Lillls
|
||||||
|
* @since 5.5.0
|
||||||
|
*/
|
||||||
|
public class NullWrapperBean<T> {
|
||||||
|
|
||||||
|
private final Class<T> clazz;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param clazz null的类型
|
||||||
|
*/
|
||||||
|
public NullWrapperBean(Class<T> clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取null值对应的类型
|
||||||
|
*
|
||||||
|
* @return 类型
|
||||||
|
*/
|
||||||
|
public Class<T> getWrappedClass() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,404 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.annotation.PropIgnore;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ModifierUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
|
import java.beans.Transient;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性描述,包括了字段、getter、setter和相应的方法执行
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class PropDesc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段
|
||||||
|
*/
|
||||||
|
final Field field;
|
||||||
|
/**
|
||||||
|
* Getter方法
|
||||||
|
*/
|
||||||
|
protected Method getter;
|
||||||
|
/**
|
||||||
|
* Setter方法
|
||||||
|
*/
|
||||||
|
protected Method setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造<br>
|
||||||
|
* Getter和Setter方法设置为默认可访问
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @param getter get方法
|
||||||
|
* @param setter set方法
|
||||||
|
*/
|
||||||
|
public PropDesc(Field field, Method getter, Method setter) {
|
||||||
|
this.field = field;
|
||||||
|
this.getter = ClassUtil.setAccessible(getter);
|
||||||
|
this.setter = ClassUtil.setAccessible(setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段名,如果存在Alias注解,读取注解的值作为名称
|
||||||
|
*
|
||||||
|
* @return 字段名
|
||||||
|
*/
|
||||||
|
public String getFieldName() {
|
||||||
|
return ReflectUtil.getFieldName(this.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段名称
|
||||||
|
*
|
||||||
|
* @return 字段名
|
||||||
|
* @since 5.1.6
|
||||||
|
*/
|
||||||
|
public String getRawFieldName() {
|
||||||
|
return null == this.field ? null : this.field.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段
|
||||||
|
*
|
||||||
|
* @return 字段
|
||||||
|
*/
|
||||||
|
public Field getField() {
|
||||||
|
return this.field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字段类型<br>
|
||||||
|
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
|
||||||
|
*
|
||||||
|
* @return 字段类型
|
||||||
|
*/
|
||||||
|
public Type getFieldType() {
|
||||||
|
if (null != this.field) {
|
||||||
|
return TypeUtil.getType(this.field);
|
||||||
|
}
|
||||||
|
return findPropType(getter, setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字段类型<br>
|
||||||
|
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
|
||||||
|
*
|
||||||
|
* @return 字段类型
|
||||||
|
*/
|
||||||
|
public Class<?> getFieldClass() {
|
||||||
|
if (null != this.field) {
|
||||||
|
return TypeUtil.getClass(this.field);
|
||||||
|
}
|
||||||
|
return findPropClass(getter, setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Getter方法,可能为{@code null}
|
||||||
|
*
|
||||||
|
* @return Getter方法
|
||||||
|
*/
|
||||||
|
public Method getGetter() {
|
||||||
|
return this.getter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Setter方法,可能为{@code null}
|
||||||
|
*
|
||||||
|
* @return {@link Method}Setter 方法对象
|
||||||
|
*/
|
||||||
|
public Method getSetter() {
|
||||||
|
return this.setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
|
||||||
|
*
|
||||||
|
* @param checkTransient 是否检查Transient关键字或注解
|
||||||
|
* @return 是否可读
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public boolean isReadable(boolean checkTransient) {
|
||||||
|
// 检查是否有getter方法或是否为public修饰
|
||||||
|
if (null == this.getter && false == ModifierUtil.isPublic(this.field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查transient关键字和@Transient注解
|
||||||
|
if (checkTransient && isTransientForGet()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查@PropIgnore注解
|
||||||
|
return false == isIgnoreGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性值<br>
|
||||||
|
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值<br>
|
||||||
|
* 此方法不检查任何注解,使用前需调用 {@link #isReadable(boolean)} 检查是否可读
|
||||||
|
*
|
||||||
|
* @param bean Bean对象
|
||||||
|
* @return 字段值
|
||||||
|
* @since 4.0.5
|
||||||
|
*/
|
||||||
|
public Object getValue(Object bean) {
|
||||||
|
if (null != this.getter) {
|
||||||
|
return ReflectUtil.invoke(bean, this.getter);
|
||||||
|
} else if (ModifierUtil.isPublic(this.field)) {
|
||||||
|
return ReflectUtil.getFieldValue(bean, this.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取属性值,自动转换属性值类型<br>
|
||||||
|
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
|
||||||
|
*
|
||||||
|
* @param bean Bean对象
|
||||||
|
* @param targetType 返回属性值需要转换的类型,null表示不转换
|
||||||
|
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||||
|
* @return this
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public Object getValue(Object bean, Type targetType, boolean ignoreError) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
result = getValue(bean);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (false == ignoreError) {
|
||||||
|
throw new BeanException(e, "Get value of [{}] error!", getFieldName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != result && null != targetType) {
|
||||||
|
// 尝试将结果转换为目标类型,如果转换失败,返回null,即跳过此属性值。
|
||||||
|
// 来自:issues#I41WKP@Gitee,当忽略错误情况下,目标类型转换失败应返回null
|
||||||
|
// 如果返回原值,在集合注入时会成功,但是集合取值时会报类型转换错误
|
||||||
|
return Convert.convertWithCheck(targetType, result, null, ignoreError);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
|
||||||
|
*
|
||||||
|
* @param checkTransient 是否检查Transient关键字或注解
|
||||||
|
* @return 是否可读
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public boolean isWritable(boolean checkTransient) {
|
||||||
|
// 检查是否有getter方法或是否为public修饰
|
||||||
|
if (null == this.setter && false == ModifierUtil.isPublic(this.field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查transient关键字和@Transient注解
|
||||||
|
if (checkTransient && isTransientForSet()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查@PropIgnore注解
|
||||||
|
return false == isIgnoreSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Bean的字段值<br>
|
||||||
|
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值<br>
|
||||||
|
* 此方法不检查任何注解,使用前需调用 {@link #isWritable(boolean)} 检查是否可写
|
||||||
|
*
|
||||||
|
* @param bean Bean对象
|
||||||
|
* @param value 值,必须与字段值类型匹配
|
||||||
|
* @return this
|
||||||
|
* @since 4.0.5
|
||||||
|
*/
|
||||||
|
public PropDesc setValue(Object bean, Object value) {
|
||||||
|
if (null != this.setter) {
|
||||||
|
ReflectUtil.invoke(bean, this.setter, value);
|
||||||
|
} else if (ModifierUtil.isPublic(this.field)) {
|
||||||
|
ReflectUtil.setFieldValue(bean, this.field, value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性值,可以自动转换字段类型为目标类型
|
||||||
|
*
|
||||||
|
* @param bean Bean对象
|
||||||
|
* @param value 属性值,可以为任意类型
|
||||||
|
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
|
||||||
|
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||||
|
* @return this
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError) {
|
||||||
|
return setValue(bean, value, ignoreNull, ignoreError, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性值,可以自动转换字段类型为目标类型
|
||||||
|
*
|
||||||
|
* @param bean Bean对象
|
||||||
|
* @param value 属性值,可以为任意类型
|
||||||
|
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
|
||||||
|
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||||
|
* @param override 是否覆盖目标值,如果不覆盖,会先读取bean的值,{@code null}则写,否则忽略。如果覆盖,则不判断直接写
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError, boolean override) {
|
||||||
|
if (null == value && ignoreNull) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue#I4JQ1N@Gitee
|
||||||
|
// 非覆盖模式下,如果目标值存在,则跳过
|
||||||
|
if (false == override && null != getValue(bean)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当类型不匹配的时候,执行默认转换
|
||||||
|
if (null != value) {
|
||||||
|
final Class<?> propClass = getFieldClass();
|
||||||
|
if (false == propClass.isInstance(value)) {
|
||||||
|
value = Convert.convertWithCheck(propClass, value, null, ignoreError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 属性赋值
|
||||||
|
if (null != value || false == ignoreNull) {
|
||||||
|
try {
|
||||||
|
this.setValue(bean, value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (false == ignoreError) {
|
||||||
|
throw new BeanException(e, "Set value of [{}] error!", getFieldName());
|
||||||
|
}
|
||||||
|
// 忽略注入失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------ Private method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过Getter和Setter方法中找到属性类型
|
||||||
|
*
|
||||||
|
* @param getter Getter方法
|
||||||
|
* @param setter Setter方法
|
||||||
|
* @return {@link Type}
|
||||||
|
*/
|
||||||
|
private Type findPropType(Method getter, Method setter) {
|
||||||
|
Type type = null;
|
||||||
|
if (null != getter) {
|
||||||
|
type = TypeUtil.getReturnType(getter);
|
||||||
|
}
|
||||||
|
if (null == type && null != setter) {
|
||||||
|
type = TypeUtil.getParamType(setter, 0);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过Getter和Setter方法中找到属性类型
|
||||||
|
*
|
||||||
|
* @param getter Getter方法
|
||||||
|
* @param setter Setter方法
|
||||||
|
* @return {@link Type}
|
||||||
|
*/
|
||||||
|
private Class<?> findPropClass(Method getter, Method setter) {
|
||||||
|
Class<?> type = null;
|
||||||
|
if (null != getter) {
|
||||||
|
type = TypeUtil.getReturnClass(getter);
|
||||||
|
}
|
||||||
|
if (null == type && null != setter) {
|
||||||
|
type = TypeUtil.getFirstParamClass(setter);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为:
|
||||||
|
* <pre>
|
||||||
|
* 1. 在字段上有{@link PropIgnore} 注解
|
||||||
|
* 2. 在setXXX方法上有{@link PropIgnore} 注解
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return 是否忽略写
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
private boolean isIgnoreSet() {
|
||||||
|
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|
||||||
|
|| AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为:
|
||||||
|
* <pre>
|
||||||
|
* 1. 在字段上有{@link PropIgnore} 注解
|
||||||
|
* 2. 在getXXX方法上有{@link PropIgnore} 注解
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return 是否忽略读
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
private boolean isIgnoreGet() {
|
||||||
|
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|
||||||
|
|| AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段和Getter方法是否为Transient关键字修饰的
|
||||||
|
*
|
||||||
|
* @return 是否为Transient关键字修饰的
|
||||||
|
* @since 5.3.11
|
||||||
|
*/
|
||||||
|
private boolean isTransientForGet() {
|
||||||
|
boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
|
||||||
|
|
||||||
|
// 检查Getter方法
|
||||||
|
if (false == isTransient && null != this.getter) {
|
||||||
|
isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT);
|
||||||
|
|
||||||
|
// 检查注解
|
||||||
|
if (false == isTransient) {
|
||||||
|
isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isTransient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段和Getter方法是否为Transient关键字修饰的
|
||||||
|
*
|
||||||
|
* @return 是否为Transient关键字修饰的
|
||||||
|
* @since 5.3.11
|
||||||
|
*/
|
||||||
|
private boolean isTransientForSet() {
|
||||||
|
boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
|
||||||
|
|
||||||
|
// 检查Getter方法
|
||||||
|
if (false == isTransient && null != this.setter) {
|
||||||
|
isTransient = ModifierUtil.hasModifier(this.setter, ModifierUtil.ModifierType.TRANSIENT);
|
||||||
|
|
||||||
|
// 检查注解
|
||||||
|
if (false == isTransient) {
|
||||||
|
isTransient = AnnotationUtil.hasAnnotation(this.setter, Transient.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isTransient;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------ Private method end
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.copier.Copier;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象的对象拷贝封装,提供来源对象、目标对象持有
|
||||||
|
*
|
||||||
|
* @param <S> 来源对象类型
|
||||||
|
* @param <T> 目标对象类型
|
||||||
|
* @author looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public abstract class AbsCopier<S, T> implements Copier<T> {
|
||||||
|
|
||||||
|
protected final S source;
|
||||||
|
protected final T target;
|
||||||
|
/**
|
||||||
|
* 拷贝选项
|
||||||
|
*/
|
||||||
|
protected final CopyOptions copyOptions;
|
||||||
|
|
||||||
|
public AbsCopier(S source, T target, CopyOptions copyOptions) {
|
||||||
|
this.source = source;
|
||||||
|
this.target = target;
|
||||||
|
this.copyOptions = ObjectUtil.defaultIfNull(copyOptions, CopyOptions::create);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.copier.Copier;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean拷贝,提供:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. Bean 转 Bean
|
||||||
|
* 2. Bean 转 Map
|
||||||
|
* 3. Map 转 Bean
|
||||||
|
* 4. Map 转 Map
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*
|
||||||
|
* @param <T> 目标对象类型
|
||||||
|
* @since 3.2.3
|
||||||
|
*/
|
||||||
|
public class BeanCopier<T> implements Copier<T>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Copier<T> copier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建BeanCopier
|
||||||
|
*
|
||||||
|
* @param <T> 目标Bean类型
|
||||||
|
* @param source 来源对象,可以是Bean或者Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param copyOptions 拷贝属性选项
|
||||||
|
* @return BeanCopier
|
||||||
|
*/
|
||||||
|
public static <T> BeanCopier<T> create(Object source, T target, CopyOptions copyOptions) {
|
||||||
|
return create(source, target, target.getClass(), copyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建BeanCopier
|
||||||
|
*
|
||||||
|
* @param <T> 目标Bean类型
|
||||||
|
* @param source 来源对象,可以是Bean或者Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param destType 目标的泛型类型,用于标注有泛型参数的Bean对象
|
||||||
|
* @param copyOptions 拷贝属性选项
|
||||||
|
* @return BeanCopier
|
||||||
|
*/
|
||||||
|
public static <T> BeanCopier<T> create(Object source, T target, Type destType, CopyOptions copyOptions) {
|
||||||
|
return new BeanCopier<>(source, target, destType, copyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source 来源对象,可以是Bean或者Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param targetType 目标的泛型类型,用于标注有泛型参数的Bean对象
|
||||||
|
* @param copyOptions 拷贝属性选项
|
||||||
|
*/
|
||||||
|
public BeanCopier(Object source, T target, Type targetType, CopyOptions copyOptions) {
|
||||||
|
Assert.notNull(source, "Source bean must be not null!");
|
||||||
|
Assert.notNull(target, "Target bean must be not null!");
|
||||||
|
Copier<T> copier;
|
||||||
|
if (source instanceof Map) {
|
||||||
|
if (target instanceof Map) {
|
||||||
|
//noinspection unchecked
|
||||||
|
copier = (Copier<T>) new MapToMapCopier((Map<?, ?>) source, (Map<?, ?>) target, targetType, copyOptions);
|
||||||
|
} else {
|
||||||
|
copier = new MapToBeanCopier<>((Map<?, ?>) source, target, targetType, copyOptions);
|
||||||
|
}
|
||||||
|
}else if(source instanceof ValueProvider){
|
||||||
|
//noinspection unchecked
|
||||||
|
copier = new ValueProviderToBeanCopier<>((ValueProvider<String>) source, target, targetType, copyOptions);
|
||||||
|
} else {
|
||||||
|
if (target instanceof Map) {
|
||||||
|
//noinspection unchecked
|
||||||
|
copier = (Copier<T>) new BeanToMapCopier(source, (Map<?, ?>) target, targetType, copyOptions);
|
||||||
|
} else {
|
||||||
|
copier = new BeanToBeanCopier<>(source, target, targetType, copyOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.copier = copier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T copy() {
|
||||||
|
return copier.copy();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.PropDesc;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean属性拷贝到Bean中的拷贝器
|
||||||
|
*
|
||||||
|
* @param <S> 源Bean类型
|
||||||
|
* @param <T> 目标Bean类型
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class BeanToBeanCopier<S, T> extends AbsCopier<S, T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标的类型(用于泛型类注入)
|
||||||
|
*/
|
||||||
|
private final Type targetType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source 来源Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param targetType 目标泛型类型
|
||||||
|
* @param copyOptions 拷贝选项
|
||||||
|
*/
|
||||||
|
public BeanToBeanCopier(S source, T target, Type targetType, CopyOptions copyOptions) {
|
||||||
|
super(source, target, copyOptions);
|
||||||
|
this.targetType = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T copy() {
|
||||||
|
Class<?> actualEditable = target.getClass();
|
||||||
|
if (null != copyOptions.editable) {
|
||||||
|
// 检查限制类是否为target的父类或接口
|
||||||
|
Assert.isTrue(copyOptions.editable.isInstance(target),
|
||||||
|
"Target class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
|
||||||
|
actualEditable = copyOptions.editable;
|
||||||
|
}
|
||||||
|
final Map<String, PropDesc> targetPropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);
|
||||||
|
|
||||||
|
final Map<String, PropDesc> sourcePropDescMap = BeanUtil.getBeanDesc(source.getClass()).getPropMap(copyOptions.ignoreCase);
|
||||||
|
sourcePropDescMap.forEach((sFieldName, sDesc) -> {
|
||||||
|
if (null == sFieldName || false == sDesc.isReadable(copyOptions.transientSupport)) {
|
||||||
|
// 字段空或不可读,跳过
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sFieldName = copyOptions.editFieldName(sFieldName);
|
||||||
|
// 对key做转换,转换后为null的跳过
|
||||||
|
if (null == sFieldName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略不需要拷贝的 key,
|
||||||
|
if (false == copyOptions.testKeyFilter(sFieldName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标字段可写性
|
||||||
|
final PropDesc tDesc = targetPropDescMap.get(sFieldName);
|
||||||
|
if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) {
|
||||||
|
// 字段不可写,跳过之
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查源对象属性是否过滤属性
|
||||||
|
Object sValue = sDesc.getValue(this.source);
|
||||||
|
if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标字段真实类型并转换源值
|
||||||
|
final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
|
||||||
|
//sValue = Convert.convertWithCheck(fieldType, sValue, null, this.copyOptions.ignoreError);
|
||||||
|
sValue = this.copyOptions.convertField(fieldType, sValue);
|
||||||
|
sValue = copyOptions.editFieldValue(sFieldName, sValue);
|
||||||
|
|
||||||
|
// 目标赋值
|
||||||
|
tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
|
||||||
|
});
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.PropDesc;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean属性拷贝到Map中的拷贝器
|
||||||
|
*
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class BeanToMapCopier extends AbsCopier<Object, Map> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标的Map类型(用于泛型类注入)
|
||||||
|
*/
|
||||||
|
private final Type targetType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source 来源Map
|
||||||
|
* @param target 目标Map对象
|
||||||
|
* @param targetType 目标泛型类型
|
||||||
|
* @param copyOptions 拷贝选项
|
||||||
|
*/
|
||||||
|
public BeanToMapCopier(Object source, Map target, Type targetType, CopyOptions copyOptions) {
|
||||||
|
super(source, target, copyOptions);
|
||||||
|
this.targetType = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map copy() {
|
||||||
|
Class<?> actualEditable = source.getClass();
|
||||||
|
if (null != copyOptions.editable) {
|
||||||
|
// 检查限制类是否为target的父类或接口
|
||||||
|
Assert.isTrue(copyOptions.editable.isInstance(source),
|
||||||
|
"Source class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
|
||||||
|
actualEditable = copyOptions.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, PropDesc> sourcePropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);
|
||||||
|
sourcePropDescMap.forEach((sFieldName, sDesc) -> {
|
||||||
|
if (null == sFieldName || false == sDesc.isReadable(copyOptions.transientSupport)) {
|
||||||
|
// 字段空或不可读,跳过
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sFieldName = copyOptions.editFieldName(sFieldName);
|
||||||
|
// 对key做转换,转换后为null的跳过
|
||||||
|
if (null == sFieldName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略不需要拷贝的 key,
|
||||||
|
if (false == copyOptions.testKeyFilter(sFieldName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查源对象属性是否过滤属性
|
||||||
|
Object sValue = sDesc.getValue(this.source);
|
||||||
|
if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标值真实类型并转换源值
|
||||||
|
final Type[] typeArguments = TypeUtil.getTypeArguments(this.targetType);
|
||||||
|
if(null != typeArguments){
|
||||||
|
//sValue = Convert.convertWithCheck(typeArguments[1], sValue, null, this.copyOptions.ignoreError);
|
||||||
|
sValue = this.copyOptions.convertField(typeArguments[1], sValue);
|
||||||
|
sValue = copyOptions.editFieldValue(sFieldName, sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 目标赋值
|
||||||
|
if(null != sValue || false == copyOptions.ignoreNullValue){
|
||||||
|
//noinspection unchecked
|
||||||
|
target.put(sFieldName, sValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,383 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.TypeConverter;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Editor;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Func1;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.LambdaUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性拷贝选项<br>
|
||||||
|
* 包括:<br>
|
||||||
|
* 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类<br>
|
||||||
|
* 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null<br>
|
||||||
|
* 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值<br>
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public class CopyOptions implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类<br>
|
||||||
|
* 如果目标对象是Map,源对象是Bean,则作用于源对象上
|
||||||
|
*/
|
||||||
|
protected Class<?> editable;
|
||||||
|
/**
|
||||||
|
* 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
|
||||||
|
*/
|
||||||
|
protected boolean ignoreNullValue;
|
||||||
|
/**
|
||||||
|
* 属性过滤器,断言通过的属性才会被复制<br>
|
||||||
|
* 断言参数中Field为源对象的字段对象,如果源对象为Map,使用目标对象,Object为源对象的对应值
|
||||||
|
*/
|
||||||
|
private BiPredicate<Field, Object> propertiesFilter;
|
||||||
|
/**
|
||||||
|
* 是否忽略字段注入错误
|
||||||
|
*/
|
||||||
|
protected boolean ignoreError;
|
||||||
|
/**
|
||||||
|
* 是否忽略字段大小写
|
||||||
|
*/
|
||||||
|
protected boolean ignoreCase;
|
||||||
|
/**
|
||||||
|
* 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等<br>
|
||||||
|
* 规则为,{@link Editor#edit(Object)}属性为源对象的字段名称或key,返回值为目标对象的字段名称或key
|
||||||
|
*/
|
||||||
|
private Editor<String> fieldNameEditor;
|
||||||
|
/**
|
||||||
|
* 字段属性值编辑器,用于自定义属性值转换规则,例如null转""等
|
||||||
|
*/
|
||||||
|
protected BiFunction<String, Object, Object> fieldValueEditor;
|
||||||
|
/**
|
||||||
|
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||||
|
*/
|
||||||
|
protected boolean transientSupport = true;
|
||||||
|
/**
|
||||||
|
* 是否覆盖目标值,如果不覆盖,会先读取目标对象的值,非{@code null}则写,否则忽略。如果覆盖,则不判断直接写
|
||||||
|
*/
|
||||||
|
protected boolean override = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 源对象和目标对象都是 {@code Map} 时, 需要忽略的源对象 {@code Map} key
|
||||||
|
*/
|
||||||
|
private Set<String> ignoreKeySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义类型转换器,默认使用全局万能转换器转换
|
||||||
|
*/
|
||||||
|
protected TypeConverter converter = (type, value) -> {
|
||||||
|
if(null == value){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String name = value.getClass().getName();
|
||||||
|
if(ArrayUtil.contains(new String[]{"cn.hutool.json.JSONObject", "cn.hutool.json.JSONArray"}, name)){
|
||||||
|
// 由于设计缺陷导致JSON转Bean时无法使用自定义的反序列化器,此处采用反射方式修复bug,此类问题会在6.x解决
|
||||||
|
return ReflectUtil.invoke(value, "toBean", ObjectUtil.defaultIfNull(type, Object.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.convertWithCheck(type, value, null, ignoreError);
|
||||||
|
};
|
||||||
|
|
||||||
|
//region create
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建拷贝选项
|
||||||
|
*
|
||||||
|
* @return 拷贝选项
|
||||||
|
*/
|
||||||
|
public static CopyOptions create() {
|
||||||
|
return new CopyOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建拷贝选项
|
||||||
|
*
|
||||||
|
* @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
|
||||||
|
* @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
|
||||||
|
* @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值
|
||||||
|
* @return 拷贝选项
|
||||||
|
*/
|
||||||
|
public static CopyOptions create(Class<?> editable, boolean ignoreNullValue, String... ignoreProperties) {
|
||||||
|
return new CopyOptions(editable, ignoreNullValue, ignoreProperties);
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造拷贝选项
|
||||||
|
*/
|
||||||
|
public CopyOptions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造拷贝选项
|
||||||
|
*
|
||||||
|
* @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
|
||||||
|
* @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
|
||||||
|
* @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
|
||||||
|
*/
|
||||||
|
public CopyOptions(Class<?> editable, boolean ignoreNullValue, String... ignoreProperties) {
|
||||||
|
this.propertiesFilter = (f, v) -> true;
|
||||||
|
this.editable = editable;
|
||||||
|
this.ignoreNullValue = ignoreNullValue;
|
||||||
|
this.setIgnoreProperties(ignoreProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
|
||||||
|
*
|
||||||
|
* @param editable 限制的类或接口
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setEditable(Class<?> editable) {
|
||||||
|
this.editable = editable;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
|
||||||
|
*
|
||||||
|
* @param ignoreNullVall 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setIgnoreNullValue(boolean ignoreNullVall) {
|
||||||
|
this.ignoreNullValue = ignoreNullVall;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置忽略空值,当源对象的值为null时,忽略而不注入此值
|
||||||
|
*
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 4.5.7
|
||||||
|
*/
|
||||||
|
public CopyOptions ignoreNullValue() {
|
||||||
|
return setIgnoreNullValue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性过滤器,断言通过的属性才会被复制<br>
|
||||||
|
* {@link BiPredicate#test(Object, Object)}返回{@code true}则属性通过,{@code false}不通过,抛弃之
|
||||||
|
*
|
||||||
|
* @param propertiesFilter 属性过滤器
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setPropertiesFilter(BiPredicate<Field, Object> propertiesFilter) {
|
||||||
|
this.propertiesFilter = propertiesFilter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
|
||||||
|
*
|
||||||
|
* @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setIgnoreProperties(String... ignoreProperties) {
|
||||||
|
this.ignoreKeySet = CollUtil.newHashSet(ignoreProperties);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值,Lambda方式
|
||||||
|
*
|
||||||
|
* @param <P> 参数类型
|
||||||
|
* @param <R> 返回值类型
|
||||||
|
* @param funcs 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <P, R> CopyOptions setIgnoreProperties(Func1<P, R>... funcs) {
|
||||||
|
this.ignoreKeySet = ArrayUtil.mapToSet(funcs, LambdaUtil::getFieldName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略字段的注入错误
|
||||||
|
*
|
||||||
|
* @param ignoreError 是否忽略注入错误
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setIgnoreError(boolean ignoreError) {
|
||||||
|
this.ignoreError = ignoreError;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置忽略字段的注入错误
|
||||||
|
*
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 4.5.7
|
||||||
|
*/
|
||||||
|
public CopyOptions ignoreError() {
|
||||||
|
return setIgnoreError(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略字段的大小写
|
||||||
|
*
|
||||||
|
* @param ignoreCase 是否忽略大小写
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setIgnoreCase(boolean ignoreCase) {
|
||||||
|
this.ignoreCase = ignoreCase;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置忽略字段的大小写
|
||||||
|
*
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 4.5.7
|
||||||
|
*/
|
||||||
|
public CopyOptions ignoreCase() {
|
||||||
|
return setIgnoreCase(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用<br>
|
||||||
|
* 需要注意的是,当使用ValueProvider作为数据提供者时,这个映射是相反的,即fieldMapping中key为目标Bean的名称,而value是提供者中的key
|
||||||
|
*
|
||||||
|
* @param fieldMapping 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
|
||||||
|
* @return CopyOptions
|
||||||
|
*/
|
||||||
|
public CopyOptions setFieldMapping(Map<String, String> fieldMapping) {
|
||||||
|
return setFieldNameEditor((key -> fieldMapping.getOrDefault(key, key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等<br>
|
||||||
|
* 此转换器只针对源端的字段做转换,请确认转换后与目标端字段一致<br>
|
||||||
|
* 当转换后的字段名为null时忽略这个字段<br>
|
||||||
|
* 需要注意的是,当使用ValueProvider作为数据提供者时,这个映射是相反的,即fieldMapping中key为目标Bean的名称,而value是提供者中的key
|
||||||
|
*
|
||||||
|
* @param fieldNameEditor 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public CopyOptions setFieldNameEditor(Editor<String> fieldNameEditor) {
|
||||||
|
this.fieldNameEditor = fieldNameEditor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字段属性值编辑器,用于自定义属性值转换规则,例如null转""等<br>
|
||||||
|
*
|
||||||
|
* @param fieldValueEditor 字段属性值编辑器,用于自定义属性值转换规则,例如null转""等
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 5.7.15
|
||||||
|
*/
|
||||||
|
public CopyOptions setFieldValueEditor(BiFunction<String, Object, Object> fieldValueEditor) {
|
||||||
|
this.fieldValueEditor = fieldValueEditor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑字段值
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param fieldValue 字段值
|
||||||
|
* @return 编辑后的字段值
|
||||||
|
* @since 5.7.15
|
||||||
|
*/
|
||||||
|
protected Object editFieldValue(String fieldName, Object fieldValue) {
|
||||||
|
return (null != this.fieldValueEditor) ?
|
||||||
|
this.fieldValueEditor.apply(fieldName, fieldValue) : fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||||
|
*
|
||||||
|
* @param transientSupport 是否支持
|
||||||
|
* @return this
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public CopyOptions setTransientSupport(boolean transientSupport) {
|
||||||
|
this.transientSupport = transientSupport;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否覆盖目标值,如果不覆盖,会先读取目标对象的值,为{@code null}则写,否则忽略。如果覆盖,则不判断直接写
|
||||||
|
*
|
||||||
|
* @param override 是否覆盖目标值
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public CopyOptions setOverride(boolean override) {
|
||||||
|
this.override = override;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置自定义类型转换器,默认使用全局万能转换器转换。
|
||||||
|
*
|
||||||
|
* @param converter 转换器
|
||||||
|
* @return this
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public CopyOptions setConverter(TypeConverter converter) {
|
||||||
|
this.converter = converter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用自定义转换器转换字段值<br>
|
||||||
|
* 如果自定义转换器为{@code null},则返回原值。
|
||||||
|
*
|
||||||
|
* @param targetType 目标类型
|
||||||
|
* @param fieldValue 字段值
|
||||||
|
* @return 编辑后的字段值
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
protected Object convertField(Type targetType, Object fieldValue) {
|
||||||
|
return (null != this.converter) ?
|
||||||
|
this.converter.convert(targetType, fieldValue) : fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换字段名为编辑后的字段名
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 编辑后的字段名
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
protected String editFieldName(String fieldName) {
|
||||||
|
return (null != this.fieldNameEditor) ? this.fieldNameEditor.edit(fieldName) : fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试是否保留字段,{@code true}保留,{@code false}不保留
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @param value 值
|
||||||
|
* @return 是否保留
|
||||||
|
*/
|
||||||
|
protected boolean testPropertyFilter(Field field, Object value) {
|
||||||
|
return null == this.propertiesFilter || this.propertiesFilter.test(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试是否保留key, {@code true} 不保留, {@code false} 保留
|
||||||
|
*
|
||||||
|
* @param key {@link Map} key
|
||||||
|
* @return 是否保留
|
||||||
|
*/
|
||||||
|
protected boolean testKeyFilter(Object key) {
|
||||||
|
return CollUtil.isEmpty(this.ignoreKeySet) || false == this.ignoreKeySet.contains(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.PropDesc;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapWrapper;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map属性拷贝到Bean中的拷贝器
|
||||||
|
*
|
||||||
|
* @param <T> 目标Bean类型
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class MapToBeanCopier<T> extends AbsCopier<Map<?, ?>, T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标的类型(用于泛型类注入)
|
||||||
|
*/
|
||||||
|
private final Type targetType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source 来源Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param targetType 目标泛型类型
|
||||||
|
* @param copyOptions 拷贝选项
|
||||||
|
*/
|
||||||
|
public MapToBeanCopier(Map<?, ?> source, T target, Type targetType, CopyOptions copyOptions) {
|
||||||
|
super(source, target, copyOptions);
|
||||||
|
|
||||||
|
// 针对MapWrapper特殊处理,提供的Map包装了忽略大小写的Map,则默认转Bean的时候也忽略大小写,如JSONObject
|
||||||
|
if(source instanceof MapWrapper){
|
||||||
|
final Map<?, ?> raw = ((MapWrapper<?, ?>) source).getRaw();
|
||||||
|
if(raw instanceof CaseInsensitiveMap){
|
||||||
|
copyOptions.setIgnoreCase(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.targetType = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T copy() {
|
||||||
|
Class<?> actualEditable = target.getClass();
|
||||||
|
if (null != copyOptions.editable) {
|
||||||
|
// 检查限制类是否为target的父类或接口
|
||||||
|
Assert.isTrue(copyOptions.editable.isInstance(target),
|
||||||
|
"Target class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
|
||||||
|
actualEditable = copyOptions.editable;
|
||||||
|
}
|
||||||
|
final Map<String, PropDesc> targetPropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);
|
||||||
|
|
||||||
|
this.source.forEach((sKey, sValue) -> {
|
||||||
|
if (null == sKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String sKeyStr = copyOptions.editFieldName(sKey.toString());
|
||||||
|
// 对key做转换,转换后为null的跳过
|
||||||
|
if (null == sKeyStr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略不需要拷贝的 key,
|
||||||
|
if (false == copyOptions.testKeyFilter(sKeyStr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标字段可写性
|
||||||
|
final PropDesc tDesc = findPropDesc(targetPropDescMap, sKeyStr);
|
||||||
|
if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) {
|
||||||
|
// 字段不可写,跳过之
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sKeyStr = tDesc.getFieldName();
|
||||||
|
|
||||||
|
// 检查目标是否过滤属性
|
||||||
|
if (false == copyOptions.testPropertyFilter(tDesc.getField(), sValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标字段真实类型并转换源值
|
||||||
|
final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
|
||||||
|
//Object newValue = Convert.convertWithCheck(fieldType, sValue, null, this.copyOptions.ignoreError);
|
||||||
|
Object newValue = this.copyOptions.convertField(fieldType, sValue);
|
||||||
|
newValue = copyOptions.editFieldValue(sKeyStr, newValue);
|
||||||
|
|
||||||
|
// 目标赋值
|
||||||
|
tDesc.setValue(this.target, newValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
|
||||||
|
});
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找Map对应Bean的名称<br>
|
||||||
|
* 尝试原名称、转驼峰名称、isXxx去掉is的名称
|
||||||
|
*
|
||||||
|
* @param targetPropDescMap 目标bean的属性描述Map
|
||||||
|
* @param sKeyStr 键或字段名
|
||||||
|
* @return {@link PropDesc}
|
||||||
|
*/
|
||||||
|
private PropDesc findPropDesc(Map<String, PropDesc> targetPropDescMap, String sKeyStr){
|
||||||
|
PropDesc propDesc = targetPropDescMap.get(sKeyStr);
|
||||||
|
if(null != propDesc){
|
||||||
|
return propDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转驼峰尝试查找
|
||||||
|
sKeyStr = StrUtil.toCamelCase(sKeyStr);
|
||||||
|
propDesc = targetPropDescMap.get(sKeyStr);
|
||||||
|
if(null != propDesc){
|
||||||
|
return propDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// boolean类型参数名转换尝试查找
|
||||||
|
if(sKeyStr.startsWith("is")){
|
||||||
|
sKeyStr = StrUtil.removePreAndLowerFirst(sKeyStr, 2);
|
||||||
|
propDesc = targetPropDescMap.get(sKeyStr);
|
||||||
|
return propDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map属性拷贝到Map中的拷贝器
|
||||||
|
*
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
public class MapToMapCopier extends AbsCopier<Map, Map> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标的类型(用于泛型类注入)
|
||||||
|
*/
|
||||||
|
private final Type targetType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source 来源Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param targetType 目标泛型类型
|
||||||
|
* @param copyOptions 拷贝选项
|
||||||
|
*/
|
||||||
|
public MapToMapCopier(Map source, Map target, Type targetType, CopyOptions copyOptions) {
|
||||||
|
super(source, target, copyOptions);
|
||||||
|
this.targetType = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map copy() {
|
||||||
|
this.source.forEach((sKey, sValue) -> {
|
||||||
|
if (null == sKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 忽略空值
|
||||||
|
if (true == copyOptions.ignoreNullValue && sValue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String sKeyStr = copyOptions.editFieldName(sKey.toString());
|
||||||
|
// 对key做转换,转换后为null的跳过
|
||||||
|
if (null == sKeyStr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略不需要拷贝的 key,
|
||||||
|
if (false == copyOptions.testKeyFilter(sKeyStr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object targetValue = target.get(sKeyStr);
|
||||||
|
// 非覆盖模式下,如果目标值存在,则跳过
|
||||||
|
if (false == copyOptions.override && null != targetValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标值真实类型并转换源值
|
||||||
|
final Type[] typeArguments = TypeUtil.getTypeArguments(this.targetType);
|
||||||
|
if (null != typeArguments) {
|
||||||
|
//sValue = Convert.convertWithCheck(typeArguments[1], sValue, null, this.copyOptions.ignoreError);
|
||||||
|
sValue = this.copyOptions.convertField(typeArguments[1], sValue);
|
||||||
|
sValue = copyOptions.editFieldValue(sKeyStr, sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 目标赋值
|
||||||
|
target.put(sKeyStr, sValue);
|
||||||
|
});
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 值提供者,用于提供Bean注入时参数对应值得抽象接口<br>
|
||||||
|
* 继承或匿名实例化此接口<br>
|
||||||
|
* 在Bean注入过程中,Bean获得字段名,通过外部方式根据这个字段名查找相应的字段值,然后注入Bean<br>
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @param <T> KEY类型,一般情况下为 {@link String}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ValueProvider<T>{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取值<br>
|
||||||
|
* 返回值一般需要匹配被注入类型,如果不匹配会调用默认转换 Convert#convert(Type, Object)实现转换
|
||||||
|
*
|
||||||
|
* @param key Bean对象中参数名
|
||||||
|
* @param valueType 被注入的值的类型
|
||||||
|
* @return 对应参数名的值
|
||||||
|
*/
|
||||||
|
Object value(T key, Type valueType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含指定KEY,如果不包含则忽略注入<br>
|
||||||
|
* 此接口方法单独需要实现的意义在于:有些值提供者(比如Map)key是存在的,但是value为null,此时如果需要注入这个null,需要根据此方法判断
|
||||||
|
*
|
||||||
|
* @param key Bean对象中参数名
|
||||||
|
* @return 是否包含指定KEY
|
||||||
|
*/
|
||||||
|
boolean containsKey(T key);
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.PropDesc;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ValueProvider}属性拷贝到Bean中的拷贝器
|
||||||
|
*
|
||||||
|
* @param <T> 目标Bean类型
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class ValueProviderToBeanCopier<T> extends AbsCopier<ValueProvider<String>, T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标的类型(用于泛型类注入)
|
||||||
|
*/
|
||||||
|
private final Type targetType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source 来源Map
|
||||||
|
* @param target 目标Bean对象
|
||||||
|
* @param targetType 目标泛型类型
|
||||||
|
* @param copyOptions 拷贝选项
|
||||||
|
*/
|
||||||
|
public ValueProviderToBeanCopier(ValueProvider<String> source, T target, Type targetType, CopyOptions copyOptions) {
|
||||||
|
super(source, target, copyOptions);
|
||||||
|
this.targetType = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T copy() {
|
||||||
|
Class<?> actualEditable = target.getClass();
|
||||||
|
if (null != copyOptions.editable) {
|
||||||
|
// 检查限制类是否为target的父类或接口
|
||||||
|
Assert.isTrue(copyOptions.editable.isInstance(target),
|
||||||
|
"Target class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
|
||||||
|
actualEditable = copyOptions.editable;
|
||||||
|
}
|
||||||
|
final Map<String, PropDesc> targetPropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);
|
||||||
|
|
||||||
|
targetPropDescMap.forEach((tFieldName, tDesc) -> {
|
||||||
|
if (null == tFieldName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tFieldName = copyOptions.editFieldName(tFieldName);
|
||||||
|
// 对key做转换,转换后为null的跳过
|
||||||
|
if (null == tFieldName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无字段内容跳过
|
||||||
|
if(false == source.containsKey(tFieldName)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略不需要拷贝的 key,
|
||||||
|
if (false == copyOptions.testKeyFilter(tFieldName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标字段可写性
|
||||||
|
if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) {
|
||||||
|
// 字段不可写,跳过之
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标字段真实类型
|
||||||
|
final Type fieldType = TypeUtil.getActualType(this.targetType ,tDesc.getFieldType());
|
||||||
|
|
||||||
|
// 检查目标对象属性是否过滤属性
|
||||||
|
Object sValue = source.value(tFieldName, fieldType);
|
||||||
|
if (false == copyOptions.testPropertyFilter(tDesc.getField(), sValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sValue = copyOptions.editFieldValue(tFieldName, sValue);
|
||||||
|
|
||||||
|
// 目标赋值
|
||||||
|
tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
|
||||||
|
});
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* Bean拷贝实现,包括拷贝选项等
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
|
@ -0,0 +1,100 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier.provider;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.PropDesc;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.copier.ValueProvider;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Editor;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.FuncKeyMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean的值提供者
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class BeanValueProvider implements ValueProvider<String> {
|
||||||
|
|
||||||
|
private final Object source;
|
||||||
|
private final boolean ignoreError;
|
||||||
|
final Map<String, PropDesc> sourcePdMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bean Bean
|
||||||
|
* @param ignoreCase 是否忽略字段大小写
|
||||||
|
* @param ignoreError 是否忽略字段值读取错误
|
||||||
|
*/
|
||||||
|
public BeanValueProvider(Object bean, boolean ignoreCase, boolean ignoreError) {
|
||||||
|
this(bean, ignoreCase, ignoreError, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bean Bean
|
||||||
|
* @param ignoreCase 是否忽略字段大小写
|
||||||
|
* @param ignoreError 是否忽略字段值读取错误
|
||||||
|
* @param keyEditor 键编辑器
|
||||||
|
*/
|
||||||
|
public BeanValueProvider(Object bean, boolean ignoreCase, boolean ignoreError, Editor<String> keyEditor) {
|
||||||
|
this.source = bean;
|
||||||
|
this.ignoreError = ignoreError;
|
||||||
|
final Map<String, PropDesc> sourcePdMap = BeanUtil.getBeanDesc(source.getClass()).getPropMap(ignoreCase);
|
||||||
|
// issue#2202@Github
|
||||||
|
// 如果用户定义了键编辑器,则提供的map中的数据必须全部转换key
|
||||||
|
// issue#I5VRHW@Gitee 使Function可以被序列化
|
||||||
|
this.sourcePdMap = new FuncKeyMap<>(new HashMap<>(sourcePdMap.size(), 1), (Function<Object, String> & Serializable)(key) -> {
|
||||||
|
if (ignoreCase && key instanceof CharSequence) {
|
||||||
|
key = key.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
if (null != keyEditor) {
|
||||||
|
key = keyEditor.edit(key.toString());
|
||||||
|
}
|
||||||
|
return key.toString();
|
||||||
|
});
|
||||||
|
this.sourcePdMap.putAll(sourcePdMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value(String key, Type valueType) {
|
||||||
|
final PropDesc sourcePd = getPropDesc(key, valueType);
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
if (null != sourcePd) {
|
||||||
|
result = sourcePd.getValue(this.source, valueType, this.ignoreError);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(String key) {
|
||||||
|
final PropDesc sourcePd = getPropDesc(key, null);
|
||||||
|
|
||||||
|
// 字段描述不存在或忽略读的情况下,表示不存在
|
||||||
|
return null != sourcePd && sourcePd.isReadable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得属性描述
|
||||||
|
*
|
||||||
|
* @param key 字段名
|
||||||
|
* @param valueType 值类型,用于判断是否为Boolean,可以为null
|
||||||
|
* @return 属性描述
|
||||||
|
*/
|
||||||
|
private PropDesc getPropDesc(String key, Type valueType) {
|
||||||
|
PropDesc sourcePd = sourcePdMap.get(key);
|
||||||
|
if (null == sourcePd && (null == valueType || Boolean.class == valueType || boolean.class == valueType)) {
|
||||||
|
//boolean类型字段字段名支持两种方式
|
||||||
|
sourcePd = sourcePdMap.get(StrUtil.upperFirstAndAddPre(key, "is"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourcePd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier.provider;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.DynaBean;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.bean.copier.ValueProvider;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DynaBean值提供者
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public class DynaBeanValueProvider implements ValueProvider<String> {
|
||||||
|
|
||||||
|
private final DynaBean dynaBean;
|
||||||
|
private final boolean ignoreError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param dynaBean DynaBean
|
||||||
|
* @param ignoreError 是否忽略错误
|
||||||
|
*/
|
||||||
|
public DynaBeanValueProvider(DynaBean dynaBean, boolean ignoreError) {
|
||||||
|
this.dynaBean = dynaBean;
|
||||||
|
this.ignoreError = ignoreError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value(String key, Type valueType) {
|
||||||
|
final Object value = dynaBean.get(key);
|
||||||
|
return Convert.convertWithCheck(valueType, value, null, this.ignoreError);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(String key) {
|
||||||
|
return dynaBean.containsProp(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* Bean值提供者方式封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean.copier.provider;
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* Bean相关操作,包括Bean信息描述,Bean路径表达式、动态Bean、Bean工具等
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.bean;
|
|
@ -0,0 +1,19 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.builder;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建造者模式接口定义
|
||||||
|
*
|
||||||
|
* @param <T> 建造对象类型
|
||||||
|
* @author Looly
|
||||||
|
* @since 4.2.2
|
||||||
|
*/
|
||||||
|
public interface Builder<T> extends Serializable{
|
||||||
|
/**
|
||||||
|
* 构建
|
||||||
|
*
|
||||||
|
* @return 被构建的对象
|
||||||
|
*/
|
||||||
|
T build();
|
||||||
|
}
|
|
@ -0,0 +1,975 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.builder;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于构建 {@link java.lang.Comparable#compareTo(Object)} 方法的辅助工具
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 在Bean对象中,所有相关字段都参与比对,继承的字段不参与。使用方法如下:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class MyClass {
|
||||||
|
* String field1;
|
||||||
|
* int field2;
|
||||||
|
* boolean field3;
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public int compareTo(Object o) {
|
||||||
|
* MyClass myClass = (MyClass) o;
|
||||||
|
* return new CompareToBuilder()
|
||||||
|
* .appendSuper(super.compareTo(o)
|
||||||
|
* .append(this.field1, myClass.field1)
|
||||||
|
* .append(this.field2, myClass.field2)
|
||||||
|
* .append(this.field3, myClass.field3)
|
||||||
|
* .toComparison();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* 字段值按照顺序比较,如果某个字段返回非0结果,比较终止,使用{@code toComparison()}返回结果,后续比较忽略。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 也可以使用{@link #reflectionCompare(Object, Object) reflectionCompare} 方法通过反射比较字段,使用方法如下:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public int compareTo(Object o) {
|
||||||
|
* return CompareToBuilder.reflectionCompare(this, o);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*TODO 待整理
|
||||||
|
* 来自于Apache-Commons-Lang3
|
||||||
|
* @author looly,Apache-Commons
|
||||||
|
* @since 4.2.2
|
||||||
|
*/
|
||||||
|
public class CompareToBuilder implements Builder<Integer> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 当前比较状态 */
|
||||||
|
private int comparison;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,构造后调用append方法增加比较项,然后调用{@link #toComparison()}获取结果
|
||||||
|
*/
|
||||||
|
public CompareToBuilder() {
|
||||||
|
comparison = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* 通过反射比较两个Bean对象,对象字段可以为private。比较规则如下:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>static字段不比较</li>
|
||||||
|
* <li>Transient字段不参与比较</li>
|
||||||
|
* <li>父类字段参与比较</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*<p>
|
||||||
|
*如果被比较的两个对象都为<code>null</code>,被认为相同。
|
||||||
|
*
|
||||||
|
* @param lhs 第一个对象
|
||||||
|
* @param rhs 第二个对象
|
||||||
|
* @return a negative integer, zero, or a positive integer as <code>lhs</code>
|
||||||
|
* is less than, equal to, or greater than <code>rhs</code>
|
||||||
|
* @throws NullPointerException if either (but not both) parameters are
|
||||||
|
* <code>null</code>
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
*/
|
||||||
|
public static int reflectionCompare(final Object lhs, final Object rhs) {
|
||||||
|
return reflectionCompare(lhs, rhs, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Compares two <code>Object</code>s via reflection.</p>
|
||||||
|
*
|
||||||
|
* <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
|
||||||
|
* is used to bypass normal access control checks. This will fail under a
|
||||||
|
* security manager unless the appropriate permissions are set.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Static fields will not be compared</li>
|
||||||
|
* <li>If <code>compareTransients</code> is <code>true</code>,
|
||||||
|
* compares transient members. Otherwise ignores them, as they
|
||||||
|
* are likely derived fields.</li>
|
||||||
|
* <li>Superclass fields will be compared</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
|
||||||
|
* they are considered equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @param compareTransients whether to compare transient fields
|
||||||
|
* @return a negative integer, zero, or a positive integer as <code>lhs</code>
|
||||||
|
* is less than, equal to, or greater than <code>rhs</code>
|
||||||
|
* @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
|
||||||
|
* (but not both) is <code>null</code>
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
*/
|
||||||
|
public static int reflectionCompare(final Object lhs, final Object rhs, final boolean compareTransients) {
|
||||||
|
return reflectionCompare(lhs, rhs, compareTransients, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Compares two <code>Object</code>s via reflection.</p>
|
||||||
|
*
|
||||||
|
* <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
|
||||||
|
* is used to bypass normal access control checks. This will fail under a
|
||||||
|
* security manager unless the appropriate permissions are set.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Static fields will not be compared</li>
|
||||||
|
* <li>If <code>compareTransients</code> is <code>true</code>,
|
||||||
|
* compares transient members. Otherwise ignores them, as they
|
||||||
|
* are likely derived fields.</li>
|
||||||
|
* <li>Superclass fields will be compared</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
|
||||||
|
* they are considered equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @param excludeFields Collection of String fields to exclude
|
||||||
|
* @return a negative integer, zero, or a positive integer as <code>lhs</code>
|
||||||
|
* is less than, equal to, or greater than <code>rhs</code>
|
||||||
|
* @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
|
||||||
|
* (but not both) is <code>null</code>
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
|
||||||
|
return reflectionCompare(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Compares two <code>Object</code>s via reflection.</p>
|
||||||
|
*
|
||||||
|
* <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
|
||||||
|
* is used to bypass normal access control checks. This will fail under a
|
||||||
|
* security manager unless the appropriate permissions are set.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Static fields will not be compared</li>
|
||||||
|
* <li>If <code>compareTransients</code> is <code>true</code>,
|
||||||
|
* compares transient members. Otherwise ignores them, as they
|
||||||
|
* are likely derived fields.</li>
|
||||||
|
* <li>Superclass fields will be compared</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
|
||||||
|
* they are considered equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @param excludeFields array of fields to exclude
|
||||||
|
* @return a negative integer, zero, or a positive integer as <code>lhs</code>
|
||||||
|
* is less than, equal to, or greater than <code>rhs</code>
|
||||||
|
* @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
|
||||||
|
* (but not both) is <code>null</code>
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
|
||||||
|
return reflectionCompare(lhs, rhs, false, null, excludeFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Compares two <code>Object</code>s via reflection.</p>
|
||||||
|
*
|
||||||
|
* <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
|
||||||
|
* is used to bypass normal access control checks. This will fail under a
|
||||||
|
* security manager unless the appropriate permissions are set.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Static fields will not be compared</li>
|
||||||
|
* <li>If the <code>compareTransients</code> is <code>true</code>,
|
||||||
|
* compares transient members. Otherwise ignores them, as they
|
||||||
|
* are likely derived fields.</li>
|
||||||
|
* <li>Compares superclass fields up to and including <code>reflectUpToClass</code>.
|
||||||
|
* If <code>reflectUpToClass</code> is <code>null</code>, compares all superclass fields.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
|
||||||
|
* they are considered equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @param compareTransients whether to compare transient fields
|
||||||
|
* @param reflectUpToClass last superclass for which fields are compared
|
||||||
|
* @param excludeFields fields to exclude
|
||||||
|
* @return a negative integer, zero, or a positive integer as <code>lhs</code>
|
||||||
|
* is less than, equal to, or greater than <code>rhs</code>
|
||||||
|
* @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
|
||||||
|
* (but not both) is <code>null</code>
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
* @since 2.2 (2.0 as <code>reflectionCompare(Object, Object, boolean, Class)</code>)
|
||||||
|
*/
|
||||||
|
public static int reflectionCompare(
|
||||||
|
final Object lhs,
|
||||||
|
final Object rhs,
|
||||||
|
final boolean compareTransients,
|
||||||
|
final Class<?> reflectUpToClass,
|
||||||
|
final String... excludeFields) {
|
||||||
|
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (lhs == null || rhs == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
Class<?> lhsClazz = lhs.getClass();
|
||||||
|
if (!lhsClazz.isInstance(rhs)) {
|
||||||
|
throw new ClassCastException();
|
||||||
|
}
|
||||||
|
final CompareToBuilder compareToBuilder = new CompareToBuilder();
|
||||||
|
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
|
||||||
|
while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
|
||||||
|
lhsClazz = lhsClazz.getSuperclass();
|
||||||
|
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
|
||||||
|
}
|
||||||
|
return compareToBuilder.toComparison();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to <code>builder</code> the comparison of <code>lhs</code>
|
||||||
|
* to <code>rhs</code> using the fields defined in <code>clazz</code>.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @param clazz <code>Class</code> that defines fields to be compared
|
||||||
|
* @param builder <code>CompareToBuilder</code> to append to
|
||||||
|
* @param useTransients whether to compare transient fields
|
||||||
|
* @param excludeFields fields to exclude
|
||||||
|
*/
|
||||||
|
private static void reflectionAppend(
|
||||||
|
final Object lhs,
|
||||||
|
final Object rhs,
|
||||||
|
final Class<?> clazz,
|
||||||
|
final CompareToBuilder builder,
|
||||||
|
final boolean useTransients,
|
||||||
|
final String[] excludeFields) {
|
||||||
|
|
||||||
|
final Field[] fields = clazz.getDeclaredFields();
|
||||||
|
AccessibleObject.setAccessible(fields, true);
|
||||||
|
for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
|
||||||
|
final Field f = fields[i];
|
||||||
|
if (false == ArrayUtil.contains(excludeFields, f.getName())
|
||||||
|
&& (f.getName().indexOf('$') == -1)
|
||||||
|
&& (useTransients || !Modifier.isTransient(f.getModifiers()))
|
||||||
|
&& (!Modifier.isStatic(f.getModifiers()))) {
|
||||||
|
try {
|
||||||
|
builder.append(f.get(lhs), f.get(rhs));
|
||||||
|
} catch (final IllegalAccessException e) {
|
||||||
|
// This can't happen. Would get a Security exception instead.
|
||||||
|
// Throw a runtime exception in case the impossible happens.
|
||||||
|
throw new InternalError("Unexpected IllegalAccessException");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the <code>compareTo(Object)</code>
|
||||||
|
* result of the superclass.</p>
|
||||||
|
*
|
||||||
|
* @param superCompareTo result of calling <code>super.compareTo(Object)</code>
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public CompareToBuilder appendSuper(final int superCompareTo) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = superCompareTo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>Object</code>s.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if <code>lhs == rhs</code></li>
|
||||||
|
* <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>,
|
||||||
|
* a <code>null</code> object is less than a non-<code>null</code> object</li>
|
||||||
|
* <li>Check the object contents</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p><code>lhs</code> must either be an array or implement {@link Comparable}.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final Object lhs, final Object rhs) {
|
||||||
|
return append(lhs, rhs, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>Object</code>s.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if <code>lhs == rhs</code></li>
|
||||||
|
* <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>,
|
||||||
|
* a <code>null</code> object is less than a non-<code>null</code> object</li>
|
||||||
|
* <li>Check the object contents</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>If <code>lhs</code> is an array, array comparison methods will be used.
|
||||||
|
* Otherwise <code>comparator</code> will be used to compare the objects.
|
||||||
|
* If <code>comparator</code> is <code>null</code>, <code>lhs</code> must
|
||||||
|
* implement {@link Comparable} instead.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand object
|
||||||
|
* @param rhs right-hand object
|
||||||
|
* @param comparator <code>Comparator</code> used to compare the objects,
|
||||||
|
* <code>null</code> means treat lhs as <code>Comparable</code>
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.getClass().isArray()) {
|
||||||
|
// switch on type of array, to dispatch to the correct handler
|
||||||
|
// handles multi dimensional arrays
|
||||||
|
// throws a ClassCastException if rhs is not the correct array type
|
||||||
|
if (lhs instanceof long[]) {
|
||||||
|
append((long[]) lhs, (long[]) rhs);
|
||||||
|
} else if (lhs instanceof int[]) {
|
||||||
|
append((int[]) lhs, (int[]) rhs);
|
||||||
|
} else if (lhs instanceof short[]) {
|
||||||
|
append((short[]) lhs, (short[]) rhs);
|
||||||
|
} else if (lhs instanceof char[]) {
|
||||||
|
append((char[]) lhs, (char[]) rhs);
|
||||||
|
} else if (lhs instanceof byte[]) {
|
||||||
|
append((byte[]) lhs, (byte[]) rhs);
|
||||||
|
} else if (lhs instanceof double[]) {
|
||||||
|
append((double[]) lhs, (double[]) rhs);
|
||||||
|
} else if (lhs instanceof float[]) {
|
||||||
|
append((float[]) lhs, (float[]) rhs);
|
||||||
|
} else if (lhs instanceof boolean[]) {
|
||||||
|
append((boolean[]) lhs, (boolean[]) rhs);
|
||||||
|
} else {
|
||||||
|
// not an array of primitives
|
||||||
|
// throws a ClassCastException if rhs is not an array
|
||||||
|
append((Object[]) lhs, (Object[]) rhs, comparator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the simple case, not an array, just test the element
|
||||||
|
if (comparator == null) {
|
||||||
|
@SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
|
||||||
|
final Comparable<Object> comparable = (Comparable<Object>) lhs;
|
||||||
|
comparison = comparable.compareTo(rhs);
|
||||||
|
} else {
|
||||||
|
@SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
|
||||||
|
final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
|
||||||
|
comparison = comparator2.compare(lhs, rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>long</code>s.
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final long lhs, final long rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = (Long.compare(lhs, rhs));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>int</code>s.
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final int lhs, final int rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = (Integer.compare(lhs, rhs));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>short</code>s.
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final short lhs, final short rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = (Short.compare(lhs, rhs));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>char</code>s.
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final char lhs, final char rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = (Character.compare(lhs, rhs));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>byte</code>s.
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final byte lhs, final byte rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = (Byte.compare(lhs, rhs));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>double</code>s.</p>
|
||||||
|
*
|
||||||
|
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
||||||
|
*
|
||||||
|
* <p>It is compatible with the hash code generated by
|
||||||
|
* <code>HashCodeBuilder</code>.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final double lhs, final double rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = Double.compare(lhs, rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>float</code>s.</p>
|
||||||
|
*
|
||||||
|
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
||||||
|
*
|
||||||
|
* <p>It is compatible with the hash code generated by
|
||||||
|
* <code>HashCodeBuilder</code>.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final float lhs, final float rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
comparison = Float.compare(lhs, rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends to the <code>builder</code> the comparison of
|
||||||
|
* two <code>booleans</code>s.
|
||||||
|
*
|
||||||
|
* @param lhs left-hand value
|
||||||
|
* @param rhs right-hand value
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final boolean lhs, final boolean rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == false) {
|
||||||
|
comparison = -1;
|
||||||
|
} else {
|
||||||
|
comparison = +1;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>Object</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a short length array is less than a long length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>This method will also will be called for the top level of multi-dimensional,
|
||||||
|
* ragged, and multi-typed arrays.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final Object[] lhs, final Object[] rhs) {
|
||||||
|
return append(lhs, rhs, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>Object</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a short length array is less than a long length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>This method will also will be called for the top level of multi-dimensional,
|
||||||
|
* ragged, and multi-typed arrays.</p>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @param comparator <code>Comparator</code> to use to compare the array elements,
|
||||||
|
* <code>null</code> means to treat <code>lhs</code> elements as <code>Comparable</code>.
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
* @throws ClassCastException if <code>rhs</code> is not assignment-compatible
|
||||||
|
* with <code>lhs</code>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final Object[] lhs, final Object[] rhs, final Comparator<?> comparator) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i], comparator);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>long</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(long, long)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final long[] lhs, final long[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>int</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(int, int)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final int[] lhs, final int[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>short</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(short, short)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final short[] lhs, final short[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>char</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(char, char)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final char[] lhs, final char[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>byte</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(byte, byte)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final byte[] lhs, final byte[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>double</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(double, double)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final double[] lhs, final double[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>float</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(float, float)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final float[] lhs, final float[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends to the <code>builder</code> the deep comparison of
|
||||||
|
* two <code>boolean</code> arrays.</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Check if arrays are the same using <code>==</code></li>
|
||||||
|
* <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
|
||||||
|
* <li>Check array length, a shorter length array is less than a longer length array</li>
|
||||||
|
* <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param lhs left-hand array
|
||||||
|
* @param rhs right-hand array
|
||||||
|
* @return this - used to chain append calls
|
||||||
|
*/
|
||||||
|
public CompareToBuilder append(final boolean[] lhs, final boolean[] rhs) {
|
||||||
|
if (comparison != 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null) {
|
||||||
|
comparison = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (rhs == null) {
|
||||||
|
comparison = +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
comparison = (lhs.length < rhs.length) ? -1 : +1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lhs.length && comparison == 0; i++) {
|
||||||
|
append(lhs[i], rhs[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Returns a negative integer, a positive integer, or zero as
|
||||||
|
* the <code>builder</code> has judged the "left-hand" side
|
||||||
|
* as less than, greater than, or equal to the "right-hand"
|
||||||
|
* side.
|
||||||
|
*
|
||||||
|
* @return final comparison result
|
||||||
|
* @see #build()
|
||||||
|
*/
|
||||||
|
public int toComparison() {
|
||||||
|
return comparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a negative Integer, a positive Integer, or zero as
|
||||||
|
* the <code>builder</code> has judged the "left-hand" side
|
||||||
|
* as less than, greater than, or equal to the "right-hand"
|
||||||
|
* side.
|
||||||
|
*
|
||||||
|
* @return final comparison result as an Integer
|
||||||
|
* @see #toComparison()
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer build() {
|
||||||
|
return toComparison();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,563 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.builder;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Pair;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@link Object#equals(Object)} 方法的构建器</p>
|
||||||
|
*
|
||||||
|
* <p>两个对象equals必须保证hashCode值相等,hashCode值相等不能保证一定equals</p>
|
||||||
|
*
|
||||||
|
* <p>使用方法如下:</p>
|
||||||
|
* <pre>
|
||||||
|
* public boolean equals(Object obj) {
|
||||||
|
* if (obj == null) { return false; }
|
||||||
|
* if (obj == this) { return true; }
|
||||||
|
* if (obj.getClass() != getClass()) {
|
||||||
|
* return false;
|
||||||
|
* }
|
||||||
|
* MyClass rhs = (MyClass) obj;
|
||||||
|
* return new EqualsBuilder()
|
||||||
|
* .appendSuper(super.equals(obj))
|
||||||
|
* .append(field1, rhs.field1)
|
||||||
|
* .append(field2, rhs.field2)
|
||||||
|
* .append(field3, rhs.field3)
|
||||||
|
* .isEquals();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p> 我们也可以通过反射判断所有字段是否equals:</p>
|
||||||
|
* <pre>
|
||||||
|
* public boolean equals(Object obj) {
|
||||||
|
* return EqualsBuilder.reflectionEquals(this, obj);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* 来自Apache Commons Lang改造
|
||||||
|
*/
|
||||||
|
public class EqualsBuilder implements Builder<Boolean> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the registry of object pairs being traversed by the reflection
|
||||||
|
* methods in the current thread.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Set the registry of objects being traversed
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
static Set<Pair<IDKey, IDKey>> getRegistry() {
|
||||||
|
return REGISTRY.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Converters value pair into a register pair.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs {@code this} object
|
||||||
|
* @param rhs the other object
|
||||||
|
* @return the pair
|
||||||
|
*/
|
||||||
|
static Pair<IDKey, IDKey> getRegisterPair(final Object lhs, final Object rhs) {
|
||||||
|
final IDKey left = new IDKey(lhs);
|
||||||
|
final IDKey right = new IDKey(rhs);
|
||||||
|
return new Pair<>(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns {@code true} if the registry contains the given object pair.
|
||||||
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
|
* Objects might be swapped therefore a check is needed if the object pair
|
||||||
|
* is registered in given or swapped order.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs {@code this} object to lookup in registry
|
||||||
|
* @param rhs the other object to lookup on registry
|
||||||
|
* @return boolean {@code true} if the registry contains the given object.
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
static boolean isRegistered(final Object lhs, final Object rhs) {
|
||||||
|
final Set<Pair<IDKey, IDKey>> registry = getRegistry();
|
||||||
|
final Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
|
||||||
|
final Pair<IDKey, IDKey> swappedPair = new Pair<>(pair.getKey(), pair.getValue());
|
||||||
|
|
||||||
|
return registry != null
|
||||||
|
&& (registry.contains(pair) || registry.contains(swappedPair));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Registers the given object pair.
|
||||||
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs {@code this} object to register
|
||||||
|
* @param rhs the other object to register
|
||||||
|
*/
|
||||||
|
static void register(final Object lhs, final Object rhs) {
|
||||||
|
synchronized (EqualsBuilder.class) {
|
||||||
|
if (getRegistry() == null) {
|
||||||
|
REGISTRY.set(new HashSet<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<Pair<IDKey, IDKey>> registry = getRegistry();
|
||||||
|
final Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
|
||||||
|
registry.add(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Unregisters the given object pair.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
|
*
|
||||||
|
* @param lhs {@code this} object to unregister
|
||||||
|
* @param rhs the other object to unregister
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
static void unregister(final Object lhs, final Object rhs) {
|
||||||
|
Set<Pair<IDKey, IDKey>> registry = getRegistry();
|
||||||
|
if (registry != null) {
|
||||||
|
final Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
|
||||||
|
registry.remove(pair);
|
||||||
|
synchronized (EqualsBuilder.class) {
|
||||||
|
//read again
|
||||||
|
registry = getRegistry();
|
||||||
|
if (registry != null && registry.isEmpty()) {
|
||||||
|
REGISTRY.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否equals,此值随着构建会变更,默认true
|
||||||
|
*/
|
||||||
|
private boolean isEquals = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,初始状态值为true
|
||||||
|
*/
|
||||||
|
public EqualsBuilder() {
|
||||||
|
// do nothing for now.
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>反射检查两个对象是否equals,此方法检查对象及其父对象的属性(包括私有属性)是否equals</p>
|
||||||
|
*
|
||||||
|
* @param lhs 此对象
|
||||||
|
* @param rhs 另一个对象
|
||||||
|
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
||||||
|
* @return 两个对象是否equals,是返回{@code true}
|
||||||
|
*/
|
||||||
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
|
||||||
|
return reflectionEquals(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>反射检查两个对象是否equals,此方法检查对象及其父对象的属性(包括私有属性)是否equals</p>
|
||||||
|
*
|
||||||
|
* @param lhs 此对象
|
||||||
|
* @param rhs 另一个对象
|
||||||
|
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
||||||
|
* @return 两个对象是否equals,是返回{@code true}
|
||||||
|
*/
|
||||||
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final String... excludeFields) {
|
||||||
|
return reflectionEquals(lhs, rhs, false, null, excludeFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This method uses reflection to determine if the two {@code Object}s
|
||||||
|
* are equal.</p>
|
||||||
|
*
|
||||||
|
* <p>It uses {@code AccessibleObject.setAccessible} to gain access to private
|
||||||
|
* fields. This means that it will throw a security exception if run under
|
||||||
|
* a security manager, if the permissions are not set up correctly. It is also
|
||||||
|
* not as efficient as testing explicitly. Non-primitive fields are compared using
|
||||||
|
* {@code equals()}.</p>
|
||||||
|
*
|
||||||
|
* <p>If the TestTransients parameter is set to {@code true}, transient
|
||||||
|
* members will be tested, otherwise they are ignored, as they are likely
|
||||||
|
* derived fields, and not part of the value of the {@code Object}.</p>
|
||||||
|
*
|
||||||
|
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
||||||
|
*
|
||||||
|
* @param lhs {@code this} object
|
||||||
|
* @param rhs the other object
|
||||||
|
* @param testTransients whether to include transient fields
|
||||||
|
* @return {@code true} if the two Objects have tested equals.
|
||||||
|
*/
|
||||||
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients) {
|
||||||
|
return reflectionEquals(lhs, rhs, testTransients, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This method uses reflection to determine if the two {@code Object}s
|
||||||
|
* are equal.</p>
|
||||||
|
*
|
||||||
|
* <p>It uses {@code AccessibleObject.setAccessible} to gain access to private
|
||||||
|
* fields. This means that it will throw a security exception if run under
|
||||||
|
* a security manager, if the permissions are not set up correctly. It is also
|
||||||
|
* not as efficient as testing explicitly. Non-primitive fields are compared using
|
||||||
|
* {@code equals()}.</p>
|
||||||
|
*
|
||||||
|
* <p>If the testTransients parameter is set to {@code true}, transient
|
||||||
|
* members will be tested, otherwise they are ignored, as they are likely
|
||||||
|
* derived fields, and not part of the value of the {@code Object}.</p>
|
||||||
|
*
|
||||||
|
* <p>Static fields will not be included. Superclass fields will be appended
|
||||||
|
* up to and including the specified superclass. A null superclass is treated
|
||||||
|
* as java.lang.Object.</p>
|
||||||
|
*
|
||||||
|
* @param lhs {@code this} object
|
||||||
|
* @param rhs the other object
|
||||||
|
* @param testTransients whether to include transient fields
|
||||||
|
* @param reflectUpToClass the superclass to reflect up to (inclusive),
|
||||||
|
* may be {@code null}
|
||||||
|
* @param excludeFields array of field names to exclude from testing
|
||||||
|
* @return {@code true} if the two Objects have tested equals.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients, final Class<?> reflectUpToClass,
|
||||||
|
final String... excludeFields) {
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lhs == null || rhs == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Find the leaf class since there may be transients in the leaf
|
||||||
|
// class or in classes between the leaf and root.
|
||||||
|
// If we are not testing transients or a subclass has no ivars,
|
||||||
|
// then a subclass can test equals to a superclass.
|
||||||
|
final Class<?> lhsClass = lhs.getClass();
|
||||||
|
final Class<?> rhsClass = rhs.getClass();
|
||||||
|
Class<?> testClass;
|
||||||
|
if (lhsClass.isInstance(rhs)) {
|
||||||
|
testClass = lhsClass;
|
||||||
|
if (!rhsClass.isInstance(lhs)) {
|
||||||
|
// rhsClass is a subclass of lhsClass
|
||||||
|
testClass = rhsClass;
|
||||||
|
}
|
||||||
|
} else if (rhsClass.isInstance(lhs)) {
|
||||||
|
testClass = rhsClass;
|
||||||
|
if (!lhsClass.isInstance(rhs)) {
|
||||||
|
// lhsClass is a subclass of rhsClass
|
||||||
|
testClass = lhsClass;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The two classes are not related.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final EqualsBuilder equalsBuilder = new EqualsBuilder();
|
||||||
|
try {
|
||||||
|
if (testClass.isArray()) {
|
||||||
|
equalsBuilder.append(lhs, rhs);
|
||||||
|
} else {
|
||||||
|
reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
|
||||||
|
while (testClass.getSuperclass() != null && testClass != reflectUpToClass) {
|
||||||
|
testClass = testClass.getSuperclass();
|
||||||
|
reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
// In this case, we tried to test a subclass vs. a superclass and
|
||||||
|
// the subclass has ivars or the ivars are transient and
|
||||||
|
// we are testing transients.
|
||||||
|
// If a subclass has ivars that we are trying to test them, we get an
|
||||||
|
// exception and we know that the objects are not equal.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return equalsBuilder.isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends the fields and values defined by the given object of the
|
||||||
|
* given Class.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand object
|
||||||
|
* @param rhs the right hand object
|
||||||
|
* @param clazz the class to append details of
|
||||||
|
* @param builder the builder to append to
|
||||||
|
* @param useTransients whether to test transient fields
|
||||||
|
* @param excludeFields array of field names to exclude from testing
|
||||||
|
*/
|
||||||
|
private static void reflectionAppend(
|
||||||
|
final Object lhs,
|
||||||
|
final Object rhs,
|
||||||
|
final Class<?> clazz,
|
||||||
|
final EqualsBuilder builder,
|
||||||
|
final boolean useTransients,
|
||||||
|
final String[] excludeFields) {
|
||||||
|
|
||||||
|
if (isRegistered(lhs, rhs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
register(lhs, rhs);
|
||||||
|
final Field[] fields = clazz.getDeclaredFields();
|
||||||
|
AccessibleObject.setAccessible(fields, true);
|
||||||
|
for (int i = 0; i < fields.length && builder.isEquals; i++) {
|
||||||
|
final Field f = fields[i];
|
||||||
|
if (false == ArrayUtil.contains(excludeFields, f.getName())
|
||||||
|
&& (f.getName().indexOf('$') == -1)
|
||||||
|
&& (useTransients || !Modifier.isTransient(f.getModifiers()))
|
||||||
|
&& (!Modifier.isStatic(f.getModifiers()))) {
|
||||||
|
try {
|
||||||
|
builder.append(f.get(lhs), f.get(rhs));
|
||||||
|
} catch (final IllegalAccessException e) {
|
||||||
|
//this can't happen. Would get a Security exception instead
|
||||||
|
//throw a runtime exception in case the impossible happens.
|
||||||
|
throw new InternalError("Unexpected IllegalAccessException");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
unregister(lhs, rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Adds the result of {@code super.equals()} to this builder.</p>
|
||||||
|
*
|
||||||
|
* @param superEquals the result of calling {@code super.equals()}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public EqualsBuilder appendSuper(final boolean superEquals) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = superEquals;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code Object}s are equal using their
|
||||||
|
* {@code equals} method.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand object
|
||||||
|
* @param rhs the right hand object
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final Object lhs, final Object rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == null || rhs == null) {
|
||||||
|
return setEquals(false);
|
||||||
|
}
|
||||||
|
if (ArrayUtil.isArray(lhs)) {
|
||||||
|
// 判断数组的equals
|
||||||
|
return setEquals(ArrayUtil.equals(lhs, rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The simple case, not an array, just test the element
|
||||||
|
return setEquals(lhs.equals(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code long} s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code long}
|
||||||
|
* @param rhs the right hand {@code long}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final long lhs, final long rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = (lhs == rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code int}s are equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code int}
|
||||||
|
* @param rhs the right hand {@code int}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final int lhs, final int rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = (lhs == rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code short}s are equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code short}
|
||||||
|
* @param rhs the right hand {@code short}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final short lhs, final short rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = (lhs == rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code char}s are equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code char}
|
||||||
|
* @param rhs the right hand {@code char}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final char lhs, final char rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = (lhs == rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code byte}s are equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code byte}
|
||||||
|
* @param rhs the right hand {@code byte}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final byte lhs, final byte rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = (lhs == rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code double}s are equal by testing that the
|
||||||
|
* pattern of bits returned by {@code doubleToLong} are equal.</p>
|
||||||
|
*
|
||||||
|
* <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
|
||||||
|
*
|
||||||
|
* <p>It is compatible with the hash code generated by
|
||||||
|
* {@code HashCodeBuilder}.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code double}
|
||||||
|
* @param rhs the right hand {@code double}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final double lhs, final double rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code float}s are equal byt testing that the
|
||||||
|
* pattern of bits returned by doubleToLong are equal.</p>
|
||||||
|
*
|
||||||
|
* <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
|
||||||
|
*
|
||||||
|
* <p>It is compatible with the hash code generated by
|
||||||
|
* {@code HashCodeBuilder}.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code float}
|
||||||
|
* @param rhs the right hand {@code float}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final float lhs, final float rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test if two {@code booleans}s are equal.</p>
|
||||||
|
*
|
||||||
|
* @param lhs the left hand {@code boolean}
|
||||||
|
* @param rhs the right hand {@code boolean}
|
||||||
|
* @return EqualsBuilder - used to chain calls.
|
||||||
|
*/
|
||||||
|
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
|
||||||
|
if (isEquals == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEquals = (lhs == rhs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns {@code true} if the fields that have been checked
|
||||||
|
* are all equal.</p>
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public boolean isEquals() {
|
||||||
|
return this.isEquals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns {@code true} if the fields that have been checked
|
||||||
|
* are all equal.</p>
|
||||||
|
*
|
||||||
|
* @return {@code true} if all of the fields that have been checked
|
||||||
|
* are equal, {@code false} otherwise.
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Boolean build() {
|
||||||
|
return isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@code isEquals} value.
|
||||||
|
*
|
||||||
|
* @param isEquals The value to set.
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
protected EqualsBuilder setEquals(boolean isEquals) {
|
||||||
|
this.isEquals = isEquals;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the EqualsBuilder so you can use the same object again
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
this.isEquals = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.builder;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Consumer3;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Supplier1;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Supplier2;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Supplier3;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Supplier4;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.func.Supplier5;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>通用Builder</p>
|
||||||
|
* 参考: <a href="https://blog.csdn.net/weixin_43935907/article/details/105003719">一看就会的java8通用Builder</a>
|
||||||
|
* <p>使用方法如下:</p>
|
||||||
|
* <pre>
|
||||||
|
* Box box = GenericBuilder
|
||||||
|
* .of(Box::new)
|
||||||
|
* .with(Box::setId, 1024L)
|
||||||
|
* .with(Box::setTitle, "Hello World!")
|
||||||
|
* .with(Box::setLength, 9)
|
||||||
|
* .with(Box::setWidth, 8)
|
||||||
|
* .with(Box::setHeight, 7)
|
||||||
|
* .build();
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p> 我们也可以对已创建的对象进行修改:</p>
|
||||||
|
* <pre>
|
||||||
|
* Box boxModified = GenericBuilder
|
||||||
|
* .of(() -> box)
|
||||||
|
* .with(Box::setTitle, "Hello Friend!")
|
||||||
|
* .with(Box::setLength, 3)
|
||||||
|
* .with(Box::setWidth, 4)
|
||||||
|
* .with(Box::setHeight, 5)
|
||||||
|
* .build();
|
||||||
|
* </pre>
|
||||||
|
* <p> 我们还可以对这样调用有参构造,这对于创建一些在有参构造中包含初始化函数的对象是有意义的:</p>
|
||||||
|
* <pre>
|
||||||
|
* Box box1 = GenericBuilder
|
||||||
|
* .of(Box::new, 2048L, "Hello Partner!", 222, 333, 444)
|
||||||
|
* .with(Box::alis)
|
||||||
|
* .build();
|
||||||
|
* </pre>
|
||||||
|
* <p> 还可能这样构建Map对象:</p>
|
||||||
|
* {@code
|
||||||
|
* HashMap<String, String> colorMap = GenericBuilder
|
||||||
|
* .of(HashMap<String,String>::new)
|
||||||
|
* .with(Map::put, "red", "#FF0000")
|
||||||
|
* .with(Map::put, "yellow", "#FFFF00")
|
||||||
|
* .with(Map::put, "blue", "#0000FF")
|
||||||
|
* .build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* <p>注意:本工具类支持调用的构造方法的参数数量不超过5个,一般方法的参数数量不超过2个,更多的参数不利于阅读和维护。</p>
|
||||||
|
*
|
||||||
|
* @author TomXin
|
||||||
|
* @since 5.7.21
|
||||||
|
*/
|
||||||
|
public class GenericBuilder<T> implements Builder<T> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实例化器
|
||||||
|
*/
|
||||||
|
private final Supplier<T> instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改器列表
|
||||||
|
*/
|
||||||
|
private final List<Consumer<T>> modifiers = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
*/
|
||||||
|
public GenericBuilder(Supplier<T> instant) {
|
||||||
|
this.instant = instant;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过无参数实例化器创建GenericBuilder
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
* @param <T> 目标类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public static <T> GenericBuilder<T> of(Supplier<T> instant) {
|
||||||
|
return new GenericBuilder<>(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过1参数实例化器创建GenericBuilder
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param <T> 目标类型
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public static <T, P1> GenericBuilder<T> of(Supplier1<T, P1> instant, P1 p1) {
|
||||||
|
return of(instant.toSupplier(p1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过2参数实例化器创建GenericBuilder
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param p2 参数二
|
||||||
|
* @param <T> 目标类型
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @param <P2> 参数二类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public static <T, P1, P2> GenericBuilder<T> of(Supplier2<T, P1, P2> instant, P1 p1, P2 p2) {
|
||||||
|
return of(instant.toSupplier(p1, p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过3参数实例化器创建GenericBuilder
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param p2 参数二
|
||||||
|
* @param p3 参数三
|
||||||
|
* @param <T> 目标类型
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @param <P2> 参数二类型
|
||||||
|
* @param <P3> 参数三类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public static <T, P1, P2, P3> GenericBuilder<T> of(Supplier3<T, P1, P2, P3> instant, P1 p1, P2 p2, P3 p3) {
|
||||||
|
return of(instant.toSupplier(p1, p2, p3));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过4参数实例化器创建GenericBuilder
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param p2 参数二
|
||||||
|
* @param p3 参数三
|
||||||
|
* @param p4 参数四
|
||||||
|
* @param <T> 目标类型
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @param <P2> 参数二类型
|
||||||
|
* @param <P3> 参数三类型
|
||||||
|
* @param <P4> 参数四类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public static <T, P1, P2, P3, P4> GenericBuilder<T> of(Supplier4<T, P1, P2, P3, P4> instant, P1 p1, P2 p2, P3 p3, P4 p4) {
|
||||||
|
return of(instant.toSupplier(p1, p2, p3, p4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过5参数实例化器创建GenericBuilder
|
||||||
|
*
|
||||||
|
* @param instant 实例化器
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param p2 参数二
|
||||||
|
* @param p3 参数三
|
||||||
|
* @param p4 参数四
|
||||||
|
* @param p5 参数五
|
||||||
|
* @param <T> 目标类型
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @param <P2> 参数二类型
|
||||||
|
* @param <P3> 参数三类型
|
||||||
|
* @param <P4> 参数四类型
|
||||||
|
* @param <P5> 参数五类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public static <T, P1, P2, P3, P4, P5> GenericBuilder<T> of(Supplier5<T, P1, P2, P3, P4, P5> instant, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
|
||||||
|
return of(instant.toSupplier(p1, p2, p3, p4, p5));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用无参数方法
|
||||||
|
*
|
||||||
|
* @param consumer 无参数Consumer
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public GenericBuilder<T> with(Consumer<T> consumer) {
|
||||||
|
modifiers.add(consumer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用1参数方法
|
||||||
|
*
|
||||||
|
* @param consumer 1参数Consumer
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public <P1> GenericBuilder<T> with(BiConsumer<T, P1> consumer, P1 p1) {
|
||||||
|
modifiers.add(instant -> consumer.accept(instant, p1));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用2参数方法
|
||||||
|
*
|
||||||
|
* @param consumer 2参数Consumer
|
||||||
|
* @param p1 参数一
|
||||||
|
* @param p2 参数二
|
||||||
|
* @param <P1> 参数一类型
|
||||||
|
* @param <P2> 参数二类型
|
||||||
|
* @return GenericBuilder对象
|
||||||
|
*/
|
||||||
|
public <P1, P2> GenericBuilder<T> with(Consumer3<T, P1, P2> consumer, P1 p1, P2 p2) {
|
||||||
|
modifiers.add(instant -> consumer.accept(instant, p1, p2));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建
|
||||||
|
*
|
||||||
|
* @return 目标对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T build() {
|
||||||
|
T value = instant.get();
|
||||||
|
modifiers.forEach(modifier -> modifier.accept(value));
|
||||||
|
modifiers.clear();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,958 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.core.builder;
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Assists in implementing {@link Object#hashCode()} methods.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class enables a good <code>hashCode</code> method to be built for any class. It follows the rules laid out in
|
||||||
|
* the book <a href="http://www.oracle.com/technetwork/java/effectivejava-136174.html">Effective Java</a> by Joshua Bloch. Writing a
|
||||||
|
* good <code>hashCode</code> method is actually quite difficult. This class aims to simplify the process.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The following is the approach taken. When appending a data field, the current total is multiplied by the
|
||||||
|
* multiplier then a relevant value
|
||||||
|
* for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then
|
||||||
|
* appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* All relevant fields from the object should be included in the <code>hashCode</code> method. Derived fields may be
|
||||||
|
* excluded. In general, any field used in the <code>equals</code> method must be used in the <code>hashCode</code>
|
||||||
|
* method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To use this class write code as follows:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Person {
|
||||||
|
* String name;
|
||||||
|
* int age;
|
||||||
|
* boolean smoker;
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public int hashCode() {
|
||||||
|
* // you pick a hard-coded, randomly chosen, non-zero, odd number
|
||||||
|
* // ideally different for each class
|
||||||
|
* return new HashCodeBuilder(17, 37).
|
||||||
|
* append(name).
|
||||||
|
* append(age).
|
||||||
|
* append(smoker).
|
||||||
|
* toHashCode();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If required, the superclass <code>hashCode()</code> can be added using {@link #appendSuper}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
|
||||||
|
* usually private, the method, <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code>
|
||||||
|
* to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
|
||||||
|
* are set up correctly. It is also slower than testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A typical invocation for this method would look like:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public int hashCode() {
|
||||||
|
* return HashCodeBuilder.reflectionHashCode(this);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* TODO 待整理
|
||||||
|
* 来自于Apache-Commons-Lang3
|
||||||
|
* @author looly,Apache-Commons
|
||||||
|
* @since 4.2.2
|
||||||
|
*/
|
||||||
|
public class HashCodeBuilder implements Builder<Integer> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default initial value to use in reflection hash code building.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_INITIAL_VALUE = 17;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default multipler value to use in reflection hash code building.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_MULTIPLIER_VALUE = 37;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
||||||
|
* we are in the process of calculating.
|
||||||
|
*
|
||||||
|
* So we generate a one-to-one mapping from the original object to a new object.
|
||||||
|
*
|
||||||
|
* Now HashSet uses equals() to determine if two elements with the same hashcode really
|
||||||
|
* are equal, so we also need to ensure that the replacement objects are only equal
|
||||||
|
* if the original objects are identical.
|
||||||
|
*
|
||||||
|
* The original implementation (2.4 and before) used the System.indentityHashCode()
|
||||||
|
* method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
|
||||||
|
*
|
||||||
|
* We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
|
||||||
|
* to disambiguate the duplicate ids.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the registry of objects being traversed by the reflection methods in the current thread.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Set the registry of objects being traversed
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
private static Set<IDKey> getRegistry() {
|
||||||
|
return REGISTRY.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns <code>true</code> if the registry contains the given object. Used by the reflection methods to avoid
|
||||||
|
* infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The object to lookup in the registry.
|
||||||
|
* @return boolean <code>true</code> if the registry contains the given object.
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
private static boolean isRegistered(final Object value) {
|
||||||
|
final Set<IDKey> registry = getRegistry();
|
||||||
|
return registry != null && registry.contains(new IDKey(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Appends the fields and values defined by the given object of the given <code>Class</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the object to append details of
|
||||||
|
* @param clazz
|
||||||
|
* the class to append details of
|
||||||
|
* @param builder
|
||||||
|
* the builder to append to
|
||||||
|
* @param useTransients
|
||||||
|
* whether to use transient fields
|
||||||
|
* @param excludeFields
|
||||||
|
* Collection of String field names to exclude from use in calculation of hash code
|
||||||
|
*/
|
||||||
|
private static void reflectionAppend(final Object object, final Class<?> clazz, final HashCodeBuilder builder, final boolean useTransients,
|
||||||
|
final String[] excludeFields) {
|
||||||
|
if (isRegistered(object)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
register(object);
|
||||||
|
final Field[] fields = clazz.getDeclaredFields();
|
||||||
|
AccessibleObject.setAccessible(fields, true);
|
||||||
|
for (final Field field : fields) {
|
||||||
|
if (false == ArrayUtil.contains(excludeFields, field.getName())
|
||||||
|
&& (field.getName().indexOf('$') == -1)
|
||||||
|
&& (useTransients || !Modifier.isTransient(field.getModifiers()))
|
||||||
|
&& (!Modifier.isStatic(field.getModifiers()))) {
|
||||||
|
try {
|
||||||
|
final Object fieldValue = field.get(object);
|
||||||
|
builder.append(fieldValue);
|
||||||
|
} catch (final IllegalAccessException e) {
|
||||||
|
// this can't happen. Would get a Security exception instead
|
||||||
|
// throw a runtime exception in case the impossible happens.
|
||||||
|
throw new InternalError("Unexpected IllegalAccessException");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
unregister(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||||
|
* <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initialNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the initial value. This will be the returned
|
||||||
|
* value if no fields are found to include in the hash code
|
||||||
|
* @param multiplierNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the multiplier
|
||||||
|
* @param object
|
||||||
|
* the Object to create a <code>hashCode</code> for
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is <code>null</code>
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is zero or even
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object) {
|
||||||
|
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initialNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the initial value. This will be the returned
|
||||||
|
* value if no fields are found to include in the hash code
|
||||||
|
* @param multiplierNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the multiplier
|
||||||
|
* @param object
|
||||||
|
* the Object to create a <code>hashCode</code> for
|
||||||
|
* @param testTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is <code>null</code>
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is zero or even
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object,
|
||||||
|
final boolean testTransients) {
|
||||||
|
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be included. Superclass fields will be included up to and including the specified
|
||||||
|
* superclass. A null superclass is treated as java.lang.Object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object involved
|
||||||
|
* @param initialNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the initial value. This will be the returned
|
||||||
|
* value if no fields are found to include in the hash code
|
||||||
|
* @param multiplierNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the multiplier
|
||||||
|
* @param object
|
||||||
|
* the Object to create a <code>hashCode</code> for
|
||||||
|
* @param testTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param reflectUpToClass
|
||||||
|
* the superclass to reflect up to (inclusive), may be <code>null</code>
|
||||||
|
* @param excludeFields
|
||||||
|
* array of field names to exclude from use in calculation of hash code
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is <code>null</code>
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is zero or even
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static <T> int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final T object,
|
||||||
|
final boolean testTransients, final Class<? super T> reflectUpToClass, final String... excludeFields) {
|
||||||
|
|
||||||
|
if (object == null) {
|
||||||
|
throw new IllegalArgumentException("The object to build a hash code for must not be null");
|
||||||
|
}
|
||||||
|
final HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
|
||||||
|
Class<?> clazz = object.getClass();
|
||||||
|
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||||
|
while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||||
|
}
|
||||||
|
return builder.toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||||
|
* in the hash code, the result of this method will be constant.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to create a <code>hashCode</code> for
|
||||||
|
* @param testTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the object is <code>null</code>
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final Object object, final boolean testTransients) {
|
||||||
|
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object,
|
||||||
|
testTransients, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||||
|
* <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||||
|
* in the hash code, the result of this method will be constant.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to create a <code>hashCode</code> for
|
||||||
|
* @param excludeFields
|
||||||
|
* Collection of String field names to exclude from use in calculation of hash code
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the object is <code>null</code>
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final Object object, final Collection<String> excludeFields) {
|
||||||
|
return reflectionHashCode(object, ArrayUtil.toArray(excludeFields, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||||
|
* <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||||
|
* in the hash code, the result of this method will be constant.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to create a <code>hashCode</code> for
|
||||||
|
* @param excludeFields
|
||||||
|
* array of field names to exclude from use in calculation of hash code
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the object is <code>null</code>
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final Object object, final String... excludeFields) {
|
||||||
|
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object, false,
|
||||||
|
null, excludeFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Registers the given object. Used by the reflection methods to avoid infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The object to register.
|
||||||
|
*/
|
||||||
|
static void register(final Object value) {
|
||||||
|
synchronized (HashCodeBuilder.class) {
|
||||||
|
if (getRegistry() == null) {
|
||||||
|
REGISTRY.set(new HashSet<IDKey>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getRegistry().add(new IDKey(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Unregisters the given object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The object to unregister.
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
static void unregister(final Object value) {
|
||||||
|
Set<IDKey> registry = getRegistry();
|
||||||
|
if (registry != null) {
|
||||||
|
registry.remove(new IDKey(value));
|
||||||
|
synchronized (HashCodeBuilder.class) {
|
||||||
|
//read again
|
||||||
|
registry = getRegistry();
|
||||||
|
if (registry != null && registry.isEmpty()) {
|
||||||
|
REGISTRY.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant to use in building the hashCode.
|
||||||
|
*/
|
||||||
|
private final int iConstant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Running total of the hashCode.
|
||||||
|
*/
|
||||||
|
private int iTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses two hard coded choices for the constants needed to build a <code>hashCode</code>.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder() {
|
||||||
|
iConstant = 37;
|
||||||
|
iTotal = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initialOddNumber
|
||||||
|
* an odd number used as the initial value
|
||||||
|
* @param multiplierOddNumber
|
||||||
|
* an odd number used as the multiplier
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is even
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder(final int initialOddNumber, final int multiplierOddNumber) {
|
||||||
|
Assert.isTrue(initialOddNumber % 2 != 0, "HashCodeBuilder requires an odd initial value");
|
||||||
|
Assert.isTrue(multiplierOddNumber % 2 != 0, "HashCodeBuilder requires an odd multiplier");
|
||||||
|
iConstant = multiplierOddNumber;
|
||||||
|
iTotal = initialOddNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>boolean</code>.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This adds <code>1</code> when true, and <code>0</code> when false to the <code>hashCode</code>.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This is in contrast to the standard <code>java.lang.Boolean.hashCode</code> handling, which computes
|
||||||
|
* a <code>hashCode</code> value of <code>1231</code> for <code>java.lang.Boolean</code> instances
|
||||||
|
* that represent <code>true</code> or <code>1237</code> for <code>java.lang.Boolean</code> instances
|
||||||
|
* that represent <code>false</code>.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This is in accordance with the <i>Effective Java</i> design.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the boolean to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final boolean value) {
|
||||||
|
iTotal = iTotal * iConstant + (value ? 0 : 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>boolean</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final boolean[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final boolean element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>byte</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the byte to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final byte value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>byte</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final byte[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final byte element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>char</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the char to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final char value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>char</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final char[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final char element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>double</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the double to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final double value) {
|
||||||
|
return append(Double.doubleToLongBits(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>double</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final double[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final double element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>float</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the float to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final float value) {
|
||||||
|
iTotal = iTotal * iConstant + Float.floatToIntBits(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>float</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final float[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final float element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for an <code>int</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the int to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final int value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for an <code>int</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final int[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final int element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>long</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the long to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
// NOTE: This method uses >> and not >>> as Effective Java and
|
||||||
|
// Long.hashCode do. Ideally we should switch to >>> at
|
||||||
|
// some stage. There are backwards compat issues, so
|
||||||
|
// that will have to wait for the time being. cf LANG-342.
|
||||||
|
public HashCodeBuilder append(final long value) {
|
||||||
|
iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>long</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final long[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final long element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for an <code>Object</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final Object object) {
|
||||||
|
if (object == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(object.getClass().isArray()) {
|
||||||
|
// 'Switch' on type of array, to dispatch to the correct handler
|
||||||
|
// This handles multi dimensional arrays
|
||||||
|
if (object instanceof long[]) {
|
||||||
|
append((long[]) object);
|
||||||
|
} else if (object instanceof int[]) {
|
||||||
|
append((int[]) object);
|
||||||
|
} else if (object instanceof short[]) {
|
||||||
|
append((short[]) object);
|
||||||
|
} else if (object instanceof char[]) {
|
||||||
|
append((char[]) object);
|
||||||
|
} else if (object instanceof byte[]) {
|
||||||
|
append((byte[]) object);
|
||||||
|
} else if (object instanceof double[]) {
|
||||||
|
append((double[]) object);
|
||||||
|
} else if (object instanceof float[]) {
|
||||||
|
append((float[]) object);
|
||||||
|
} else if (object instanceof boolean[]) {
|
||||||
|
append((boolean[]) object);
|
||||||
|
} else {
|
||||||
|
// Not an array of primitives
|
||||||
|
append((Object[]) object);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iTotal = iTotal * iConstant + object.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for an <code>Object</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final Object[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final Object element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>short</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the short to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final short value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a <code>hashCode</code> for a <code>short</code> array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the <code>hashCode</code>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final short[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final short element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Adds the result of super.hashCode() to this builder.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param superHashCode
|
||||||
|
* the result of calling <code>super.hashCode()</code>
|
||||||
|
* @return this HashCodeBuilder, used to chain calls.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder appendSuper(final int superHashCode) {
|
||||||
|
iTotal = iTotal * iConstant + superHashCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Return the computed <code>hashCode</code>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return <code>hashCode</code> based on the fields appended
|
||||||
|
*/
|
||||||
|
public int toHashCode() {
|
||||||
|
return iTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the computed <code>hashCode</code>.
|
||||||
|
*
|
||||||
|
* @return <code>hashCode</code> based on the fields appended
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer build() {
|
||||||
|
return toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The computed <code>hashCode</code> from toHashCode() is returned due to the likelihood
|
||||||
|
* of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for
|
||||||
|
* HashCodeBuilder itself is.</p>
|
||||||
|
*
|
||||||
|
* @return <code>hashCode</code> based on the fields appended
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue