好多东西添加

main
youHong.ai 2023-03-14 15:12:53 +08:00
parent 7489f8d291
commit 8b16f9ca57
732 changed files with 136283 additions and 1420 deletions

View File

@ -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() {

View File

@ -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

View File

@ -0,0 +1 @@
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmvEwYkqzj0oCREd53foFtngqEo1x0VcRF+eAHcc5rQ3uEZ+jpbzFRtRi1n0543cGy01XmVKo06YGZAnRiKfZ44I7Or+qEO3/Pg4ULwcqegUjVwRzPlx/mJ5iKIXEKZx+6rw0/JeNSKNiFWW3jQVjJQLZ8+5Wl1orGJGBA1JvB1zmnSi5F/mqlnRGCRxsvM2DfZLTVh+z/AVwVAFHdb7U2YYROgTHfztFkqloYKQAWxWfFSlU/+buHZV1MpCjWwiq7H16IixP+6ZW3x44tNrFHWgNSwDheEDmk3j2hmbv+eU1gmGT41iq1tnNBq/Jkv9TEK4Nc7FldnOfRqizTFiArQIDAQAB

View File

@ -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;
} }
} }

View File

@ -18,7 +18,8 @@ public @interface Association {
String column(); String column();
String select();
String select() default "";
Id id(); Id id();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -17,7 +17,7 @@ public @interface CollectionMapping {
String property(); String property();
/** 数据库字段名 */ /** 数据库字段名 */
String column(); String column() default "";
/** 查询方法全限定类名 */ /** 查询方法全限定类名 */
String select() default ""; String select() default "";

View File

@ -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 "";
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -11,7 +11,8 @@ import java.lang.annotation.*;
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Documented @Documented
public @interface Select { public @interface Select {
String value() default ""; String value() default "";
// sql是否是在参数中
boolean custom() default false; // sql是否是在参数中
boolean custom() default false;
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
} }

File diff suppressed because it is too large Load Diff

View File

@ -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;
@ -29,217 +28,217 @@ import java.util.UUID;
*/ */
public class PdfUtil { public class PdfUtil {
private static final Logger log = Util.getLogger("util_water_log"); private static final Logger log = Util.getLogger("util_water_log");
/** /**
* *
* *
* @param inputStream pdf * @param inputStream pdf
* @param keyword * @param keyword
* @return * @return
*/ */
public static List<PdfPointItem> findKeywordPoints(InputStream inputStream, String keyword) { public static List<PdfPointItem> findKeywordPoints(InputStream inputStream, String keyword) {
PdfReader pdfReader = null; PdfReader pdfReader = null;
try { try {
pdfReader = new PdfReader(inputStream); pdfReader = new PdfReader(inputStream);
} catch (IOException e) { } catch (IOException e) {
throw new CustomerException("读取pdf失败", e); throw new CustomerException("读取pdf失败", e);
} }
return getKeywordPoints(pdfReader, keyword); return getKeywordPoints(pdfReader, keyword);
} }
/** /**
* *
* *
* @param pdfReader pdf * @param pdfReader pdf
* @param keyword * @param keyword
* @return * @return
*/ */
private static List<PdfPointItem> getKeywordPoints(PdfReader pdfReader, String keyword) { private static List<PdfPointItem> getKeywordPoints(PdfReader pdfReader, String keyword) {
int totalPage = pdfReader.getNumberOfPages(); int totalPage = pdfReader.getNumberOfPages();
PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader); PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);
CustomerPdfRenderListener customerPdfRenderListener = new CustomerPdfRenderListener(); CustomerPdfRenderListener customerPdfRenderListener = new CustomerPdfRenderListener();
customerPdfRenderListener.setKeyWord(keyword); customerPdfRenderListener.setKeyWord(keyword);
customerPdfRenderListener.setTotalPage(totalPage); customerPdfRenderListener.setTotalPage(totalPage);
for (int page = 1; page <= totalPage; page++) { for (int page = 1; page <= totalPage; page++) {
customerPdfRenderListener.setPage(page); customerPdfRenderListener.setPage(page);
try { try {
pdfReaderContentParser.processContent(page, customerPdfRenderListener); pdfReaderContentParser.processContent(page, customerPdfRenderListener);
} catch (IOException e) { } catch (IOException e) {
throw new CustomerException("解析pdf失败", e); throw new CustomerException("解析pdf失败", e);
} }
} }
pdfReader.close(); pdfReader.close();
return customerPdfRenderListener.getPoints(); return customerPdfRenderListener.getPoints();
} }
/** /**
* *
* *
* @param pdfImageFileId pdfid * @param pdfImageFileId pdfid
* @param pictureInputStream * @param pictureInputStream
* @param keyword * @param keyword
* @param allKeyword * @param allKeyword
* @param opacity * @param opacity
* @param imageFitWidth * @param imageFitWidth
* @param imageFitHeight * @param imageFitHeight
* @param offsetX x * @param offsetX x
* @param offsetY y * @param offsetY y
* @return pdf * @return pdf
*/ */
public static int addPictureWater2pdfByKeyword(int pdfImageFileId, public static int addPictureWater2pdfByKeyword(int pdfImageFileId,
InputStream pictureInputStream, InputStream pictureInputStream,
String keyword, String keyword,
boolean allKeyword, boolean allKeyword,
float opacity, float opacity,
int imageFitWidth, int imageFitWidth,
int imageFitHeight, int imageFitHeight,
int offsetX, int offsetX,
int offsetY) { int offsetY) {
ImageFileManager imageFileManager = new ImageFileManager(); ImageFileManager imageFileManager = new ImageFileManager();
imageFileManager.getImageFileInfoById(pdfImageFileId); imageFileManager.getImageFileInfoById(pdfImageFileId);
String imageFileName = imageFileManager.getImageFileName(); String imageFileName = imageFileManager.getImageFileName();
String suffix = imageFileName.substring(imageFileName.lastIndexOf(".") + 1); String suffix = imageFileName.substring(imageFileName.lastIndexOf(".") + 1);
if (!"pdf".equalsIgnoreCase(suffix)) { if (!"pdf".equalsIgnoreCase(suffix)) {
throw new CustomerException("不支持的文件类型 " + suffix); throw new CustomerException("不支持的文件类型 " + suffix);
} }
InputStream inputStream = ImageFileManager.getInputStreamById(pdfImageFileId); InputStream inputStream = ImageFileManager.getInputStreamById(pdfImageFileId);
List<PdfPointItem> keywordPoints = findKeywordPoints(inputStream, keyword); List<PdfPointItem> keywordPoints = findKeywordPoints(inputStream, keyword);
PdfReader pdfReader = null; PdfReader pdfReader = null;
Image image = null; Image image = null;
try { try {
byte[] imgb = org.apache.commons.io.IOUtils.toByteArray(pictureInputStream); byte[] imgb = org.apache.commons.io.IOUtils.toByteArray(pictureInputStream);
image = Image.getInstance(imgb); image = Image.getInstance(imgb);
} catch (Exception e) { } catch (Exception e) {
throw new CustomerException("获取水印图片失败!", e); throw new CustomerException("获取水印图片失败!", e);
} }
PdfGState gs = new PdfGState(); PdfGState gs = new PdfGState();
gs.setFillOpacity(opacity); gs.setFillOpacity(opacity);
image.scaleToFit(imageFitWidth, imageFitHeight); image.scaleToFit(imageFitWidth, imageFitHeight);
String createDir = FileUpload.getCreateDir(new SystemComInfo().getFilesystem()) + "tempfile" + File.separator; String createDir = FileUpload.getCreateDir(new SystemComInfo().getFilesystem()) + "tempfile" + File.separator;
// 防止高并发下文件名重复导致文件覆盖的问题 // 防止高并发下文件名重复导致文件覆盖的问题
String tempPath = createDir + imageFileName + System.currentTimeMillis() + UUID.randomUUID() + ".pdf"; String tempPath = createDir + imageFileName + System.currentTimeMillis() + UUID.randomUUID() + ".pdf";
File file = new File(tempPath); File file = new File(tempPath);
if (!file.getParentFile().exists()) { if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
} }
if (!file.exists()) { if (!file.exists()) {
try { try {
file.createNewFile(); file.createNewFile();
} catch (IOException e) { } catch (IOException e) {
throw new CustomerException("创建临时文件失败!", e); throw new CustomerException("创建临时文件失败!", e);
} }
} }
// 输出到临时目录 // 输出到临时目录
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;
try { try {
inputStream = ImageFileManager.getInputStreamById(pdfImageFileId); inputStream = ImageFileManager.getInputStreamById(pdfImageFileId);
pdfReader = new PdfReader(inputStream); pdfReader = new PdfReader(inputStream);
pdfStamper = new PdfStamper(pdfReader, outputStreamTem); pdfStamper = new PdfStamper(pdfReader, outputStreamTem);
} catch (IOException | DocumentException e) { } catch (IOException | DocumentException e) {
throw new CustomerException("读取pdf失败", e); throw new CustomerException("读取pdf失败", e);
} }
if (allKeyword) { if (allKeyword) {
addPictureWater2pdfByKeywordAllKeyword(pdfStamper, keywordPoints, image, gs, offsetX, offsetY); addPictureWater2pdfByKeywordAllKeyword(pdfStamper, keywordPoints, image, gs, offsetX, offsetY);
} else { } else {
PdfPointItem keywordPoint = keywordPoints.get(0); PdfPointItem keywordPoint = keywordPoints.get(0);
PdfContentByte overContent = pdfStamper.getOverContent(keywordPoint.getStartPage()); PdfContentByte overContent = pdfStamper.getOverContent(keywordPoint.getStartPage());
overContent.setGState(gs); overContent.setGState(gs);
float absoluteX = keywordPoint.getStartPointX() + offsetX; float absoluteX = keywordPoint.getStartPointX() + offsetX;
float absoluteY = keywordPoint.getStartPointY() + offsetY; float absoluteY = keywordPoint.getStartPointY() + offsetY;
image.setAbsolutePosition(absoluteX, absoluteY); image.setAbsolutePosition(absoluteX, absoluteY);
try { try {
overContent.addImage(image); overContent.addImage(image);
} catch (DocumentException e) { } catch (DocumentException e) {
throw new CustomerException("添加水印图片失败!", e); throw new CustomerException("添加水印图片失败!", e);
} }
} }
try { try {
pdfStamper.close(); pdfStamper.close();
outputStreamTem.close(); outputStreamTem.close();
} catch (IOException | DocumentException e) { } catch (IOException | DocumentException e) {
e.printStackTrace(); e.printStackTrace();
log.error("关闭流失败!", e); log.error("关闭流失败!", e);
} }
int imageFileId = -1; int imageFileId = -1;
InputStream waterPdf = null; InputStream waterPdf = null;
try { try {
waterPdf = new FileInputStream(tempPath); waterPdf = new FileInputStream(tempPath);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw new CustomerException("文件水印添加失败!", e); throw new CustomerException("文件水印添加失败!", e);
} }
imageFileId = Util.createFileByInputSteam(waterPdf, imageFileName); imageFileId = Util.createFileByInputSteam(waterPdf, imageFileName);
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;
} }
/** /**
* *
* *
* @param pdfImageFileId pdfid * @param pdfImageFileId pdfid
* @param pictureImageFileId id * @param pictureImageFileId id
* @param keyword * @param keyword
* @param allKeyword * @param allKeyword
* @param opacity * @param opacity
* @param imageFitWidth * @param imageFitWidth
* @param imageFitHeight * @param imageFitHeight
* @param offsetX x * @param offsetX x
* @param offsetY y * @param offsetY y
* @return pdf * @return pdf
*/ */
public static int addPictureWater2pdfByKeyword(int pdfImageFileId, public static int addPictureWater2pdfByKeyword(int pdfImageFileId,
int pictureImageFileId, int pictureImageFileId,
String keyword, String keyword,
boolean allKeyword, boolean allKeyword,
float opacity, float opacity,
int imageFitWidth, int imageFitWidth,
int imageFitHeight, int imageFitHeight,
int offsetX, int offsetX,
int offsetY) { int offsetY) {
InputStream pictureInputStream = ImageFileManager.getInputStreamById(pictureImageFileId); InputStream pictureInputStream = ImageFileManager.getInputStreamById(pictureImageFileId);
return addPictureWater2pdfByKeyword(pdfImageFileId, pictureInputStream, keyword, allKeyword, opacity, imageFitWidth, imageFitHeight, offsetX, offsetY); return addPictureWater2pdfByKeyword(pdfImageFileId, pictureInputStream, keyword, allKeyword, opacity, imageFitWidth, imageFitHeight, offsetX, offsetY);
} }
/** /**
* *
* *
* @param pdfStamper pdfStamper * @param pdfStamper pdfStamper
* @param keywordPoints * @param keywordPoints
* @param image * @param image
* @param gs * @param gs
* @param offsetX X * @param offsetX X
* @param offsetY Y * @param offsetY Y
*/ */
private static void addPictureWater2pdfByKeywordAllKeyword(PdfStamper pdfStamper, private static void addPictureWater2pdfByKeywordAllKeyword(PdfStamper pdfStamper,
List<PdfPointItem> keywordPoints, List<PdfPointItem> keywordPoints,
Image image, PdfGState gs, Image image, PdfGState gs,
int offsetX, int offsetY) { int offsetX, int offsetY) {
PdfContentByte overContent = null; PdfContentByte overContent = null;
for (PdfPointItem keywordPoint : keywordPoints) { for (PdfPointItem keywordPoint : keywordPoints) {
overContent = pdfStamper.getOverContent(keywordPoint.getStartPage()); overContent = pdfStamper.getOverContent(keywordPoint.getStartPage());
overContent.setGState(gs); overContent.setGState(gs);
float absoluteX = keywordPoint.getStartPointX() + offsetX; float absoluteX = keywordPoint.getStartPointX() + offsetX;
float absoluteY = keywordPoint.getStartPointY() + offsetY; float absoluteY = keywordPoint.getStartPointY() + offsetY;
image.setAbsolutePosition(absoluteX, absoluteY); image.setAbsolutePosition(absoluteX, absoluteY);
try { try {
overContent.addImage(image); overContent.addImage(image);
} catch (DocumentException e) { } catch (DocumentException e) {
throw new CustomerException("添加水印图片失败!", e); throw new CustomerException("添加水印图片失败!", e);
} }
} }
} }
} }

