diff --git a/.gitignore b/.gitignore index c6d98d1..5ff6309 100644 --- a/.gitignore +++ b/.gitignore @@ -1,98 +1,38 @@ -# ---> JetBrains -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -# ---> Maven target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -# https://github.com/takari/maven-wrapper#usage-without-binary-jar -.mvn/wrapper/maven-wrapper.jar +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ -# Eclipse m2e generated files -# Eclipse Core -.project -# JDT-specific (Eclipse Java Development Tools) +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated .classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7d3b0cf --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,4 @@ +# Default ignored files +/shelf/ +/workspace.xml +/CopilotChatHistory.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..bada8b5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e7c1e9f --- /dev/null +++ b/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + com.imyeyu.java + timi-java + 0.0.1 + jar + + + 21 + 21 + UTF-8 + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 21 + 21 + UTF-8 + + + + + + + + org.junit.jupiter + junit-jupiter + 5.10.3 + test + + + diff --git a/src/main/java/com/imyeyu/java/TimiJava.java b/src/main/java/com/imyeyu/java/TimiJava.java new file mode 100644 index 0000000..233e87d --- /dev/null +++ b/src/main/java/com/imyeyu/java/TimiJava.java @@ -0,0 +1,86 @@ +package com.imyeyu.java; + +import java.util.Collection; +import java.util.Map; + +/** + * + * @author 夜雨 + * @since 2021-02-13 11:39 + */ +public interface TimiJava { + + /** + * 通用判空 + *
+	 *     TimiJava.isEmpty(null)                = true;
+	 *     TimiJava.isEmpty("")                  = true;
+	 *     TimiJava.isEmpty(" ")                 = true;
+	 *     TimiJava.isEmpty((StringBuilder) "")  = true;
+	 *     TimiJava.isEmpty((StringBuilder) " ") = true;
+	 *     TimiJava.isEmpty((StringBuffer) "")   = true;
+	 *     TimiJava.isEmpty((StringBuffer) " ")  = true;
+	 *     TimiJava.isEmpty([])                  = true;
+	 *     TimiJava.isEmpty(list.size == 0)      = true;
+	 *     TimiJava.isEmpty(set.size == 0)       = true;
+	 *     TimiJava.isEmpty(map.size == 0)       = true;
+	 *
+	 *     otherwise                             = false;
+	 * 
+ * + * @param object 对象 + * @return true 为空 + */ + static boolean isEmpty(Object object) { + if (object == null) { + return true; + } + if (object instanceof CharSequence charSequence) { + if (charSequence instanceof String string) { + return string.isEmpty() || string.trim().isEmpty(); + } else { + return charSequence.toString().trim().isEmpty(); + } + } + if (object.getClass().isArray()) { + return ((Object[]) object).length == 0; + } + if (object instanceof Collection list) { + return list.isEmpty(); + } + if (object instanceof Map map) { + return map.isEmpty(); + } + return false; + } + + /** + * {@link #isEmpty(Object)} 取反 + * + * @param object 判定对象 + * @return true 为非空 + */ + static boolean isNotEmpty(Object object) { + return !isEmpty(object); + } + + @SafeVarargs + static T firstNotNull(T... objects) { + for (int i = 0; i < objects.length; i++) { + if (objects[i] != null) { + return objects[i]; + } + } + return null; + } + + @SafeVarargs + static T firstNotEmpty(T... objects) { + for (int i = 0; i < objects.length; i++) { + if (TimiJava.isNotEmpty(objects[i])) { + return objects[i]; + } + } + return null; + } +} diff --git a/src/main/java/com/imyeyu/java/bean/Callback.java b/src/main/java/com/imyeyu/java/bean/Callback.java new file mode 100644 index 0000000..83f041e --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/Callback.java @@ -0,0 +1,20 @@ +package com.imyeyu.java.bean; + +/** + * 通用执行接口 + * + * @author 夜雨 + * @since 2022-02-09 20:06 + */ +public interface Callback { + + /** 执行程序 */ + void handler() throws RuntimeException; + + static void handle(Callback callback) { + if (callback == null) { + return; + } + callback.handler(); + } +} diff --git a/src/main/java/com/imyeyu/java/bean/CallbackArg.java b/src/main/java/com/imyeyu/java/bean/CallbackArg.java new file mode 100644 index 0000000..7fcf092 --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/CallbackArg.java @@ -0,0 +1,32 @@ +package com.imyeyu.java.bean; + +/** + * 通用回调接口 + * + * @author 夜雨 + * @since 2021-11-09 20:53 + * + * @param 执行程序入参泛型 + */ +public interface CallbackArg { + + /** + * 执行程序 + * + * @param t 入参 + * @throws RuntimeException 运行时异常 + */ + void handler(T t) throws RuntimeException; + + /** + * 非空时执行回调 + * + * @param callbackArg 回调 + */ + static void handle(T t, CallbackArg callbackArg) { + if (callbackArg == null) { + return; + } + callbackArg.handler(t); + } +} diff --git a/src/main/java/com/imyeyu/java/bean/CallbackArgReturn.java b/src/main/java/com/imyeyu/java/bean/CallbackArgReturn.java new file mode 100644 index 0000000..3881b9b --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/CallbackArgReturn.java @@ -0,0 +1,34 @@ +package com.imyeyu.java.bean; + +/** + * 通用可返回回调接口 + * + * @author 夜雨 + * @since 2022-05-26 14:24 + * + * @param 执行程序入参泛型 + * @param 执行程序返回泛型 + */ +public interface CallbackArgReturn { + + /** + * 执行程序 + * + * @param t 入参 + * @return 执行返回 + * @throws RuntimeException 运行时异常 + */ + R handler(T t) throws RuntimeException; + + /** + * 非空时执行回调 + * + * @param callbackArgReturn 回调 + */ + static R handle(T t, CallbackArgReturn callbackArgReturn) { + if (callbackArgReturn == null) { + return null; + } + return callbackArgReturn.handler(t); + } +} diff --git a/src/main/java/com/imyeyu/java/bean/CallbackReturn.java b/src/main/java/com/imyeyu/java/bean/CallbackReturn.java new file mode 100644 index 0000000..d36a566 --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/CallbackReturn.java @@ -0,0 +1,32 @@ +package com.imyeyu.java.bean; + +/** + * 通用执行回调接口 + * + * @author 夜雨 + * @since 2022-02-09 19:59 + * + * @param 执行返回泛型 + */ +public interface CallbackReturn { + + /**s + * 执行程序 + * + * @return 执行返回 + * @throws RuntimeException 运行时异常 + */ + R handler() throws RuntimeException; + + /** + * 非空时执行回调 + * + * @param callbackReturn 回调 + */ + static R handle(CallbackReturn callbackReturn) { + if (callbackReturn == null) { + return null; + } + return callbackReturn.handler(); + } +} diff --git a/src/main/java/com/imyeyu/java/bean/Language.java b/src/main/java/com/imyeyu/java/bean/Language.java new file mode 100644 index 0000000..c2cd28d --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/Language.java @@ -0,0 +1,47 @@ +package com.imyeyu.java.bean; + +/** + * 多语言 + * + * @author 夜雨 + * @since 2022-02-23 11:25 + */ +public enum Language { + + /** 英语 */ + en_US("English"), + + /** 简中 */ + zh_CN("简体中文"), + + /** 繁中 */ + zh_TW("繁体中文"), + + /** 日语 */ + ja_JP("日本語"), + + /** 韩语 */ + ko_KR("한국인"), + + /** 俄语 */ + ru_RU("русский"), + + /** 德语 */ + de_DE("Deutsch"); + + /** 名称 */ + final String name; + + Language(String name) { + this.name = name; + } + + /** + * 获取语言名称 + * + * @return 语言名称 + */ + public String getName() { + return name; + } +} diff --git a/src/main/java/com/imyeyu/java/bean/LanguageMapping.java b/src/main/java/com/imyeyu/java/bean/LanguageMapping.java new file mode 100644 index 0000000..7949abe --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/LanguageMapping.java @@ -0,0 +1,18 @@ +package com.imyeyu.java.bean; + +/** + * @author 夜雨 + * @since 2024-04-03 11:27 + */ +public interface LanguageMapping { + + void add(String key, String value); + + boolean has(String key); + + String text(String key); + + String text(String key, String def); + + String textArgs(String key, Object... args); +} diff --git a/src/main/java/com/imyeyu/java/bean/LanguageMsgMapping.java b/src/main/java/com/imyeyu/java/bean/LanguageMsgMapping.java new file mode 100644 index 0000000..64ba65f --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/LanguageMsgMapping.java @@ -0,0 +1,20 @@ +package com.imyeyu.java.bean; + +/** + * @author 夜雨 + * @since 2024-04-01 10:28 + */ +public interface LanguageMsgMapping { + + T msg(String msg); + + T msgKey(String msgKey); + + void setMsg(String msg); + + String getMsg(); + + void setMsgKey(String msgKey); + + String getMsgKey(); +} diff --git a/src/main/java/com/imyeyu/java/bean/package-info.java b/src/main/java/com/imyeyu/java/bean/package-info.java new file mode 100644 index 0000000..ec58c6f --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/package-info.java @@ -0,0 +1,2 @@ +/** 通用 Java Bean,通用接口 */ +package com.imyeyu.java.bean; diff --git a/src/main/java/com/imyeyu/java/bean/timi/TimiCode.java b/src/main/java/com/imyeyu/java/bean/timi/TimiCode.java new file mode 100644 index 0000000..d2fed91 --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/timi/TimiCode.java @@ -0,0 +1,135 @@ +package com.imyeyu.java.bean.timi; + +/** + * 通用代码(基于 HTTP 代码扩展) + * + * @author 夜雨 + * @since 2021-05-21 14:32 + */ +public enum TimiCode { + + // ---------- 200 正常 ---------- + + /** 执行成功 */ + SUCCESS(20000), + + /** 执行成功,忽略请求 */ + IGNORE (20001), + + // ---------- 400 参数异常 ---------- + + /** 缺少参数 */ + ARG_MISS(40000), + + /** 不合法的参数 */ + ARG_BAD(40001), + + /** 过期的参数 */ + ARG_EXPIRED(40002), + + // ---------- 401 权限异常 ---------- + + /** 无权限 */ + PERMISSION_MISS(40100), + + /** 无权限 */ + PERMISSION_ERROR (40101), + + /** 非法请求 */ + REQUEST_BAD(40102), + + // ---------- 403 数据异常 ---------- + + /** 数据已存在 */ + DATA_EXIST (40301), + + // ---------- 404 资源异常 ---------- + + /** 无数据 */ + RESULT_NULL(40400), + + /** 返回数据异常 */ + RESULT_BAD (40401), + + /** 禁用的数据 */ + RESULT_BAN (40402), + + /** 上游服务器连接超时 */ + RESULT_TIMEOUT (40403), + + // ---------- 500 致命异常 ---------- + + /** 服务异常 */ + ERROR(50000), + + /** 服务异常 */ + ERROR_NOT_SUPPORT(50001), + + /** 服务异常 */ + ERROR_NPE_VARIABLE(50002), + + /** 服务关闭 */ + ERROR_SERVICE_OFF(50003), + + /** 服务繁忙 */ + ERROR_SERVICE_BUSY(50300); + + final Integer value; + + TimiCode(Integer value) { + this.value = value; + } + + /** + * 转为通用异常 + * + * @return 异常 + */ + public TimiException toException() { + return toException(toString()); + } + + /** + * 转为通用异常 + * + * @param msg 异常消息 + * @return 异常 + */ + public TimiException toException(String msg) { + return new TimiException(this, msg); + } + + /** + * 转为通用返回对象 + * + * @return 返回对象 + */ + public TimiResponse toResponse() { + return new TimiResponse<>(this).msg(toString()); + } + + /** + * 获取代码 + * + * @return 代码 + */ + public Integer getValue() { + return value; + } + + /** + * 根据代码返回对象 + * + * @param code 代码 + * @return 对象 + */ + public static TimiCode fromCode(int code) { + TimiCode[] codes = values(); + for (int i = 0; i < codes.length; i++) { + if (codes[i].getValue() == code) { + return codes[i]; + } + } + return null; + } +} diff --git a/src/main/java/com/imyeyu/java/bean/timi/TimiError.java b/src/main/java/com/imyeyu/java/bean/timi/TimiError.java new file mode 100644 index 0000000..85cfd4c --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/timi/TimiError.java @@ -0,0 +1,77 @@ +package com.imyeyu.java.bean.timi; + +import com.imyeyu.java.bean.LanguageMsgMapping; + +/** + * 致命错误 + * + * @author 夜雨 + * @since 2023-04-27 15:55 + */ +public class TimiError extends AssertionError implements LanguageMsgMapping { + + /** 代码 */ + protected final TimiCode code; + + protected String msgKey; + + /** @param code 代码 */ + public TimiError(TimiCode code) { + this.code = code; + } + + /** + * 构造器 + * + * @param code 代码 + * @param msg 消息 + */ + public TimiError(TimiCode code, String msg) { + super(msg); + this.code = code; + } + + /** + * 获取代码 + * + * @return 代码 + */ + public TimiCode getCode() { + return code; + } + + public TimiResponse toResponse() { + return new TimiResponse<>(code).msg(getMessage()).msgKey(msgKey); + } + + @Override + public TimiError msg(String msg) { + throw new UnsupportedOperationException("unsupported to change message"); + } + + @Override + public TimiError msgKey(String msgKey) { + setMsgKey(msgKey); + return this; + } + + @Override + public void setMsg(String msg) { + throw new UnsupportedOperationException("unsupported to change message"); + } + + @Override + public String getMsg() { + return getMessage(); + } + + @Override + public void setMsgKey(String msgKey) { + this.msgKey = msgKey; + } + + @Override + public String getMsgKey() { + return msgKey; + } +} diff --git a/src/main/java/com/imyeyu/java/bean/timi/TimiException.java b/src/main/java/com/imyeyu/java/bean/timi/TimiException.java new file mode 100644 index 0000000..d76ef6f --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/timi/TimiException.java @@ -0,0 +1,133 @@ +package com.imyeyu.java.bean.timi; + +import com.imyeyu.java.TimiJava; +import com.imyeyu.java.bean.CallbackReturn; +import com.imyeyu.java.bean.LanguageMsgMapping; + +/** + * 通用运行时异常,附加通用代码 + * + * @author 夜雨 + * @since 2021-05-20 15:18 + */ +public class TimiException extends RuntimeException implements LanguageMsgMapping { + + /** 代码 */ + protected final TimiCode code; + + protected String msgKey; + + /** @param code 代码 */ + public TimiException(TimiCode code) { + this.code = code; + } + + /** + * 构造器 + * + * @param code 代码 + * @param msg 消息 + */ + public TimiException(TimiCode code, String msg) { + super(msg); + this.code = code; + } + + /** + * 构造器 + * + * @param code 代码 + * @param msg 消息 + */ + public TimiException(TimiCode code, String msg, Throwable e) { + super(msg, e); + this.code = code; + } + + /** + * 获取代码 + * + * @return 代码 + */ + public TimiCode getCode() { + return code; + } + + public TimiResponse toResponse() { + return new TimiResponse<>(code).msg(getMessage()).msgKey(msgKey); + } + + @Override + public TimiException msg(String msg) { + throw new UnsupportedOperationException("unsupported to change message"); + } + + @Override + public TimiException msgKey(String msgKey) { + setMsgKey(msgKey); + return this; + } + + @Override + public void setMsg(String msg) { + throw new UnsupportedOperationException("unsupported to change message"); + } + + @Override + public String getMsg() { + return getMessage(); + } + + @Override + public void setMsgKey(String msgKey) { + this.msgKey = msgKey; + } + + @Override + public String getMsgKey() { + return msgKey; + } + + public static T required(T t, String message) { + if (TimiJava.isEmpty(t)) { + throw new TimiException(TimiCode.ARG_MISS, message); + } + return t; + } + + public static void requiredNull(T t, String message) throws TimiException { + if (t != null) { + throw new TimiException(TimiCode.ERROR, message); + } + } + + public static void requiredTrue(boolean bool, String message) throws TimiException { + if (bool) { + return; + } + throw new TimiException(TimiCode.ERROR, message); + } + + public static void requiredFalse(boolean bool, String message) throws TimiException { + if (!bool) { + return; + } + throw new TimiException(TimiCode.ERROR, message); + } + + public static void requiredTrue(CallbackReturn callback, String message) throws TimiException { + TimiException.required(callback, "not found callback"); + if (callback.handler()) { + return; + } + throw new TimiException(TimiCode.ERROR, message); + } + + public static void requiredFalse(CallbackReturn callback, String message) throws TimiException { + TimiException.required(callback, "not found callback"); + if (!callback.handler()) { + return; + } + throw new TimiException(TimiCode.ERROR, message); + } +} diff --git a/src/main/java/com/imyeyu/java/bean/timi/TimiResponse.java b/src/main/java/com/imyeyu/java/bean/timi/TimiResponse.java new file mode 100644 index 0000000..c3b1e3c --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/timi/TimiResponse.java @@ -0,0 +1,168 @@ +package com.imyeyu.java.bean.timi; + +import com.imyeyu.java.bean.LanguageMsgMapping; + +import java.io.Serializable; + +/** + * 通用接口返回对象 + * + * @author 夜雨 + * @since 2021-07-01 20:18 + */ +public class TimiResponse implements Serializable, LanguageMsgMapping> { + + /** 代码 */ + protected Integer code; + + /** 消息 */ + protected String msg; + + protected String msgKey; + + /** 数据体 */ + protected T data; + + /** 默认构造器 */ + public TimiResponse() { + } + + /** + * 构造器 + * + * @param code 代码 + */ + public TimiResponse(TimiCode code) { + this(code, null); + } + + /** + * 构造器 + * + * @param code 代码 + * @param data 数据体 + */ + public TimiResponse(TimiCode code, T data) { + this(code, data, null); + } + + /** + * 构造器 + * + * @param code 代码 + * @param data 数据体 + * @param msg 消息 + */ + public TimiResponse(TimiCode code, T data, String msg) { + this.code = code.getValue(); + this.data = data; + this.msg = msg; + } + + /** @return true 为成功,code 为 20000 段 */ + public boolean isSuccess() { + return code != null && code <= 30000; + } + + /** @return true 为失败,isSuccess 取反 */ + public boolean isFail() { + return !isSuccess(); + } + + /** + * 转为通用异常 + * + * @return 异常 + */ + public TimiException toException() { + TimiCode code = TimiCode.fromCode(this.code); + if (code == null) { + throw new NullPointerException("unknow timi code: " + this.code); + } + return code.toException(msg); + } + + /** + * 获取代码 + * + * @return 代码 + */ + public Integer getCode() { + return code; + } + + /** + * 设置代码 + * + * @param code 代码 + */ + public void setCode(Integer code) { + this.code = code; + } + + /** + * 获取数据体 + * + * @return 数据体 + */ + public T getData() { + return data; + } + + /** + * 设置数据体 + * + * @param data 数据体 + */ + public void setData(T data) { + this.data = data; + } + + /** + * 追加消息(避免和泛型字符串冲突) + * + * @param msg 消息 + * @return 本对象 + */ + @Override + public TimiResponse msg(String msg) { + this.msg = msg; + return this; + } + + /** + * 获取消息 + * + * @return 消息 + */ + @Override + public String getMsg() { + return msg; + } + + /** + * 设置消息 + * + * @param msg 消息 + */ + @Override + public void setMsg(String msg) { + this.msg = msg; + } + + @Override + public TimiResponse msgKey(String msgKey) { + this.msgKey = msgKey; + return this; + } + + @Override + public String getMsgKey() { + return msgKey; + } + + @Override + public void setMsgKey(String msgKey) { + this.msgKey = msgKey; + } +} diff --git a/src/main/java/com/imyeyu/java/bean/timi/package-info.java b/src/main/java/com/imyeyu/java/bean/timi/package-info.java new file mode 100644 index 0000000..fdb3713 --- /dev/null +++ b/src/main/java/com/imyeyu/java/bean/timi/package-info.java @@ -0,0 +1,2 @@ +/** 个人常用的通用类 */ +package com.imyeyu.java.bean.timi; \ No newline at end of file diff --git a/src/main/java/com/imyeyu/java/obs/ChangeListener.java b/src/main/java/com/imyeyu/java/obs/ChangeListener.java new file mode 100644 index 0000000..6caec26 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ChangeListener.java @@ -0,0 +1,18 @@ +package com.imyeyu.java.obs; + +import java.util.List; + +/** + * @author 夜雨 + * @since 2024-09-01 15:15 + */ +public interface ChangeListener { + + void handler(T from, T to); + + static void notifyListener(List> listenerList, T from, T to) { + for (int i = 0; i < listenerList.size(); i++) { + listenerList.get(i).handler(from, to); + } + } +} diff --git a/src/main/java/com/imyeyu/java/obs/CollectionChangeListener.java b/src/main/java/com/imyeyu/java/obs/CollectionChangeListener.java new file mode 100644 index 0000000..c075b35 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/CollectionChangeListener.java @@ -0,0 +1,29 @@ +package com.imyeyu.java.obs; + +import java.util.List; + +/** + * @author 夜雨 + * @since 2024-09-01 15:59 + */ +public interface CollectionChangeListener { + + /** + * @author 夜雨 + * @since 2024-09-01 19:47 + */ + enum ChangeType { + + ADD, + + REMOVE + } + + void handler(ChangeType type, E e) throws RuntimeException; + + static void notifyListener(List> listenerList, ChangeType type, E element) { + for (int i = 0; i < listenerList.size(); i++) { + listenerList.get(i).handler(type, element); + } + } +} diff --git a/src/main/java/com/imyeyu/java/obs/MapChangeListener.java b/src/main/java/com/imyeyu/java/obs/MapChangeListener.java new file mode 100644 index 0000000..b343b96 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/MapChangeListener.java @@ -0,0 +1,34 @@ +package com.imyeyu.java.obs; + +import java.util.List; + +/** + * @author 夜雨 + * @since 2024-09-01 18:01 + */ +public interface MapChangeListener { + + /** + * + * + * @author 夜雨 + * @since 2024-09-01 23:51 + */ + enum ChangeType { + + ADD, + + UPDATE, + + REMOVE + } + + void handler(ChangeType type, K key, V value); + + @SuppressWarnings("unchecked") + static void notifyListener(List> listenerList, ChangeType type, Object key, V value) { + for (int i = 0; i < listenerList.size(); i++) { + listenerList.get(i).handler(type, (K) key, value); + } + } +} diff --git a/src/main/java/com/imyeyu/java/obs/Observable.java b/src/main/java/com/imyeyu/java/obs/Observable.java new file mode 100644 index 0000000..07fa696 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/Observable.java @@ -0,0 +1,16 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 15:07 + */ +public interface Observable { + + T get(); + + void set(T value); + + void addListener(ChangeListener changeListener); + + void removeListener(ChangeListener changeListener); +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableBoolean.java b/src/main/java/com/imyeyu/java/obs/ObservableBoolean.java new file mode 100644 index 0000000..b65a8e2 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableBoolean.java @@ -0,0 +1,16 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 18:43 + */ +public class ObservableBoolean extends ObservableObject { + + public ObservableBoolean() { + super(); + } + + public ObservableBoolean(Boolean value) { + super(value); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableDouble.java b/src/main/java/com/imyeyu/java/obs/ObservableDouble.java new file mode 100644 index 0000000..316741b --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableDouble.java @@ -0,0 +1,16 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 18:43 + */ +public class ObservableDouble extends ObservableObject { + + public ObservableDouble() { + super(); + } + + public ObservableDouble(Number value) { + super(value.doubleValue()); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableFloat.java b/src/main/java/com/imyeyu/java/obs/ObservableFloat.java new file mode 100644 index 0000000..1a3c2ab --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableFloat.java @@ -0,0 +1,16 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 18:43 + */ +public class ObservableFloat extends ObservableObject { + + public ObservableFloat() { + super(); + } + + public ObservableFloat(Float value) { + super(value); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableInteger.java b/src/main/java/com/imyeyu/java/obs/ObservableInteger.java new file mode 100644 index 0000000..9047cc6 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableInteger.java @@ -0,0 +1,15 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 15:06 + */ +public class ObservableInteger extends ObservableObject { + + public ObservableInteger() { + } + + public ObservableInteger(Integer value) { + super(value); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableList.java b/src/main/java/com/imyeyu/java/obs/ObservableList.java new file mode 100644 index 0000000..6dce667 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableList.java @@ -0,0 +1,114 @@ +package com.imyeyu.java.obs; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +/** + * @author 夜雨 + * @since 2024-09-01 17:51 + */ +public class ObservableList extends ArrayList { + + private final List> changeListenerList = new ArrayList<>(); + + public ObservableList() { + } + + public ObservableList(Collection c) { + super(c); + } + + public void addChangeListener(CollectionChangeListener listener) { + changeListenerList.add(listener); + } + + public void removeChangeListener(CollectionChangeListener listener) { + changeListenerList.add(listener); + } + + @Override + public boolean add(E e) { + boolean result = super.add(e); + if (result) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, e); + } + return result; + } + + @Override + public void add(int index, E element) { + super.add(index, element); + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, element); + } + + @Override + public boolean addAll(Collection c) { + boolean result = super.addAll(c); + if (result) { + for (E e : c) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, e); + } + } + return result; + } + + @Override + public boolean addAll(int index, Collection c) { + boolean result = super.addAll(index, c); + if (result) { + for (E e : c) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, e); + } + } + return result; + } + + @Override + public E remove(int index) { + E removed = super.remove(index); + if (removed != null) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.REMOVE, removed); + } + return removed; + } + + @Override + public boolean remove(Object o) { + return remove(indexOf(o)) != null; + } + + @SuppressWarnings("unchecked") + @Override + public boolean removeAll(Collection c) { + boolean result = super.removeAll(c); + if (result) { + for (Object e : c) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.REMOVE, (E) e); + } + } + return result; + } + + @Override + public boolean removeIf(Predicate filter) { + boolean removed = false; + Iterator iterator = iterator(); + while (iterator.hasNext()) { + E e = iterator.next(); + if (filter.test(e)) { + iterator.remove(); // 此方法会触发 notifyListener + removed = true; + } + } + return removed; + } + + @Override + public ObservableList subList(int fromIndex, int toIndex) { + List subList = super.subList(fromIndex, toIndex); + return new ObservableList<>(subList); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableLong.java b/src/main/java/com/imyeyu/java/obs/ObservableLong.java new file mode 100644 index 0000000..985be58 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableLong.java @@ -0,0 +1,16 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 18:43 + */ +public class ObservableLong extends ObservableObject { + + public ObservableLong() { + super(); + } + + public ObservableLong(Long value) { + super(value); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableMap.java b/src/main/java/com/imyeyu/java/obs/ObservableMap.java new file mode 100644 index 0000000..c74167b --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableMap.java @@ -0,0 +1,60 @@ +package com.imyeyu.java.obs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author 夜雨 + * @since 2024-09-01 18:32 + */ +public class ObservableMap extends HashMap { + + private final List> changeListenerList = new ArrayList<>(); + + public void addChangeListener(MapChangeListener listener) { + changeListenerList.add(listener); + } + + public void removeChangeListener(MapChangeListener listener) { + changeListenerList.add(listener); + } + + @Override + public V put(K key, V value) { + V v = super.put(key, value); + if (v == null) { + MapChangeListener.notifyListener(changeListenerList, MapChangeListener.ChangeType.ADD, key, value); + } else if (!v.equals(value)) { + MapChangeListener.notifyListener(changeListenerList, MapChangeListener.ChangeType.UPDATE, key, value); + } + return v; + } + + @Override + public void putAll(Map m) { + for (Map.Entry item : m.entrySet()) { + put(item.getKey(), item.getValue()); + } + } + + @Override + public V remove(Object key) { + V removed = super.remove(key); + if (removed != null) { + MapChangeListener.notifyListener(changeListenerList, MapChangeListener.ChangeType.REMOVE, key, removed); + } + return removed; + } + + @SuppressWarnings("unchecked") + @Override + public boolean remove(Object key, Object value) { + boolean result = super.remove(key, value); + if (result) { + MapChangeListener.notifyListener(changeListenerList, MapChangeListener.ChangeType.REMOVE, key, (V) value); + } + return result; + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableObject.java b/src/main/java/com/imyeyu/java/obs/ObservableObject.java new file mode 100644 index 0000000..c35f98b --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableObject.java @@ -0,0 +1,47 @@ +package com.imyeyu.java.obs; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author 夜雨 + * @since 2024-09-01 15:22 + */ +public abstract class ObservableObject implements Observable { + + private final List> changeListenerList = new ArrayList<>(); + + private T value; + + public ObservableObject() { + } + + public ObservableObject(T value) { + this.value = value; + } + + @Override + public final T get() { + return value; + } + + @Override + public final void set(T toValue) { + final T from = this.value; + this.value = toValue; + boolean isChanged = !from.equals(toValue); + if (isChanged) { + ChangeListener.notifyListener(changeListenerList, from, toValue); + } + } + + @Override + public void addListener(ChangeListener changeListener) { + changeListenerList.add(changeListener); + } + + @Override + public void removeListener(ChangeListener changeListener) { + changeListenerList.remove(changeListener); + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableSet.java b/src/main/java/com/imyeyu/java/obs/ObservableSet.java new file mode 100644 index 0000000..bf13e91 --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableSet.java @@ -0,0 +1,81 @@ +package com.imyeyu.java.obs; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +/** + * @author 夜雨 + * @since 2024-09-01 18:29 + */ +public class ObservableSet extends HashSet { + + private final List> changeListenerList = new ArrayList<>(); + + public void addChangeListener(CollectionChangeListener listener) { + changeListenerList.add(listener); + } + + public void removeChangeListener(CollectionChangeListener listener) { + changeListenerList.add(listener); + } + + @Override + public boolean add(E e) { + boolean result = super.add(e); + if (result) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, e); + } + return result; + } + + @Override + public boolean addAll(Collection c) { + boolean result = super.addAll(c); + if (result) { + for (E e : c) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, e); + } + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public boolean remove(Object o) { + boolean removed = super.remove(o); + if (removed) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.REMOVE, (E) o); + } + return removed; + } + + @SuppressWarnings("unchecked") + @Override + public boolean removeAll(Collection c) { + boolean result = super.removeAll(c); + if (result) { + for (Object e : c) { + CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.REMOVE, (E) e); + } + } + return result; + } + + @Override + public boolean removeIf(Predicate filter) { + boolean removed = false; + Iterator iterator = iterator(); + while (iterator.hasNext()) { + E e = iterator.next(); + if (filter.test(e)) { + iterator.remove(); // 此方法会触发 notifyListener + removed = true; + } + } + return removed; + } +} diff --git a/src/main/java/com/imyeyu/java/obs/ObservableString.java b/src/main/java/com/imyeyu/java/obs/ObservableString.java new file mode 100644 index 0000000..870f78f --- /dev/null +++ b/src/main/java/com/imyeyu/java/obs/ObservableString.java @@ -0,0 +1,29 @@ +package com.imyeyu.java.obs; + +/** + * @author 夜雨 + * @since 2024-09-01 18:43 + */ +public class ObservableString extends ObservableObject { + + public ObservableString() { + super(); + } + + public ObservableString(String value) { + super(value); + } + + public boolean isEmpty() { + return get().isEmpty() || get().trim().isEmpty(); + } + + public boolean isNotEmpty() { + return !isEmpty(); + } + + @Override + public String toString() { + return get(); + } +} diff --git a/src/main/java/com/imyeyu/java/ref/Ref.java b/src/main/java/com/imyeyu/java/ref/Ref.java new file mode 100644 index 0000000..59caf83 --- /dev/null +++ b/src/main/java/com/imyeyu/java/ref/Ref.java @@ -0,0 +1,266 @@ +package com.imyeyu.java.ref; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 反射相关 + * + * @author 夜雨 + * @since 2023-05-04 15:05 + */ +public class Ref { + + /** + * 获取类字段列表 + * + * @param clazz 类 + * @return 字段列表 + */ + public static List listFields(Class clazz) { + return List.of(clazz.getDeclaredFields()); + } + + /** + * 获取类字段列表(包括父类) + * + * @param clazz 类 + * @return 字段列表 + */ + public static List listAllFields(Class clazz) { + List result = new ArrayList<>(); + while (clazz != null && clazz != Object.class) { + Collections.addAll(result, clazz.getDeclaredFields()); + clazz = clazz.getSuperclass(); + } + return result; + } + + /** + * + * @param keyName + * @return + */ + public static String getFieldName(String keyName) { + String[] splits = {"-", "_", " "}; + StringBuilder full = new StringBuilder(keyName.substring(0, 1).toUpperCase() + keyName.substring(1)); + for (int i = 0; i < splits.length; i++) { + if (keyName.contains(splits[i])) { + // 存在分隔符 + full.setLength(0); + String[] word = keyName.split(splits[i]); + for (int j = 0; j < word.length; j++) { + full.append(word[j].substring(0, 1).toUpperCase()).append(word[j].substring(1)); + } + break; + } + } + return String.valueOf(full.charAt(0)).toLowerCase() + full.substring(1); + } + + /** + * 反射获取对象字段,包括父级类,直至 {@link Object},如果都不存在则返回 null + * + * @param clazz 类 + * @param fieldName 字段名 + * @return 字段 + */ + public static Field getField(Class clazz, String fieldName) { + do { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } while (clazz != Object.class); + throw new NullPointerException(String.format("not found field: %s in %s", fieldName, clazz)); + } + + /** + * 反射获取对象字段值,包括父级类,直至 {@link Object} + * + * @param object 对象 + * @param fieldName 字段名 + * @param toClass 返回类 + * @param 返回类型 + * @return 字段值 + * @throws IllegalAccessException 反射访问失败 + * @throws NullPointerException 向上反射直至 {@link Object} 也找不到该字段 + */ + public static T getFieldValue(Object object, String fieldName, Class toClass) throws IllegalAccessException, NullPointerException { + return getFieldValue(object, getField(object.getClass(), fieldName), toClass); + } + + /** + * 反射获取对象字段值 + * + * @param object 对象 + * @param field 字段 + * @param toClass 返回类 + * @param 返回类型 + * @return 字段值 + * @throws IllegalAccessException 反射访问失败 + * @throws NullPointerException 向上反射直至 {@link Object} 也找不到该字段 + */ + public static T getFieldValue(Object object, Field field, Class toClass) throws IllegalAccessException, NullPointerException { + if (field == null) { + throw new NullPointerException("field can not be null"); + } + field.setAccessible(true); + return toClass.cast(field.get(object)); + } + + /** + * 反射获取类字段 + * + * @param objectClass 类 + * @param fieldName 字段名 + * @return 字段 + */ + public static Field getClassField(Class objectClass, String fieldName) { + try { + Field field = objectClass.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + return null; + } + } + + /** + * 反射获取指定类字段值 + * + * @param object 对象 + * @param objectClass 类 + * @param fieldName 字段名 + * @param toClass 值类型 + * @param 值类型 + * @return 字段值 + * @throws IllegalAccessException 反射访问失败 + * @throws NoSuchFieldException 字段不存在 + */ + public static T getClassFieldValue(Object object, Class objectClass, String fieldName, Class toClass) throws IllegalAccessException, NoSuchFieldException { + Field field = getClassField(objectClass, fieldName); + if (field == null) { + throw new NoSuchFieldException("not found " + fieldName + " field in " + objectClass.getSimpleName()); + } + field.setAccessible(true); + return toClass.cast(field.get(object)); + } + + /** + * 反射设置对象字段值,包括父级类,直至 {@link Object} + * + * @param object 对象 + * @param fieldName 字段名 + * @param value 字段值 + * @throws IllegalAccessException 反射访问失败 + * @throws NoSuchFieldException 向上反射直至 {@link Object} 也找不到该字段 + */ + public static void setFieldValue(Object object, String fieldName, Object value) throws IllegalAccessException, NoSuchFieldException { + Field field = getField(object.getClass(), fieldName); + if (field == null) { + throw new NoSuchFieldException("not found " + fieldName + " field in " + object.getClass().getSimpleName()); + } + setFieldValue(object, field, value); + } + + public static void setFieldValue(Object object, Field field, Object value) throws IllegalAccessException { + field.setAccessible(true); + field.set(object, value); + } + + /** + * 反射设置对象字段值 + * + * @param object 对象 + * @param objectClass 类 + * @param fieldName 字段名 + * @param value 字段值 + * @throws IllegalAccessException 反射访问失败 + * @throws NullPointerException 字段不存在 + */ + public static void setClassFieldValue(Object object, Class objectClass, String fieldName, Object value) throws IllegalAccessException, NullPointerException { + Field field = getClassField(objectClass, fieldName); + if (field == null) { + throw new NullPointerException("not found " + fieldName + " field in " + objectClass.getSimpleName() + " class"); + } + field.setAccessible(true); + field.set(object, value); + } + + /** + * 反射查找方法,包括父级类,直至 {@link Object},如果都不存在则返回 null + * + * @param clazz 类 + * @param methodName 方法名 + * @param parameterTypes 可选参 + * @return 方法对象 + */ + public static Method getMethod(Class clazz, String methodName, Class... parameterTypes) { + if (clazz == null) { + throw new NullPointerException("class can not be null"); + } + do { + try { + Method method = clazz.getDeclaredMethod(methodName, parameterTypes); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } while (clazz != Object.class); + return null; + } + + /** + * 字符串转枚举 + * + * @param clazz 枚举类 + * @param string 字符串 + * @param 泛型 + * @return 泛型 + */ + public static > T toType(Class clazz, String string) { + if (string == null) { + return null; + } + T[] ts = clazz.getEnumConstants(); + if (ts == null) { + throw new IllegalArgumentException(clazz.getName() + " is not an enum type"); + } + for (int i = 0; i < ts.length; i++) { + if (ts[i].name().equalsIgnoreCase(string)) { + return ts[i]; + } + } + return null; + } + + /** + * + * + * @param owner + * @param keyName + * @return + */ + public static RefField field(Class owner, String keyName) { + return new RefField(owner, keyName); + } + + /** + * + * @param type + * @return + * @param + * @throws Exception + */ + public static T newInstance(Class type) throws Exception { + return type.getDeclaredConstructor().newInstance(); + } +} diff --git a/src/main/java/com/imyeyu/java/ref/RefField.java b/src/main/java/com/imyeyu/java/ref/RefField.java new file mode 100644 index 0000000..93aa3f6 --- /dev/null +++ b/src/main/java/com/imyeyu/java/ref/RefField.java @@ -0,0 +1,77 @@ +package com.imyeyu.java.ref; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * + * + * @author 夜雨 + * @since 2024-04-26 15:46 + */ +public class RefField { + + private Class type; + private Field field; + private Method setter; + private Method getter; + + RefField(Class owner, String keyName) { + String fieldName = Ref.getFieldName(keyName); + String fieldNameUpper = String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1); + + field = Ref.getField(owner, fieldName); + type = field.getType(); + + String setterName; + String getterName; + + if (boolean.class.equals(field.getType()) || Boolean.class.equals(field.getType())) { + if (fieldName.startsWith("is")) { + setterName = "set" + fieldName.substring(2); + getterName = fieldName; + } else { + setterName = "set" + fieldNameUpper; + getterName = "is" + fieldNameUpper; + } + } else { + setterName = "set" + fieldNameUpper; + getterName = "get" + fieldNameUpper; + } + + setter = Ref.getMethod(owner, setterName, field.getType()); + getter = Ref.getMethod(owner, getterName); + } + + public Class getType() { + return type; + } + + public void setType(Class type) { + this.type = type; + } + + public Field getField() { + return field; + } + + public void setField(Field field) { + this.field = field; + } + + public Method getSetter() { + return setter; + } + + public void setSetter(Method setter) { + this.setter = setter; + } + + public Method getGetter() { + return getter; + } + + public void setGetter(Method getter) { + this.getter = getter; + } +} diff --git a/src/test/java/test/TestObs.java b/src/test/java/test/TestObs.java new file mode 100644 index 0000000..9c4a9f7 --- /dev/null +++ b/src/test/java/test/TestObs.java @@ -0,0 +1,60 @@ +package test; + +import com.imyeyu.java.obs.ObservableList; +import com.imyeyu.java.obs.ObservableMap; +import com.imyeyu.java.obs.ObservableString; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** + * @author 夜雨 + * @since 2024-09-01 23:26 + */ +public class TestObs { + + @Test + public void testString() { + ObservableString obsString = new ObservableString("test1"); + System.out.println("init value = " + obsString); + obsString.addListener((from, to) -> System.out.printf("from %s to %s%n", from, to)); + obsString.set("test2"); + System.out.println("now value = " + obsString); + assert obsString.get().equals("test2"); + } + + @Test + public void testList() { + ObservableList list = new ObservableList<>(); + list.addChangeListener((type, string) -> System.out.printf("%s element %s to list%n", type, string)); + list.add("test1"); + list.add("test2"); + list.add("test3"); + list.add("test4"); + list.add("test5"); + list.addAll(List.of("test7", "test8")); + list.add(0, "test0"); + System.out.println(list); + list.remove("test2"); + list.remove(0); + list.removeAll(List.of("test3", "test4")); + list.removeIf(item -> item.equals("test1")); // 移除 0, 1, 2, 3, 4 + System.out.println(list); + } + + @Test + public void testMap() { + ObservableMap map = new ObservableMap<>(); + map.addChangeListener((type, key, value) -> System.out.printf("%s element %s:%s to map%n", type, key, value)); + map.put("t1", "test1"); + map.put("t1", "test1update"); // test1 更新 + map.put("t2", "test2"); + map.put("t2", "test2"); // test2 不更新 + map.put("t3", "test3"); + map.put("t4", "test4"); + map.put("t5", "test5"); + map.remove("t1"); + map.remove("t2", "test2"); + System.out.println(map); + } +} diff --git a/src/test/java/test/TestRef.java b/src/test/java/test/TestRef.java new file mode 100644 index 0000000..7328f4f --- /dev/null +++ b/src/test/java/test/TestRef.java @@ -0,0 +1,96 @@ +package test; + +import com.imyeyu.java.ref.Ref; +import com.imyeyu.java.ref.RefField; +import org.junit.jupiter.api.Test; + +/** + * @author 夜雨 + * @since 2024-04-26 11:05 + */ +public class TestRef { + + private boolean isBoy; + private boolean boy; + private String fieldName; + + @Test + public void test() { + String t1 = Ref.getFieldName("is-boy"); + assert t1.equals("isBoy"); + String t2 = Ref.getFieldName("is_boy"); + assert t2.equals("isBoy"); + String t3 = Ref.getFieldName("is boy"); + assert t3.equals("isBoy"); + String t4 = Ref.getFieldName("isBoy"); + assert t4.equals("isBoy"); + String t5 = Ref.getFieldName("boy"); + assert t5.equals("boy"); + String t6 = Ref.getFieldName("fieldName"); + assert t6.equals("fieldName"); + String t7 = Ref.getFieldName("field name"); + assert t7.equals("fieldName"); + String t8 = Ref.getFieldName("field-Name"); + assert t8.equals("fieldName"); + String t9 = Ref.getFieldName("field_Name"); + assert t9.equals("fieldName"); + } + + @Test + public void refBooleanField() { + RefField refField = Ref.field(TestRef.class, "is-boy"); + assert refField.getSetter() != null && refField.getSetter().getName().equals("setBoy"); + assert refField.getGetter() != null && refField.getGetter().getName().equals("isBoy"); + } + + @Test + public void refBoolean2Field() { + RefField refField = Ref.field(TestRef.class, "boy"); + assert refField.getSetter() != null && refField.getSetter().getName().equals("setBoy"); + assert refField.getGetter() != null && refField.getGetter().getName().equals("isBoy"); + } + + @Test + public void refStringField() { + RefField refField = Ref.field(TestRef.class, "fieldName"); + assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName"); + assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName"); + } + + @Test + public void refString2Field() { + RefField refField = Ref.field(TestRef.class, "field-name"); + assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName"); + assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName"); + } + + @Test + public void refString3Field() { + RefField refField = Ref.field(TestRef.class, "field_name"); + assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName"); + assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName"); + } + + @Test + public void refString4Field() { + RefField refField = Ref.field(TestRef.class, "field name"); + assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName"); + assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName"); + } + + public boolean isBoy() { + return isBoy; + } + + public void setBoy(boolean boy) { + isBoy = boy; + } + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } +} \ No newline at end of file