View File

@ -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");
} }
} }

View File

@ -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);

View File

@ -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,7 +20,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0D;
} }
return Double.parseDouble(string); return Double.parseDouble(string);
} }
@ -27,7 +34,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0D;
} }
return Double.parseDouble(string); return Double.parseDouble(string);
} }
@ -36,7 +48,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0D;
} }
return Double.parseDouble(string); return Double.parseDouble(string);
} }
@ -45,7 +62,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0D;
} }
return Double.parseDouble(string); return Double.parseDouble(string);
} }

View File

@ -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,7 +20,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0F;
} }
return Float.parseFloat(string); return Float.parseFloat(string);
} }
@ -27,7 +34,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0F;
} }
return Float.parseFloat(string); return Float.parseFloat(string);
} }
@ -36,7 +48,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0F;
} }
return Float.parseFloat(string); return Float.parseFloat(string);
} }
@ -45,7 +62,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return 0.0F;
} }
return Float.parseFloat(string); return Float.parseFloat(string);
} }

View File

@ -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,7 +18,10 @@ 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)) {
return null; if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
return -1;
} }
if (string.contains(".")) { if (string.contains(".")) {
string = string.substring(0, string.indexOf(".")); string = string.substring(0, string.indexOf("."));
@ -28,7 +33,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return -1;
} }
if (string.contains(".")) { if (string.contains(".")) {
string = string.substring(0, string.indexOf(".")); string = string.substring(0, string.indexOf("."));
@ -40,7 +50,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return -1;
} }
if (string.contains(".")) { if (string.contains(".")) {
string = string.substring(0, string.indexOf(".")); string = string.substring(0, string.indexOf("."));
@ -52,7 +67,12 @@ 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)) {
return null; if (!Objects.isNull(declaredField)) {
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
return null;
}
}
return -1;
} }
if (string.contains(".")) { if (string.contains(".")) {
string = string.substring(0, string.indexOf(".")); string = string.substring(0, string.indexOf("."));

View File

@ -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,6 +60,145 @@ 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) {
SqlHandler sqlHandler = new SqlHandler();
ResultMapper resultMapper = new ResultMapper();
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
switch (type) {
case 1: {
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!");
}
Util.getLogger(SQL_LOG).info("解析sql===>" + handler);
if (handler.getArgs().isEmpty()) {
rs.executeQuery(handler.getSqlStr());
} else {
rs.executeQuery(handler.getSqlStr(), handler.getArgs());
}
return resultMapper.mapperResult(rs, method, method.getReturnType(), this);
}
case 2: {
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!");
}
Util.getLogger(SQL_LOG).info(handler.toString());
Class<?> returnType = method.getReturnType();
boolean b;
if (handler.getArgs().isEmpty()) {
b = rs.executeUpdate(handler.getSqlStr());
} else {
b = rs.executeUpdate(handler.getSqlStr(), handler.getArgs());
}
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(int.class) || returnType.equals(Integer.class)) {
if (b) {
return 1;
} else {
return 0;
}
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
}
case 3: {
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!");
}
Util.getLogger(SQL_LOG).info(handler.toString());
Class<?> returnType = method.getReturnType();
boolean b;
if (handler.getArgs().isEmpty()) {
b = rs.executeUpdate(handler.getSqlStr());
} else {
b = rs.executeUpdate(handler.getSqlStr(), handler.getArgs());
}
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
}
case 4: {
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!");
}
Util.getLogger(SQL_LOG).info(handler.toString());
Class<?> returnType = method.getReturnType();
boolean b;
if (handler.getArgs().isEmpty()) {
b = rs.executeUpdate(handler.getSqlStr());
} else {
b = rs.executeUpdate(handler.getSqlStr(), handler.getArgs());
}
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
}
case 5: {
Class<?> returnType = method.getReturnType();
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
if (batchSqlResult.getBatchList().isEmpty()) {
throw new CustomerException("execute batch sql error , batch sql args is empty!");
}
if (!batchSqlResult.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!");
}
boolean b = rs.executeBatchSql(batchSqlResult.getSqlStr(), batchSqlResult.getBatchList());
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
}
case 6: {
Class<?> returnType = method.getReturnType();
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
if (batchSqlResult.getBatchList().isEmpty()) {
throw new CustomerException("execute batch sql error , batch sql args is empty!");
}
if (!batchSqlResult.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!");
}
boolean b = rs.executeBatchSql(batchSqlResult.getSqlStr(), batchSqlResult.getBatchList());
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
}
case 7: {
Class<?> returnType = method.getReturnType();
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
if (batchSqlResult.getBatchList().isEmpty()) {
throw new CustomerException("execute batch sql error , batch sql args is empty!");
}
if (!batchSqlResult.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!");
}
boolean b = rs.executeBatchSql(batchSqlResult.getSqlStr(), batchSqlResult.getBatchList());
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
}
default:
throw new CustomerException("不支持的sql注解类型 " + type);
}
}
private Object invokeRs(Object proxy, Method method, Object[] args, String name) { private Object invokeRs(Object proxy, Method method, Object[] args, String name) {
String mapperKey = method.getDeclaringClass().getName(); String mapperKey = method.getDeclaringClass().getName();
@ -71,175 +211,97 @@ public class RecordsetUtil implements InvocationHandler {
rs = rsManager.getRs(mapperKey); rs = rsManager.getRs(mapperKey);
} }
SqlHandler sqlHandler = new SqlHandler(); SqlHandler sqlHandler = new SqlHandler();
ResultMapper resultMapper = new ResultMapper(); 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); Select select = method.getAnnotation(Select.class);
if (select != null) { if (select != null) {
// 查询
String sql = select.value(); String sql = select.value();
boolean custom = select.custom(); boolean custom = select.custom();
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args); return invokeDefault(1, sql, custom, rs, method, args);
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!");
}
Util.getLogger(SQL_LOG).info("解析sql===>" + handler);
if (handler.getArgs().isEmpty()) {
rs.executeQuery(handler.getSqlStr());
} else {
rs.executeQuery(handler.getSqlStr(), handler.getArgs());
}
return resultMapper.mapperResult(rs, method, method.getReturnType(), this);
} }
Update update = method.getAnnotation(Update.class); Update update = method.getAnnotation(Update.class);
if (update != null) { if (update != null) {
// 查询
String sql = update.value(); String sql = update.value();
boolean custom = update.custom(); boolean custom = update.custom();
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args); return invokeDefault(2, sql, custom, rs, method, args);
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!");
}
Util.getLogger(SQL_LOG).info(handler.toString());
Class<?> returnType = method.getReturnType();
boolean b;
if (handler.getArgs().isEmpty()) {
b = rs.executeUpdate(handler.getSqlStr());
} else {
b = rs.executeUpdate(handler.getSqlStr(), handler.getArgs());
}
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(int.class) || returnType.equals(Integer.class)) {
if (b) {
return 1;
} else {
return 0;
}
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
} }
Insert insert = method.getAnnotation(Insert.class); Insert insert = method.getAnnotation(Insert.class);
if (insert != null) { if (insert != null) {
// 查询
String sql = insert.value(); String sql = insert.value();
boolean custom = insert.custom(); boolean custom = insert.custom();
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args); return invokeDefault(3, sql, custom, rs, method, args);
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!");
}
Util.getLogger(SQL_LOG).info(handler.toString());
Class<?> returnType = method.getReturnType();
boolean b;
if (handler.getArgs().isEmpty()) {
b = rs.executeUpdate(handler.getSqlStr());
} else {
b = rs.executeUpdate(handler.getSqlStr(), handler.getArgs());
}
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
} }
Delete delete = method.getAnnotation(Delete.class); Delete delete = method.getAnnotation(Delete.class);
if (delete != null) { if (delete != null) {
// 查询
String sql = delete.value(); String sql = delete.value();
boolean custom = delete.custom(); boolean custom = delete.custom();
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args); return invokeDefault(4, sql, custom, rs, method, args);
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!");
}
Util.getLogger(SQL_LOG).info(handler.toString());
Class<?> returnType = method.getReturnType();
boolean b;
if (handler.getArgs().isEmpty()) {
b = rs.executeUpdate(handler.getSqlStr());
} else {
b = rs.executeUpdate(handler.getSqlStr(), handler.getArgs());
}
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
} }
boolean hasBatchInsert = method.isAnnotationPresent(BatchInsert.class); BatchInsert batchInsert = method.getAnnotation(BatchInsert.class);
if (hasBatchInsert) { if (batchInsert != null) {
BatchInsert batchInsert = method.getAnnotation(BatchInsert.class);
String sql = batchInsert.value(); String sql = batchInsert.value();
Class<?> returnType = method.getReturnType();
boolean custom = batchInsert.custom(); boolean custom = batchInsert.custom();
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args); return invokeDefault(5, sql, custom, rs, method, args);
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
if (batchSqlResult.getBatchList().isEmpty()) {
throw new CustomerException("execute batch sql error , batch sql args is empty!");
}
if (!batchSqlResult.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!");
}
boolean b = rs.executeBatchSql(batchSqlResult.getSqlStr(), batchSqlResult.getBatchList());
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
} }
boolean hasBatchUpdate = method.isAnnotationPresent(BatchUpdate.class); BatchUpdate batchUpdate = method.getAnnotation(BatchUpdate.class);
if (hasBatchUpdate) { if (batchUpdate != null) {
BatchUpdate batchUpdate = method.getAnnotation(BatchUpdate.class);
String sql = batchUpdate.value(); String sql = batchUpdate.value();
Class<?> returnType = method.getReturnType();
boolean custom = batchUpdate.custom(); boolean custom = batchUpdate.custom();
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args); return invokeDefault(6, sql, custom, rs, method, args);
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
if (batchSqlResult.getBatchList().isEmpty()) {
throw new CustomerException("execute batch sql error , batch sql args is empty!");
}
if (!batchSqlResult.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!");
}
boolean b = rs.executeBatchSql(batchSqlResult.getSqlStr(), batchSqlResult.getBatchList());
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
} }
boolean hasBatchDelete = method.isAnnotationPresent(BatchDelete.class); BatchDelete batchDelete = method.getAnnotation(BatchDelete.class);
if (hasBatchDelete) { if (batchDelete != null) {
BatchDelete batchDelete = method.getAnnotation(BatchDelete.class);
String sql = batchDelete.value(); String sql = batchDelete.value();
Class<?> returnType = method.getReturnType();
boolean custom = batchDelete.custom(); boolean custom = batchDelete.custom();
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args); return invokeDefault(7, sql, custom, rs, method, args);
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
if (batchSqlResult.getBatchList().isEmpty()) {
throw new CustomerException("execute batch sql error , batch sql args is empty!");
}
if (!batchSqlResult.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!");
}
boolean b = rs.executeBatchSql(batchSqlResult.getSqlStr(), batchSqlResult.getBatchList());
if (returnType.equals(void.class)) {
return null;
}
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
return b;
}
} }
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) {

View File

@ -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,7 +398,9 @@ 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);
} }
propertyDescriptor.getWriteMethod().invoke(o, value); if (!Objects.isNull(value)) {
propertyDescriptor.getWriteMethod().invoke(o, value);
}
} }
} catch (Exception e) { } catch (Exception 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);
propertyDescriptor.getWriteMethod().invoke(o, cassociationValue); if (cassociationValue == null) {
continue;
}
if (paramType.containsKey(declaredField.getType())) {
cassociationValue = paramType.get(declaredField.getType()).apply(String.valueOf(cassociationValue));
}
try {
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);
propertyDescriptor.getWriteMethod().invoke(o, collection); try {
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);
} }
propertyDescriptor.getWriteMethod().invoke(o, value); try {
if (!Objects.isNull(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;
} }

View File

@ -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);
});
}
}

View File

@ -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())
);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;
/**
* 使BeanBeanMap
*
* @author Looly
* @since 5.1.1
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Alias {
/**
* 使
*
* @return
*/
String value();
}

View File

@ -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);
}
}

View File

@ -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 "";
}

View File

@ -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()
);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @param isToCombination
* @return
*/
public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination) {
return getAnnotations(annotationEle, isToCombination, (Predicate<Annotation>) null);
}
/**
*
*
* @param <T>
* @param annotationEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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;
}
/**
* SOURCECLASSRUNTIME 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} AABBCD
* <pre>
* |-&gt; C.class [@a, @b]
* A.class -&gt; B.class [@a] -|
* |-&gt; 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>
* |-&gt; B.class [@a, @b]
* A.class [@a] -|
* |-&gt; 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] -&gt; B#X()[@b] -&gt; 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}ABC
* <pre>
* A -&gt; M3
* B -&gt; M1 -&gt; M2 -&gt; M3
* C -&gt; M2 -&gt; M3
* </pre>
* {@code annotationType}{@code M2}B
*
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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}ABC
* <pre>
* A -&gt; M1 -&gt; M2
* B -&gt; M3 -&gt; M1 -&gt; M2
* C -&gt; M2
* </pre>
* {@code annotationType}{@code M1}AB
*
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
/**
* JDKSpring<br>
* 使
*
* @author Succy, Looly
* @since 4.0.9
**/
public class CombinationAnnotationElement implements AnnotatedElement, Serializable {
private static final long serialVersionUID = 1L;
/**
* CombinationAnnotationElement
*
* @param element ClassMethodFieldConstructorReflectPermission
* @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 ClassMethodFieldConstructorReflectPermission
*/
public CombinationAnnotationElement(AnnotatedElement element) {
this(element, null);
}
/**
*
*
* @param element ClassMethodFieldConstructorReflectPermission
* @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);
}
}

View File

@ -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 "";
}

View File

@ -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();
}
}

View File

@ -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
* ABBCAABC{@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);
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}

View File

@ -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 "";
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
/**
* 使BeanBeanMap<br>
* setXXXgetXXX
*
* @author Looly
* @since 5.4.2
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface PropIgnore {
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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>
* abab{@link MirroredAnnotationAttribute}
* cacab{@link AliasedAnnotationAttribute}<br>
* ab{@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;
}
}

View File

@ -0,0 +1,7 @@
/**
*
*
* @author looly
*
*/
package aiyh.utils.tool.cn.hutool.core.annotation;

View File

@ -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;
}
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @return
*/
default boolean support(AnnotatedElement annotatedEle) {
return false;
}
/**
* {@link #support(AnnotatedElement)}true
*
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @return
*/
default List<Annotation> getAnnotationsIfSupport(AnnotatedElement annotatedEle) {
return support(annotatedEle) ? getAnnotations(annotatedEle) : Collections.emptyList();
}
/**
*
* {@link #support(AnnotatedElement)}true
*
* @param consumer
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @param filter
*/
default void scanIfSupport(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
if (support(annotatedEle)) {
scan(consumer, annotatedEle, filter);
}
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @return
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return ObjectUtil.isNotNull(annotatedEle);
}
/**
* {@link AnnotatedElement}{@link #support(AnnotatedElement)}true
*
* @param consumer
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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));
}
}

View File

@ -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
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @return
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Field;
}
/**
* {@link Field}{@link #support(AnnotatedElement)}true
*
* @param consumer
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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);
}
}
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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);
})
);
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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}ClassMethodFieldConstructorReflectPermission
* @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());
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @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());
}
}

View File

@ -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}ClassMethodFieldConstructorReflectPermission
* @return
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Class;
}
/**
* {@link Class}
*
* @param annotatedEle {@link AnnotatedElement}ClassMethodFieldConstructorReflectPermission
* @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;
}
}
}

View File

@ -0,0 +1,7 @@
/**
*
*
* @author looly
*
*/
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;

View File

@ -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;
/**
* BeanBeanInfoJavaBeansettersgetters<br>
* GetterSetter
*
* <pre>
* 1.
* 2. GettergetXXXisXXXgetIsXXX
* 3. SettersetXXXsetIsXXX
* 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 truefalse
* @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();
}
/**
* Getternull
*
* @param fieldName
* @return Getter
*/
public Method getGetter(String fieldName) {
final PropDesc desc = this.propMap.get(fieldName);
return null == desc ? null : desc.getGetter();
}
/**
* Setternull
*
* @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>
* GetterSettergetXXXsetXXX
*
* @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>
* GetterSetter
*
* <pre>
* 1.
* 2. GettergetXXXisXXXgetIsXXX
* 3. SettersetXXXsetIsXXX
* 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;
}
/**
* GetterSetter
*
* @param field
* @param gettersOrSetters GetterSetter
* @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
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
/**
* BeanBeanBean<br>
* Bean
* <ol>
* <li>.BeanMapkey</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;
/**
* BeanBean<br>
* BeanBeanBean<br>
* Bean
* <ol>
* <li>.BeanMapkey</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 BeanMapList
* @return null
*/
public Object get(final Object bean) {
return get(this.patternParts, bean, false);
}
/**
* filed<br>
* ListMapputkeyBean<br>
*
*
* <pre>
* 1. ListList
* 2.
* </pre>
*
* @param bean BeanMapList
* @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>
* ListMapputkeyBean<br>
*
*
* <pre>
* 1. ListList
* 2.
* </pre>
*
* @param bean BeanMapList
* @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 BeanMapList
* @param ignoreLast setread
* @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

View File

@ -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;
/**
* BeanBean<br>
* MapBean
*
* @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();
}
}

View File

@ -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;
}
}

View File

@ -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;
/**
* gettersetter
*
* @author looly
*/
public class PropDesc {
/**
*
*/
final Field field;
/**
* Getter
*/
protected Method getter;
/**
* Setter
*/
protected Method setter;
/**
* <br>
* GetterSetter访
*
* @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>
* GetterSetter
*
* @return
*/
public Type getFieldType() {
if (null != this.field) {
return TypeUtil.getType(this.field);
}
return findPropType(getter, setter);
}
/**
* <br>
* GetterSetter
*
* @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>
* GetterGetterpublic<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>
* GetterGetterpublic
*
* @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>
* SetterSetterpublic<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
/**
* GetterSetter
*
* @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;
}
/**
* GetterSetter
*
* @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);
}
/**
* GetterTransient
*
* @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;
}
/**
* GetterTransient
*
* @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
}

View File

@ -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);
}
}

View File

@ -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 BeanMap
* @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 BeanMap
* @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 BeanMap
* @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();
}
}

View File

@ -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;
/**
* BeanBean
*
* @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;
}
}

View File

@ -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;
/**
* BeanMap
*
* @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;
}
}

View File

@ -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>
* 1editable<br>
* 2nulltrue: false: null<br>
* 3<br>
*
* @author Looly
*/
public class CopyOptions implements Serializable {
private static final long serialVersionUID = 1L;
/**
* editable<br>
* MapBean
*/
protected Class<?> editable;
/**
* nulltrue: 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)}keykey
*/
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 nulltrue: 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 nulltrue: 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;
}
/**
* nulltrue: false: null
*
* @param ignoreNullVall nulltrue: 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>
* 使ValueProviderfieldMappingkeyBeanvaluekey
*
* @param fieldMapping
* @return CopyOptions
*/
public CopyOptions setFieldMapping(Map<String, String> fieldMapping) {
return setFieldNameEditor((key -> fieldMapping.getOrDefault(key, key)));
}
/**
* 线<br>
* <br>
* null<br>
* 使ValueProviderfieldMappingkeyBeanvaluekey
*
* @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);
}
}

View File

@ -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;
/**
* MapBean
*
* @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;
}
/**
* MapBean<br>
* isXxxis
*
* @param targetPropDescMap beanMap
* @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;
}
}

View File

@ -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;
/**
* MapMap
*
* @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;
}
}

View File

@ -0,0 +1,34 @@
package aiyh.utils.tool.cn.hutool.core.bean.copier;
import java.lang.reflect.Type;
/**
* Bean<br>
* <br>
* BeanBeanBean<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>
* Mapkeyvaluenullnull
*
* @param key Bean
* @return KEY
*/
boolean containsKey(T key);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
/**
* Bean
*
* @author looly
*
*/
package aiyh.utils.tool.cn.hutool.core.bean.copier;

View File

@ -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 Booleannull
* @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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,7 @@
/**
* Bean
*
* @author looly
*
*/
package aiyh.utils.tool.cn.hutool.core.bean.copier.provider;

View File

@ -0,0 +1,7 @@
/**
* BeanBeanBeanBeanBean
*
* @author looly
*
*/
package aiyh.utils.tool.cn.hutool.core.bean;

View File

@ -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();
}

View File

@ -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 loolyApache-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;
}
//-----------------------------------------------------------------------
/**
* Beanprivate
*
* <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();
}
}

View File

@ -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>equalshashCodehashCodeequals</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();
}
}
}
}
/**
* equalstrue
*/
private boolean isEquals = true;
/**
* true
*/
public EqualsBuilder() {
// do nothing for now.
}
//-------------------------------------------------------------------------
/**
* <p>equalsequals</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>equalsequals</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;
}
}

View File

@ -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">java8Builder</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(() -&gt; 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>52</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);
}
/**
* 1GenericBuilder
*
* @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));
}
/**
* 2GenericBuilder
*
* @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));
}
/**
* 3GenericBuilder
*
* @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));
}
/**
* 4GenericBuilder
*
* @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));
}
/**
* 5GenericBuilder
*
* @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 1Consumer
* @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 2Consumer
* @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;
}
}

View File

@ -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 loolyApache-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