mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-03 09:14:01 +00:00
docs: Translate the Chinese comments in the event module into English.
This commit is contained in:
1 parent
19748528da
commit
9dd775abf2
106 files changed
+1537
-1553
No files matched your search
@@ -34,16 +34,17 @@ public class ProtocolManager {
|
||||
public static final byte MAX_MODULE_NUM = Byte.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* The protocol corresponding to the protocolId.(协议号protocolId对应的协议,数组下标是协议号protocolId)
|
||||
* The protocol corresponding to the protocolId. Array index equals the protocolId.
|
||||
*/
|
||||
public static final IProtocolRegistration[] protocols = new IProtocolRegistration[MAX_PROTOCOL_NUM];
|
||||
/**
|
||||
* The modules of the protocol.(协议的模块)
|
||||
* The modules of the protocol.
|
||||
*/
|
||||
public static final ProtocolModule[] modules = new ProtocolModule[MAX_MODULE_NUM];
|
||||
|
||||
/**
|
||||
* key:packet class,value:protocolId.(如果所有协议Class返回的hashcode都不相同(大概率事件),则使用高性能的HashMapIntShort)
|
||||
* key: packet class, value: protocolId.
|
||||
* If all protocol Class hashCodes are unique (which is highly likely), use the high-performance HashMapIntShort.
|
||||
*/
|
||||
public static Map<Class<?>, Short> protocolIdMap = new HashMap<>();
|
||||
public static HashMapIntShort protocolIdPrimitiveMap = new HashMapIntShort();
|
||||
@@ -101,7 +102,7 @@ public class ProtocolManager {
|
||||
*/
|
||||
public static ProtocolModule moduleByModuleId(byte moduleId) {
|
||||
var module = modules[moduleId];
|
||||
AssertionUtils.notNull(module, "[moduleId:{}]不存在", moduleId);
|
||||
AssertionUtils.notNull(module, "[moduleId:{}] does not exist", moduleId);
|
||||
return module;
|
||||
}
|
||||
|
||||
@@ -132,7 +133,7 @@ public class ProtocolManager {
|
||||
* Register protocol
|
||||
*
|
||||
* @param protocolClassSet A list of protocols that need to be initialized
|
||||
* @param generateOperation Protocol configuration(需要生成哪些语言的协议文件 是否折叠等信息)
|
||||
* @param generateOperation Protocol configuration (which languages to generate and whether to fold, etc.)
|
||||
*/
|
||||
public static void initProtocol(Set<Class<?>> protocolClassSet, GenerateOperation generateOperation) {
|
||||
ProtocolAnalysis.analyze(protocolClassSet, generateOperation);
|
||||
@@ -143,8 +144,7 @@ public class ProtocolManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* EN:Register protocol and automatically generates a protocol ID if the subprotocol does not specify a protocol ID
|
||||
* CN:子协议会自动注册协议号protocolId,如果子协议没有指定protocolId则自动生成protocolId
|
||||
* Register protocol and automatically generate a protocol ID for sub-protocols that do not specify one.
|
||||
*/
|
||||
public static void initProtocolAuto(List<Class<?>> protocolClassList, GenerateOperation generateOperation) {
|
||||
ProtocolAnalysis.analyzeAuto(protocolClassList, generateOperation);
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* EN: Make the fields of the old and new protocols compatible in different version
|
||||
* CN: 让新老协议的字段前后兼容
|
||||
* Ensures forward and backward compatibility between old and new protocol fields
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -27,7 +27,7 @@ public @interface Compatible {
|
||||
|
||||
/**
|
||||
* EN: new compatible field value must be always greater than old compatible field value
|
||||
* CN: 新的兼容字段值必须始终大于旧的兼容字段
|
||||
* The ordinal of a new compatible field must always be greater than that of existing compatible fields
|
||||
*/
|
||||
int value();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* EN: Generate comments in other languages
|
||||
* CN: 生成其它语言的注释
|
||||
* Generates comments/documentation for other target languages
|
||||
*
|
||||
* @author meiw
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* EN: Unique protocol number
|
||||
* CN: 唯一的协议号
|
||||
* The unique protocol ID
|
||||
*
|
||||
* @author meiw
|
||||
*/
|
||||
@@ -29,7 +29,7 @@ public @interface Protocol {
|
||||
|
||||
/**
|
||||
* EN: Use Javassist bytecode to enhance serialization of the protocol
|
||||
* CN: 使用Javassist字节码增强协议的序列化
|
||||
* Serialize the protocol using Javassist bytecode enhancement
|
||||
*/
|
||||
boolean enhance() default true;
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* zfoo的自定义私有协议格式,可以非常方便的对大部分数据定制序列化和反序列化实现,可以对大部分数据结构定制高性能实现方式
|
||||
* zfoo's custom private protocol format; easily customizes Serialize/Deserialize for most data types with high-performance implementations
|
||||
* <p>
|
||||
* “可变长字节码算法”的压缩数据的算法,压缩数据和减少磁盘IO。google的ProtocolBuf和Facebook的thrift底层的通信协议都是由这个算法实现
|
||||
* "Variable-length bytecode" compression algorithm; reduces data and I/O. Used by Google ProtocolBuffers and Facebook Thrift
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -40,7 +40,7 @@ public abstract class ByteBufUtils {
|
||||
public static final Float ZERO_FLOAT = Float.valueOf(0F);
|
||||
|
||||
public static void adjustPadding(ByteBuf byteBuf, int predictionLength, int beforeWriteIndex) {
|
||||
// 因为写入的是可变长的int,如果预留的位置过多,则清除多余的位置
|
||||
// Variable-length int; clear any over-reserved positions
|
||||
var currentWriteIndex = byteBuf.writerIndex();
|
||||
var predictionCount = writeIntCount(predictionLength);
|
||||
var length = currentWriteIndex - beforeWriteIndex - predictionCount;
|
||||
@@ -133,7 +133,7 @@ public abstract class ByteBufUtils {
|
||||
}
|
||||
|
||||
//---------------------------------int--------------------------------------
|
||||
// 用Zigzag算法压缩int和long的值,再用Varint紧凑算法表示数字的有效位
|
||||
// Use Zigzag to compress int/long values, then Varint to compactly represent the significant bits
|
||||
public static int writeInt(ByteBuf byteBuf, int value) {
|
||||
return writeVarInt(byteBuf, (value << 1) ^ (value >> 31));
|
||||
}
|
||||
@@ -376,7 +376,7 @@ public abstract class ByteBufUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
// 预估需要写入的字节数,并预留位置
|
||||
// Estimate the bytes needed and reserve space
|
||||
var beforeWriteIndex = byteBuf.writerIndex();
|
||||
var maxLength = ByteBufUtil.utf8MaxBytes(value);
|
||||
var writeIntCountByte = writeInt(byteBuf, maxLength);
|
||||
@@ -385,7 +385,7 @@ public abstract class ByteBufUtils {
|
||||
|
||||
var currentWriteIndex = byteBuf.writerIndex();
|
||||
|
||||
// 因为写入的是可变长的int,如果预留的位置过多,则清除多余的位置
|
||||
// Variable-length int; clear any over-reserved positions
|
||||
var padding = writeIntCountByte - writeIntCount(length);
|
||||
if (padding == 0) {
|
||||
byteBuf.writerIndex(beforeWriteIndex);
|
||||
@@ -407,7 +407,7 @@ public abstract class ByteBufUtils {
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
//---------------------------------以下方法会被字节码生成的代码调用--------------------------------------
|
||||
//---------------------------------The following methods are called by bytecode-generated code--------------------------------------
|
||||
public static void writePacketCollection(ByteBuf byteBuf, Collection<?> collection, IProtocolRegistration protocolRegistration) {
|
||||
if (collection == null) {
|
||||
byteBuf.writeByte(0);
|
||||
|
||||
@@ -20,14 +20,14 @@ import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义协议格式,可以针对某些特定场景对做特定优化
|
||||
* Custom protocol format optimized for specific scenarios
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
public abstract class CustomByteBuf {
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// 针对于int数组提高性能的简单方式
|
||||
// Simple performance improvement for int arrays
|
||||
public static void writeIntArraySimple(ByteBuf byteBuf, int[] array) {
|
||||
if (array == null) {
|
||||
byteBuf.writeByte(0);
|
||||
@@ -56,7 +56,7 @@ public abstract class CustomByteBuf {
|
||||
return ints;
|
||||
}
|
||||
|
||||
// 针对于int数组提高性能的复杂方式
|
||||
// Advanced performance improvement for int arrays
|
||||
public static void writeIntArrayMemoryCopy(ByteBuf byteBuf, int[] array) {
|
||||
if (array == null) {
|
||||
byteBuf.writeByte(0);
|
||||
|
||||
@@ -96,7 +96,7 @@ public abstract class CollectionUtils {
|
||||
|
||||
/**
|
||||
* EN: The safety limit of the array initialization length prevents deserialization exceptions from causing a sudden increase in memory
|
||||
* CN: 数组初始化长度的安全上限限制,防止反序列化异常导致内存突然升高
|
||||
* Safe upper limit for array initialization length; prevents memory spikes from malformed deserialization
|
||||
*/
|
||||
public static final long MAX_BYTE_LENGTH_ARRAY = 64 * IOUtils.BYTES_PER_MB;
|
||||
public static final long MAX_LENGTH_SHORT_ARRAY = IOUtils.BYTES_PER_MB / 2;
|
||||
@@ -149,7 +149,7 @@ public abstract class CollectionUtils {
|
||||
* EN: Calculate the appropriate size for HashMap initialization. For safety, a maximum limit must be given
|
||||
* to the initialized collection to prevent deserialization of an illegal package from causing a sudden increase in memory.
|
||||
* <p>
|
||||
* CN: 计算HashMap初始化合适的大小,为了安全必须给初始化的集合一个最大上限,防止反序列化一个不合法的包导致内存突然升高
|
||||
* Compute an appropriate initial HashMap size; a maximum is required to prevent memory spikes from deserializing invalid packets
|
||||
*/
|
||||
public static int comfortableCapacity(int capacity) {
|
||||
if (capacity >= MAX_SIZE_MAP) {
|
||||
@@ -167,7 +167,7 @@ public abstract class CollectionUtils {
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------归并排序----------------------------------
|
||||
// ----------------------------------Merge Sort----------------------------------
|
||||
|
||||
/**
|
||||
* Merges two sorted Collections, a and b, into a single, sorted List
|
||||
@@ -286,7 +286,7 @@ public abstract class CollectionUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获取集合的最后几个元素
|
||||
* Get the last N elements of a collection
|
||||
*/
|
||||
public static <T> List<T> subListLast(List<T> list, int num) {
|
||||
if (isEmpty(list)) {
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* EN: The performance is much lower than ConcurrentHashMap of the JDK, and the purpose is to reduce the memory footprint of the Long wrapper type
|
||||
* CN: 性能远低于JDK自带的ConcurrentHashMap,主要就是减少Long包装类型的内存占用
|
||||
* Performance is far below JDK's ConcurrentHashMap; the primary benefit is reducing memory overhead from Long boxing
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -31,11 +31,11 @@ public class ConcurrentHashMapLongObject<V> implements Map<Long, V> {
|
||||
|
||||
public static final int DEFAULT_BUCKET_SIZE = 16;
|
||||
|
||||
// 分段锁
|
||||
// Striped lock
|
||||
private int buckets;
|
||||
private int mask;
|
||||
private ReadWriteLock[] locks;
|
||||
// bucket对应的分段map
|
||||
// Striped map for each bucket
|
||||
private List<LongObjectHashMap<V>> maps;
|
||||
|
||||
public ConcurrentHashMapLongObject(int buckets) {
|
||||
|
||||
@@ -131,7 +131,7 @@ public class FileChannelMap<V> implements LpMap<V>, Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从startKey到endKey的值
|
||||
* Get values from startKey to endKey
|
||||
*
|
||||
* @param startKey inclusive
|
||||
* @param endKey exclusive
|
||||
@@ -205,11 +205,11 @@ public class FileChannelMap<V> implements LpMap<V>, Closeable {
|
||||
|
||||
protocolRegistration.write(dbBuffer, value);
|
||||
|
||||
// db文件
|
||||
// DB file
|
||||
var packetPosition = dbFileChannel.size();
|
||||
// db文件数据的起始位置
|
||||
// Start offset of data in the DB file
|
||||
indexBuffer.writeLong(packetPosition);
|
||||
// db文件的值的大小
|
||||
// Size of a value entry in the DB file
|
||||
indexBuffer.writeLong(dbBuffer.readableBytes());
|
||||
indexFileChannel.write(indexBuffer.nioBuffer(), key * 16);
|
||||
dbFileChannel.write(dbBuffer.nioBuffer(), packetPosition);
|
||||
|
||||
@@ -128,7 +128,7 @@ public class FileHeapMap<V> implements LpMap<V> {
|
||||
fileOutputStream = FileUtils.openOutputStream(dbFile, false);
|
||||
buffer = ByteBufAllocator.DEFAULT.heapBuffer(1000);
|
||||
|
||||
// 写入长度
|
||||
// Write length
|
||||
ByteBufUtils.writeLong(buffer, heapMap.map.size());
|
||||
buffer.readBytes(fileOutputStream, buffer.readableBytes());
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ import com.zfoo.protocol.exception.RunException;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* 类型固定的map,key为long,value为Object
|
||||
* 其中long必须大于等于0,value可以为null
|
||||
* Fixed-type map with long keys and Object values
|
||||
* The long key must be >= 0; value may be null
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -38,7 +38,7 @@ public interface LpMap<V> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回被删除的那个值
|
||||
* @return the removed value
|
||||
*/
|
||||
V delete(long key);
|
||||
|
||||
@@ -54,7 +54,7 @@ public interface LpMap<V> {
|
||||
|
||||
default void checkKey(long key) {
|
||||
if (key < 0) {
|
||||
throw new RunException("key[{}]只能为大于等于0的正数", key);
|
||||
throw new RunException("key[{}] must be a non-negative integer (>= 0)", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,38 +19,38 @@ import com.zfoo.protocol.util.StringUtils;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* DFA(Deterministic Finite Automaton 确定有穷自动机)
|
||||
* DFA单词树(以下简称单词树),常用于在某大段文字中快速查找某几个关键词是否存在。<br>
|
||||
* 单词树使用group区分不同的关键字集合,不同的分组可以共享树枝,避免重复建树。<br>
|
||||
* 单词树使用树状结构表示一组单词。<br>
|
||||
* 例如:红领巾,红河构建树后为:<br>
|
||||
* <br> 红
|
||||
* DFA (Deterministic Finite Automaton)
|
||||
* DFA word tree (a.k.a. word tree): commonly used to quickly scan large text for the presence of certain keywords.<br>
|
||||
* The word tree uses groups to distinguish different keyword sets; groups share branches to avoid rebuilding.<br>
|
||||
* The word tree uses a tree structure to represent a set of words.<br>
|
||||
* For example, after building a tree with 'red-scarf' and 'red-river':<br>
|
||||
* <br> (red)
|
||||
* <br> / \
|
||||
* <br> 领 河
|
||||
* <br> (scarf) (river)
|
||||
* <br> /
|
||||
* <br> 巾
|
||||
* 其中每个节点都是一个WordTree对象,查找时从上向下查找。<br>
|
||||
* <br> (-)
|
||||
* Each node is a WordTree object; search proceeds top-down.<br>
|
||||
*/
|
||||
public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
|
||||
|
||||
/**
|
||||
* 敏感词字符末尾标识,用于标识单词末尾字符
|
||||
* End-of-word marker; marks the last character of a word
|
||||
*/
|
||||
private Set<Character> endCharacterSet = new HashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
* Default constructor
|
||||
*/
|
||||
public DfaWordTree() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 增加一组单词
|
||||
* Add a group of words
|
||||
*
|
||||
* @param words 单词集合
|
||||
* @param words set of words
|
||||
*/
|
||||
public void addWords(Collection<String> words) {
|
||||
var wordSet = new HashSet<>(words);
|
||||
@@ -60,9 +60,9 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单词,使用默认类型
|
||||
* Add a word using the default type
|
||||
*
|
||||
* @param word 单词
|
||||
* @param word the word
|
||||
*/
|
||||
public void addWord(String word) {
|
||||
DfaWordTree parent = null;
|
||||
@@ -72,11 +72,11 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
int length = word.length();
|
||||
for (var i = 0; i < length; i++) {
|
||||
currentChar = word.charAt(i);
|
||||
// 只处理合法字符
|
||||
// Only process valid characters
|
||||
if (!StringUtils.isStopChar(currentChar)) {
|
||||
child = current.get(currentChar);
|
||||
if (child == null) {
|
||||
// 无子类,新建一个子节点后存放下一个字符
|
||||
// No children yet; create a new child node for the next character
|
||||
child = new DfaWordTree();
|
||||
current.put(currentChar, child);
|
||||
}
|
||||
@@ -92,10 +92,10 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
//------------------------------------------------------------------------------- match
|
||||
|
||||
/**
|
||||
* 指定文本是否包含树中的词
|
||||
* Check if the given text contains any word in the tree
|
||||
*
|
||||
* @param text 被检查的文本
|
||||
* @return 是否包含
|
||||
* @param text the text to check
|
||||
* @return true if a match was found
|
||||
*/
|
||||
public boolean isMatch(String text) {
|
||||
if (StringUtils.isBlank(text)) {
|
||||
@@ -109,36 +109,36 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
//------------------------------------------------------------------------------- match all
|
||||
|
||||
/**
|
||||
* 找出所有匹配的关键字
|
||||
* Find all matching keywords
|
||||
*
|
||||
* @param text 被检查的文本
|
||||
* @return 匹配的词列表
|
||||
* @param text the text to check
|
||||
* @return list of matched words
|
||||
*/
|
||||
public List<String> matchAll(String text) {
|
||||
return matchAll(text, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找出所有匹配的关键字
|
||||
* Find all matching keywords
|
||||
*
|
||||
* @param text 被检查的文本
|
||||
* @param limit 限制匹配个数
|
||||
* @return 匹配的词列表
|
||||
* @param text the text to check
|
||||
* @param limit maximum number of matches
|
||||
* @return list of matched words
|
||||
*/
|
||||
public List<String> matchAll(String text, int limit) {
|
||||
return matchAll(text, limit, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找出所有匹配的关键字<br>
|
||||
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||
* 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
|
||||
* Find all matching keywords<br>
|
||||
* Dense matching: if keywords are [ab, b] and text is 'abab', matches [ab, b, ab]<br>
|
||||
* Greedy (longest) matching: if keywords are [a, ab], matches [a, ab]
|
||||
*
|
||||
* @param text 被检查的文本
|
||||
* @param limit 限制匹配个数
|
||||
* @param isDensityMatch 是否使用密集匹配原则
|
||||
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||
* @return 匹配的词列表
|
||||
* @param text the text to check
|
||||
* @param limit maximum number of matches
|
||||
* @param isDensityMatch true to use dense matching
|
||||
* @param isGreedMatch true to use greedy (longest) matching
|
||||
* @return list of matched words
|
||||
*/
|
||||
public List<String> matchAll(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
|
||||
if (StringUtils.isBlank(text)) {
|
||||
@@ -148,7 +148,7 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
var findWords = new ArrayList<String>();
|
||||
DfaWordTree current = this;
|
||||
int length = text.length();
|
||||
// 存放查找到的字符缓存。完整出现一个词时加到findedWords中,否则清空
|
||||
// Buffer for characters found during search; flushed to findedWords on a complete match
|
||||
StringBuilder wordBuffer;
|
||||
char currentChar;
|
||||
for (int i = 0; i < length; i++) {
|
||||
@@ -157,31 +157,31 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
currentChar = text.charAt(j);
|
||||
if (StringUtils.isStopChar(currentChar)) {
|
||||
if (wordBuffer.length() > 0) {
|
||||
// 做为关键词中间的停顿词被当作关键词的一部分被返回
|
||||
// Pause words in the middle of a keyword are included as part of the match
|
||||
wordBuffer.append(currentChar);
|
||||
} else {
|
||||
// 停顿词做为关键词的第一个字符时需要跳过
|
||||
// Skip pause words that appear as the first character of a potential keyword
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
} else if (!current.containsKey(currentChar)) {
|
||||
// 非关键字符被整体略过,重新以下个字符开始检查
|
||||
// Non-keyword characters are skipped entirely; restart check from the next character
|
||||
break;
|
||||
}
|
||||
wordBuffer.append(currentChar);
|
||||
if (current.isEnd(currentChar)) {
|
||||
// 到达单词末尾,关键词成立,从此词的下一个位置开始查找
|
||||
// End of word reached; keyword confirmed. Continue search from the next position
|
||||
findWords.add(wordBuffer.toString());
|
||||
if (limit > 0 && findWords.size() >= limit) {
|
||||
// 超过匹配限制个数,直接返回
|
||||
// Exceeded match limit; return immediately
|
||||
return findWords;
|
||||
}
|
||||
if (!isDensityMatch) {
|
||||
// 如果非密度匹配,跳过匹配到的词
|
||||
// If not dense matching, skip over the matched word
|
||||
i = j;
|
||||
}
|
||||
if (!isGreedMatch) {
|
||||
// 如果懒惰匹配(非贪婪匹配)。当遇到第一个结尾标记就结束本轮匹配
|
||||
// Lazy (non-greedy) matching: stop at the first end marker
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -197,19 +197,19 @@ public class DfaWordTree extends HashMap<Character, DfaWordTree> {
|
||||
|
||||
|
||||
/**
|
||||
* 是否末尾
|
||||
* Check if this node is a word-end marker
|
||||
*
|
||||
* @param c 检查的字符
|
||||
* @return 是否末尾
|
||||
* @param c the character to check
|
||||
* @return Check if this node is a word-end marker
|
||||
*/
|
||||
private boolean isEnd(Character c) {
|
||||
return this.endCharacterSet.contains(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否到达末尾
|
||||
* Set whether this is a word-end node
|
||||
*
|
||||
* @param c 设置结尾的字符
|
||||
* @param c the character to mark as end
|
||||
*/
|
||||
private void setEnd(Character c) {
|
||||
if (null != c) {
|
||||
|
||||
@@ -15,7 +15,7 @@ package com.zfoo.protocol.collection.tree;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 多叉树
|
||||
* Multi-way (N-ary) tree
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ public class GeneralTree<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 移除所有数据结点
|
||||
* Remove all data nodes
|
||||
*/
|
||||
public void clear() {
|
||||
rootNode.clear();
|
||||
|
||||
@@ -31,10 +31,10 @@ public class TreeNode<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 创建树的结点
|
||||
* Create a tree node
|
||||
*
|
||||
* @param name 数据结点名称
|
||||
* @param parent 父数据结点
|
||||
* @param name node name
|
||||
* @param parent parent node
|
||||
*/
|
||||
|
||||
TreeNode(String name, TreeNode<T> parent) {
|
||||
@@ -43,7 +43,7 @@ public class TreeNode<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测数据结点名称是否合法
|
||||
* Validate the node name
|
||||
*/
|
||||
private static void checkName(String name) {
|
||||
if (StringUtils.isBlank(name) || name.contains(StringUtils.PERIOD)) {
|
||||
@@ -53,7 +53,7 @@ public class TreeNode<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 获取数据结点的完整名称。
|
||||
* Get the full name of the data node.
|
||||
*/
|
||||
public String fullName() {
|
||||
if (parent == null) {
|
||||
@@ -70,10 +70,10 @@ public class TreeNode<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 根据名称检查是否存在子数据结点
|
||||
* Check if a child node with the given name exists
|
||||
*
|
||||
* @param name 子数据结点名称
|
||||
* @return 是否存在子数据结点
|
||||
* @param name child node name
|
||||
* @return true if the child node exists
|
||||
*/
|
||||
public boolean hasChild(String name) {
|
||||
checkName(name);
|
||||
@@ -92,10 +92,10 @@ public class TreeNode<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取子数据结点
|
||||
* Get the child node by name
|
||||
*
|
||||
* @param name 子数据结点名称
|
||||
* @return 指定名称的子数据结点,如果没有找到,则返回空
|
||||
* @param name child node name
|
||||
* @return the child node with the given name; null if not found
|
||||
*/
|
||||
public TreeNode<T> childByName(String name) {
|
||||
checkName(name);
|
||||
@@ -114,7 +114,7 @@ public class TreeNode<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有子节点,包括父节点和子节点的子节点
|
||||
* Get all child nodes recursively, including descendants
|
||||
*/
|
||||
public List<TreeNode<T>> flatTreeNodes() {
|
||||
var result = new ArrayList<TreeNode<T>>();
|
||||
@@ -143,10 +143,10 @@ public class TreeNode<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 根据名称获取或增加子数据结点
|
||||
* Get or create the child node with the given name
|
||||
*
|
||||
* @param name 子数据结点名称
|
||||
* @return 指定名称的子数据结点,如果对应名称的子数据结点已存在,则返回已存在的子数据结点,否则增加子数据结点
|
||||
* @param name child node name
|
||||
* @return the existing or newly created child node with the given name
|
||||
*/
|
||||
public TreeNode<T> getOrAddChild(String name) {
|
||||
var node = childByName(name);
|
||||
@@ -167,9 +167,9 @@ public class TreeNode<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 根据名称移除子数据结点
|
||||
* Remove the child node with the given name
|
||||
*
|
||||
* @param name 子数据结点名称
|
||||
* @param name child node name
|
||||
*/
|
||||
public void removeChild(String name) {
|
||||
var node = childByName(name);
|
||||
|
||||
@@ -22,14 +22,14 @@ import java.util.Arrays;
|
||||
public abstract class ExceptionUtils {
|
||||
|
||||
/**
|
||||
* 获取异常全部信息,格式是:
|
||||
* Get full exception information in the format:
|
||||
* <p>
|
||||
* 类名称: 异常信息
|
||||
* 异常堆栈
|
||||
* ClassName: ExceptionMessage
|
||||
* StackTrace
|
||||
* </p>
|
||||
*
|
||||
* @param throwable the throwable to get a message for, null returns empty string
|
||||
* @return 异常的信息
|
||||
* @return the exception information string
|
||||
*/
|
||||
public static String getMessage(final Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* EN: Action class for creating protocol files
|
||||
* CN: 创建协议文件的操作类
|
||||
* Configuration class for protocol file generation
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -29,13 +29,13 @@ public class GenerateOperation {
|
||||
|
||||
/**
|
||||
* EN: No protocol file is created
|
||||
* CN: 不创建任何协议文件
|
||||
* Do not generate any protocol files
|
||||
*/
|
||||
public static final GenerateOperation NO_OPERATION = new GenerateOperation();
|
||||
|
||||
/**
|
||||
* EN: Generate one protocol file under a package
|
||||
* CN: 将一个包下的协议文件生成在一个协议文件里
|
||||
* CN: Generate all protocol files under a package into a single file
|
||||
*/
|
||||
private boolean mergeProtocol;
|
||||
|
||||
@@ -43,24 +43,24 @@ public class GenerateOperation {
|
||||
* EN: Fold the protocol. The generated protocol file will maintain the same directory structure as the Java source file.
|
||||
* It will not be folded by default.
|
||||
* <p>
|
||||
* CN: 折叠协议,生成协议文件会和Java源文件保持相同的目录结构,默认不折叠
|
||||
* Fold protocols; generated files mirror the Java source directory structure (default: not folded)
|
||||
*/
|
||||
private boolean foldProtocol;
|
||||
|
||||
/**
|
||||
* EN: The path to generate the protocol, the default is the path of the currently running project
|
||||
* CN: 生成协议的路径,默认为当前运行项目的路径
|
||||
* Output path for generated protocols; defaults to the current project path
|
||||
*/
|
||||
private String protocolPath = StringUtils.EMPTY;
|
||||
|
||||
/**
|
||||
* Reserved parameters,保留参数
|
||||
* Reserved parameters
|
||||
*/
|
||||
private String protocolParam;
|
||||
|
||||
/**
|
||||
* EN: The protocol files that need to be generated (c#, js, ts... and other languages that need to generate the protocol are listed)
|
||||
* CN: 需要生成的协议文件(c#、js、ts...等需要生成协议的语言都列出来)
|
||||
* List of target languages (C#, JS, TS, etc.) for which to generate protocol files
|
||||
*/
|
||||
private final Set<CodeLanguage> generateLanguages = EnumSet.noneOf(CodeLanguage.class);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public abstract class GenerateProtocolFile {
|
||||
|
||||
/**
|
||||
* EN: Filter for generating protocol files. If no filter is set, all protocols will be generated by default.
|
||||
* CN: 生成协议文件的过滤器,如果没有设置过滤器默认生成全部的协议
|
||||
* Filter for protocol file generation; generates all protocols if no filter is set
|
||||
*/
|
||||
public static Predicate<IProtocolRegistration> generateProtocolFilter = registration -> true;
|
||||
|
||||
@@ -50,12 +50,12 @@ public abstract class GenerateProtocolFile {
|
||||
* 4. Common protocols should be placed in the common module.
|
||||
* 5. Internal protocols are not allowed to be used outside their scope.
|
||||
* <p>
|
||||
* CN: 网络包的约定规则如下:
|
||||
* 1. 客户端的请求约定以Request结尾,服务器的响应约定以Response结尾
|
||||
* 2. 服务器内部请求约定以Ask结尾,服务器内部的响应约定以Answer结尾
|
||||
* 3. 服务器主动通知客户端以Notice结尾
|
||||
* 4. 公共的协议放在common模块
|
||||
* 5. 内部协议范围不允许使用
|
||||
* Naming conventions for network packets:
|
||||
* 1. Client requests end with 'Request'; server responses end with 'Response'
|
||||
* 2. Internal server requests end with 'Ask'; internal responses end with 'Answer'
|
||||
* 3. Server-push notifications to clients end with 'Notice'
|
||||
* 4. Shared protocols go in the common module
|
||||
* 5. Internal protocol range must not
|
||||
*/
|
||||
public static final String NET_REQUEST_SUFFIX = "Request";
|
||||
public static final String NET_RESPONSE_SUFFIX = "Response";
|
||||
@@ -110,7 +110,7 @@ public abstract class GenerateProtocolFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每行新增若干Tab
|
||||
* Add a number of Tab characters to each line
|
||||
*/
|
||||
public static String addTabs(String str, int deep) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
@@ -128,23 +128,23 @@ public abstract class GenerateProtocolFile {
|
||||
|
||||
/**
|
||||
* EN: Generate protocol files to various languages
|
||||
* CN: 生成各种语言的协议文件
|
||||
* Generate protocol files for all target languages
|
||||
*/
|
||||
public static void generate(GenerateOperation generateOperation) throws IOException, ClassNotFoundException {
|
||||
var protocols = ProtocolManager.protocols;
|
||||
|
||||
// 如果没有需要生成的协议则直接返回
|
||||
// Return immediately if there are no protocols to generate
|
||||
if (generateOperation.getGenerateLanguages().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 外层需要生成的协议
|
||||
// Outer-layer protocols to generate
|
||||
var outsideGenerateProtocols = Arrays.stream(protocols)
|
||||
.filter(it -> Objects.nonNull(it))
|
||||
.filter(it -> generateProtocolFilter.test(it))
|
||||
.toList();
|
||||
|
||||
// 需要生成的子协议,因为外层协议的内部有其它协议
|
||||
// Sub-protocols to generate, because the outer protocol references other protocols
|
||||
var insideGenerateProtocols = outsideGenerateProtocols.stream()
|
||||
.map(it -> ProtocolAnalysis.getAllSubProtocolIds(it.protocolId()))
|
||||
.flatMap(it -> it.stream())
|
||||
@@ -156,15 +156,15 @@ public abstract class GenerateProtocolFile {
|
||||
allGenerateProtocols.addAll(outsideGenerateProtocols);
|
||||
allGenerateProtocols.addAll(insideGenerateProtocols);
|
||||
|
||||
// 通过协议号,从小到大排序
|
||||
// Sort by protocol ID in ascending order
|
||||
var generateProtocols = allGenerateProtocols.stream()
|
||||
.sorted((a, b) -> a.protocolId() - b.protocolId())
|
||||
.map(it -> (ProtocolRegistration) it)
|
||||
.toList();
|
||||
|
||||
// 解析协议的文档注释
|
||||
// Parse protocol documentation comments
|
||||
GenerateProtocolNote.initProtocolNote(generateProtocols);
|
||||
// 计算协议生成的路径
|
||||
// Compute the output path for the protocol
|
||||
GenerateProtocolPath.initProtocolPath(generateProtocols);
|
||||
|
||||
for (var language : generateOperation.getGenerateLanguages()) {
|
||||
@@ -179,7 +179,7 @@ public abstract class GenerateProtocolFile {
|
||||
}
|
||||
}
|
||||
|
||||
// 预留参数,以后可能会用,比如给Lua修改一个后缀名称
|
||||
// Reserved parameter; may be used in the future, e.g., to change a file extension for Lua
|
||||
var protocolParam = generateOperation.getProtocolParam();
|
||||
|
||||
GenerateProtocolNote.clear();
|
||||
@@ -196,7 +196,7 @@ public abstract class GenerateProtocolFile {
|
||||
throw new UnknownException();
|
||||
}
|
||||
|
||||
// 子协议优先生成
|
||||
// Generate sub-protocols first
|
||||
public static List<ProtocolRegistration> subProtocolFirst(List<ProtocolRegistration> registrations) {
|
||||
var registrations1 = new ArrayList<ProtocolRegistration>(registrations);
|
||||
var subFirstRegistrations = new ArrayList<ProtocolRegistration>();
|
||||
|
||||
@@ -26,25 +26,25 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* EN: When generating the protocol, the document comments and field comments of the protocol will use this class
|
||||
* CN: 生成协议的时候,协议的文档注释和字段注释会使用这个类
|
||||
* During protocol generation, this class handles documentation and field comments
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
public abstract class GenerateProtocolNote {
|
||||
|
||||
// 临时变量,启动完成就会销毁,协议的文档,外层map的key为协议类;pair的key为总的注释,value为属性字段的注释,value表示的map的key为属性名称
|
||||
// 比如在Test中的ComplexObject生成的pari是如下格式
|
||||
// Temporary variable destroyed after startup; outer map key=protocol class; pair key=class comment, pair value=field comment map (key=field name)
|
||||
// For example, the pair generated for ComplexObject in the test is in the following format:
|
||||
/**
|
||||
* key is docTitle note:
|
||||
* // 复杂的对象
|
||||
* // 包括了各种复杂的结构,数组,List,Set,Map
|
||||
* // Complex object
|
||||
* // Includes various complex structures: arrays, List, Set, Map
|
||||
* //
|
||||
* // @author godotg
|
||||
* // @version 1.0
|
||||
* <p>
|
||||
* value is field note:
|
||||
* // byte的包装类型
|
||||
* // 优先使用基础类型,包装类型会有装箱拆箱
|
||||
* // Byte wrapper type
|
||||
* // Prefer primitives; wrapper types incur boxing/unboxing overhead
|
||||
*/
|
||||
private static Map<Short, Pair<String, Map<String, String>>> protocolNoteMap = new HashMap<>();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* EN: When generating a protocol, the final generation path of the protocol will use this class.
|
||||
* CN: 生成协议的时候,协议的最终生成路径会使用这个类
|
||||
* During protocol generation, this class determines the final output path
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -30,7 +30,7 @@ public abstract class GenerateProtocolPath {
|
||||
|
||||
/**
|
||||
* EN: The paths and temporary variables generated by the protocol will be destroyed after startup is completed.
|
||||
* CN: 协议生成的路径,临时变量,启动完成就会销毁
|
||||
* Temporary path variable for protocol generation; destroyed after startup
|
||||
*/
|
||||
private static Map<Short, String> protocolPathMap = new HashMap<>();
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class GenerateProtocolPath {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议生成的路径
|
||||
* Get the protocol generation path
|
||||
*/
|
||||
public static String protocolPathPeriod(short protocolId) {
|
||||
return protocolPathMap.get(protocolId);
|
||||
@@ -54,14 +54,14 @@ public abstract class GenerateProtocolPath {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议生成的首字母大写的路径
|
||||
* Get the protocol generation path with first letter capitalized
|
||||
*/
|
||||
public static String capitalizeProtocolPathFold(short protocolId) {
|
||||
return StringUtils.joinWith(StringUtils.SLASH, Arrays.stream(protocolPathSlash(protocolId).split(StringUtils.SLASH)).map(it -> StringUtils.capitalize(it)).toArray());
|
||||
}
|
||||
|
||||
public static String relativePath(short protocolId, short relativeProtocolId) {
|
||||
// 不是折叠协议的话,protocolPathMap一定是空,这里返回“”,上层会解析为同一个文件下
|
||||
// For non-folded protocols, protocolPathMap is always empty; returns“”; the upper layer treats it as the same file
|
||||
if (CollectionUtils.isEmpty(protocolPathMap)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
@@ -97,12 +97,12 @@ public abstract class GenerateProtocolPath {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析协议的路径
|
||||
* Parse the protocol path
|
||||
*
|
||||
* @param protocolRegistrations 需要解析的路径
|
||||
* @param protocolRegistrations registrations whose paths need to be resolved
|
||||
*/
|
||||
public static void initProtocolPath(List<ProtocolRegistration> protocolRegistrations) {
|
||||
// 将需要生成的协议的路径添加到多叉树中
|
||||
// Add the protocol's generation path to the multi-way tree
|
||||
var protocolPathTree = new GeneralTree<ProtocolRegistration>();
|
||||
protocolRegistrations.forEach(it -> protocolPathTree.addNode(it.protocolConstructor().getDeclaringClass().getCanonicalName(), it));
|
||||
|
||||
@@ -116,19 +116,19 @@ public abstract class GenerateProtocolPath {
|
||||
while (!queue.isEmpty()) {
|
||||
var treeNode = queue.poll();
|
||||
var childChildren = treeNode.getChildren();
|
||||
// 如果子节点为空,则以当前节点为路径
|
||||
// If child nodes are empty, use the current node as the path
|
||||
if (CollectionUtils.isEmpty(childChildren)) {
|
||||
toProtocolPath(treeNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果子节点的协议数据有一个不为空的,则以当前节点为路径
|
||||
// If any child node has non-empty protocol data, use the current node as the path
|
||||
if (childChildren.stream().anyMatch(it -> it.getData() != null)) {
|
||||
toProtocolPath(treeNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 继续深度便利子节点的路径
|
||||
// Continue depth-first traversal of child node paths
|
||||
for (var child : childChildren) {
|
||||
queue.offer(child);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ package com.zfoo.protocol.model;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 键值对对象,只能在构造时传入键值
|
||||
* Key-value pair; values can only be set via the constructor
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @param <K> key type
|
||||
* @param <V> value type
|
||||
* @author godotg
|
||||
*/
|
||||
public class Pair<K, V> {
|
||||
@@ -32,10 +32,10 @@ public class Pair<K, V> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
* Constructor
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public Pair(K key, V value) {
|
||||
this.key = key;
|
||||
@@ -43,18 +43,18 @@ public class Pair<K, V> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键
|
||||
* Get the key
|
||||
*
|
||||
* @return 键
|
||||
* @return the key
|
||||
*/
|
||||
public K getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值
|
||||
* Get the value
|
||||
*
|
||||
* @return 值
|
||||
* @return the value
|
||||
*/
|
||||
public V getValue() {
|
||||
return this.value;
|
||||
|
||||
@@ -16,9 +16,9 @@ package com.zfoo.protocol.model;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 键值对对象,只能在构造时传入键值
|
||||
* Key-value pair; values can only be set via the constructor
|
||||
*
|
||||
* @param <V> 值类型
|
||||
* @param <V> value type
|
||||
* @author godotg
|
||||
*/
|
||||
public class PairLong<V> {
|
||||
@@ -31,10 +31,10 @@ public class PairLong<V> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
* Constructor
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public PairLong(long key, V value) {
|
||||
this.key = key;
|
||||
@@ -42,18 +42,18 @@ public class PairLong<V> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键
|
||||
* Get the key
|
||||
*
|
||||
* @return 键
|
||||
* @return the key
|
||||
*/
|
||||
public long getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值
|
||||
* Get the value
|
||||
*
|
||||
* @return 值
|
||||
* @return the value
|
||||
*/
|
||||
public V getValue() {
|
||||
return this.value;
|
||||
|
||||
@@ -32,13 +32,13 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 对应于ProtocolRegistration
|
||||
* Corresponds to ProtocolRegistration
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
public abstract class EnhanceUtils {
|
||||
|
||||
// 临时变量,是一个基本类型序列化器对应的增强类型序列化器
|
||||
// Temporary variable: maps primitive type serializers to their enhanced counterparts
|
||||
private static Map<ISerializer, IEnhanceSerializer> enhanceSerializerMap = new HashMap<>();
|
||||
|
||||
public static String byteBufUtils = ByteBufUtils.class.getSimpleName();
|
||||
@@ -52,7 +52,7 @@ public abstract class EnhanceUtils {
|
||||
|
||||
var classPool = ClassPool.getDefault();
|
||||
|
||||
// 导入需要的包
|
||||
// Import required packages
|
||||
classPool.importPackage(ByteBufUtils.class.getName());
|
||||
classPool.importPackage(CollectionUtils.class.getName());
|
||||
classPool.importPackage(ArrayUtils.class.getName());
|
||||
@@ -61,7 +61,7 @@ public abstract class EnhanceUtils {
|
||||
classPool.importPackage(Map.class.getName());
|
||||
classPool.importPackage(Set.class.getName());
|
||||
|
||||
// 增加类的路径
|
||||
// Add class path
|
||||
for (var clazz : classArray) {
|
||||
if (classPool.find(clazz.getName()) == null) {
|
||||
ClassClassPath classPath = new ClassClassPath(clazz);
|
||||
@@ -103,25 +103,25 @@ public abstract class EnhanceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registration 需要增强的类
|
||||
* @return 返回类的名称格式:EnhanceUtilsProtocolRegistration1
|
||||
* @param registration the class to enhance
|
||||
* @return class name in format: EnhanceUtilsProtocolRegistration1
|
||||
*/
|
||||
public static IProtocolRegistration createProtocolRegistration(ProtocolRegistration registration) throws NotFoundException, CannotCompileException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
var classPool = ClassPool.getDefault();
|
||||
var protocolId = registration.getId();
|
||||
var packetFields = registration.getFieldRegistrations();
|
||||
|
||||
// 定义类名称
|
||||
// Define class name
|
||||
CtClass enhanceClazz = classPool.makeClass(ProtocolRegistration.class.getName() + protocolId);
|
||||
enhanceClazz.addInterface(classPool.get(IProtocolRegistration.class.getName()));
|
||||
|
||||
// 定义类中的一个成员
|
||||
// Define a member field
|
||||
CtField constructorFiled = new CtField(classPool.get(Constructor.class.getName()), "constructor", enhanceClazz);
|
||||
constructorFiled.setModifiers(Modifier.PRIVATE);
|
||||
enhanceClazz.addField(constructorFiled);
|
||||
|
||||
|
||||
// 定义类所包含的所有子协议成员
|
||||
// Define all sub-protocol member fields
|
||||
var allSubProtocolIds = ProtocolAnalysis.getAllSubProtocolIds(protocolId)
|
||||
.stream()
|
||||
.sorted((a, b) -> Short.compare(a, b))
|
||||
@@ -133,13 +133,13 @@ public abstract class EnhanceUtils {
|
||||
enhanceClazz.addField(protocolRegistrationField);
|
||||
}
|
||||
|
||||
// 定义类的构造器
|
||||
// Define the constructor
|
||||
CtConstructor constructor = new CtConstructor(classPool.get(new String[]{Constructor.class.getName()}), enhanceClazz);
|
||||
constructor.setBody("{this.constructor=$1;}");
|
||||
constructor.setModifiers(Modifier.PUBLIC);
|
||||
enhanceClazz.addConstructor(constructor);
|
||||
|
||||
// 定义类实现的接口方法
|
||||
// Define the interface methods implemented by the class
|
||||
CtMethod protocolIdMethod = new CtMethod(classPool.get(short.class.getName()), "protocolId", null, enhanceClazz);
|
||||
protocolIdMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
|
||||
protocolIdMethod.setBody("{return " + registration.protocolId() + ";}");
|
||||
@@ -165,7 +165,7 @@ public abstract class EnhanceUtils {
|
||||
readMethod.setBody(readMethodBody(registration));
|
||||
enhanceClazz.addMethod(readMethod);
|
||||
|
||||
// 释放缓存
|
||||
// Release cache
|
||||
enhanceClazz.detach();
|
||||
|
||||
Class<?> resultClazz = enhanceClazz.toClass(IProtocolRegistration.class);
|
||||
@@ -234,7 +234,7 @@ public abstract class EnhanceUtils {
|
||||
var field = fields[i];
|
||||
var fieldRegistration = fieldRegistrations[i];
|
||||
int index = originFields.indexOf(field);
|
||||
// protocol backwards compatibility,协议向后兼容
|
||||
// Protocol backwards compatibility
|
||||
if (field.isAnnotationPresent(Compatible.class)) {
|
||||
var defaultReadObject = enhanceSerializer(fieldRegistration.serializer()).defaultValue(builder, field, fieldRegistration);
|
||||
builder.append(StringUtils.format("if ({}.compatibleRead($1, beforeReadIndex, length)) {", byteBufUtils));
|
||||
@@ -256,7 +256,7 @@ public abstract class EnhanceUtils {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var field = fields[i];
|
||||
var fieldRegistration = fieldRegistrations[i];
|
||||
// protocol backwards compatibility,协议向后兼容
|
||||
// Protocol backwards compatibility
|
||||
if (field.isAnnotationPresent(Compatible.class)) {
|
||||
builder.append(StringUtils.format("if ({}.compatibleRead($1, beforeReadIndex, length)) {", byteBufUtils));
|
||||
var compatibleReadObject = enhanceSerializer(fieldRegistration.serializer()).readObject(builder, field, fieldRegistration);
|
||||
|
||||
@@ -29,12 +29,12 @@ public interface IProtocolRegistration {
|
||||
Constructor<?> protocolConstructor();
|
||||
|
||||
/**
|
||||
* 序列化
|
||||
* Serialize
|
||||
*/
|
||||
void write(ByteBuf buffer, Object packet);
|
||||
|
||||
/**
|
||||
* 反序列化
|
||||
* Deserialize
|
||||
*/
|
||||
Object read(ByteBuf buffer);
|
||||
|
||||
|
||||
@@ -40,20 +40,17 @@ import static com.zfoo.protocol.ProtocolManager.*;
|
||||
public class ProtocolAnalysis {
|
||||
|
||||
/**
|
||||
* EN: Temp field will be destroyed after startup, and the class corresponding to the protocolId
|
||||
* CN: 临时变量,启动完成就会销毁,协议Id对应的Class类
|
||||
* Temp field: will be destroyed after startup. Maps protocolId to the corresponding Class.
|
||||
*/
|
||||
private static Map<Short, Class<?>> protocolClassMap = new HashMap<>(MAX_PROTOCOL_NUM);
|
||||
|
||||
/**
|
||||
* EN: Temp field, sub protocols
|
||||
* CN: 临时变量,启动完成就会销毁,协议下包含的子协议,只包含一层子协议
|
||||
* Temp field: will be destroyed after startup. Maps each protocol to its direct sub-protocols (one level only).
|
||||
*/
|
||||
private static Map<Short, Set<Short>> subProtocolIdMap = new HashMap<>(MAX_PROTOCOL_NUM);
|
||||
|
||||
/**
|
||||
* EN: Temp field, name reserved keywords, The name of the protocol cannot be named with the following name
|
||||
* CN: 临时变量,启动完成就会销毁,协议名称保留字符,即协议的名称不能用以下名称命名
|
||||
* Temp field: will be destroyed after startup. Reserved names that cannot be used as protocol class names.
|
||||
*/
|
||||
private static Set<String> protocolReserved = Set.of("IProtocol", "IProtocolRegistration", "ProtocolManager", "IFieldRegistration"
|
||||
, "Buffer", "ByteBuf", "IByteBuffer", "ByteBuffer", "LittleEndianByteBuffer", "NormalByteBuffer"
|
||||
@@ -64,19 +61,17 @@ public class ProtocolAnalysis {
|
||||
, "Protocols");
|
||||
|
||||
/**
|
||||
* EN: Temp field, unsupported type
|
||||
* CN: 临时变量,启动完成就会销毁,不支持的类型
|
||||
* Temp field: will be destroyed after startup. Types that are not supported as protocol fields.
|
||||
*/
|
||||
private static Set<Class<?>> unsupportedTypes = Set.of(char.class, Character.class);
|
||||
|
||||
/**
|
||||
* EN: Temp field, base type serializer
|
||||
* CN: 临时变量,启动完成就会销毁,是基本类型序列化器
|
||||
* Temp field: will be destroyed after startup. Serializers for base primitive types.
|
||||
*/
|
||||
private static Map<Class<?>, ISerializer> baseSerializerMap = new HashMap<>(128);
|
||||
|
||||
static {
|
||||
// 初始化基础类型序列化器
|
||||
// Initialize base type serializers
|
||||
baseSerializerMap.put(boolean.class, BoolSerializer.INSTANCE);
|
||||
baseSerializerMap.put(Boolean.class, BoolSerializer.INSTANCE);
|
||||
baseSerializerMap.put(byte.class, ByteSerializer.INSTANCE);
|
||||
@@ -99,20 +94,20 @@ public class ProtocolAnalysis {
|
||||
*/
|
||||
public static synchronized void analyze(Set<Class<?>> protocolClassSet, GenerateOperation generateOperation) {
|
||||
AssertionUtils.notNull(subProtocolIdMap, "[{}] initialization has already been completed, please do not repeat the initialization", ProtocolManager.class.getSimpleName());
|
||||
// 检查协议类是否合法
|
||||
// Validate protocol class
|
||||
for (var protocolClass : protocolClassSet) {
|
||||
var protocolId = getProtocolIdAndCheckClass(protocolClass);
|
||||
AssertionUtils.isTrue(protocolId >= 0, "[class:{}] must use annotation @Protocol annotation", protocolClass.getCanonicalName());
|
||||
initProtocolClass(protocolId, protocolClass);
|
||||
}
|
||||
|
||||
// 协议id和协议信息对应起来
|
||||
// Map protocol ID to protocol info
|
||||
for (var protocolClass : protocolClassSet) {
|
||||
var registration = parseProtocolRegistration(protocolClass, ProtocolModule.DEFAULT_PROTOCOL_MODULE);
|
||||
protocols[registration.protocolId()] = registration;
|
||||
}
|
||||
|
||||
// 通过指定类注册的协议,全部使用字节码增强
|
||||
// All protocols registered via specific classes use bytecode enhancement
|
||||
var enhanceList = Arrays.stream(protocols).filter(Objects::nonNull).toList();
|
||||
enhance(generateOperation, enhanceList);
|
||||
}
|
||||
@@ -128,7 +123,7 @@ public class ProtocolAnalysis {
|
||||
.sorted((a, b) -> a.getCanonicalName().compareTo(b.getCanonicalName()))
|
||||
.toList();*/
|
||||
|
||||
// 检查协议类是否合法
|
||||
// Validate protocol class
|
||||
var noProtocolClassList = new ArrayList<Class<?>>();
|
||||
for (var protocolClass : relevantClassSet) {
|
||||
var protocolId = getProtocolIdAndCheckClass(protocolClass);
|
||||
@@ -148,20 +143,20 @@ public class ProtocolAnalysis {
|
||||
initProtocolClass(countProtocolId, protocolClass);
|
||||
}
|
||||
|
||||
// 协议id和协议信息对应起来
|
||||
// Map protocol ID to protocol info
|
||||
for (var protocolClass : relevantClassSet) {
|
||||
var registration = parseProtocolRegistration(protocolClass, ProtocolModule.DEFAULT_PROTOCOL_MODULE);
|
||||
protocols[registration.protocolId()] = registration;
|
||||
}
|
||||
|
||||
// 通过指定类注册的协议,全部使用字节码增强
|
||||
// All protocols registered via specific classes use bytecode enhancement
|
||||
var enhanceList = Arrays.stream(protocols).filter(Objects::nonNull).toList();
|
||||
enhance(generateOperation, enhanceList);
|
||||
}
|
||||
|
||||
/**
|
||||
* EN: If the path of the package contains a classpath, the moduleId of the classpath is preferred, which means that the priority of the classpath will be higher.
|
||||
* CN: 可以同时在一个protocol.xml文件中使用包路径和类路径。如果包的路径包含了类路径,则优先使用类路径的moduleId,也就是说类路径的优先级会更加的高。
|
||||
* Both package paths and class paths can be used in a single protocol.xml file. When a package path contains a class path, the class path's moduleId takes priority.
|
||||
*/
|
||||
public static synchronized void analyze(XmlProtocols xmlProtocols, GenerateOperation generateOperation) {
|
||||
AssertionUtils.notNull(subProtocolIdMap, "[{}] initialization has already been completed, please do not repeat the initialization", ProtocolManager.class.getSimpleName());
|
||||
@@ -170,7 +165,7 @@ public class ProtocolAnalysis {
|
||||
var classModuleDefinitionMap = new HashMap<Class<?>, Byte>();
|
||||
var moduleDefinitionClassMap = new HashMap<Byte, Set<Class<?>>>();
|
||||
|
||||
// 先注册类,再注册包
|
||||
// Register classes first, then packages
|
||||
for (var moduleDefinition : xmlProtocols.getModules()) {
|
||||
var moduleId = moduleDefinition.getId();
|
||||
var module = new ProtocolModule(moduleId, moduleDefinition.getName());
|
||||
@@ -181,7 +176,7 @@ public class ProtocolAnalysis {
|
||||
if (CollectionUtils.isEmpty(moduleDefinition.getProtocols())) {
|
||||
continue;
|
||||
}
|
||||
//模块定义所有协议
|
||||
// Module defines all protocols
|
||||
var protocolModuleSet = moduleDefinitionClassMap.computeIfAbsent(module.getId(), it -> new HashSet<>());
|
||||
for (var protocolDefinition : moduleDefinition.getProtocols()) {
|
||||
var id = protocolDefinition.getId();
|
||||
@@ -189,20 +184,20 @@ public class ProtocolAnalysis {
|
||||
var enhance = protocolDefinition.isEnhance();
|
||||
|
||||
// Use the class path first to obtain the class name, and search the directory if it cannot be obtained
|
||||
// 优先使用类路径获取类名,获取不到才去搜索目录
|
||||
// Prefer class path to get the class name; fall back to directory scan if not found
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = ClassUtils.forName(location);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
// 如果定义的是类,则需要检查一下格式
|
||||
// If a class is defined, validate its format
|
||||
if (clazz == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var protocolId = getProtocolIdAndCheckClass(clazz);
|
||||
// 没有使用Protocol注解,则使用xml定义的protocolId
|
||||
// No @Protocol annotation; use the protocolId from XML
|
||||
if (protocolId < 0) {
|
||||
if (id < 0) {
|
||||
throw new RunException("[{}] Can not find protocol id, use @Protocol annotation or specify id in xml", clazz.getSimpleName());
|
||||
@@ -221,7 +216,7 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
}
|
||||
|
||||
// 再注册包路径,扫描包开始注册
|
||||
// Then register package paths by scanning
|
||||
for (var moduleDefinition : xmlProtocols.getModules()) {
|
||||
var moduleId = moduleDefinition.getId();
|
||||
var module = modules[moduleId];
|
||||
@@ -229,7 +224,7 @@ public class ProtocolAnalysis {
|
||||
continue;
|
||||
}
|
||||
|
||||
//模块定义所有协议
|
||||
// Module defines all protocols
|
||||
var protocolModuleSet = moduleDefinitionClassMap.computeIfAbsent(module.getId(), it -> new HashSet<>());
|
||||
for (var protocolDefinition : moduleDefinition.getProtocols()) {
|
||||
var id = protocolDefinition.getId();
|
||||
@@ -237,20 +232,20 @@ public class ProtocolAnalysis {
|
||||
var enhance = protocolDefinition.isEnhance();
|
||||
|
||||
// Use the class path first to obtain the class name, and search the directory if it cannot be obtained
|
||||
// 优先使用类路径获取类名,获取不到才去搜索目录
|
||||
// Prefer class path to get the class name; fall back to directory scan if not found
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = ClassUtils.forName(location);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
// 如果定义的是类,则需要检查一下格式
|
||||
// If a class is defined, validate its format
|
||||
if (clazz != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var packetClazzList = scanPackageList(protocolDefinition.getLocation());
|
||||
// 是类路径的话一定不能指定protocol id
|
||||
// If it is a class path, protocol id must not be specified
|
||||
if (CollectionUtils.isEmpty(packetClazzList)) {
|
||||
throw new RunException("can not scan any protocol class in [{}]", location);
|
||||
}
|
||||
@@ -259,7 +254,7 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
|
||||
for (Class<?> protocolClass : packetClazzList) {
|
||||
// 如果location已经指定过了,则优先使用class的绝对路径定义的moduleId
|
||||
// If location was already specified, prefer the moduleId from the class absolute path
|
||||
if (classModuleDefinitionMap.containsKey(protocolClass)) {
|
||||
continue;
|
||||
}
|
||||
@@ -282,11 +277,11 @@ public class ProtocolAnalysis {
|
||||
for (Class<?> clazz : packetClazzList) {
|
||||
var protocolId = ProtocolManager.protocolId(clazz);
|
||||
var registration = parseProtocolRegistration(clazz, module);
|
||||
// Protocol注解或者xml任意一个定义了增强协议,那么就增强协议
|
||||
// Enhance the protocol if either @Protocol annotation or XML defines enhancement
|
||||
if ((clazz.isAnnotationPresent(Protocol.class) && clazz.getAnnotation(Protocol.class).enhance()) || protocolXmlEnhanceMap.get(clazz)) {
|
||||
enhanceList.add(registration);
|
||||
}
|
||||
// 注册协议
|
||||
// Register protocol
|
||||
protocols[protocolId] = registration;
|
||||
}
|
||||
}
|
||||
@@ -295,7 +290,7 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
|
||||
public static List<Class<?>> scanPackageList(String location) {
|
||||
// 获取该路径下所有类
|
||||
// Get all classes under the path
|
||||
var clazzNameSet = new HashSet<String>();
|
||||
try {
|
||||
var clazzList = ClassUtils.getAllClasses(location);
|
||||
@@ -324,11 +319,11 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
|
||||
private static void enhanceProtocolBefore(GenerateOperation generateOperation) throws IOException, ClassNotFoundException {
|
||||
// 检查协议格式
|
||||
// Validate protocol format
|
||||
checkAllProtocolClass();
|
||||
// 检查模块格式
|
||||
// Validate module format
|
||||
checkAllModules();
|
||||
// 生成协议
|
||||
// Generate protocol
|
||||
GenerateProtocolFile.generate(generateOperation);
|
||||
}
|
||||
|
||||
@@ -336,12 +331,12 @@ public class ProtocolAnalysis {
|
||||
if (GraalVmUtils.isGraalVM()) {
|
||||
return;
|
||||
}
|
||||
// 字节码增强
|
||||
// Bytecode enhancement
|
||||
for (var registration : enhanceList) {
|
||||
protocols[registration.protocolId()] = EnhanceUtils.createProtocolRegistration((ProtocolRegistration) registration);
|
||||
}
|
||||
|
||||
// 字节码增强过后,初始化各个子协议成员变量
|
||||
// After bytecode enhancement, initialize sub-protocol member variables
|
||||
for (var registration : enhanceList) {
|
||||
var enhanceProtocolRegistration = protocols[registration.protocolId()];
|
||||
var subProtocolIds = getAllSubProtocolIds(registration.protocolId());
|
||||
@@ -378,11 +373,11 @@ public class ProtocolAnalysis {
|
||||
continue;
|
||||
}
|
||||
if (!clazz.isRecord() && Modifier.isFinal(modifiers)) {
|
||||
// 协议号中的属性的访问修饰符不能为final
|
||||
// Protocol fields must not be final
|
||||
throw new RunException("The access modifier for the [field:{}] attribute in the [{}] protocol number cannot be final", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
if (!Modifier.isPublic(modifiers) && !Modifier.isPrivate(modifiers)) {
|
||||
// 协议号中的属性的访问修饰符必须是public或者private
|
||||
// Protocol fields must be public or private
|
||||
throw new RunException("The access modifier of the [field:{}] attribute in the [{}] protocol number must be public or private", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
@@ -400,7 +395,7 @@ public class ProtocolAnalysis {
|
||||
var order = field.getAnnotation(Compatible.class).value();
|
||||
var oldField = compatibleFieldMap.put(order, field);
|
||||
if (oldField != null) {
|
||||
// 协议号中的属性不能有相同的Compatible order顺序
|
||||
// Protocol fields must not have duplicate @Compatible ordinals
|
||||
throw new RunException("[field:{}] and [field:{}] in the [{}] protocol number cannot have the same Compatible order [order:{}]"
|
||||
, clazz.getCanonicalName(), oldField.getName(), field.getName(), oldField, order);
|
||||
}
|
||||
@@ -409,11 +404,11 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
}
|
||||
|
||||
// 默认无法兼容的协议变量名称从小到大排序,如果想自定义私有协议规则,修改这个排序规则即可
|
||||
// 如果为了增加协议的安全性,每个版本都可以重新修改协议排序规则,让每个版本的协议都不相同,间接实现加密
|
||||
// By default, incompatible protocol fields are sorted alphabetically; customize this rule for private protocols
|
||||
// For added security, update the field sort rule each version; different byte orderings per version act as a form of encryption
|
||||
notCompatibleFields.sort((a, b) -> a.getName().compareTo(b.getName()));
|
||||
|
||||
// 可兼容的协议变量默认都添加到最后
|
||||
// Compatible protocol fields are appended at the end by default
|
||||
var compatibleFields = compatibleFieldMap.entrySet()
|
||||
.stream()
|
||||
.sorted((a, b) -> a.getKey() - b.getKey())
|
||||
@@ -426,7 +421,7 @@ public class ProtocolAnalysis {
|
||||
public static ProtocolRegistration parseProtocolRegistration(Class<?> clazz, ProtocolModule module) {
|
||||
var protocolId = ProtocolManager.protocolId(clazz);
|
||||
var declaredFields = getFields(clazz);
|
||||
// 对象需要被序列化的属性
|
||||
// Fields of the object to be serialized
|
||||
var fields = customFieldOrder(clazz, declaredFields);
|
||||
|
||||
try {
|
||||
@@ -455,55 +450,55 @@ public class ProtocolAnalysis {
|
||||
|
||||
ISerializer serializer = baseSerializerMap.get(fieldTypeClazz);
|
||||
|
||||
// 是一个基本类型变量
|
||||
// Is a primitive type variable
|
||||
if (serializer != null) {
|
||||
return BaseField.valueOf(serializer);
|
||||
} else if (fieldTypeClazz.isArray()) {
|
||||
// 是一个数组
|
||||
// Is an array
|
||||
Class<?> arrayClazz = fieldTypeClazz.getComponentType();
|
||||
|
||||
IFieldRegistration registration = typeToRegistration(clazz, arrayClazz);
|
||||
return ArrayField.valueOf(registration, field.getType().getComponentType());
|
||||
} else if (Set.class.isAssignableFrom(fieldTypeClazz)) {
|
||||
if (!fieldTypeClazz.equals(Set.class)) {
|
||||
// 必须是Set接口类型
|
||||
// Must be the Set interface type
|
||||
throw new RunException("[class:{}] type declaration is incorrect, it must be of the Set interface type", clazz.getCanonicalName());
|
||||
}
|
||||
|
||||
Type type = field.getGenericType();
|
||||
|
||||
if (!(type instanceof ParameterizedType)) {
|
||||
// 必须是泛型类
|
||||
// Must be a generic class
|
||||
throw new RunException("[class:{}] type declaration is incorrect, not a generic class[field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
|
||||
if (types.length != 1) {
|
||||
// Set类型必须声明泛型类
|
||||
// Set type must declare generic type
|
||||
throw new RunException("Set type declaration in [class:{}] is incorrect, and the generic class must be declared in [field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
IFieldRegistration registration = typeToRegistration(clazz, types[0]);
|
||||
return SetField.valueOf(registration, type);
|
||||
} else if (List.class.isAssignableFrom(fieldTypeClazz)) {
|
||||
// 是一个List
|
||||
// Is a List
|
||||
if (!fieldTypeClazz.equals(List.class)) {
|
||||
// 必须是List接口类型
|
||||
// Must be the List interface type
|
||||
throw new RunException("[class:{}] type declaration is incorrect, it must be a List interface type", clazz.getCanonicalName());
|
||||
}
|
||||
|
||||
Type type = field.getGenericType();
|
||||
|
||||
if (!(type instanceof ParameterizedType)) {
|
||||
// List类型必须声明泛型类
|
||||
// List type must declare generic type
|
||||
throw new RunException("[class:{}] type declaration is incorrect, not a generic class[field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
|
||||
if (types.length != 1) {
|
||||
// List类型必须声明泛型类
|
||||
// List type must declare generic type
|
||||
throw new RunException("List type declaration in [class:{}] is incorrect, and the generic class must be declared in [field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
@@ -512,21 +507,21 @@ public class ProtocolAnalysis {
|
||||
|
||||
} else if (Map.class.isAssignableFrom(fieldTypeClazz)) {
|
||||
if (!fieldTypeClazz.equals(Map.class)) {
|
||||
// 必须是Map接口类型
|
||||
// Must be the Map interface type
|
||||
throw new RunException("[class:{}] type declaration is incorrect, it must be a Map interface type", clazz.getCanonicalName());
|
||||
}
|
||||
|
||||
Type type = field.getGenericType();
|
||||
|
||||
if (!(type instanceof ParameterizedType)) {
|
||||
// Map类型必须声明泛型类
|
||||
// Map type must declare generic type
|
||||
throw new RunException("Map type declaration in [class:{}] is incorrect, and [field:{}] is not a generic class", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
|
||||
if (types.length != 2) {
|
||||
// Map类型必须声明泛型类
|
||||
// Map type must declare generic type
|
||||
throw new RunException("Map type declaration in [class:{}] is incorrect, and the generic class must be declared in [field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
@@ -536,7 +531,7 @@ public class ProtocolAnalysis {
|
||||
return MapField.valueOf(keyRegistration, valueRegistration, type);
|
||||
} else {
|
||||
checkUnsupportedType(fieldTypeClazz);
|
||||
// 是一个协议引用变量
|
||||
// Is a protocol reference variable
|
||||
if (!protocolIdMap.containsKey(fieldTypeClazz)) {
|
||||
throw new RunException("sub protocol:[{}] needs to register in protocol:[{}]", fieldTypeClazz.getCanonicalName(), clazz.getCanonicalName());
|
||||
}
|
||||
@@ -549,7 +544,7 @@ public class ProtocolAnalysis {
|
||||
|
||||
private static IFieldRegistration typeToRegistration(Class<?> currentProtocolClass, Type type) {
|
||||
if (type instanceof ParameterizedType) {
|
||||
// 泛型类
|
||||
// Generic class
|
||||
Class<?> clazz = (Class<?>) ((ParameterizedType) type).getRawType();
|
||||
if (Set.class.equals(clazz)) {
|
||||
// Set<Set<String>>
|
||||
@@ -569,17 +564,17 @@ public class ProtocolAnalysis {
|
||||
Class<?> clazz = ((Class<?>) type);
|
||||
ISerializer serializer = baseSerializerMap.get(clazz);
|
||||
if (serializer != null) {
|
||||
// 基础类型
|
||||
// Primitive type
|
||||
return BaseField.valueOf(serializer);
|
||||
} else if (clazz.isArray()) {
|
||||
// 不支持多维数组或集合嵌套数组类型,仅支持一维数组
|
||||
// Multi-dimensional arrays or collection-nested arrays are not supported; only one-dimensional arrays allowed
|
||||
throw new RunException("Multi-dimensional array or set nested arrays [type:{}] types are not supported, only one-dimensional arrays are supported", type);
|
||||
} else if (clazz.equals(List.class) || clazz.equals(Set.class) || clazz.equals(Map.class)) {
|
||||
// 不支持数组和集合联合使用类型
|
||||
// Combining arrays with collections is not supported
|
||||
throw new RunException("The combination of arrays and collections with the [type:{}] type is not supported", type);
|
||||
} else {
|
||||
checkUnsupportedType(clazz);
|
||||
// 是一个协议引用变量
|
||||
// Is a protocol reference variable
|
||||
if (!protocolIdMap.containsKey(clazz)) {
|
||||
throw new RunException("sub protocol:[{}] needs to register in protocol:[{}]", clazz.getSimpleName(), currentProtocolClass.getCanonicalName());
|
||||
}
|
||||
@@ -595,7 +590,7 @@ public class ProtocolAnalysis {
|
||||
|
||||
/**
|
||||
* EN: This method is only called when the protocol is generated, and cannot be called once it is run
|
||||
* CN: 此方法仅在生成协议的时候调用,一旦运行,不能调用
|
||||
* This method is called only during protocol generation; cannot be called once running
|
||||
*/
|
||||
public static Set<Short> getFirstSubProtocolIds(short protocolId) {
|
||||
return subProtocolIdMap.getOrDefault(protocolId, Collections.emptySet());
|
||||
@@ -603,10 +598,10 @@ public class ProtocolAnalysis {
|
||||
|
||||
/**
|
||||
* EN: This method is only called when the protocol is generated, and cannot be called once it is run
|
||||
* CN: 此方法仅在生成协议的时候调用,一旦运行,不能调用
|
||||
* This method is called only during protocol generation; cannot be called once running
|
||||
*/
|
||||
public static Set<Short> getAllSubProtocolIds(short protocolId) {
|
||||
// 初始化完成过后不能调用getAllSubProtocolIds
|
||||
// Cannot call getAllSubProtocolIds after initialization is complete
|
||||
AssertionUtils.notNull(subProtocolIdMap, "[{}] has been initially completed, and after the initialization is completed, you cannot call getAllSubProtocolIds", ProtocolAnalysis.class.getSimpleName());
|
||||
|
||||
if (!subProtocolIdMap.containsKey(protocolId)) {
|
||||
@@ -621,7 +616,7 @@ public class ProtocolAnalysis {
|
||||
var firstSubProtocolId = queue.poll();
|
||||
if (subProtocolIdMap.containsKey(firstSubProtocolId)) {
|
||||
for (var subClassId : subProtocolIdMap.get(firstSubProtocolId)) {
|
||||
// 不支持循环引用协议
|
||||
// Circular protocol references are not supported
|
||||
if (subClassId == protocolId) {
|
||||
throw new RunException("[class:{}] contains circular reference protocol[class:{}] in the lower level protocol[class:{}]"
|
||||
, protocolClass.getSimpleName(), protocols[firstSubProtocolId].protocolConstructor().getDeclaringClass(), protocolClass.getSimpleName());
|
||||
@@ -637,17 +632,17 @@ public class ProtocolAnalysis {
|
||||
return allSubProtocolIdSet;
|
||||
}
|
||||
|
||||
// 协议智能语法分析,错误的协议定义将无法启动程序并给出错误警告
|
||||
// Smart protocol syntax analysis; incorrect protocol definitions will prevent startup and show an error
|
||||
//-----------------------------------------------------------------------
|
||||
public static void initProtocolClass(short protocolId, Class<?> clazz) {
|
||||
// 协议号重复定义
|
||||
// Duplicate protocol ID definition
|
||||
if (protocolIdMap.containsKey(clazz)) {
|
||||
throw new RunException("duplicate protocol:[{}] protocolId:[{}] and [protocolId:{}]", clazz.getSimpleName(), protocolIdMap.get(clazz), protocolId);
|
||||
}
|
||||
protocolIdMap.put(clazz, protocolId);
|
||||
protocolIdPrimitiveMap.putPrimitive(clazz.hashCode(), protocolId);
|
||||
var previousProtocolClass = protocolClassMap.put(protocolId, clazz);
|
||||
// 协议号重复
|
||||
// Duplicate protocol ID
|
||||
if (previousProtocolClass != null) {
|
||||
throw new RunException("[{}][{}] protocol number [protocolId:{}] is repeated", clazz.getCanonicalName(), previousProtocolClass.getCanonicalName(), protocolId);
|
||||
}
|
||||
@@ -655,12 +650,12 @@ public class ProtocolAnalysis {
|
||||
|
||||
public static short getProtocolIdAndCheckClass(Class<?> clazz) {
|
||||
checkUnsupportedType(clazz);
|
||||
// 是否为一个简单的javabean
|
||||
// Check if it is a simple JavaBean
|
||||
ReflectionUtils.assertIsPojoClass(clazz);
|
||||
// 不能是泛型类
|
||||
// Must not be a generic class
|
||||
AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}] can't be a generic class", clazz.getCanonicalName());
|
||||
|
||||
// 普通Pojo必须要有一个空的构造器
|
||||
// POJO must have a no-args constructor
|
||||
if (!clazz.isRecord()) {
|
||||
Constructor<?> constructor = ReflectionUtils.publicEmptyConstructor(clazz);
|
||||
}
|
||||
@@ -684,14 +679,14 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
|
||||
private static void checkAllModules() {
|
||||
// 模块id不能重复
|
||||
// Module ID must be unique
|
||||
var moduleIdSet = new HashSet<Byte>();
|
||||
Arrays.stream(modules)
|
||||
.filter(Objects::nonNull)
|
||||
.peek(it -> AssertionUtils.isTrue(!moduleIdSet.contains(it.getId()), "duplicate id in module [{}], and the id of modules cannot be duplicated", it))
|
||||
.forEach(it -> moduleIdSet.add(it.getId()));
|
||||
|
||||
// 模块名称不能重复
|
||||
// Module name must be unique
|
||||
var moduleNameSet = new HashSet<String>();
|
||||
Arrays.stream(modules)
|
||||
.filter(Objects::nonNull)
|
||||
@@ -700,9 +695,9 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
|
||||
private static void checkAllProtocolClass() {
|
||||
// 检查协议格式
|
||||
// Validate protocol format
|
||||
|
||||
// 协议的名称不能重复
|
||||
// Protocol name must be unique
|
||||
var allProtocolNameMap = new HashMap<String, Class<?>>();
|
||||
for (var protocolRegistration : protocols) {
|
||||
if (protocolRegistration == null) {
|
||||
@@ -711,13 +706,13 @@ public class ProtocolAnalysis {
|
||||
|
||||
var protocolClass = protocolRegistration.protocolConstructor().getDeclaringClass();
|
||||
var protocolName = protocolClass.getSimpleName();
|
||||
// 协议不能含有重复的名称
|
||||
// Protocol must not have duplicate names
|
||||
if (allProtocolNameMap.containsKey(protocolName)) {
|
||||
throw new RunException("[class:{}] and [class:{}] has duplicate protocol name, protocol cannot contain duplicate names"
|
||||
, protocolClass.getCanonicalName(), allProtocolNameMap.get(protocolName).getCanonicalName());
|
||||
}
|
||||
|
||||
//协议的名称不能是保留名称
|
||||
// Protocol class name must not be a reserved keyword
|
||||
if (protocolReserved.stream().anyMatch(it -> it.equalsIgnoreCase(protocolName))) {
|
||||
throw new RunException("The name of the protocol [class:{}] cannot be a reserved name [{}]", protocolClass.getCanonicalName(), protocolName);
|
||||
}
|
||||
@@ -726,11 +721,11 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
|
||||
|
||||
// 拓扑排序检查循环协议
|
||||
// Topological sort to detect circular protocol dependencies
|
||||
if (subProtocolIdMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 先判断自循环引用
|
||||
// Check for self-referencing protocols first
|
||||
for (var protocolEntry : subProtocolIdMap.entrySet()) {
|
||||
var protocolId = protocolEntry.getKey();
|
||||
var subProtocolSet = protocolEntry.getValue();
|
||||
@@ -739,9 +734,9 @@ public class ProtocolAnalysis {
|
||||
throw new RunException("[class:{}] cannot be circular reference protocol", protocolClass.getSimpleName());
|
||||
}
|
||||
}
|
||||
// 入度
|
||||
// In-degree
|
||||
var inDegree = new HashMap<Short, Integer>();
|
||||
// 初始化入度
|
||||
// Initialize in-degrees
|
||||
for (var protocolEntry : subProtocolIdMap.entrySet()) {
|
||||
var protocolId = protocolEntry.getKey();
|
||||
inDegree.put(protocolId, inDegree.getOrDefault(protocolId, 0));
|
||||
@@ -769,13 +764,13 @@ public class ProtocolAnalysis {
|
||||
}
|
||||
}
|
||||
var circularReferenceProtocols = new ArrayList<String>();
|
||||
// 入度不为0的表示存在循环引用的协议
|
||||
// Non-zero in-degree means a circular protocol dependency exists
|
||||
for (var protocolEntry : inDegree.entrySet()) {
|
||||
if (protocolEntry.getValue() > 0) {
|
||||
circularReferenceProtocols.add(protocols[protocolEntry.getKey()].protocolConstructor().getDeclaringClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
// 抛出所有存在循环引用的协议类名
|
||||
// Throw listing all class names in the circular dependency
|
||||
if (!circularReferenceProtocols.isEmpty()) {
|
||||
throw new RunException("[class:{}] cannot be circular reference protocol", StringUtils.joinWith(",", circularReferenceProtocols.toArray()));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ package com.zfoo.protocol.registration;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 模块id和模块名的封装
|
||||
* Encapsulates a module ID and module name
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -30,7 +30,7 @@ public class ProtocolModule {
|
||||
|
||||
public ProtocolModule(byte id, String name) {
|
||||
if (id < 0) {
|
||||
throw new IllegalArgumentException(StringUtils.format("模块[{}]的id[{}]必须大于0", name, id));
|
||||
throw new IllegalArgumentException(StringUtils.format("Module [{}] must have an id [{}] greater than 0", name, id));
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.zfoo.protocol.registration.field.IFieldRegistration;
|
||||
import com.zfoo.protocol.serializer.reflect.ISerializer;
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -36,11 +35,11 @@ public class ProtocolRegistration implements IProtocolRegistration {
|
||||
private Field[] fields;
|
||||
|
||||
/**
|
||||
* 所有的协议里的发送顺序都是按字段名称排序
|
||||
* All protocols serialize fields in alphabetical order by field name
|
||||
*/
|
||||
private IFieldRegistration[] fieldRegistrations;
|
||||
|
||||
// 兼容相关
|
||||
// Compatibility-related
|
||||
private boolean compatible;
|
||||
private int predictionLength;
|
||||
|
||||
@@ -96,14 +95,14 @@ public class ProtocolRegistration implements IProtocolRegistration {
|
||||
}
|
||||
|
||||
if (compatible) {
|
||||
// 因为写入的是可变长的int,如果预留的位置过多,则清除多余的位置
|
||||
// Variable-length int; clear any over-reserved positions
|
||||
ByteBufUtils.adjustPadding(byteBuf, predictionLength, beforeWriteIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(ByteBuf byteBuf) {
|
||||
// length为-1代表协议没有可兼容的部分,0代表为空对象,正数代表需要兼容的协议长度
|
||||
// length=-1: no compatible part; 0: empty object; positive: compatible protocol byte length
|
||||
var length = ByteBufUtils.readInt(byteBuf);
|
||||
if (length == 0) {
|
||||
return null;
|
||||
@@ -120,7 +119,7 @@ public class ProtocolRegistration implements IProtocolRegistration {
|
||||
var index = originFields.indexOf(field);
|
||||
var packetFieldRegistration = fieldRegistrations[i];
|
||||
|
||||
// 协议向后兼容
|
||||
// Backward-compatible protocol
|
||||
if (field.isAnnotationPresent(Compatible.class)) {
|
||||
if (!ByteBufUtils.compatibleRead(byteBuf, beforeReadIndex, length)) {
|
||||
constructorParams[index] = packetFieldRegistration.defaultValue();
|
||||
@@ -138,7 +137,7 @@ public class ProtocolRegistration implements IProtocolRegistration {
|
||||
Field field = fields[i];
|
||||
IFieldRegistration packetFieldRegistration = fieldRegistrations[i];
|
||||
ISerializer serializer = packetFieldRegistration.serializer();
|
||||
// 协议向后兼容
|
||||
// Backward-compatible protocol
|
||||
if (field.isAnnotationPresent(Compatible.class)) {
|
||||
if (!ByteBufUtils.compatibleRead(byteBuf, beforeReadIndex, length)) {
|
||||
ReflectionUtils.setField(field, object, packetFieldRegistration.defaultValue());
|
||||
|
||||
@@ -16,8 +16,8 @@ package com.zfoo.protocol.registration.field;
|
||||
import com.zfoo.protocol.serializer.reflect.ISerializer;
|
||||
|
||||
/**
|
||||
* 一个包里所包含的变量还有这个变量的序列化器
|
||||
* 描述boolean,byte,short,int,long,float,double,String等基本序列化器
|
||||
* Holds a packet field and its corresponding serializer
|
||||
* Describes basic serializers for boolean, byte, short, int, long, float, double, and String
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ package com.zfoo.protocol.registration.field;
|
||||
import com.zfoo.protocol.serializer.reflect.ISerializer;
|
||||
|
||||
/**
|
||||
* 标记性接口,所有协议里描述变量都要实现这个接口
|
||||
* Marker interface; all field descriptors in protocols must implement this interface
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import com.zfoo.protocol.serializer.reflect.ObjectProtocolSerializer;
|
||||
public class ObjectProtocolField implements IFieldRegistration {
|
||||
|
||||
/**
|
||||
* 协议序列号是ProtocolRegistration的id
|
||||
* The protocol ordinal is the ID of ProtocolRegistration
|
||||
*/
|
||||
private short protocolId;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import com.zfoo.protocol.serializer.typescript.CodeGenerateTypeScript;
|
||||
public enum CodeLanguage {
|
||||
|
||||
/**
|
||||
* Javassist字节码增强
|
||||
* Javassist bytecode enhancement
|
||||
*/
|
||||
Enhance(1, null),
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public enum CodeTemplatePlaceholder {
|
||||
.filter(it -> lineTrim.startsWith(it.placeholder))
|
||||
.findAny();
|
||||
var formatLine = line;
|
||||
// 形如 space ${protocol_class} 的占位符多了几个space,在生成代码的时候将每一行都添加相等的space,以此来对齐生成的多行代码块
|
||||
// A placeholder like 'space ${protocol_class} has extra leading spaces; each generated line gets the same indentation to align the multi-line block
|
||||
if (startPlaceholderOptional.isPresent()) {
|
||||
var startPlaceholder = startPlaceholderOptional.get();
|
||||
// calculate the tab length
|
||||
|
||||
@@ -43,7 +43,7 @@ public class CutDownArraySerializer implements ICutDownSerializer {
|
||||
var arrayName = getArrayClassName(arrayField);
|
||||
var flag = true;
|
||||
|
||||
// 直接在字节码里调用方法是为了减小生成字节码的体积,下面的代码去掉也不会有任何影响
|
||||
// Calling the method directly in bytecode reduces generated bytecode size; removing this code has no functional impact
|
||||
switch (arrayName) {
|
||||
case "boolean":
|
||||
switch (language) {
|
||||
@@ -1285,7 +1285,7 @@ public class CutDownArraySerializer implements ICutDownSerializer {
|
||||
var protocolId = ((ObjectProtocolField) arrayField.getArrayElementRegistration()).getProtocolId();
|
||||
var protocolName = EnhanceObjectProtocolSerializer.getProtocolClassSimpleName(protocolId);
|
||||
switch (language) {
|
||||
// Java不支持泛型的数组初始化,这里不做任何操作
|
||||
// Java does not support generic array initialization; no operation here
|
||||
case Swift:
|
||||
builder.append(StringUtils.format("let {} = buffer.readPacketArray({}) as! Array<{}>", array, protocolId, protocolName)).append(LS);
|
||||
break;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class CutDownMapSerializer implements ICutDownSerializer {
|
||||
var keySerializer = keyRegistration.serializer();
|
||||
var valueSerializer = valueRegistration.serializer();
|
||||
|
||||
// 直接在字节码里调用方法是为了减小生成字节码的体积,下面的代码去掉也不会有任何影响
|
||||
// Calling the method directly in bytecode reduces generated bytecode size; removing this code has no functional impact
|
||||
if (keyRegistration instanceof BaseField) {
|
||||
if (keySerializer == IntSerializer.INSTANCE) {
|
||||
if (valueSerializer == IntSerializer.INSTANCE) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public class CutDownSetSerializer implements ICutDownSerializer {
|
||||
var flag = true;
|
||||
var setName = getSetClassName(setField);
|
||||
|
||||
// 直接在字节码里调用方法是为了减小生成字节码的体积,下面的代码去掉也不会有任何影响
|
||||
// Calling the method directly in bytecode reduces generated bytecode size; removing this code has no functional impact
|
||||
switch (setName) {
|
||||
case "Boolean":
|
||||
switch (language) {
|
||||
|
||||
@@ -7,19 +7,19 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 协议修改过后的测试流程:
|
||||
* 1.测试Java的老协议和新协议的兼容性,NormalObject/ObjectA/ObjectB有几个兼容字段
|
||||
* 2.测试Java在字节码增强,CutDown,反射三种情况下的代码下的协议解析的准确性
|
||||
* 3.测试resource/compatible中有5种协议兼容情况需要测试
|
||||
* 4.1/2/3总共有2*3*5=30种情况
|
||||
* Test workflow after modifying a protocol:
|
||||
* 1. Test backward compatibility between old and new Java protocols; NormalObject/ObjectA/ObjectB have compatible fields
|
||||
* 2. Test parsing accuracy under bytecode enhancement, CutDown, and reflection
|
||||
* 3. Test 5 compatibility scenarios in resource/compatible
|
||||
* 4. Steps 1/2/3 produce 2*3*5=30 test cases
|
||||
* <p>
|
||||
* 协议生成修改后的测试流程:
|
||||
* 1.测试老协议和新协议的兼容性,NormalObject/ObjectA/ObjectB有几个兼容字段
|
||||
* 2.测试普通生成,CutDown两种种情况下的代码下的协议解析的准确性
|
||||
* 3.测试merge,fold,default三种种生成方式
|
||||
* 4.测试resource/compatible中有5种协议兼容情况需要测试
|
||||
* 5.1/2/3/4总共2*2*3*5=60种情况
|
||||
* 6.简单的测试流程,NormalObject/ObjectA打开兼容字段,因为生成都是走的同一个方法直接测试非CutDown的fold
|
||||
* Test workflow after modifying protocol generation:
|
||||
* 1. Test compatibility; NormalObject/ObjectA/ObjectB have compatible fields
|
||||
* 2. Test parsing accuracy under normal generation and CutDown
|
||||
* 3. Test three generation modes: merge, fold, and default
|
||||
* 4. Test 5 compatibility scenarios in resource/compatible
|
||||
* 5. Steps 1/2/3/4 produce 2*2*3*5=60 test cases
|
||||
* 6. Simple test: enable compat fields in NormalObject/ObjectA; directly test non-CutDown fold
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -28,17 +28,17 @@ public interface ICodeGenerate {
|
||||
void init(GenerateOperation generateOperation);
|
||||
|
||||
/**
|
||||
* 将一个包下的协议文件生成在一个协议文件里
|
||||
* Generate all protocol files under a package into a single file
|
||||
*/
|
||||
void mergerProtocol(List<ProtocolRegistration> registrations) throws IOException;
|
||||
|
||||
/**
|
||||
* 折叠协议,生成协议文件会和Java源文件保持相同的目录结构
|
||||
* Fold protocols; generated files mirror the Java source directory structure
|
||||
*/
|
||||
void foldProtocol(List<ProtocolRegistration> registrations) throws IOException;
|
||||
|
||||
/**
|
||||
* 默认把所有的协议文件放在同一个更目录下,不做任何操作
|
||||
* Default: place all protocol files in the same root directory; no further action
|
||||
*/
|
||||
void defaultProtocol(List<ProtocolRegistration> registrations) throws IOException;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import com.zfoo.protocol.registration.field.IFieldRegistration;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 减少生成的字节码和代码的体积,即使不调用这个方法,程序也依然能够正常运行
|
||||
* Reduces generated bytecode and code size; the program runs correctly even without calling this method
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
|
||||
@@ -280,7 +280,7 @@ public class CodeGenerateCpp implements ICodeGenerate {
|
||||
var fieldType = fieldTypeDefaultValue.getKey();
|
||||
|
||||
var propertyFullName = StringUtils.format("{} {};", fieldType, fieldName);
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Cpp);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
cppBuilder.append(fieldNote).append(LS);
|
||||
@@ -358,7 +358,7 @@ public class CodeGenerateCpp implements ICodeGenerate {
|
||||
typeName = typeName.replaceAll("java.util.|java.lang.", StringUtils.EMPTY);
|
||||
typeName = typeName.replaceAll("[a-zA-Z0-9_.]*\\.", StringUtils.EMPTY);
|
||||
|
||||
// CSharp不适用基础类型的泛型,会影响性能
|
||||
// C# does not support primitives as generics; it degrades performance
|
||||
switch (typeName) {
|
||||
case "boolean":
|
||||
case "Boolean":
|
||||
@@ -395,59 +395,59 @@ public class CodeGenerateCpp implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "bool");
|
||||
typeName = typeName.replace("<Boolean", "<bool");
|
||||
typeName = typeName.replace("Boolean>", "bool>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "int8_t");
|
||||
typeName = typeName.replace("Byte>", "int8_t>");
|
||||
typeName = typeName.replace("<Byte", "<int8_t");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "int16_t");
|
||||
typeName = typeName.replace("Short>", "int16_t>");
|
||||
typeName = typeName.replace("<Short", "<int16_t");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "int32_t");
|
||||
typeName = typeName.replace("Integer>", "int32_t>");
|
||||
typeName = typeName.replace("<Integer", "<int32_t");
|
||||
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "int64_t");
|
||||
typeName = typeName.replace("Long>", "int64_t>");
|
||||
typeName = typeName.replace("<Long", "<int64_t");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "float");
|
||||
typeName = typeName.replace("Float>", "float>");
|
||||
typeName = typeName.replace("<Float", "<float");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "double");
|
||||
typeName = typeName.replace("Double>", "double>");
|
||||
typeName = typeName.replace("<Double", "<double");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "char");
|
||||
typeName = typeName.replace("Character>", "char>");
|
||||
typeName = typeName.replace("<Character", "<char");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "string");
|
||||
typeName = typeName.replace("String>", "string>");
|
||||
typeName = typeName.replace("<String", "<string");
|
||||
|
||||
// 将Map转为map
|
||||
// Convert Map to map
|
||||
typeName = typeName.replace("Map<", "map<");
|
||||
|
||||
// 将Set转为set
|
||||
// Convert Set to set
|
||||
typeName = typeName.replace("Set<", "set<");
|
||||
|
||||
// 将List转为vector
|
||||
// Convert List to vector
|
||||
typeName = typeName.replace("List<", "list<");
|
||||
|
||||
return typeName;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class CppSetSerializer implements ICppSerializer {
|
||||
var size = "size" + GenerateProtocolFile.localVariableId++;
|
||||
builder.append(StringUtils.format("int32_t {} = buffer.readInt();", size)).append(LS);
|
||||
GenerateProtocolFile.addTab(builder, deep);
|
||||
// unity里不支持HashSet的初始化大小
|
||||
// Unity does not support specifying initial HashSet capacity
|
||||
// builder.append("var " + result + " = new " + typeName + "(" + size + ");" + LS);
|
||||
builder.append(StringUtils.format("{} {};", typeName, result)).append(LS);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.reflect.Field;
|
||||
public interface ICppSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型和默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
+14
-14
@@ -225,7 +225,7 @@ public class CodeGenerateCsharp implements ICodeGenerate {
|
||||
var fieldName = field.getName();
|
||||
var propertyType = toCsClassName(field.getGenericType().getTypeName());
|
||||
var propertyFullName = StringUtils.format("public {} {};", propertyType, fieldName);
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.CSharp);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
csBuilder.append(fieldNote).append(LS);
|
||||
@@ -285,7 +285,7 @@ public class CodeGenerateCsharp implements ICodeGenerate {
|
||||
typeName = typeName.replaceAll("java.util.|java.lang.", StringUtils.EMPTY);
|
||||
typeName = typeName.replaceAll("[a-zA-Z0-9_.]*\\.", StringUtils.EMPTY);
|
||||
|
||||
// CSharp不适用基础类型的泛型,会影响性能
|
||||
// C# does not support primitives as generics; it degrades performance
|
||||
switch (typeName) {
|
||||
case "boolean":
|
||||
case "Boolean":
|
||||
@@ -318,69 +318,69 @@ public class CodeGenerateCsharp implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replace(" boolean ", " bool ");
|
||||
typeName = typeName.replace(" Boolean ", " bool ");
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "bool[");
|
||||
typeName = typeName.replace("<Boolean", "<bool");
|
||||
typeName = typeName.replace("Boolean>", "bool>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace(" Byte ", " byte ");
|
||||
typeName = typeName.replace("Byte[", "byte[");
|
||||
typeName = typeName.replace("Byte>", "byte>");
|
||||
typeName = typeName.replace("<Byte", "<byte");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace(" Short ", " short ");
|
||||
typeName = typeName.replace("Short[", "short[");
|
||||
typeName = typeName.replace("Short>", "short>");
|
||||
typeName = typeName.replace("<Short", "<short");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace(" Integer ", " int ");
|
||||
typeName = typeName.replace("Integer[", "int[");
|
||||
typeName = typeName.replace("Integer>", "int>");
|
||||
typeName = typeName.replace("<Integer", "<int");
|
||||
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace(" Long ", " long ");
|
||||
typeName = typeName.replace("Long[", "long[");
|
||||
typeName = typeName.replace("Long>", "long>");
|
||||
typeName = typeName.replace("<Long", "<long");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace(" Float ", " float ");
|
||||
typeName = typeName.replace("Float[", "float[");
|
||||
typeName = typeName.replace("Float>", "float>");
|
||||
typeName = typeName.replace("<Float", "<float");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace(" Double ", " double ");
|
||||
typeName = typeName.replace("Double[", "double[");
|
||||
typeName = typeName.replace("Double>", "double>");
|
||||
typeName = typeName.replace("<Double", "<double");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace(" Character ", " char ");
|
||||
typeName = typeName.replace("Character[", "char[");
|
||||
typeName = typeName.replace("Character>", "char>");
|
||||
typeName = typeName.replace("<Character", "<char");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String ", "string ");
|
||||
typeName = typeName.replace("String[", "string[");
|
||||
typeName = typeName.replace("String>", "string>");
|
||||
typeName = typeName.replace("<String", "<string");
|
||||
|
||||
// 将Map转为Dictionary
|
||||
// Convert Map to Dictionary
|
||||
typeName = typeName.replace("Map<", "Dictionary<");
|
||||
|
||||
// 将Set转为HashSet
|
||||
// Convert Set to HashSet
|
||||
typeName = typeName.replace("Set<", "HashSet<");
|
||||
|
||||
// 将private转为public
|
||||
// Convert private to public
|
||||
typeName = typeName.replace(" private ", " public ");
|
||||
|
||||
return typeName;
|
||||
|
||||
@@ -86,7 +86,7 @@ public class CsSetSerializer implements ICsSerializer {
|
||||
var size = "size" + GenerateProtocolFile.localVariableId++;
|
||||
builder.append(StringUtils.format("int {} = buffer.ReadInt();", size)).append(LS);
|
||||
GenerateProtocolFile.addTab(builder, deep);
|
||||
// unity里不支持HashSet的初始化大小
|
||||
// Unity does not support specifying initial HashSet capacity
|
||||
// builder.append("var " + result + " = new " + typeName + "(" + size + ");" + LS);
|
||||
builder.append(StringUtils.format("var {} = new {}();", result, typeName)).append(LS);
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ public class CodeGenerateDart implements ICodeGenerate {
|
||||
var field = sequencedFields.get(i);
|
||||
IFieldRegistration fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Dart);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
dartBuilder.append(fieldNote).append(LS);
|
||||
@@ -368,47 +368,47 @@ public class CodeGenerateDart implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "bool");
|
||||
typeName = typeName.replace("<Boolean", "<bool");
|
||||
typeName = typeName.replace("Boolean>", "bool>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "int");
|
||||
typeName = typeName.replace("Byte>", "int>");
|
||||
typeName = typeName.replace("<Byte", "<int");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "int");
|
||||
typeName = typeName.replace("Short>", "int>");
|
||||
typeName = typeName.replace("<Short", "<int");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "int");
|
||||
typeName = typeName.replace("Integer>", "int>");
|
||||
typeName = typeName.replace("<Integer", "<int");
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "int");
|
||||
typeName = typeName.replace("Long>", "int>");
|
||||
typeName = typeName.replace("<Long", "<int");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "double");
|
||||
typeName = typeName.replace("Float>", "double>");
|
||||
typeName = typeName.replace("<Float", "<double");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "double");
|
||||
typeName = typeName.replace("Double>", "double>");
|
||||
typeName = typeName.replace("<Double", "<double");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "String");
|
||||
typeName = typeName.replace("Character>", "String>");
|
||||
typeName = typeName.replace("<Character", "<String");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "String");
|
||||
typeName = typeName.replace("String>", "String>");
|
||||
typeName = typeName.replace("<String", "<String");
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.lang.reflect.Field;
|
||||
*/
|
||||
public interface IDartSerializer {
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
+3
-3
@@ -123,7 +123,7 @@ public class CodeGenerateEcmaScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.mjs文件
|
||||
// Generate ProtocolManager.mjs file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("ecmascript/ProtocolManagerTemplate.mjs");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -164,7 +164,7 @@ public class CodeGenerateEcmaScript implements ICodeGenerate {
|
||||
public void defaultProtocol(List<ProtocolRegistration> registrations) throws IOException {
|
||||
createTemplateFile();
|
||||
|
||||
// 生成ProtocolManager.mjs文件
|
||||
// Generate ProtocolManager.mjs file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("ecmascript/ProtocolManagerTemplate.mjs");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -248,7 +248,7 @@ public class CodeGenerateEcmaScript implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.EcmaScript);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
fieldDefinitionBuilder.append(fieldNote).append(LS);
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.reflect.Field;
|
||||
public interface IEsSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import com.zfoo.protocol.util.StringUtils;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 对应于ObjectProtocolSerializer
|
||||
* Corresponds to ObjectProtocolSerializer
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
|
||||
+5
-8
@@ -18,13 +18,11 @@ import com.zfoo.protocol.generate.GenerateOperation;
|
||||
import com.zfoo.protocol.generate.GenerateProtocolFile;
|
||||
import com.zfoo.protocol.generate.GenerateProtocolNote;
|
||||
import com.zfoo.protocol.generate.GenerateProtocolPath;
|
||||
import com.zfoo.protocol.registration.ProtocolAnalysis;
|
||||
import com.zfoo.protocol.registration.ProtocolRegistration;
|
||||
import com.zfoo.protocol.registration.field.IFieldRegistration;
|
||||
import com.zfoo.protocol.serializer.CodeLanguage;
|
||||
import com.zfoo.protocol.serializer.CodeTemplatePlaceholder;
|
||||
import com.zfoo.protocol.serializer.ICodeGenerate;
|
||||
import com.zfoo.protocol.serializer.enhance.EnhanceObjectProtocolSerializer;
|
||||
import com.zfoo.protocol.serializer.reflect.*;
|
||||
import com.zfoo.protocol.serializer.typescript.CodeGenerateTypeScript;
|
||||
import com.zfoo.protocol.util.ClassUtils;
|
||||
@@ -36,7 +34,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -85,7 +82,7 @@ public class CodeGenerateGdScript implements ICodeGenerate {
|
||||
public void mergerProtocol(List<ProtocolRegistration> registrations) throws IOException {
|
||||
createByteBufferFile();
|
||||
|
||||
// 生成ProtocolManager.gd文件
|
||||
// Generate ProtocolManager.gd file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("gdscript/ProtocolManagerTemplate.gd");
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
for (var registration : registrations) {
|
||||
@@ -166,7 +163,7 @@ public class CodeGenerateGdScript implements ICodeGenerate {
|
||||
private void createTemplateFile(List<ProtocolRegistration> registrations) throws IOException {
|
||||
createByteBufferFile();
|
||||
|
||||
// 生成ProtocolManager.gd文件
|
||||
// Generate ProtocolManager.gd file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("gdscript/ProtocolManagerTemplate.gd");
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
for (var registration : registrations) {
|
||||
@@ -227,19 +224,19 @@ public class CodeGenerateGdScript implements ICodeGenerate {
|
||||
var fields = registration.getFields();
|
||||
var fieldRegistrations = registration.getFieldRegistrations();
|
||||
var gdBuilder = new StringBuilder();
|
||||
// 生成源代码字段的时候,按照原始定义的方式生成
|
||||
// Generate source code fields in their original declaration order
|
||||
var sequencedFields = ReflectionUtils.notStaticAndTransientFields(registration.getConstructor().getDeclaringClass());
|
||||
for (int i = 0; i < sequencedFields.size(); i++) {
|
||||
var field = sequencedFields.get(i);
|
||||
IFieldRegistration fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.GdScript);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
gdBuilder.append(fieldNote).append(LS);
|
||||
}
|
||||
var fieldType = gdSerializer(fieldRegistration.serializer()).fieldType(field, fieldRegistration);
|
||||
// 生成类型的注释
|
||||
// Generate type comment
|
||||
gdBuilder.append(StringUtils.format("var {}: {}", fieldName, fieldType));
|
||||
if (fieldType.equals("Dictionary") || fieldType.equals("Array")) {
|
||||
var typeNote = CodeGenerateTypeScript.toTsClassName(field.getGenericType().toString());
|
||||
|
||||
@@ -200,7 +200,7 @@ public class CodeGenerateGolang implements ICodeGenerate {
|
||||
var fieldType = goSerializer(fieldRegistration.serializer()).fieldType(field, fieldRegistration);
|
||||
|
||||
var propertyFullName = StringUtils.format("{} {}", fieldName, fieldType);
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, field.getName(), CodeLanguage.Golang);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
goBuilder.append(fieldNote).append(LS);
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.lang.reflect.Field;
|
||||
public interface IGoSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型
|
||||
* Get the property type
|
||||
*/
|
||||
String fieldType(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ public class CodeGenerateJava implements ICodeGenerate {
|
||||
var fieldName = field.getName();
|
||||
var propertyType = toJavaClassName(field.getGenericType().getTypeName());
|
||||
var propertyFullName = StringUtils.format("public {} {};", propertyType, fieldName);
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Java);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
javaBuilder.append(fieldNote).append(LS);
|
||||
|
||||
@@ -78,7 +78,7 @@ public class JavaSetSerializer implements IJavaSerializer {
|
||||
var size = "size" + GenerateProtocolFile.localVariableId++;
|
||||
builder.append(StringUtils.format("int {} = buffer.readInt();", size)).append(LS);
|
||||
GenerateProtocolFile.addTab(builder, deep);
|
||||
// unity里不支持HashSet的初始化大小
|
||||
// Unity does not support specifying initial HashSet capacity
|
||||
// builder.append("var " + result + " = new " + typeName + "(" + size + ");" + LS);
|
||||
builder.append(StringUtils.format("var {} = new Hash{}();", result, typeName)).append(LS);
|
||||
|
||||
|
||||
+4
-4
@@ -79,7 +79,7 @@ public class CodeGenerateJavaScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.js文件
|
||||
// Generate ProtocolManager.js file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("javascript/ProtocolManagerTemplate.js");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -127,7 +127,7 @@ public class CodeGenerateJavaScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.js文件
|
||||
// Generate ProtocolManager.js file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("javascript/ProtocolManagerTemplate.js");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -166,7 +166,7 @@ public class CodeGenerateJavaScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.js文件
|
||||
// Generate ProtocolManager.js file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("javascript/ProtocolManagerTemplate.js");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -248,7 +248,7 @@ public class CodeGenerateJavaScript implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.JavaScript);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
fieldDefinitionBuilder.append(fieldNote).append(LS);
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.reflect.Field;
|
||||
public interface IJsSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
+10
-10
@@ -274,7 +274,7 @@ public class CodeGenerateKotlin implements ICodeGenerate {
|
||||
var field = sequencedFields.get(i);
|
||||
IFieldRegistration fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Kotlin);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
ktBuilder.append(fieldNote).append(LS);
|
||||
@@ -374,47 +374,47 @@ public class CodeGenerateKotlin implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "Boolean");
|
||||
typeName = typeName.replace("<Boolean", "<Boolean");
|
||||
typeName = typeName.replace("Boolean>", "Boolean>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "Byte");
|
||||
typeName = typeName.replace("Byte>", "Byte>");
|
||||
typeName = typeName.replace("<Byte", "<Byte");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "Short");
|
||||
typeName = typeName.replace("Short>", "Short>");
|
||||
typeName = typeName.replace("<Short", "<Short");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "Int");
|
||||
typeName = typeName.replace("Integer>", "Int>");
|
||||
typeName = typeName.replace("<Integer", "<Int");
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "Long");
|
||||
typeName = typeName.replace("Long>", "Long>");
|
||||
typeName = typeName.replace("<Long", "<Long");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "Float");
|
||||
typeName = typeName.replace("Float>", "Float>");
|
||||
typeName = typeName.replace("<Float", "<Float");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "Double");
|
||||
typeName = typeName.replace("Double>", "Double>");
|
||||
typeName = typeName.replace("<Double", "<Double");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "String");
|
||||
typeName = typeName.replace("Character>", "String>");
|
||||
typeName = typeName.replace("<Character", "<String");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "String");
|
||||
typeName = typeName.replace("String>", "String>");
|
||||
typeName = typeName.replace("<String", "<String");
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.lang.reflect.Field;
|
||||
*/
|
||||
public interface IKtSerializer {
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ public class CodeGenerateLua implements ICodeGenerate {
|
||||
var field = fields[i];
|
||||
var fieldRegistration = fieldRegistrations[i];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Lua);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
luaBuilder.append(fieldNote).append(LS);
|
||||
@@ -273,7 +273,7 @@ public class CodeGenerateLua implements ICodeGenerate {
|
||||
luaBuilder.append(StringUtils.format("{} = {},", fieldName, fieldDefaultValue));
|
||||
}
|
||||
var typeNote = CodeGenerateCsharp.toCsClassName(field.getGenericType().getTypeName());
|
||||
// 生成类型的注释
|
||||
// Generate type comment
|
||||
luaBuilder.append(" -- ").append(typeNote).append(LS);
|
||||
}
|
||||
return luaBuilder.toString();
|
||||
|
||||
@@ -238,7 +238,7 @@ public class CodeGeneratePhp implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Php);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
phpBuilder.append(fieldNote).append(LS);
|
||||
@@ -325,48 +325,48 @@ public class CodeGeneratePhp implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "boolean");
|
||||
typeName = typeName.replace("<Boolean", "<boolean");
|
||||
typeName = typeName.replace("Boolean>", "boolean>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "number");
|
||||
typeName = typeName.replace("Byte>", "number>");
|
||||
typeName = typeName.replace("<Byte", "<number");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "number");
|
||||
typeName = typeName.replace("Short>", "number>");
|
||||
typeName = typeName.replace("<Short", "<number");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "number");
|
||||
typeName = typeName.replace("Integer>", "number>");
|
||||
typeName = typeName.replace("<Integer", "<number");
|
||||
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "number");
|
||||
typeName = typeName.replace("Long>", "number>");
|
||||
typeName = typeName.replace("<Long", "<number");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "number");
|
||||
typeName = typeName.replace("Float>", "number>");
|
||||
typeName = typeName.replace("<Float", "<number");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "number");
|
||||
typeName = typeName.replace("Double>", "number>");
|
||||
typeName = typeName.replace("<Double", "<number");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "string");
|
||||
typeName = typeName.replace("Character>", "string>");
|
||||
typeName = typeName.replace("<Character", "<string");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "string");
|
||||
typeName = typeName.replace("String>", "string>");
|
||||
typeName = typeName.replace("<String", "<string");
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.reflect.Field;
|
||||
public interface IPhpSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public abstract class GeneratePbUtils {
|
||||
|
||||
/**
|
||||
* EN: If the tag of a protobuf field exceeds this value, this field is considered to be a compatible protocol field
|
||||
* CN: 如果protobuf的字段的tag超过这个值,则视这个字段为需要兼容的协议字段
|
||||
* If a protobuf field tag exceeds this value, treat the field as backward-compatible
|
||||
*/
|
||||
public static final int COMPATIBLE_FIELD_TAG = 1000;
|
||||
|
||||
@@ -293,18 +293,18 @@ public abstract class GeneratePbUtils {
|
||||
}
|
||||
|
||||
private static void buildImports(PbGenerateOperation pbGenerateOperation, List<Proto> protos, Proto proto, String fieldType, Set<String> imports) {
|
||||
// 基本数据类型不需要导入
|
||||
// Primitive types do not require imports
|
||||
var typeProtobuf = PbType.typeOfProtobuf(fieldType);
|
||||
if (typeProtobuf != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 属于同一个包不需要导入
|
||||
// Same-package types do not require imports
|
||||
if (proto.getPbMessages().stream().anyMatch(it -> it.getName().equals(fieldType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历其它的proto找到需要导入的类
|
||||
// Scan other proto files to find types that need to be imported
|
||||
for (var pt : protos) {
|
||||
for (var msg : pt.getPbMessages()) {
|
||||
if (msg.getName().equals(fieldType)) {
|
||||
@@ -502,7 +502,7 @@ public abstract class GeneratePbUtils {
|
||||
if (StringUtils.isBlank(split)) {
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
// Capitalize the first letter
|
||||
builder.append(StringUtils.capitalize(split.trim()));
|
||||
}
|
||||
var outClassName = builder.toString();
|
||||
@@ -561,18 +561,18 @@ public abstract class GeneratePbUtils {
|
||||
}
|
||||
|
||||
private static void buildOneProtocolImports(PbGenerateOperation pbGenerateOperation, List<Proto> protos, Proto proto, String fieldType, Set<String> imports) {
|
||||
// 基本数据类型不需要导入
|
||||
// Primitive types do not require imports
|
||||
var typeProtobuf = PbType.typeOfProtobuf(fieldType);
|
||||
if (typeProtobuf != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 属于同一个包不需要导入
|
||||
// Same-package types do not require imports
|
||||
if (proto.getPbMessages().stream().anyMatch(it -> it.getName().equals(fieldType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历其它的proto找到需要导入的类
|
||||
// Scan other proto files to find types that need to be imported
|
||||
for (var pt : protos) {
|
||||
for (var msg : pt.getPbMessages()) {
|
||||
if (msg.getName().equals(fieldType)) {
|
||||
|
||||
@@ -20,27 +20,27 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* protocol buffer协议消息体属性数据类型定义
|
||||
* Defines the field data types for a protobuf message
|
||||
*/
|
||||
public class PbField {
|
||||
/**
|
||||
* 属性是否赋值类型
|
||||
* Indicates whether the field is an assignment type
|
||||
*/
|
||||
public enum Cardinality {
|
||||
/**
|
||||
* 未知类型
|
||||
* Unknown type
|
||||
*/
|
||||
UNKNOWN("unknown"),
|
||||
/**
|
||||
* 必须赋值
|
||||
* Required
|
||||
*/
|
||||
REQUIRED("required"),
|
||||
/**
|
||||
* 可以不赋值
|
||||
* Optional
|
||||
*/
|
||||
OPTIONAL("optional"),
|
||||
/**
|
||||
* 可以赋多个值
|
||||
* Repeated
|
||||
*/
|
||||
REPEATED("repeated");
|
||||
|
||||
@@ -68,23 +68,23 @@ public class PbField {
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息属性的规则
|
||||
* Field rule in the message
|
||||
*/
|
||||
private Cardinality cardinality;
|
||||
/**
|
||||
* 消息属性名称
|
||||
* Field name
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 消息属性值的类型
|
||||
* Field value type
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 属性的顺序标记一个消息内不允许重复
|
||||
* Field tag; must be unique within a message
|
||||
*/
|
||||
private int tag;
|
||||
/**
|
||||
* 消息属性的注释信息
|
||||
* Field documentation comment
|
||||
*/
|
||||
private List<String> comments = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
package com.zfoo.protocol.serializer.protobuf;
|
||||
|
||||
/**
|
||||
* protocol buffer中Map的Field的结构体定义
|
||||
* Defines the structure for a Map field in protobuf
|
||||
*/
|
||||
public class PbMapField extends PbField {
|
||||
|
||||
|
||||
@@ -17,23 +17,23 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* protocol buffer消息体的结构定义
|
||||
* Defines the structure of a protobuf message
|
||||
*/
|
||||
public class PbMessage {
|
||||
/**
|
||||
* 消息的名称
|
||||
* Message name
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 协议Id
|
||||
* Protocol ID
|
||||
*/
|
||||
private short protocolId;
|
||||
/**
|
||||
* 消息包含的Fields列表
|
||||
* List of fields in the message
|
||||
*/
|
||||
private List<PbField> fields = new ArrayList<>();
|
||||
/**
|
||||
* 消息的备注信息
|
||||
* Message documentation comment
|
||||
*/
|
||||
private List<String> comments = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
package com.zfoo.protocol.serializer.protobuf;
|
||||
|
||||
/**
|
||||
* protocol buffer协议的Option的数据类型定义
|
||||
* Defines the data type for a protobuf option
|
||||
*/
|
||||
public class PbOption {
|
||||
|
||||
/**
|
||||
* 选项名称
|
||||
* Option name
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 选项对应的值
|
||||
* Option value
|
||||
*/
|
||||
private String value;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ package com.zfoo.protocol.serializer.protobuf;
|
||||
import com.zfoo.protocol.collection.ArrayUtils;
|
||||
|
||||
/**
|
||||
* java的数据类型和protocol buffer的数据类型的对应关系以及默认值
|
||||
* Mapping between Java data types and protobuf data types with default values
|
||||
*/
|
||||
public enum PbTypeJava {
|
||||
INT("int", "Integer", 0),
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
package com.zfoo.protocol.serializer.protobuf;
|
||||
|
||||
/**
|
||||
* protocol buffer的协议版本
|
||||
* Protobuf syntax version
|
||||
*/
|
||||
public enum Syntax {
|
||||
|
||||
|
||||
@@ -17,45 +17,44 @@ import com.zfoo.protocol.serializer.protobuf.PbMessage;
|
||||
import com.zfoo.protocol.serializer.protobuf.PbOption;
|
||||
import com.zfoo.protocol.serializer.protobuf.Syntax;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* proto文件的结构定义
|
||||
* Defines the structure of a proto file
|
||||
*/
|
||||
public class Proto {
|
||||
|
||||
/**
|
||||
* 协议的起始Id
|
||||
* Starting protocol ID
|
||||
*/
|
||||
private short startProtocolId;
|
||||
/**
|
||||
* Proto描述文件的名称
|
||||
* Proto file name
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* Proto的可选信息
|
||||
* Optional proto settings
|
||||
*/
|
||||
private List<PbOption> options = new ArrayList<>();
|
||||
/**
|
||||
* 文档版本没有指定默认为proto3
|
||||
* Defaults to proto3 if no syntax version is specified
|
||||
*/
|
||||
private Syntax syntax = Syntax.PROTO_3;
|
||||
/**
|
||||
* proto文件的包名
|
||||
* Package name of the proto file
|
||||
*/
|
||||
private String protoPackage;
|
||||
/**
|
||||
* 定义的消息列表
|
||||
* List of defined messages
|
||||
*/
|
||||
private List<PbMessage> pbMessages = new ArrayList<>();
|
||||
/**
|
||||
* 文档导入的文件的路径列表
|
||||
* List of imported file paths
|
||||
*/
|
||||
private List<String> imports = new ArrayList<>();
|
||||
/**
|
||||
* proto文件的单行注释列表
|
||||
* List of single-line comments in the proto file
|
||||
*/
|
||||
private List<String> comments = new ArrayList<>();
|
||||
|
||||
|
||||
+22
-23
@@ -16,33 +16,32 @@ package com.zfoo.protocol.serializer.protobuf.parser;
|
||||
import com.zfoo.protocol.collection.CollectionUtils;
|
||||
import com.zfoo.protocol.serializer.protobuf.*;
|
||||
import com.zfoo.protocol.serializer.protobuf.PbField.Cardinality;
|
||||
import com.zfoo.protocol.util.NumberUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* proto文件的解析器
|
||||
* Parser for proto files
|
||||
*/
|
||||
public class ProtoParser {
|
||||
/**
|
||||
* 需要解析的字符数组
|
||||
* Character array to be parsed
|
||||
*/
|
||||
protected char[] data;
|
||||
/**
|
||||
* 解析的游标
|
||||
* Parsing cursor position
|
||||
*/
|
||||
protected int pos;
|
||||
/**
|
||||
* 正在解析的行数
|
||||
* Current line number being parsed
|
||||
*/
|
||||
protected int row;
|
||||
/**
|
||||
* 每行开始对应的游标
|
||||
* Cursor positions for the start of each line
|
||||
*/
|
||||
protected Map<Integer, Integer> rowsPos = new HashMap<>();
|
||||
/**
|
||||
* 解析时临时存放解析的注释信息
|
||||
* Temporary storage for parsed comment content
|
||||
*/
|
||||
protected List<String> comments = new ArrayList<>();
|
||||
|
||||
@@ -161,7 +160,7 @@ public class ProtoParser {
|
||||
} else if ("reserved".equals(token)) {
|
||||
notSupportReserved();
|
||||
} else if ("message".equals(token)) {
|
||||
// 为了让用法简单,屏蔽内部类的消息定义
|
||||
// Hide inner class message definitions to keep usage simple
|
||||
// msg.addMessage(parseMessage());
|
||||
notSupportInnerMessage();
|
||||
} else if (Cardinality.cardinalityOf(token) != null) {
|
||||
@@ -261,7 +260,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个标识符
|
||||
* Read an identifier token
|
||||
*/
|
||||
private String readToken() throws RuntimeException {
|
||||
trim();
|
||||
@@ -411,7 +410,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析判断语句块是否开始,去掉空格和空行后第一个字符为"{"的返回语句块开始
|
||||
* Check if a block starts; the first non-whitespace character being '{' signals the start
|
||||
*/
|
||||
private void blockStarted(String structName) throws RuntimeException {
|
||||
trim();
|
||||
@@ -434,7 +433,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析判断语句块是否结束,语句块以"}"标示符为结束符
|
||||
* Check if a block ends; block terminates with"}"as the terminator
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -457,7 +456,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析判断Field是否结束,去掉空行和空格,以";"结束表示field解析解决
|
||||
* Check if field parsing is done; strip empty lines and spaces; ';' signals end of field
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -476,9 +475,9 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析判断去掉空格,tab建后第一个字符是否与提供的字符相同
|
||||
* Check if the first non-whitespace/tab character matches the given character
|
||||
*
|
||||
* @param endChar 指定的字符
|
||||
* @param endChar the character to match
|
||||
* @return
|
||||
*/
|
||||
private boolean isEndChar(char endChar) {
|
||||
@@ -498,7 +497,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取单行的注释内容,如果读取到换行符或者文件结束则返回注释的内容
|
||||
* Read a single-line comment; returns when a newline or EOF is encountered
|
||||
*/
|
||||
private String readSingleLineComment() {
|
||||
var builder = new StringBuilder();
|
||||
@@ -542,12 +541,12 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
private boolean isMultiLineCommentFinish() {
|
||||
//如果"*"后为空格则跳过空格
|
||||
// Skip space after '*'
|
||||
if (pos + 1 < data.length) {
|
||||
char c2 = data[pos + 1];
|
||||
if (c2 == ' ') {
|
||||
pos += 2;
|
||||
} else if (c2 == '/') { //多行注释结束
|
||||
} else if (c2 == '/') { // End of block comment
|
||||
pos += 2;
|
||||
trim();
|
||||
nextLine();
|
||||
@@ -594,7 +593,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取proto描述文件中的值,知道分号结束
|
||||
* Read a value from the proto file until a semicolon
|
||||
*/
|
||||
private ProtoValue readValueUtilSemicolon() throws RuntimeException {
|
||||
char c;
|
||||
@@ -640,7 +639,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取proto描述文件中的值
|
||||
* Read a value from the proto file
|
||||
*
|
||||
* @return
|
||||
* @throws RuntimeException
|
||||
@@ -735,7 +734,7 @@ public class ProtoParser {
|
||||
if (v.length() > 0 && v.charAt(v.length() - 1) == '\\') {
|
||||
v.deleteCharAt(v.length() - 1);
|
||||
v.append(c);
|
||||
} else { //如果由引号引起来的字符串第二次出现引号时值结束
|
||||
} else { // Second quote closes a quoted string
|
||||
pos++;
|
||||
trim();
|
||||
if (data[pos] != ';' && data[pos] != ']' && data[pos] != ',') {
|
||||
@@ -787,9 +786,9 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉space,tab,'\r'回车
|
||||
* Strip spaces, tabs, and carriage returns
|
||||
*
|
||||
* @return 去掉空格和tab的个数
|
||||
* @return number of stripped whitespace/tab characters
|
||||
*/
|
||||
private int trim() {
|
||||
char c;
|
||||
@@ -810,7 +809,7 @@ public class ProtoParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否当前字符为换行符,如果是换行符则进行换行
|
||||
* Check if the current character is a newline; increment line counter if so
|
||||
*/
|
||||
private boolean nextLine() {
|
||||
if (pos < data.length && data[pos] == '\r') {
|
||||
|
||||
@@ -243,13 +243,13 @@ public class CodeGeneratePython implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Python);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
pyBuilder.append(fieldNote).append(LS);
|
||||
}
|
||||
var fieldDefaultValue = pySerializer(fieldRegistration.serializer()).fieldDefaultValue(field, fieldRegistration);
|
||||
// 生成类型的注释
|
||||
// Generate type comment
|
||||
pyBuilder.append(StringUtils.format("{} = {}", fieldName, fieldDefaultValue));
|
||||
pyBuilder.append(StringUtils.format(" # {}", CodeGenerateCsharp.toCsClassName(field.getGenericType().getTypeName())));
|
||||
pyBuilder.append(LS);
|
||||
|
||||
@@ -22,11 +22,11 @@ import io.netty.buffer.ByteBuf;
|
||||
public interface ISerializer {
|
||||
|
||||
/**
|
||||
* 往buffer中写入这个值是多少,及其反序列化时要用的解析类型是啥
|
||||
* Write the value to the buffer along with the type info needed for deserialization
|
||||
*
|
||||
* @param buffer buffer缓冲区,存储二进制数控
|
||||
* @param object 要写入到buffer中的对象
|
||||
* @param fieldRegistration 标记下反序列化时读取这个值要用啥类型方式读取
|
||||
* @param buffer buffer; stores binary-encoded data
|
||||
* @param object the object to write into the buffer
|
||||
* @param fieldRegistration marks the type to use when reading this value during deserialization
|
||||
*/
|
||||
void writeObject(ByteBuf buffer, Object object, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class MapSerializer implements ISerializer {
|
||||
Map<?, ?> map = (Map<?, ?>) object;
|
||||
MapField mapField = (MapField) fieldRegistration;
|
||||
|
||||
// map有几组key、value
|
||||
// Number of key-value pairs in the map
|
||||
int size = map.size();
|
||||
if (size == 0) {
|
||||
ByteBufUtils.writeInt(buffer, 0);
|
||||
@@ -47,7 +47,7 @@ public class MapSerializer implements ISerializer {
|
||||
}
|
||||
ByteBufUtils.writeInt(buffer, size);
|
||||
|
||||
// buffer中顺序写入
|
||||
// Write sequentially into the buffer
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
mapField.getMapKeyRegistration().serializer().writeObject(buffer, entry.getKey(), mapField.getMapKeyRegistration());
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import com.zfoo.protocol.registration.field.ObjectProtocolField;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* 只要是protocol都是使用FieldSerializer
|
||||
* All protocol objects use FieldSerializer
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
|
||||
@@ -240,12 +240,12 @@ public class CodeGenerateRuby implements ICodeGenerate {
|
||||
var sequencedFields = ReflectionUtils.notStaticAndTransientFields(registration.getConstructor().getDeclaringClass());
|
||||
for (var field : sequencedFields) {
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Ruby);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
rbBuilder.append(fieldNote).append(LS);
|
||||
}
|
||||
// 生成类型的注释
|
||||
// Generate type comment
|
||||
rbBuilder.append(StringUtils.format("attr_accessor :{}", fieldName));
|
||||
rbBuilder.append(StringUtils.format(" # {}", CodeGenerateCsharp.toCsClassName(field.getGenericType().getTypeName())));
|
||||
rbBuilder.append(LS);
|
||||
|
||||
@@ -81,7 +81,7 @@ public class CodeGenerateRust implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成mod文件
|
||||
// Generate mod file
|
||||
var modBuilder = new StringBuilder();
|
||||
modBuilder.append("pub mod i_byte_buffer;").append(LS);
|
||||
modBuilder.append("pub mod byte_buffer;").append(LS);
|
||||
@@ -144,7 +144,7 @@ public class CodeGenerateRust implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成mod文件
|
||||
// Generate mod file
|
||||
var modBuilder = new StringBuilder();
|
||||
modBuilder.append("pub mod i_byte_buffer;").append(LS);
|
||||
modBuilder.append("pub mod byte_buffer;").append(LS);
|
||||
@@ -260,7 +260,7 @@ public class CodeGenerateRust implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Rust);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
rustBuilder.append(fieldNote).append(LS);
|
||||
@@ -283,7 +283,7 @@ public class CodeGenerateRust implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Rust);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
rustBuilder.append(fieldNote).append(LS);
|
||||
@@ -344,7 +344,7 @@ public class CodeGenerateRust implements ICodeGenerate {
|
||||
typeName = typeName.replaceAll("java.util.|java.lang.", StringUtils.EMPTY);
|
||||
typeName = typeName.replaceAll("[a-zA-Z0-9_.]*\\.", StringUtils.EMPTY);
|
||||
|
||||
// CSharp不适用基础类型的泛型,会影响性能
|
||||
// C# does not support primitives as generics; it degrades performance
|
||||
switch (typeName) {
|
||||
case "boolean":
|
||||
case "Boolean":
|
||||
@@ -380,54 +380,54 @@ public class CodeGenerateRust implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "bool");
|
||||
typeName = typeName.replace("<Boolean", "<bool");
|
||||
typeName = typeName.replace("Boolean>", "bool>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "i8");
|
||||
typeName = typeName.replace("Byte>", "i8>");
|
||||
typeName = typeName.replace("<Byte", "<i8");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "i16");
|
||||
typeName = typeName.replace("Short>", "i16>");
|
||||
typeName = typeName.replace("<Short", "<i16");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "i32");
|
||||
typeName = typeName.replace("Integer>", "i32>");
|
||||
typeName = typeName.replace("<Integer", "<i32");
|
||||
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "i64");
|
||||
typeName = typeName.replace("Long>", "i64>");
|
||||
typeName = typeName.replace("<Long", "<i64");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "f32");
|
||||
typeName = typeName.replace("Float>", "f32>");
|
||||
typeName = typeName.replace("<Float", "<f32");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "f64");
|
||||
typeName = typeName.replace("Double>", "f64>");
|
||||
typeName = typeName.replace("<Double", "<f64");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "String");
|
||||
typeName = typeName.replace("String>", "String>");
|
||||
typeName = typeName.replace("<String", "<String");
|
||||
|
||||
// 将Map转为map
|
||||
// Convert Map to map
|
||||
typeName = typeName.replace("Map<", "HashMap<");
|
||||
|
||||
// 将Set转为set
|
||||
// Convert Set to set
|
||||
typeName = typeName.replace("Set<", "HashSet<");
|
||||
|
||||
// 将List转为vector
|
||||
// Convert List to vector
|
||||
typeName = typeName.replace("List<", "Vec<");
|
||||
|
||||
return typeName;
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.reflect.Field;
|
||||
public interface IRustSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ public class CodeGenerateScala implements ICodeGenerate {
|
||||
var field = sequencedFields.get(i);
|
||||
IFieldRegistration fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Scala);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
scalaBuilder.append(fieldNote).append(LS);
|
||||
@@ -374,47 +374,47 @@ public class CodeGenerateScala implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "Boolean");
|
||||
typeName = typeName.replace("<Boolean", "[Boolean");
|
||||
typeName = typeName.replace("Boolean>", "Boolean]");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "Byte");
|
||||
typeName = typeName.replace("Byte>", "Byte]");
|
||||
typeName = typeName.replace("<Byte", "[Byte");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "Short");
|
||||
typeName = typeName.replace("Short>", "Short]");
|
||||
typeName = typeName.replace("<Short", "[Short");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "Int");
|
||||
typeName = typeName.replace("Integer>", "Int]");
|
||||
typeName = typeName.replace("<Integer", "[Int");
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "Long");
|
||||
typeName = typeName.replace("Long>", "Long]");
|
||||
typeName = typeName.replace("<Long", "[Long");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "Float");
|
||||
typeName = typeName.replace("Float>", "Float]");
|
||||
typeName = typeName.replace("<Float", "[Float");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "Double");
|
||||
typeName = typeName.replace("Double>", "Double]");
|
||||
typeName = typeName.replace("<Double", "[Double");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "String");
|
||||
typeName = typeName.replace("Character>", "String]");
|
||||
typeName = typeName.replace("<Character", "[String");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "String");
|
||||
typeName = typeName.replace("String>", "String]");
|
||||
typeName = typeName.replace("<String", "[String");
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.lang.reflect.Field;
|
||||
*/
|
||||
public interface IScalaSerializer {
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ public class CodeGenerateSwift implements ICodeGenerate {
|
||||
var field = sequencedFields.get(i);
|
||||
IFieldRegistration fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.Swift);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
swiftBuilder.append(fieldNote).append(LS);
|
||||
@@ -302,47 +302,47 @@ public class CodeGenerateSwift implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "Boolean");
|
||||
typeName = typeName.replace("<Boolean", "<Boolean");
|
||||
typeName = typeName.replace("Boolean>", "Boolean>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "Int8");
|
||||
typeName = typeName.replace("Byte>", "Int8>");
|
||||
typeName = typeName.replace("<Byte", "<Int8");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "Int16");
|
||||
typeName = typeName.replace("Short>", "Int16>");
|
||||
typeName = typeName.replace("<Short", "<Int16");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "Int");
|
||||
typeName = typeName.replace("Integer>", "Int>");
|
||||
typeName = typeName.replace("<Integer", "<Int");
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "Int64");
|
||||
typeName = typeName.replace("Long>", "Int64>");
|
||||
typeName = typeName.replace("<Long", "<Int64");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "Float32");
|
||||
typeName = typeName.replace("Float>", "Float32>");
|
||||
typeName = typeName.replace("<Float", "<Float32");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "Float64");
|
||||
typeName = typeName.replace("Double>", "Float64>");
|
||||
typeName = typeName.replace("<Double", "<Float64");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "String");
|
||||
typeName = typeName.replace("Character>", "String>");
|
||||
typeName = typeName.replace("<Character", "<String");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "String");
|
||||
typeName = typeName.replace("String>", "String>");
|
||||
typeName = typeName.replace("<String", "<String");
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.lang.reflect.Field;
|
||||
*/
|
||||
public interface ISwiftSerializer {
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
+13
-13
@@ -82,7 +82,7 @@ public class CodeGenerateTypeScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.ts文件
|
||||
// Generate ProtocolManager.ts file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("typescript/ProtocolManagerTemplate.ts");
|
||||
var protocol_imports_manager = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -127,7 +127,7 @@ public class CodeGenerateTypeScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.ts文件
|
||||
// Generate ProtocolManager.ts file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("typescript/ProtocolManagerTemplate.ts");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -170,7 +170,7 @@ public class CodeGenerateTypeScript implements ICodeGenerate {
|
||||
createTemplateFile();
|
||||
|
||||
|
||||
// 生成ProtocolManager.ts文件
|
||||
// Generate ProtocolManager.ts file
|
||||
var protocolManagerTemplate = ClassUtils.getFileFromClassPathToString("typescript/ProtocolManagerTemplate.ts");
|
||||
var protocol_imports = new StringBuilder();
|
||||
var protocol_manager_registrations = new StringBuilder();
|
||||
@@ -288,7 +288,7 @@ public class CodeGenerateTypeScript implements ICodeGenerate {
|
||||
for (var field : sequencedFields) {
|
||||
var fieldRegistration = fieldRegistrations[GenerateProtocolFile.indexOf(fields, field)];
|
||||
var fieldName = field.getName();
|
||||
// 生成注释
|
||||
// Generate comment
|
||||
var fieldNotes = GenerateProtocolNote.fieldNotes(protocolId, fieldName, CodeLanguage.TypeScript);
|
||||
for (var fieldNote : fieldNotes) {
|
||||
tsBuilder.append(fieldNote).append(LS);
|
||||
@@ -375,48 +375,48 @@ public class CodeGenerateTypeScript implements ICodeGenerate {
|
||||
default:
|
||||
}
|
||||
|
||||
// 将boolean转为bool
|
||||
// Convert boolean to bool
|
||||
typeName = typeName.replaceAll("[B|b]oolean\\[", "boolean");
|
||||
typeName = typeName.replace("<Boolean", "<boolean");
|
||||
typeName = typeName.replace("Boolean>", "boolean>");
|
||||
|
||||
// 将Byte转为byte
|
||||
// Convert Byte to byte
|
||||
typeName = typeName.replace("Byte[", "number");
|
||||
typeName = typeName.replace("Byte>", "number>");
|
||||
typeName = typeName.replace("<Byte", "<number");
|
||||
|
||||
// 将Short转为short
|
||||
// Convert Short to short
|
||||
typeName = typeName.replace("Short[", "number");
|
||||
typeName = typeName.replace("Short>", "number>");
|
||||
typeName = typeName.replace("<Short", "<number");
|
||||
|
||||
// 将Integer转为int
|
||||
// Convert Integer to int
|
||||
typeName = typeName.replace("Integer[", "number");
|
||||
typeName = typeName.replace("Integer>", "number>");
|
||||
typeName = typeName.replace("<Integer", "<number");
|
||||
|
||||
|
||||
// 将Long转为long
|
||||
// Convert Long to long
|
||||
typeName = typeName.replace("Long[", "number");
|
||||
typeName = typeName.replace("Long>", "number>");
|
||||
typeName = typeName.replace("<Long", "<number");
|
||||
|
||||
// 将Float转为float
|
||||
// Convert Float to float
|
||||
typeName = typeName.replace("Float[", "number");
|
||||
typeName = typeName.replace("Float>", "number>");
|
||||
typeName = typeName.replace("<Float", "<number");
|
||||
|
||||
// 将Double转为double
|
||||
// Convert Double to double
|
||||
typeName = typeName.replace("Double[", "number");
|
||||
typeName = typeName.replace("Double>", "number>");
|
||||
typeName = typeName.replace("<Double", "<number");
|
||||
|
||||
// 将Character转为Char
|
||||
// Convert Character to Char
|
||||
typeName = typeName.replace("Character[", "string");
|
||||
typeName = typeName.replace("Character>", "string>");
|
||||
typeName = typeName.replace("<Character", "<string");
|
||||
|
||||
// 将String转为string
|
||||
// Convert String to string
|
||||
typeName = typeName.replace("String[", "string");
|
||||
typeName = typeName.replace("String>", "string>");
|
||||
typeName = typeName.replace("<String", "<string");
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.reflect.Field;
|
||||
public interface ITsSerializer {
|
||||
|
||||
/**
|
||||
* 获取属性的类型,默认值
|
||||
* Get the property type and default value
|
||||
*/
|
||||
Pair<String, String> fieldTypeDefaultValue(Field field, IFieldRegistration fieldRegistration);
|
||||
|
||||
|
||||
@@ -44,11 +44,11 @@ public abstract class AssertionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 可支持带参数format的类型:类{}的成员变量:{}不能有set方法:{}
|
||||
* Supports parameterized format: field {} of class {} must not have a setter: {}
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @param format 格式
|
||||
* @param args 参数
|
||||
* @param expression the boolean expression
|
||||
* @param format format string
|
||||
* @param args arguments
|
||||
*/
|
||||
public static void isTrue(boolean expression, String format, Object... args) {
|
||||
if (!expression) {
|
||||
@@ -64,10 +64,10 @@ public abstract class AssertionUtils {
|
||||
// ----------------------------------long----------------------------------
|
||||
|
||||
/**
|
||||
* lt 参数1是否小于参数2
|
||||
* le 参数1是否小于等于参数2
|
||||
* gt 参数1是否大于参数2
|
||||
* ge 参数1是否大于等于参数2
|
||||
* lt: whether arg1 < arg2
|
||||
* le: whether arg1 <= arg2
|
||||
* gt: whether arg1 > arg2
|
||||
* ge: whether arg1 >= arg2
|
||||
*/
|
||||
public static void ge(long x, long y) {
|
||||
if (x < y) {
|
||||
|
||||
@@ -49,11 +49,11 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描指定包下的class文件
|
||||
* Scan class files under the specified package
|
||||
*
|
||||
* @param packageName 包名称(xxx.xxx)
|
||||
* @return 返回指定包下的class全称集合
|
||||
* @throws IOException 假如扫描失败,则抛出该异常
|
||||
* @param packageName package name(xxx.xxx)
|
||||
* @return set of fully-qualified class names under the package
|
||||
* @throws IOException if the scan fails
|
||||
*/
|
||||
public static Set<String> getAllClasses(String packageName) throws IOException {
|
||||
Set<String> classSet = new HashSet<>();
|
||||
@@ -61,11 +61,11 @@ public abstract class ClassUtils {
|
||||
Enumeration<URL> resourceUrls = getDefaultClassLoader().getResources(packagePath);
|
||||
while (resourceUrls.hasMoreElements()) {
|
||||
URL packageUrl = resourceUrls.nextElement();
|
||||
// 如果是以文件的形式保存在服务器上
|
||||
// If stored as files on the server
|
||||
if (isFileProtocol(packageUrl)) {
|
||||
// file类型的扫描
|
||||
// Scan as file type
|
||||
File file = getFile(packageUrl);
|
||||
// 以文件的方式扫描整个包下的文件 并添加到集合中
|
||||
// Scan all files under the package as file entries and add to the collection
|
||||
findClassesInPackageByFile(packageName, file, classSet);
|
||||
} else if (isJarProtocol(packageUrl)) {
|
||||
findClassesInJarFile(packageName, packageUrl, classSet);
|
||||
@@ -75,15 +75,15 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描jar文件中的class
|
||||
* Scan classes within a JAR file
|
||||
*
|
||||
* @param packageName
|
||||
* 包名称
|
||||
* package name
|
||||
* @param packageUrl
|
||||
* jar的url
|
||||
* URL of the JAR
|
||||
* @param classSet
|
||||
* class全路径集合
|
||||
* @throws IOException 假如解析出现io异常时,则抛出该异常
|
||||
* set of fully-qualified class names
|
||||
* @throws IOException if an I/O error occurs during parsing
|
||||
*/
|
||||
private static void findClassesInJarFile(String packageName, URL packageUrl,
|
||||
Set<String> classSet) throws IOException {
|
||||
@@ -99,9 +99,9 @@ public abstract class ClassUtils {
|
||||
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
|
||||
closeJarFile = !jarCon.getUseCaches();
|
||||
} else {
|
||||
//不是JarURLConnection->需要依赖于URL文件解析。
|
||||
//我们假设URL的格式为“jar:path!/entry”,只要遵循条目格式,协议就是任意的。
|
||||
//我们还将处理带和不带前导“file:”前缀的路径。
|
||||
// Not a JarURLConnection -> fall back to URL file parsing
|
||||
// Assume URL format is "jar:path!/entry"; protocol is arbitrary if entry format is followed
|
||||
// Handle paths both with and without a leading "file:" prefix
|
||||
String urlFile = packageUrl.getFile();
|
||||
try {
|
||||
int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
|
||||
@@ -131,21 +131,21 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定jar文件中所有class名称(包名+类名称)格式为xx.xx.yy
|
||||
* Get all class names (package + class) from a JAR file in xx.xx.yy format
|
||||
*
|
||||
* @param jarFile jar文件
|
||||
* @return class名称集合, 假如参数为null,返回大小为0的集合
|
||||
* @param jarFile the JAR file
|
||||
* @return set of class names; empty set if parameter is null
|
||||
*/
|
||||
public Set<String> findByJarFile(JarFile jarFile) {
|
||||
return findByJarFile(StringUtils.EMPTY, jarFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定jar文件中所有class名称(包名+类名称) xx.xx.yy
|
||||
* Get all class names from a JAR file filtered by package prefix (xx.xx.yy format)
|
||||
*
|
||||
* @param packageName 包名前缀xx.xx
|
||||
* @param jarFile jar文件
|
||||
* @return class名称集合, 假如参数为null,返回大小为0的集合
|
||||
* @param packageName package name prefix (xx.xx)
|
||||
* @param jarFile the JAR file
|
||||
* @return set of class names; empty set if parameter is null
|
||||
*/
|
||||
public static Set<String> findByJarFile(String packageName, JarFile jarFile) {
|
||||
Set<String> classSet = new HashSet<>();
|
||||
@@ -154,7 +154,7 @@ public abstract class ClassUtils {
|
||||
}
|
||||
String packageBasePath = packageName.replaceAll(StringUtils.PERIOD_REGEX, StringUtils.SLASH);
|
||||
if (!"".equals(packageBasePath) && !packageBasePath.endsWith("/")) {
|
||||
// 根条目路径必须以斜杠结束,以允许正确的匹配。匹配sunjre在这里不返回斜杠,但是beajrockit返回。
|
||||
// Root entry path must end with a slash for correct matching. Sun JRE omits the slash; BEA JRockit includes it.
|
||||
packageBasePath = packageBasePath + StringUtils.SLASH;
|
||||
}
|
||||
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
|
||||
@@ -174,7 +174,7 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将给定的jar文件URL解析为JarFile对象
|
||||
* Resolve the given JAR file URL to a JarFile object
|
||||
*
|
||||
* @param jarFileUrl
|
||||
*/
|
||||
@@ -191,42 +191,42 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 以文件的方式扫描整个包下的文件 并添加到集合中
|
||||
* Scan all files under the package as file entries and add to the collection
|
||||
*
|
||||
* @param packageName
|
||||
* 包名称
|
||||
* package name
|
||||
* @param dirOrFile
|
||||
* 查找包对应的文件或文件夹
|
||||
* locate the file or directory for the package
|
||||
* @param classSet
|
||||
* class全路径集合
|
||||
* set of fully-qualified class names
|
||||
*/
|
||||
private static void findClassesInPackageByFile(String packageName, File dirOrFile,
|
||||
Set<String> classSet) {
|
||||
// 如果不存在或者 也不是目录就直接返回
|
||||
// Return if path does not exist or is not a directory
|
||||
if (!dirOrFile.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!dirOrFile.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
// 如果存在 就获取包下的所有文件 包括目录
|
||||
// Get all files under the package, including subdirectories
|
||||
File[] dirFiles = dirOrFile.listFiles();
|
||||
if (dirFiles == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 循环所有文件
|
||||
// Iterate over all files
|
||||
for (File file : dirFiles) {
|
||||
String name = file.getName();
|
||||
// 如果是目录 则继续扫描
|
||||
// If it is a directory, scan recursively
|
||||
if (file.isDirectory()) {
|
||||
findClassesInPackageByFile(packageName + "." + name, file, classSet);
|
||||
} else {
|
||||
String filename = file.getName();
|
||||
// 如果是java类文件 去掉后面的.class 只留下类名
|
||||
// If it is a .class file, strip the extension
|
||||
String className = filename.substring(0, filename.length() - CLASS_FILE_EXT.length());
|
||||
className = packageName + "." + className;
|
||||
//去掉前缀“.”
|
||||
// Strip the prefix“.”
|
||||
if (className.startsWith(".")) {
|
||||
className = className.substring(1);
|
||||
}
|
||||
@@ -237,7 +237,7 @@ public abstract class ClassUtils {
|
||||
|
||||
public static File getFile(URL url) {
|
||||
if (!FILE_PROTOCOL.equals(url.getProtocol())) {
|
||||
throw new IllegalArgumentException("给定的URL无法解析为绝对文件路径: " + url);
|
||||
throw new IllegalArgumentException("The given URL cannot be resolved to an absolute file path: " + url);
|
||||
}
|
||||
try {
|
||||
return new File(toURI(url).getSchemeSpecificPart());
|
||||
@@ -247,10 +247,10 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用给定的URL创建URI
|
||||
* 用“%20”编码替换URI的空格。
|
||||
* @param url 要转换为URI实例的URL
|
||||
* @return URI对象
|
||||
* Create a URI from the given URL
|
||||
* Replace spaces in URI with“%20”encoding.
|
||||
* @param url the URL to convert to a URI
|
||||
* @return URI object
|
||||
* @see java.net.URL#toURI()
|
||||
*/
|
||||
public static URI toURI(URL url) {
|
||||
@@ -258,24 +258,24 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用给定的字符串创建URI
|
||||
* 用“%20”编码替换URI的空格。
|
||||
* @param location 要转换为URI实例的字符串
|
||||
* @return URI对象
|
||||
* Create a URI from the given string
|
||||
* Replace spaces in URI with“%20”encoding.
|
||||
* @param location the string to convert to a URI
|
||||
* @return URI object
|
||||
*/
|
||||
public static URI toURI(String location) {
|
||||
try {
|
||||
return new URI(location.replace(" ", "%20"));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RunException("uri配置错误");
|
||||
throw new RunException("URI configuration error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定的URL是不是file协议
|
||||
* Check if the given URL uses the file protocol
|
||||
*
|
||||
* @param url url
|
||||
* @return 假如是file协议,返回true,否则返回false
|
||||
* @return true if the URL uses the file protocol, false otherwise
|
||||
*/
|
||||
public static boolean isFileProtocol(URL url){
|
||||
if (url == null) {
|
||||
@@ -285,10 +285,10 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定的URL是不是Jar协议
|
||||
* Check if the given URL uses the JAR protocol
|
||||
*
|
||||
* @param url url
|
||||
* @return 假如是Jar协议,返回true,否则返回false
|
||||
* @return true if the URL uses the JAR protocol, false otherwise
|
||||
*/
|
||||
public static boolean isJarProtocol(URL url){
|
||||
if (url == null) {
|
||||
@@ -299,7 +299,7 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 在给定的连接上设置“useCaches”标志,对于基于JNLP的资源,设置false,其他资源该标志保留原样
|
||||
* Set the 'useCaches' flag on the given connection;“useCaches”false for JNLP-based resources, unchanged for others
|
||||
*
|
||||
* @param urlConnection urlConnection
|
||||
*/
|
||||
@@ -310,9 +310,9 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从类路径中读取文件
|
||||
* Read a file from the classpath
|
||||
*
|
||||
* @param filePath 一般指resources中的文件,也可以在jar中
|
||||
* @param filePath typically a file in resources; may also be inside a JAR
|
||||
*/
|
||||
public static InputStream getFileFromClassPath(String filePath) throws IOException {
|
||||
var classLoader = getDefaultClassLoader();
|
||||
@@ -329,10 +329,10 @@ public abstract class ClassUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取编译过后的类文件(*.class)的绝对路径
|
||||
* Get the absolute path of the compiled class file (*.class)
|
||||
*
|
||||
* @param clazz 类Class
|
||||
* @return 对应类的绝对路径
|
||||
* @param clazz the class
|
||||
* @return absolute path of the corresponding class file
|
||||
*/
|
||||
public static String getClassAbsPath(Class<?> clazz) {
|
||||
File file = new File(clazz.getResource("").getPath());
|
||||
@@ -358,7 +358,7 @@ public abstract class ClassUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 返回和clazz相关的所有子协议,协议和protocol协议一致
|
||||
* Return all sub-protocols related to clazz; follows the same protocol conventions
|
||||
*/
|
||||
public static Set<Class<?>> relevantClass(Class<?> clazz) {
|
||||
var classSet = new HashSet<Class<?>>();
|
||||
@@ -371,55 +371,55 @@ public abstract class ClassUtils {
|
||||
return classSet;
|
||||
}
|
||||
|
||||
// 是否为一个简单的javabean,为了防止不同层对象混用造成潜在的并发问题,特别是网络层和po层混用
|
||||
// 不能是泛型类
|
||||
// Check if it is a simple JavaBean; prevents cross-layer misuse causing concurrency issues
|
||||
// Must not be a generic class
|
||||
AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}] cannot be generic class", clazz.getCanonicalName());
|
||||
|
||||
var filedList = ReflectionUtils.notStaticAndTransientFields(clazz);
|
||||
|
||||
for (var field : filedList) {
|
||||
// 是一个基本类型变量
|
||||
// Is a primitive type variable
|
||||
var fieldType = field.getType();
|
||||
if (isBaseType(fieldType)) {
|
||||
// do nothing
|
||||
} else if (fieldType.isArray()) {
|
||||
// 是一个数组
|
||||
// Is an array
|
||||
Class<?> arrayClazz = fieldType.getComponentType();
|
||||
relevantClass0(arrayClazz, classSet);
|
||||
} else if (Set.class.isAssignableFrom(fieldType)) {
|
||||
AssertionUtils.isTrue(fieldType.equals(Set.class), "[class:{}]类型声明不正确,必须是Set接口类型", clazz.getCanonicalName());
|
||||
AssertionUtils.isTrue(fieldType.equals(Set.class), "[class:{}] incorrect type declaration; must be the Set interface", clazz.getCanonicalName());
|
||||
|
||||
var type = field.getGenericType();
|
||||
AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}]类型声明不正确,不是泛型类[field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}] incorrect type declaration; field [field:{}] is not generic", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
var types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
AssertionUtils.isTrue(types.length == 1, "[class:{}]中Set类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
|
||||
AssertionUtils.isTrue(types.length == 1, "[class:{}] Set field [field:{}] must declare a generic type", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
relevantClass0(types[0], classSet);
|
||||
} else if (List.class.isAssignableFrom(fieldType)) {
|
||||
// 是一个List
|
||||
AssertionUtils.isTrue(fieldType.equals(List.class), "[class:{}]类型声明不正确,必须是List接口类型", clazz.getCanonicalName());
|
||||
// Is a List
|
||||
AssertionUtils.isTrue(fieldType.equals(List.class), "[class:{}] incorrect type declaration; must be the List interface", clazz.getCanonicalName());
|
||||
|
||||
var type = field.getGenericType();
|
||||
AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}]类型声明不正确,不是泛型类[field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}] incorrect type declaration; field [field:{}] is not generic", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
var types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
AssertionUtils.isTrue(types.length == 1, "[class:{}]中List类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
|
||||
AssertionUtils.isTrue(types.length == 1, "[class:{}] List field [field:{}] must declare a generic type", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
relevantClass0(types[0], classSet);
|
||||
} else if (Map.class.isAssignableFrom(fieldType)) {
|
||||
if (!fieldType.equals(Map.class)) {
|
||||
throw new RunException("[class:{}]类型声明不正确,必须是Map接口类型", clazz.getCanonicalName());
|
||||
throw new RunException("[class:{}] incorrect type declaration; must be the Map interface", clazz.getCanonicalName());
|
||||
}
|
||||
|
||||
var type = field.getGenericType();
|
||||
if (!(type instanceof ParameterizedType)) {
|
||||
throw new RunException("[class:{}]中数组类型声明不正确,[field:{}]不是泛型类", clazz.getCanonicalName(), field.getName());
|
||||
throw new RunException("[class:{}] array field [field:{}] is not generic", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
var types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
if (types.length != 2) {
|
||||
throw new RunException("[class:{}]中数组类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
|
||||
throw new RunException("[class:{}] array field [field:{}] must declare a generic type", clazz.getCanonicalName(), field.getName());
|
||||
}
|
||||
|
||||
var keyType = types[0];
|
||||
@@ -436,7 +436,7 @@ public abstract class ClassUtils {
|
||||
|
||||
private static void relevantClass0(Type type, Set<Class<?>> classSet) {
|
||||
if (type instanceof ParameterizedType) {
|
||||
// 泛型类
|
||||
// Generic class
|
||||
Class<?> clazz = (Class<?>) ((ParameterizedType) type).getRawType();
|
||||
if (Set.class.equals(clazz)) {
|
||||
// Set<Set<String>>
|
||||
@@ -461,10 +461,10 @@ public abstract class ClassUtils {
|
||||
// do nothing
|
||||
return;
|
||||
} else if (clazz.getComponentType() != null) {
|
||||
// 是一个二维以上数组
|
||||
throw new RunException("不支持多维数组或集合嵌套数组[type:{}]类型,仅支持一维数组", type);
|
||||
// Is a multi-dimensional array
|
||||
throw new RunException("Multi-dimensional or collection-nested arrays not supported for [type:{}]; only one-dimensional arrays are allowed", type);
|
||||
} else if (clazz.equals(List.class) || clazz.equals(Set.class) || clazz.equals(Map.class)) {
|
||||
throw new RunException("不支持数组和集合联合使用[type:{}]类型", type);
|
||||
throw new RunException("Combining arrays with collections is not supported for [type:{}]", type);
|
||||
} else {
|
||||
if (!classSet.add(clazz)) {
|
||||
return;
|
||||
@@ -473,7 +473,7 @@ public abstract class ClassUtils {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RunException("[type:{}]类型不正确", type);
|
||||
throw new RunException("[type:{}] has an incorrect type", type);
|
||||
}
|
||||
|
||||
public static boolean isBaseType(Class<?> clazz) {
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* DOM(Document Object Model)文档对象模型
|
||||
* DOM (Document Object Model) utility
|
||||
* <p>
|
||||
* Convenience methods for working with the DOM API,in particular for working with DOM Nodes and DOM Elements.
|
||||
* </p>
|
||||
@@ -39,7 +39,7 @@ public abstract class DomUtils {
|
||||
|
||||
private static final XmlMapper MAPPER = XmlMapper.builder()
|
||||
.defaultUseWrapper(false)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) // 当反序列化有未知属性则抛异常,true打开这个设置
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) // Throw exception on unknown properties during deserialization; true enables this
|
||||
.build();
|
||||
|
||||
public static <T> T string2Object(String xml, Class<T> clazz) {
|
||||
@@ -70,7 +70,7 @@ public abstract class DomUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获取包含有指定属性的Element
|
||||
* Get the Element that contains the specified attribute
|
||||
*/
|
||||
public static List<Element> getElementsByAttribute(Element element, String attribute, String attributeValue) {
|
||||
var elements = new ArrayList<Element>();
|
||||
@@ -123,7 +123,7 @@ public abstract class DomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 只返回第一层的孩子节点,不返回第一层孩子节点的孩子节点
|
||||
* Only returns direct children; does not recurse into grandchildren
|
||||
* <p>
|
||||
* Retrieves all child elements of the given DOM element
|
||||
* </p>
|
||||
|
||||
@@ -3,11 +3,9 @@ package com.zfoo.protocol.util;
|
||||
import com.zfoo.protocol.exception.RunException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 属性工具类
|
||||
* Field utility class
|
||||
*
|
||||
* @author veione
|
||||
*/
|
||||
@@ -27,11 +25,11 @@ public abstract class FieldUtils {
|
||||
clazz.getDeclaredMethod(methodName);
|
||||
return methodName;
|
||||
} catch (NoSuchMethodException e) {
|
||||
// java的get方法对boolean值有可能对应get或者is,所以尝试获取两种不同的get方法,当两种都获取不到才抛异常
|
||||
// Java getters for booleans may use 'get' or 'is'; try both and throw only if neither exists
|
||||
}
|
||||
|
||||
// 如果属性名的第一个字母是小写且第二个字母大写,那么该属性名直接用作 getter/setter。例如属性名为uName,对应的方法是getuName/setuName。
|
||||
// 如果属性名以大写字母开头,属性名直接用作 getter/setter 方法中 get/set 的后部分。例如属性名为Name,对应的方法是getName/setName。
|
||||
// If the first letter is lowercase and the second uppercase, use the name as-is. E.g., 'uName' -> 'getuName'/'setuName'.
|
||||
// If the field name starts with an uppercase letter, append it directly to get/set. E.g., 'Name' -> 'getName'/'setName'.
|
||||
methodName = "get" + fieldName;
|
||||
try {
|
||||
clazz.getDeclaredMethod(methodName);
|
||||
@@ -72,7 +70,7 @@ public abstract class FieldUtils {
|
||||
|
||||
public static String getMethodToField(Class<?> clazz, String methodName) {
|
||||
try {
|
||||
// 查看clazz时候真的有methodName方法
|
||||
// Check if clazz actually has a method named methodName
|
||||
clazz.getDeclaredMethod(methodName);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RunException("clazz:[{}] has no getMethod:[{}]", clazz.getSimpleName(), methodName);
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* 文件操作工具类
|
||||
* File operation utility class
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -30,17 +30,17 @@ public abstract class FileUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 类Unix路径分隔符
|
||||
* Unix-like path separator
|
||||
*/
|
||||
private static final String UNIX_SEPARATOR = StringUtils.SLASH;
|
||||
/**
|
||||
* Windows路径分隔符
|
||||
* Windows path separator
|
||||
*/
|
||||
private static final String WINDOWS_SEPARATOR = StringUtils.BACK_SLASH;
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前系统的换行分隔符
|
||||
* Get the line separator for the current OS
|
||||
* <pre>
|
||||
* Windows: \r\n
|
||||
* Mac: \r
|
||||
@@ -58,16 +58,16 @@ public abstract class FileUtils {
|
||||
/**
|
||||
* User's current working directory
|
||||
*
|
||||
* @return 绝对路径路径
|
||||
* @return absolute path
|
||||
*/
|
||||
public static String getProAbsPath() {
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接父路径和子路径
|
||||
* Join parent and child paths
|
||||
*
|
||||
* @return 绝对路径
|
||||
* @return absolute path
|
||||
*/
|
||||
public static String joinPath(String parentPath, String childPath) {
|
||||
if (StringUtils.isEmpty(parentPath)) {
|
||||
@@ -81,14 +81,14 @@ public abstract class FileUtils {
|
||||
return new File(parentPath, childPath).getAbsolutePath();
|
||||
}
|
||||
|
||||
//---------------------------------搜索文件--------------------------------------
|
||||
//---------------------------------File search--------------------------------------
|
||||
|
||||
/**
|
||||
* 深度优先搜索文件
|
||||
* Depth-first file search
|
||||
*
|
||||
* @param file 需要搜索的文件
|
||||
* @param fileName 需要搜索的目标文件名
|
||||
* @return 搜索到的文件
|
||||
* @param file the directory to search
|
||||
* @param fileName the target file name
|
||||
* @return the found file
|
||||
*/
|
||||
private static File searchFileInProject(File file, String fileName) {
|
||||
if (file.isFile() && file.getName().equals(fileName)) {
|
||||
@@ -112,10 +112,10 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 广度优先搜索文件
|
||||
* Breadth-first file search
|
||||
*
|
||||
* @param fileOrDirectory 需要查找的文件夹
|
||||
* @return 所有可读的文件
|
||||
* @param fileOrDirectory the directory to search
|
||||
* @return all readable files
|
||||
*/
|
||||
public static List<File> getAllReadableFiles(File fileOrDirectory) {
|
||||
List<File> readableFileList = new ArrayList<>();
|
||||
@@ -139,10 +139,10 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索文件
|
||||
* Search for a file
|
||||
*
|
||||
* @param file 需要查找的文件
|
||||
* @return 如果没有搜索到返回null
|
||||
* @param file the file or directory to search
|
||||
* @return the file if found; null otherwise
|
||||
*/
|
||||
public static File searchFileInProject(File file) {
|
||||
return searchFileInProject(new File(getProAbsPath()), file.getName());
|
||||
@@ -150,37 +150,37 @@ public abstract class FileUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 搜索文件
|
||||
* Search for a file
|
||||
* <p>
|
||||
* 注意:文件名必须是文件全称,包括文件名的后缀
|
||||
* Note: the file name must include the extension
|
||||
*
|
||||
* @param fileName 文件名的全称,包括文件名的后缀
|
||||
* @return 如果没有搜索到返回null
|
||||
* @param fileName the full file name including extension
|
||||
* @return the file if found; null otherwise
|
||||
*/
|
||||
public static File searchFileInProject(String fileName) {
|
||||
return searchFileInProject(new File(getProAbsPath()), fileName);
|
||||
}
|
||||
|
||||
//---------------------------------创建,删除文件--------------------------------------
|
||||
//---------------------------------File creation and deletion--------------------------------------
|
||||
|
||||
/**
|
||||
* 在path文件夹下创建一个fileName文件
|
||||
* Create a file named fileName under the path directory
|
||||
*
|
||||
* @param path 路径
|
||||
* @param fileName 文件名
|
||||
* @return 新创建的File
|
||||
* @throws IOException IO异常
|
||||
* @param path the directory path
|
||||
* @param fileName the file name
|
||||
* @return the newly created File
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
public static File createFile(String path, String fileName) throws IOException {
|
||||
var file = createDirectory(path);
|
||||
|
||||
var newFile = new File(file.getAbsoluteFile() + File.separator + fileName);
|
||||
if (newFile.exists()) {
|
||||
throw new RuntimeException(StringUtils.format("文件已经存在[fileName:{}]", fileName));
|
||||
throw new RuntimeException(StringUtils.format("File already exists [fileName:{}]", fileName));
|
||||
}
|
||||
|
||||
if (!newFile.createNewFile()) {
|
||||
throw new RuntimeException(StringUtils.format("创建文件[fileName:{}]失败", fileName));
|
||||
throw new RuntimeException(StringUtils.format("Failed to create file [fileName:{}]", fileName));
|
||||
}
|
||||
return newFile;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
if (!newFile.createNewFile()) {
|
||||
throw new RuntimeException(StringUtils.format("创建文件[fileName:{}]失败", fileName));
|
||||
throw new RuntimeException(StringUtils.format("Failed to create file [fileName:{}]", fileName));
|
||||
}
|
||||
return newFile;
|
||||
}
|
||||
@@ -236,7 +236,7 @@ public abstract class FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------复制文件------------------------------------------------
|
||||
// ------------------------------------------------File copy------------------------------------------------
|
||||
/**
|
||||
* Copies a file to a new location.
|
||||
* <p>
|
||||
@@ -402,7 +402,7 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------读取文件------------------------------------------------
|
||||
// ------------------------------------------------File reading------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reads the contents of a file into a byte array.
|
||||
@@ -449,33 +449,33 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一个content
|
||||
* Write content to a file
|
||||
*
|
||||
* @param file 文件的绝对路径
|
||||
* @param content 写入的内容
|
||||
* @param append 是否追加
|
||||
* @param file absolute file path
|
||||
* @param content content to write
|
||||
* @param append whether to append
|
||||
*/
|
||||
public static void writeStringToFile(File file, String content, boolean append) {
|
||||
// 字节流
|
||||
// Byte stream
|
||||
FileOutputStream fileOutputStream = null;
|
||||
// 转换流,设置编码集和解码集 .处理乱码问题,是字节到字符的桥梁
|
||||
// Conversion stream: sets encoding; bridges bytes to characters and handles encoding issues
|
||||
OutputStreamWriter outputStreamWriter = null;
|
||||
//处理流中的缓冲流,提高效率
|
||||
// Buffered stream for improved efficiency
|
||||
BufferedWriter bufferedWriter = null;
|
||||
// 如果不用缓冲流的话,程序是读一个数据,写一个数据,这样在数据量大的程序中非常影响效率。
|
||||
// 缓冲流作用是把数据先写入缓冲区,等缓冲区满了,再把数据写到文件里。这样效率就大大提高了
|
||||
// Without buffering, each byte is written individually which is very slow for large data.
|
||||
// Buffered stream: writes to buffer first; flushes to file when full, greatly improving efficiency
|
||||
try {
|
||||
// 以追加的方式打开文件
|
||||
// Open file in append mode
|
||||
fileOutputStream = openOutputStream(file, append);
|
||||
outputStreamWriter = new OutputStreamWriter(fileOutputStream, StringUtils.DEFAULT_CHARSET_NAME);
|
||||
bufferedWriter = new BufferedWriter(outputStreamWriter);
|
||||
bufferedWriter.write(content);// 写数据
|
||||
bufferedWriter.write(content); // Write data
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
// Java的垃圾回收机制不会回收任何的物理资源,只会回收堆内存中对象所占用的内存
|
||||
// finally总会被执行,即使try块中和catch块中有return,也会被执行。
|
||||
// 用来显示回收数据库连接,网络连接,磁盘文件
|
||||
// Java GC reclaims only heap memory; physical resources must be closed explicitly
|
||||
// finally always executes, even when try or catch contains a return statement
|
||||
// Used to explicitly close database connections, network connections, and files
|
||||
IOUtils.closeIO(bufferedWriter, outputStreamWriter, fileOutputStream);
|
||||
}
|
||||
}
|
||||
@@ -492,7 +492,7 @@ public abstract class FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------打开,关闭,文件流--------------------------------------
|
||||
//---------------------------------Opening and closing file streams--------------------------------------
|
||||
|
||||
/**
|
||||
* Opens a {@link FileInputStream} for the specified file, providing better
|
||||
@@ -519,7 +519,7 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果文件不存在,则创建该文件。最好指定为true,以追加的方式打开文件
|
||||
* If the file does not exist, create it. Prefer true to open in append mode
|
||||
* <p>
|
||||
* The parent directory will be created if it does not exist.The file will be created if it does not exist.
|
||||
*
|
||||
@@ -550,13 +550,13 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------文件名称------------------------------------------------
|
||||
// ------------------------------------------------File naming------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获得文件的扩展名,扩展名不带“.”
|
||||
* Get the file extension (without the leading dot)
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 扩展名
|
||||
* @param fileName the file name
|
||||
* @return extension
|
||||
*/
|
||||
public static String fileExtName(String fileName) {
|
||||
if (StringUtils.isBlank(fileName)) {
|
||||
@@ -570,7 +570,7 @@ public abstract class FileUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得文件的名称,不带“.”和扩展名
|
||||
* Get the file name (without extension)
|
||||
*/
|
||||
public static String fileSimpleName(String fileName) {
|
||||
if (StringUtils.isBlank(fileName)) {
|
||||
|
||||
@@ -47,7 +47,7 @@ public abstract class IOUtils {
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @param output the <code>OutputStream</code> to write to
|
||||
* @return the number of bytes copied, or -1 if > Integer.MAX_VALUE
|
||||
* @throws IOException IO异常
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
public static int copy(final InputStream input, final OutputStream output) throws IOException {
|
||||
byte[] buffer = new byte[BYTES_PER_KB];
|
||||
@@ -69,7 +69,7 @@ public abstract class IOUtils {
|
||||
*
|
||||
* @param input the InputStream to read from
|
||||
* @return the requested byte array
|
||||
* @throws IOException IO异常
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
public static byte[] toByteArray(final InputStream input) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
|
||||
@@ -32,29 +32,29 @@ import java.util.*;
|
||||
public abstract class JsonUtils {
|
||||
|
||||
/**
|
||||
* 适用于任何场景下的json转换,只要在各个类方法中不调用configure方法,则MAPPER都是线程安全的
|
||||
* For all JSON conversion use cases; thread-safe as long as configure() is not called on the instance
|
||||
*/
|
||||
public static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
|
||||
/**
|
||||
* 涡轮增压的jackson,会使用字节码增强技术
|
||||
* Turbo-charged Jackson using bytecode enhancement
|
||||
*/
|
||||
public static final ObjectMapper MAPPER_TURBO = new ObjectMapper();
|
||||
|
||||
static {
|
||||
//序列化
|
||||
// Serialization
|
||||
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
// MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||
//序列化枚举是以toString()来输出,默认false,即默认以name()来输出
|
||||
// Serialize enums using toString(); default is false (uses name())
|
||||
// MAPPER.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true);
|
||||
|
||||
|
||||
//反序列化
|
||||
//当反序列化有未知属性则抛异常,true打开这个设置
|
||||
// Deserialization
|
||||
// Throw exception on unknown properties; set to true to enable
|
||||
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
|
||||
//美化输出
|
||||
// Pretty-print output
|
||||
DefaultPrettyPrinter prettyPrinter = (DefaultPrettyPrinter) MAPPER.getSerializationConfig().getDefaultPrettyPrinter();
|
||||
DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter(StringUtils.TAB_ASCII, FileUtils.LS);
|
||||
prettyPrinter.indentObjectsWith(indenter);
|
||||
@@ -73,7 +73,7 @@ public abstract class JsonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
//普通输出
|
||||
// Compact output
|
||||
public static String object2String(Object object) {
|
||||
try {
|
||||
return MAPPER.writeValueAsString(object);
|
||||
@@ -82,7 +82,7 @@ public abstract class JsonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化/美化/优雅的输出
|
||||
// Format / pretty-print / elegant output
|
||||
public static String object2StringPrettyPrinter(Object object) {
|
||||
try {
|
||||
return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
@@ -92,7 +92,7 @@ public abstract class JsonUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 涡轮增压极大提升json转换性能,字节码增强
|
||||
* Turbo-charges JSON conversion performance via bytecode enhancement
|
||||
*/
|
||||
public static <T> T string2ObjectTurbo(String json, Class<T> clazz) {
|
||||
try {
|
||||
@@ -103,7 +103,7 @@ public abstract class JsonUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 涡轮增压极大提升json转换性能,字节码增强
|
||||
* Turbo-charges JSON conversion performance via bytecode enhancement
|
||||
*/
|
||||
public static String object2StringTurbo(Object object) {
|
||||
try {
|
||||
@@ -122,7 +122,7 @@ public abstract class JsonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
//元素不可重复
|
||||
// Elements must be unique
|
||||
public static <T> Set<T> string2Set(String json, Class<T> clazz) {
|
||||
var collectionType = MAPPER.getTypeFactory().constructCollectionType(HashSet.class, clazz);
|
||||
try {
|
||||
@@ -160,21 +160,21 @@ public abstract class JsonUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 在js中获取属性名为nodeName的节点
|
||||
* Get the node whose attribute name is nodeName
|
||||
* <p>
|
||||
* 树模型是基于流式操作
|
||||
* The tree model is based on streaming operations
|
||||
*
|
||||
* @param json json string
|
||||
* @param nodeName 节点名称
|
||||
* @return 节点名称为nodeName的json节点,没有返回空
|
||||
* @param nodeName node name
|
||||
* @return the JSON node with name nodeName; null if not found
|
||||
*/
|
||||
public static JsonNode getNode(String json, String nodeName) {
|
||||
try {
|
||||
var queue = new ArrayDeque<JsonNode>();
|
||||
// 将Json串以树状结构读入内存
|
||||
// Read JSON string into memory as a tree
|
||||
var rootNode = MAPPER.readTree(json);
|
||||
queue.add(rootNode);
|
||||
// 深度优先遍历算法
|
||||
// Depth-first traversal
|
||||
while (!queue.isEmpty()) {
|
||||
var pollNode = queue.poll();
|
||||
var resultNode = pollNode.get(nodeName);
|
||||
@@ -182,7 +182,7 @@ public abstract class JsonUtils {
|
||||
return resultNode;
|
||||
}
|
||||
var iterator = pollNode.elements();
|
||||
// 循环遍历子节点下的信息
|
||||
// Iterate over child nodes
|
||||
while (iterator.hasNext()) {
|
||||
var node = iterator.next();
|
||||
queue.add(node);
|
||||
@@ -198,14 +198,14 @@ public abstract class JsonUtils {
|
||||
try {
|
||||
var jsonMap = new HashMap<String, String>();
|
||||
var queue = new ArrayDeque<JsonNode>();
|
||||
// 将Json串以树状结构读入内存
|
||||
// Read JSON string into memory as a tree
|
||||
var rootNode = MAPPER.readTree(json);
|
||||
queue.add(rootNode);
|
||||
// 深度优先遍历算法
|
||||
// Depth-first traversal
|
||||
while (!queue.isEmpty()) {
|
||||
var pollNode = queue.poll();
|
||||
var iterator = pollNode.fields();
|
||||
// 循环遍历子节点下的信息
|
||||
// Iterate over child nodes
|
||||
while (iterator.hasNext()) {
|
||||
var node = iterator.next();
|
||||
var field = node.getKey();
|
||||
|
||||
@@ -21,33 +21,33 @@ import java.text.NumberFormat;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 数字工具类<br>
|
||||
* BigDecimal(double val)构造方法的结果有一定的不可预知性,例如:
|
||||
* Number utility class<br>
|
||||
* The BigDecimal(double val) constructor is unpredictable, e.g.:
|
||||
*
|
||||
* <pre>
|
||||
* new BigDecimal(0.1)
|
||||
* </pre>
|
||||
* <p>
|
||||
* 表示的不是0.1而是0.1000000000000000055511151231257827021181583404541015625
|
||||
* does not represent 0.1 but 0.1000000000000000055511151231257827021181583404541015625
|
||||
* <p>
|
||||
* 这是因为0.1无法准确的表示为double。因此应该使用new BigDecimal(String)。
|
||||
* because 0.1 cannot be represented exactly as a double. Use new BigDecimal(String) instead.
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
public abstract class NumberUtils {
|
||||
|
||||
/**
|
||||
* 默认除法运算精度
|
||||
* Default division scale
|
||||
*/
|
||||
private static final int DEFAULT_DIV_SCALE = 10;
|
||||
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate addition<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被加值
|
||||
* @return 和
|
||||
* @param values values to add
|
||||
* @return sum
|
||||
*/
|
||||
public static BigDecimal add(Number... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -66,11 +66,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate addition<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被加值
|
||||
* @return 求和
|
||||
* @param values values to add
|
||||
* @return sum
|
||||
*/
|
||||
public static BigDecimal add(String... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -89,11 +89,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate addition<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被加值
|
||||
* @return 求和
|
||||
* @param values values to add
|
||||
* @return sum
|
||||
*/
|
||||
public static BigDecimal add(BigDecimal... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -113,11 +113,11 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate subtraction<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被减值
|
||||
* @return 差
|
||||
* @param values values to subtract
|
||||
* @return difference
|
||||
*/
|
||||
public static BigDecimal sub(Number... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -136,11 +136,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate subtraction<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被减值
|
||||
* @return 差
|
||||
* @param values values to subtract
|
||||
* @return difference
|
||||
*/
|
||||
public static BigDecimal sub(String... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -159,11 +159,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate subtraction<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被减值
|
||||
* @return 差
|
||||
* @param values values to subtract
|
||||
* @return difference
|
||||
*/
|
||||
public static BigDecimal sub(BigDecimal... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -183,11 +183,11 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate multiplication<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被乘值
|
||||
* @return 积
|
||||
* @param values values to multiply
|
||||
* @return product
|
||||
*/
|
||||
public static BigDecimal mul(Number... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -206,22 +206,22 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算
|
||||
* Provides accurate multiplication
|
||||
*
|
||||
* @param a 被乘数
|
||||
* @param b 乘数
|
||||
* @return 积
|
||||
* @param a multiplicand
|
||||
* @param b multiplier
|
||||
* @return product
|
||||
*/
|
||||
public static BigDecimal mul(String a, String b) {
|
||||
return mul(new BigDecimal(a), new BigDecimal(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate multiplication<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被乘值
|
||||
* @return 积
|
||||
* @param values values to multiply
|
||||
* @return product
|
||||
*/
|
||||
public static BigDecimal mul(String... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -240,11 +240,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate multiplication<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被乘值
|
||||
* @return 积
|
||||
* @param values values to multiply
|
||||
* @return product
|
||||
*/
|
||||
public static BigDecimal mul(BigDecimal... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -264,22 +264,22 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况的时候,精确到小数点后10位,后面的四舍五入
|
||||
* Provides (relatively) accurate division; if indivisible, rounds to 10 decimal places
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(Number a, Number b) {
|
||||
return div(a, b, DEFAULT_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况的时候,精确到小数点后10位,后面的四舍五入
|
||||
* Provides (relatively) accurate division; if indivisible, rounds to 10 decimal places
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(String a, String b) {
|
||||
return div(a, b, DEFAULT_DIV_SCALE);
|
||||
@@ -287,63 +287,63 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度,后面的四舍五入
|
||||
* Provides (relatively) accurate division; scale specifies precision; half-up rounding
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @param scale 精确度,如果为负值,取绝对值
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @param scale precision; negative value uses its absolute value
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(Number a, Number b, int scale) {
|
||||
return div(a, b, scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度,后面的四舍五入
|
||||
* Provides (relatively) accurate division; scale specifies precision; half-up rounding
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @param scale 精确度,如果为负值,取绝对值
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @param scale precision; negative value uses its absolute value
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(String a, String b, int scale) {
|
||||
return div(a, b, scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度
|
||||
* Provides (relatively) accurate division; scale specifies precision
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @param scale 精确度,如果为负值,取绝对值
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @param scale precision; negative value uses its absolute value
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(Number a, Number b, int scale, RoundingMode roundingMode) {
|
||||
return div(a.toString(), b.toString(), scale, roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度
|
||||
* Provides (relatively) accurate division; scale specifies precision
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @param scale 精确度,如果为负值,取绝对值
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @param scale precision; negative value uses its absolute value
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(String a, String b, int scale, RoundingMode roundingMode) {
|
||||
return div(new BigDecimal(a), new BigDecimal(b), scale, roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度
|
||||
* Provides (relatively) accurate division; scale specifies precision
|
||||
*
|
||||
* @param a 被除数
|
||||
* @param b 除数
|
||||
* @param scale 精确度,如果为负值,取绝对值
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 两个参数的商
|
||||
* @param a dividend
|
||||
* @param b divisor
|
||||
* @param scale precision; negative value uses its absolute value
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return quotient
|
||||
*/
|
||||
public static BigDecimal div(BigDecimal a, BigDecimal b, int scale, RoundingMode roundingMode) {
|
||||
AssertionUtils.notNull(b, "Divisor must be not null !");
|
||||
@@ -357,11 +357,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算<br>
|
||||
* 如果传入多个值为null或者空,则返回0
|
||||
* Provides accurate multiplication<br>
|
||||
* Returns 0 if null or empty values are passed
|
||||
*
|
||||
* @param values 多个被乘值
|
||||
* @return 积
|
||||
* @param values values to multiply
|
||||
* @return product
|
||||
*/
|
||||
public static BigDecimal div(BigDecimal... values) {
|
||||
if (ArrayUtils.isEmpty(values)) {
|
||||
@@ -382,13 +382,13 @@ public abstract class NumberUtils {
|
||||
// ------------------------------------------------------------------------------------------- round
|
||||
|
||||
/**
|
||||
* 保留固定位数小数<br>
|
||||
* 采用四舍五入策略 {@link RoundingMode#HALF_UP}<br>
|
||||
* 例如保留2位小数:123.456789 =》 123.46
|
||||
* Retain a fixed number of decimal places<br>
|
||||
* Uses {@link RoundingMode#HALF_UP} rounding strategy<br>
|
||||
* E.g., 2 decimal places: 123.456789 => 123.46
|
||||
*
|
||||
* @param v 值
|
||||
* @param scale 保留小数位数
|
||||
* @return 新值
|
||||
* @param v the value
|
||||
* @param scale number of decimal places
|
||||
* @return new value
|
||||
*/
|
||||
public static BigDecimal round(double v, int scale) {
|
||||
return round(v, scale, RoundingMode.HALF_UP);
|
||||
@@ -396,52 +396,52 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 保留固定位数小数<br>
|
||||
* 采用四舍五入策略 {@link RoundingMode#HALF_UP}<br>
|
||||
* 例如保留2位小数:123.456789 =》 123.46
|
||||
* Retain a fixed number of decimal places<br>
|
||||
* Uses {@link RoundingMode#HALF_UP} rounding strategy<br>
|
||||
* E.g., 2 decimal places: 123.456789 => 123.46
|
||||
*
|
||||
* @param numberStr 数字值的字符串表现形式
|
||||
* @param scale 保留小数位数
|
||||
* @return 新值
|
||||
* @param numberStr string representation of the number
|
||||
* @param scale number of decimal places
|
||||
* @return new value
|
||||
*/
|
||||
public static BigDecimal round(String numberStr, int scale) {
|
||||
return round(numberStr, scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留固定位数小数<br>
|
||||
* 采用四舍五入策略 {@link RoundingMode#HALF_UP}<br>
|
||||
* 例如保留2位小数:123.456789 =》 123.46
|
||||
* Retain a fixed number of decimal places<br>
|
||||
* Uses {@link RoundingMode#HALF_UP} rounding strategy<br>
|
||||
* E.g., 2 decimal places: 123.456789 => 123.46
|
||||
*
|
||||
* @param number 数字值
|
||||
* @param scale 保留小数位数
|
||||
* @return 新值
|
||||
* @param number the number value
|
||||
* @param scale number of decimal places
|
||||
* @return new value
|
||||
*/
|
||||
public static BigDecimal round(BigDecimal number, int scale) {
|
||||
return round(number, scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留固定位数小数<br>
|
||||
* 例如保留四位小数:123.456789 =》 123.4567
|
||||
* Retain a fixed number of decimal places<br>
|
||||
* E.g., 4 decimal places: 123.456789 => 123.4567
|
||||
*
|
||||
* @param v 值
|
||||
* @param scale 保留小数位数
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 新值
|
||||
* @param v the value
|
||||
* @param scale number of decimal places
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return new value
|
||||
*/
|
||||
public static BigDecimal round(double v, int scale, RoundingMode roundingMode) {
|
||||
return round(Double.toString(v), scale, roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留固定位数小数<br>
|
||||
* 例如保留四位小数:123.456789 =》 123.4567
|
||||
* Retain a fixed number of decimal places<br>
|
||||
* E.g., 4 decimal places: 123.456789 => 123.4567
|
||||
*
|
||||
* @param numberStr 数字值的字符串表现形式
|
||||
* @param scale 保留小数位数,如果传入小于0,则默认0
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode},如果传入null则默认四舍五入
|
||||
* @return 新值
|
||||
* @param numberStr string representation of the number
|
||||
* @param scale number of decimal places; defaults to 0 if negative
|
||||
* @param roundingMode rounding mode {@link RoundingMode}; defaults to HALF_UP if null
|
||||
* @return new value
|
||||
*/
|
||||
public static BigDecimal round(String numberStr, int scale, RoundingMode roundingMode) {
|
||||
AssertionUtils.isTrue(StringUtils.isNotBlank(numberStr));
|
||||
@@ -452,13 +452,13 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留固定位数小数<br>
|
||||
* 例如保留四位小数:123.456789 =》 123.4567
|
||||
* Retain a fixed number of decimal places<br>
|
||||
* E.g., 4 decimal places: 123.456789 => 123.4567
|
||||
*
|
||||
* @param number 数字值
|
||||
* @param scale 保留小数位数,如果传入小于0,则默认0
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode},如果传入null则默认四舍五入
|
||||
* @return 新值
|
||||
* @param number the number
|
||||
* @param scale number of decimal places; defaults to 0 if negative
|
||||
* @param roundingMode rounding mode {@link RoundingMode}; defaults to HALF_UP if null
|
||||
* @return new value
|
||||
*/
|
||||
public static BigDecimal round(BigDecimal number, int scale, RoundingMode roundingMode) {
|
||||
if (null == number) {
|
||||
@@ -476,68 +476,68 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 四舍六入五成双计算法
|
||||
* Banker's rounding (round-half-to-even)
|
||||
* <p>
|
||||
* 四舍六入五成双是一种比较精确比较科学的计数保留法,是一种数字修约规则。
|
||||
* Banker's rounding is a more accurate and scientific rounding rule.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 算法规则:
|
||||
* 四舍六入五考虑,
|
||||
* 五后非零就进一,
|
||||
* 五后皆零看奇偶,
|
||||
* 五前为偶应舍去,
|
||||
* 五前为奇要进一。
|
||||
* Algorithm rules:
|
||||
* 4 rounds down, 6 rounds up, 5 considers parity:
|
||||
* if there are non-zero digits after 5, round up;
|
||||
* if digits after 5 are all zero, check parity of the preceding digit.
|
||||
* If the digit before 5 is even, round down,
|
||||
* if it is odd, round up.
|
||||
* </pre>
|
||||
*
|
||||
* @param number 需要科学计算的数据
|
||||
* @param scale 保留的小数位
|
||||
* @return 结果
|
||||
* @param number the numeric value
|
||||
* @param scale number of decimal places to retain
|
||||
* @return result
|
||||
*/
|
||||
public static BigDecimal roundHalfEven(Number number, int scale) {
|
||||
return roundHalfEven(toBigDecimal(number), scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 四舍六入五成双计算法
|
||||
* Banker's rounding (round-half-to-even)
|
||||
* <p>
|
||||
* 四舍六入五成双是一种比较精确比较科学的计数保留法,是一种数字修约规则。
|
||||
* Banker's rounding is a more accurate and scientific rounding rule.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* 算法规则:
|
||||
* 四舍六入五考虑,
|
||||
* 五后非零就进一,
|
||||
* 五后皆零看奇偶,
|
||||
* 五前为偶应舍去,
|
||||
* 五前为奇要进一。
|
||||
* Algorithm rules:
|
||||
* 4 rounds down, 6 rounds up, 5 considers parity:
|
||||
* if there are non-zero digits after 5, round up;
|
||||
* if digits after 5 are all zero, check parity of the preceding digit.
|
||||
* If the digit before 5 is even, round down,
|
||||
* if it is odd, round up.
|
||||
* </pre>
|
||||
*
|
||||
* @param value 需要科学计算的数据
|
||||
* @param scale 保留的小数位
|
||||
* @return 结果
|
||||
* @param value the numeric value
|
||||
* @param scale number of decimal places to retain
|
||||
* @return result
|
||||
*/
|
||||
public static BigDecimal roundHalfEven(BigDecimal value, int scale) {
|
||||
return round(value, scale, RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留固定小数位数,舍去多余位数
|
||||
* Retain a fixed number of decimal places, discarding excess digits
|
||||
*
|
||||
* @param number 需要科学计算的数据
|
||||
* @param scale 保留的小数位
|
||||
* @return 结果
|
||||
* @param number the numeric value
|
||||
* @param scale number of decimal places to retain
|
||||
* @return result
|
||||
*/
|
||||
public static BigDecimal roundDown(Number number, int scale) {
|
||||
return roundDown(toBigDecimal(number), scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留固定小数位数,舍去多余位数
|
||||
* Retain a fixed number of decimal places, discarding excess digits
|
||||
*
|
||||
* @param value 需要科学计算的数据
|
||||
* @param scale 保留的小数位
|
||||
* @return 结果
|
||||
* @param value the numeric value
|
||||
* @param scale number of decimal places to retain
|
||||
* @return result
|
||||
*/
|
||||
public static BigDecimal roundDown(BigDecimal value, int scale) {
|
||||
return round(value, scale, RoundingMode.DOWN);
|
||||
@@ -546,65 +546,65 @@ public abstract class NumberUtils {
|
||||
// ------------------------------------------------------------------------------------------- decimalFormat
|
||||
|
||||
/**
|
||||
* 格式化double<br>
|
||||
* 对 {@link DecimalFormat} 做封装<br>
|
||||
* Format a double value<br>
|
||||
* Wraps {@link DecimalFormat}<br>
|
||||
*
|
||||
* @param pattern 格式 格式中主要以 # 和 0 两种占位符号来指定数字长度。0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。<br>
|
||||
* @param pattern format string using # and 0 as placeholders. 0 pads with zeros; # shows digits only if present.<br>
|
||||
* <ul>
|
||||
* <li>0 =》 取一位整数</li>
|
||||
* <li>0.00 =》 取一位整数和两位小数</li>
|
||||
* <li>00.000 =》 取两位整数和三位小数</li>
|
||||
* <li># =》 取所有整数部分</li>
|
||||
* <li>#.##% =》 以百分比方式计数,并取两位小数</li>
|
||||
* <li>#.#####E0 =》 显示为科学计数法,并取五位小数</li>
|
||||
* <li>,### =》 每三位以逗号进行分隔,例如:299,792,458</li>
|
||||
* <li>光速大小为每秒,###米 =》 将格式嵌入文本</li>
|
||||
* <li>0 => one integer digit</li>
|
||||
* <li>0.00 => one integer digit and two decimal places</li>
|
||||
* <li>00.000 => two integer digits and three decimal places</li>
|
||||
* <li># => all integer digits</li>
|
||||
* <li>#.##% => percentage with two decimal places</li>
|
||||
* <li>#.#####E0 => scientific notation with five decimal places</li>
|
||||
* <li>,### => comma-separated thousands, e.g. 299,792,458</li>
|
||||
* <li>Speed of light is ,### metres per second => embed format in text</li>
|
||||
* </ul>
|
||||
* @param value 值
|
||||
* @return 格式化后的值
|
||||
* @param value the value
|
||||
* @return formatted value
|
||||
*/
|
||||
public static String decimalFormat(String pattern, double value) {
|
||||
return new DecimalFormat(pattern).format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化double<br>
|
||||
* 对 {@link DecimalFormat} 做封装<br>
|
||||
* Format a double value<br>
|
||||
* Wraps {@link DecimalFormat}<br>
|
||||
*
|
||||
* @param pattern 格式 格式中主要以 # 和 0 两种占位符号来指定数字长度。0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。<br>
|
||||
* @param pattern format string using # and 0 as placeholders. 0 pads with zeros; # shows digits only if present.<br>
|
||||
* <ul>
|
||||
* <li>0 =》 取一位整数</li>
|
||||
* <li>0.00 =》 取一位整数和两位小数</li>
|
||||
* <li>00.000 =》 取两位整数和三位小数</li>
|
||||
* <li># =》 取所有整数部分</li>
|
||||
* <li>#.##% =》 以百分比方式计数,并取两位小数</li>
|
||||
* <li>#.#####E0 =》 显示为科学计数法,并取五位小数</li>
|
||||
* <li>,### =》 每三位以逗号进行分隔,例如:299,792,458</li>
|
||||
* <li>光速大小为每秒,###米 =》 将格式嵌入文本</li>
|
||||
* <li>0 => one integer digit</li>
|
||||
* <li>0.00 => one integer digit and two decimal places</li>
|
||||
* <li>00.000 => two integer digits and three decimal places</li>
|
||||
* <li># => all integer digits</li>
|
||||
* <li>#.##% => percentage with two decimal places</li>
|
||||
* <li>#.#####E0 => scientific notation with five decimal places</li>
|
||||
* <li>,### => comma-separated thousands, e.g. 299,792,458</li>
|
||||
* <li>Speed of light is ,### metres per second => embed format in text</li>
|
||||
* </ul>
|
||||
* @param value 值
|
||||
* @return 格式化后的值
|
||||
* @param value the value
|
||||
* @return formatted value
|
||||
*/
|
||||
public static String decimalFormat(String pattern, long value) {
|
||||
return new DecimalFormat(pattern).format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额输出,每三位用逗号分隔
|
||||
* Format a monetary amount with comma-separated thousands
|
||||
*
|
||||
* @param value 金额
|
||||
* @return 格式化后的值
|
||||
* @param value the amount
|
||||
* @return formatted value
|
||||
*/
|
||||
public static String decimalFormatMoney(double value) {
|
||||
return decimalFormat(",##0.00", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化百分比,小数采用四舍五入方式
|
||||
* Format as a percentage; decimals are rounded with half-up
|
||||
*
|
||||
* @param number 值
|
||||
* @param scale 保留小数位数
|
||||
* @return 百分比
|
||||
* @param number the value
|
||||
* @param scale number of decimal places
|
||||
* @return percentage string
|
||||
*/
|
||||
public static String formatPercent(double number, int scale) {
|
||||
final NumberFormat format = NumberFormat.getPercentInstance();
|
||||
@@ -615,11 +615,11 @@ public abstract class NumberUtils {
|
||||
// ------------------------------------------------------------------------------------------- isXXX
|
||||
|
||||
/**
|
||||
* 判断String是否是整数<br>
|
||||
* 支持8、10、16进制
|
||||
* Check if a String is an integer<br>
|
||||
* Supports octal, decimal, and hexadecimal
|
||||
*
|
||||
* @param s String
|
||||
* @return 是否为整数
|
||||
* @return true if the string is an integer
|
||||
*/
|
||||
public static boolean isInteger(String s) {
|
||||
try {
|
||||
@@ -631,11 +631,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否是Long类型<br>
|
||||
* 支持8、10、16进制
|
||||
* Check if a String represents a Long value<br>
|
||||
* Supports octal, decimal, and hexadecimal
|
||||
*
|
||||
* @param s String
|
||||
* @return 是否为{@link Long}类型
|
||||
* @return true if the string is a {@link Long}
|
||||
*/
|
||||
public static boolean isLong(String s) {
|
||||
try {
|
||||
@@ -647,10 +647,10 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否是浮点数
|
||||
* Check if a String is a floating-point number
|
||||
*
|
||||
* @param s String
|
||||
* @return 是否为{@link Double}类型
|
||||
* @return true if the string is a {@link Double}
|
||||
*/
|
||||
public static boolean isDouble(String s) {
|
||||
try {
|
||||
@@ -662,7 +662,7 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否全部都为数字
|
||||
* Check if all characters of the string are digits
|
||||
* <pre>
|
||||
* StringUtils.isNumeric(null) = false
|
||||
* StringUtils.isNumeric("") = false
|
||||
@@ -688,11 +688,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是质数(素数)<br>
|
||||
* 质数表的质数又称素数。指整数在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。
|
||||
* Check if a number is prime<br>
|
||||
* A prime (or prime number) is a natural number greater than 1 that cannot be divided evenly by any integer other than 1 and itself.
|
||||
*
|
||||
* @param n 数字
|
||||
* @return 是否是质数
|
||||
* @param n the number
|
||||
* @return true if the number is prime
|
||||
*/
|
||||
public static boolean isPrimes(int n) {
|
||||
AssertionUtils.isTrue(n > 1, "The number must be > 1");
|
||||
@@ -708,33 +708,33 @@ public abstract class NumberUtils {
|
||||
// ------------------------------------------------------------------------------------------- range
|
||||
|
||||
/**
|
||||
* 从0开始给定范围内的整数列表,步进为1
|
||||
* Integer list from 0 to stop (inclusive), step 1
|
||||
*
|
||||
* @param stop 结束(包含)
|
||||
* @return 整数列表
|
||||
* @param stop end (inclusive)
|
||||
* @return list of integers
|
||||
*/
|
||||
public static int[] range(int stop) {
|
||||
return range(0, stop);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定范围内的整数列表,步进为1
|
||||
* Integer list from start to stop (inclusive), step 1
|
||||
*
|
||||
* @param start 开始(包含)
|
||||
* @param stop 结束(包含)
|
||||
* @return 整数列表
|
||||
* @param start start (inclusive)
|
||||
* @param stop end (inclusive)
|
||||
* @return list of integers
|
||||
*/
|
||||
public static int[] range(int start, int stop) {
|
||||
return range(start, stop, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定范围内的整数列表
|
||||
* Integer list within the given range
|
||||
*
|
||||
* @param start 开始(包含)
|
||||
* @param stop 结束(包含)
|
||||
* @param step 步进
|
||||
* @return 整数列表
|
||||
* @param start start (inclusive)
|
||||
* @param stop end (inclusive)
|
||||
* @param step step
|
||||
* @return list of integers
|
||||
*/
|
||||
public static int[] range(int start, int stop, int step) {
|
||||
if (start < stop) {
|
||||
@@ -756,25 +756,25 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将给定范围内的整数添加到已有集合中,步进为1
|
||||
* Add integers in range to the given collection, step 1
|
||||
*
|
||||
* @param start 开始(包含)
|
||||
* @param stop 结束(包含)
|
||||
* @param values 集合
|
||||
* @return 集合
|
||||
* @param start start (inclusive)
|
||||
* @param stop end (inclusive)
|
||||
* @param values the collection to add to
|
||||
* @return the collection
|
||||
*/
|
||||
public static Collection<Integer> appendRange(int start, int stop, Collection<Integer> values) {
|
||||
return appendRange(start, stop, 1, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将给定范围内的整数添加到已有集合中
|
||||
* Add integers in range to the given collection
|
||||
*
|
||||
* @param start 开始(包含)
|
||||
* @param stop 结束(包含)
|
||||
* @param step 步进
|
||||
* @param values 集合
|
||||
* @return 集合
|
||||
* @param start start (inclusive)
|
||||
* @param stop end (inclusive)
|
||||
* @param step step
|
||||
* @param values the collection to add to
|
||||
* @return the collection
|
||||
*/
|
||||
public static Collection<Integer> appendRange(int start, int stop, int step, Collection<Integer> values) {
|
||||
if (start < stop) {
|
||||
@@ -795,14 +795,14 @@ public abstract class NumberUtils {
|
||||
// ------------------------------------------------------------------------------------------- others
|
||||
|
||||
/**
|
||||
* 计算阶乘
|
||||
* Calculate factorial
|
||||
* <p>
|
||||
* n! = n * (n-1) * ... * end
|
||||
* </p>
|
||||
*
|
||||
* @param start 阶乘起始
|
||||
* @param end 阶乘结束
|
||||
* @return 结果
|
||||
* @param start factorial start
|
||||
* @param end factorial end
|
||||
* @return result
|
||||
*/
|
||||
public static long factorial(long start, long end) {
|
||||
if (start < end) {
|
||||
@@ -815,24 +815,24 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算阶乘
|
||||
* Calculate factorial
|
||||
* <p>
|
||||
* n! = n * (n-1) * ... * 2 * 1
|
||||
* </p>
|
||||
*
|
||||
* @param n 阶乘起始
|
||||
* @return 结果
|
||||
* @param n factorial start
|
||||
* @return result
|
||||
*/
|
||||
public static long factorial(long n) {
|
||||
return factorial(n, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平方根算法<br>
|
||||
* 推荐使用 {@link Math#sqrt(double)}
|
||||
* Square root algorithm<br>
|
||||
* Prefer using {@link Math#sqrt(double)}
|
||||
*
|
||||
* @param x 值
|
||||
* @return 平方根
|
||||
* @param x the value
|
||||
* @return square root
|
||||
*/
|
||||
public static long sqrt(long x) {
|
||||
long y = 0;
|
||||
@@ -851,11 +851,11 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 最大公约数
|
||||
* Greatest common divisor
|
||||
*
|
||||
* @param m 第一个值
|
||||
* @param n 第二个值
|
||||
* @return 最大公约数
|
||||
* @param m first value
|
||||
* @param n second value
|
||||
* @return Greatest common divisor
|
||||
*/
|
||||
public static int divisor(int m, int n) {
|
||||
while (m % n != 0) {
|
||||
@@ -867,21 +867,21 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小公倍数
|
||||
* Least common multiple
|
||||
*
|
||||
* @param m 第一个值
|
||||
* @param n 第二个值
|
||||
* @return 最小公倍数
|
||||
* @param m first value
|
||||
* @param n second value
|
||||
* @return Least common multiple
|
||||
*/
|
||||
public static int multiple(int m, int n) {
|
||||
return m * n / divisor(m, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得数字对应的二进制字符串
|
||||
* Get the binary string representation of a number
|
||||
*
|
||||
* @param number 数字
|
||||
* @return 二进制字符串
|
||||
* @param number the number
|
||||
* @return binary string
|
||||
*/
|
||||
public static String getBinaryStr(Number number) {
|
||||
if (number instanceof Long) {
|
||||
@@ -894,9 +894,9 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 二进制转int
|
||||
* Parse a binary string to int
|
||||
*
|
||||
* @param binaryStr 二进制字符串
|
||||
* @param binaryStr binary string
|
||||
* @return int
|
||||
*/
|
||||
public static int binaryToInt(String binaryStr) {
|
||||
@@ -904,9 +904,9 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 二进制转long
|
||||
* Parse a binary string to long
|
||||
*
|
||||
* @param binaryStr 二进制字符串
|
||||
* @param binaryStr binary string
|
||||
* @return long
|
||||
*/
|
||||
public static long binaryToLong(String binaryStr) {
|
||||
@@ -917,9 +917,9 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 数字转{@link BigDecimal}
|
||||
* Convert a number to {@link BigDecimal}
|
||||
*
|
||||
* @param number 数字
|
||||
* @param number the number
|
||||
* @return {@link BigDecimal}
|
||||
*/
|
||||
public static BigDecimal toBigDecimal(Number number) {
|
||||
@@ -930,9 +930,9 @@ public abstract class NumberUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字转{@link BigDecimal}
|
||||
* Convert a number to {@link BigDecimal}
|
||||
*
|
||||
* @param number 数字
|
||||
* @param number the number
|
||||
* @return {@link BigDecimal}
|
||||
*/
|
||||
public static BigDecimal toBigDecimal(String number) {
|
||||
@@ -941,24 +941,24 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 判断两个数字是否相邻,例如1和2相邻,1和3不相邻<br>
|
||||
* 判断方法为做差取绝对值判断是否为1
|
||||
* Check if two numbers are adjacent (differ by exactly 1)<br>
|
||||
* Method: check that the absolute difference equals 1
|
||||
*
|
||||
* @param number1 数字1
|
||||
* @param number2 数字2
|
||||
* @return 是否相邻
|
||||
* @param number1 first number
|
||||
* @param number2 second number
|
||||
* @return true if the numbers are adjacent
|
||||
*/
|
||||
public static boolean isBeside(long number1, long number2) {
|
||||
return Math.abs(number1 - number2) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个数字是否相邻,例如1和2相邻,1和3不相邻<br>
|
||||
* 判断方法为做差取绝对值判断是否为1
|
||||
* Check if two numbers are adjacent (differ by exactly 1)<br>
|
||||
* Method: check that the absolute difference equals 1
|
||||
*
|
||||
* @param number1 数字1
|
||||
* @param number2 数字2
|
||||
* @return 是否相邻
|
||||
* @param number1 first number
|
||||
* @param number2 second number
|
||||
* @return true if the numbers are adjacent
|
||||
*/
|
||||
public static boolean isBeside(int number1, int number2) {
|
||||
return Math.abs(number1 - number2) == 1;
|
||||
@@ -966,22 +966,22 @@ public abstract class NumberUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 提供精确的幂运算
|
||||
* Provides accurate power calculation
|
||||
*
|
||||
* @param number 底数
|
||||
* @param n 指数
|
||||
* @return 幂的积
|
||||
* @param number base
|
||||
* @param n exponent
|
||||
* @return the power result
|
||||
*/
|
||||
public static BigDecimal pow(Number number, int n) {
|
||||
return pow(toBigDecimal(number), n);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的幂运算
|
||||
* Provides accurate power calculation
|
||||
*
|
||||
* @param number 底数
|
||||
* @param n 指数
|
||||
* @return 幂的积
|
||||
* @param number base
|
||||
* @param n exponent
|
||||
* @return the power result
|
||||
*/
|
||||
public static BigDecimal pow(BigDecimal number, int n) {
|
||||
return number.pow(n);
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@@ -27,19 +26,19 @@ public abstract class RandomUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 用于随机选的字符和数字
|
||||
* Characters and digits used for random selection
|
||||
*/
|
||||
public static final String BASE_CHAR_NUMBER = StringUtils.ENGLISH_CHAR + StringUtils.ARAB_NUMBER;
|
||||
|
||||
/**
|
||||
* 使用ThreadLocalRandom产生随机数,能够解决多个线程发生的竞争争夺。
|
||||
* Uses ThreadLocalRandom to avoid thread contention.
|
||||
*/
|
||||
public static ThreadLocalRandom getRandom() {
|
||||
return ThreadLocalRandom.current();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
|
||||
* Get a {@link SecureRandom} instance; provides a cryptographically strong random number generator (RNG)
|
||||
*/
|
||||
public static SecureRandom getSecureRandom() {
|
||||
try {
|
||||
@@ -50,9 +49,9 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数产生器
|
||||
* Get a random number generator
|
||||
*
|
||||
* @param isSecure 是否为强随机数生成器 (RNG)
|
||||
* @param isSecure true to return a cryptographically strong RNG
|
||||
* @return {@link Random}
|
||||
*/
|
||||
public static Random getRandom(boolean isSecure) {
|
||||
@@ -60,7 +59,7 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得随机Boolean值
|
||||
* Returns a random boolean value
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
@@ -69,165 +68,165 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数
|
||||
* Returns a random number within the specified range
|
||||
*
|
||||
* @param min 最小数(包含)
|
||||
* @param max 最大数(不包含)
|
||||
* @return 随机数
|
||||
* @param min minimum (inclusive)
|
||||
* @param max maximum (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static int randomInt(int min, int max) {
|
||||
return getRandom().nextInt(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得随机数[-2^32, 2^32)
|
||||
* Returns a random number in [-2^32, 2^32)
|
||||
*
|
||||
* @return 随机数
|
||||
* @return random number
|
||||
*/
|
||||
public static int randomInt() {
|
||||
return getRandom().nextInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数 [0,limit)
|
||||
* Returns a random number in [0, limit)
|
||||
*
|
||||
* @param limit 限制随机数的范围,不包括这个数
|
||||
* @return 随机数
|
||||
* @param limit upper bound (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static int randomInt(int limit) {
|
||||
return getRandom().nextInt(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数[min, max)
|
||||
* Returns a random number within the specified range[min, max)
|
||||
*
|
||||
* @param min 最小数(包含)
|
||||
* @param max 最大数(不包含)
|
||||
* @return 随机数
|
||||
* @param min minimum (inclusive)
|
||||
* @param max maximum (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static long randomLong(long min, long max) {
|
||||
return getRandom().nextLong(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得随机数
|
||||
* Returns a random number
|
||||
*
|
||||
* @return 随机数
|
||||
* @return random number
|
||||
*/
|
||||
public static long randomLong() {
|
||||
return getRandom().nextLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数 [0,limit)
|
||||
* Returns a random number in [0, limit)
|
||||
*
|
||||
* @param limit 限制随机数的范围,不包括这个数
|
||||
* @return 随机数
|
||||
* @param limit upper bound (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static long randomLong(long limit) {
|
||||
return getRandom().nextLong(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数
|
||||
* Returns a random number within the specified range
|
||||
*
|
||||
* @param min 最小数(包含)
|
||||
* @param max 最大数(不包含)
|
||||
* @return 随机数
|
||||
* @param min minimum (inclusive)
|
||||
* @param max maximum (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static double randomDouble(double min, double max) {
|
||||
return getRandom().nextDouble(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数
|
||||
* Returns a random number within the specified range
|
||||
*
|
||||
* @param min 最小数(包含)
|
||||
* @param max 最大数(不包含)
|
||||
* @param scale 保留小数位数
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 随机数
|
||||
* @param min minimum (inclusive)
|
||||
* @param max maximum (exclusive)
|
||||
* @param scale number of decimal places
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return random number
|
||||
*/
|
||||
public static double randomDouble(double min, double max, int scale, RoundingMode roundingMode) {
|
||||
return NumberUtils.round(randomDouble(min, max), scale, roundingMode).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得随机数[0, 1)
|
||||
* Returns a random number in [0, 1)
|
||||
*
|
||||
* @return 随机数
|
||||
* @return random number
|
||||
*/
|
||||
public static double randomDouble() {
|
||||
return getRandom().nextDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数
|
||||
* Returns a random number within the specified range
|
||||
*
|
||||
* @param scale 保留小数位数
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 随机数
|
||||
* @param scale number of decimal places
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return random number
|
||||
*/
|
||||
public static double randomDouble(int scale, RoundingMode roundingMode) {
|
||||
return NumberUtils.round(randomDouble(), scale, roundingMode).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数 [0,limit)
|
||||
* Returns a random number in [0, limit)
|
||||
*
|
||||
* @param limit 限制随机数的范围,不包括这个数
|
||||
* @return 随机数
|
||||
* @param limit upper bound (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static double randomDouble(double limit) {
|
||||
return getRandom().nextDouble(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数
|
||||
* Returns a random number within the specified range
|
||||
*
|
||||
* @param limit 限制随机数的范围,不包括这个数
|
||||
* @param scale 保留小数位数
|
||||
* @param roundingMode 保留小数的模式 {@link RoundingMode}
|
||||
* @return 随机数
|
||||
* @param limit upper bound (exclusive)
|
||||
* @param scale number of decimal places
|
||||
* @param roundingMode rounding mode {@link RoundingMode}
|
||||
* @return random number
|
||||
*/
|
||||
public static double randomDouble(double limit, int scale, RoundingMode roundingMode) {
|
||||
return NumberUtils.round(randomDouble(limit), scale, roundingMode).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数[0, 1)
|
||||
* Returns a random number within the specified range[0, 1)
|
||||
*
|
||||
* @return 随机数
|
||||
* @return random number
|
||||
*/
|
||||
public static BigDecimal randomBigDecimal() {
|
||||
return NumberUtils.toBigDecimal(getRandom().nextDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数 [0,limit)
|
||||
* Returns a random number in [0, limit)
|
||||
*
|
||||
* @param limit 最大数(不包含)
|
||||
* @return 随机数
|
||||
* @param limit maximum (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static BigDecimal randomBigDecimal(BigDecimal limit) {
|
||||
return NumberUtils.toBigDecimal(getRandom().nextDouble(limit.doubleValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定范围内的随机数
|
||||
* Returns a random number within the specified range
|
||||
*
|
||||
* @param min 最小数(包含)
|
||||
* @param max 最大数(不包含)
|
||||
* @return 随机数
|
||||
* @param min minimum (inclusive)
|
||||
* @param max maximum (exclusive)
|
||||
* @return random number
|
||||
*/
|
||||
public static BigDecimal randomBigDecimal(BigDecimal min, BigDecimal max) {
|
||||
return NumberUtils.toBigDecimal(getRandom().nextDouble(min.doubleValue(), max.doubleValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机bytes
|
||||
* Random bytes
|
||||
*
|
||||
* @param length 长度
|
||||
* @param length length
|
||||
* @return bytes
|
||||
*/
|
||||
public static byte[] randomBytes(int length) {
|
||||
@@ -237,58 +236,58 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获得列表中的元素
|
||||
* Pick a random element from the list
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param list 列表
|
||||
* @return 随机元素
|
||||
* @param <T> element type
|
||||
* @param list the list
|
||||
* @return random element
|
||||
*/
|
||||
public static <T> T randomEle(List<T> list) {
|
||||
return randomEle(list, list.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获得列表中的元素
|
||||
* Pick a random element from the list
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param list 列表
|
||||
* @param limit 限制列表的前N项
|
||||
* @return 随机元素
|
||||
* @param <T> element type
|
||||
* @param list the list
|
||||
* @param limit limit to first N items
|
||||
* @return random element
|
||||
*/
|
||||
public static <T> T randomEle(List<T> list, int limit) {
|
||||
return list.get(randomInt(limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获得数组中的元素
|
||||
* Pick a random element from the array
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param array 列表
|
||||
* @return 随机元素
|
||||
* @param <T> element type
|
||||
* @param array the array
|
||||
* @return random element
|
||||
*/
|
||||
public static <T> T randomEle(T[] array) {
|
||||
return randomEle(array, array.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获得数组中的元素
|
||||
* Pick a random element from the array
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param array 列表
|
||||
* @param limit 限制列表的前N项
|
||||
* @return 随机元素
|
||||
* @param <T> element type
|
||||
* @param array the array
|
||||
* @param limit limit to first N items
|
||||
* @return random element
|
||||
*/
|
||||
public static <T> T randomEle(T[] array, int limit) {
|
||||
return array[randomInt(limit)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获得列表中的一定量元素
|
||||
* Pick a specified number of random elements from the list
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param list 列表
|
||||
* @param count 随机取出的个数
|
||||
* @return 随机元素
|
||||
* @param <T> element type
|
||||
* @param list the list
|
||||
* @param count number of elements to pick
|
||||
* @return random element
|
||||
*/
|
||||
public static <T> List<T> randomEles(List<T> list, int count) {
|
||||
final List<T> result = new ArrayList<T>(count);
|
||||
@@ -301,13 +300,13 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获得列表中的一定量的不重复元素,返回Set
|
||||
* Pick a specified number of unique random elements, returned as a Set
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param collection 列表
|
||||
* @param count 随机取出的个数
|
||||
* @return 随机元素
|
||||
* @throws IllegalArgumentException 需要的长度大于给定集合非重复总数
|
||||
* @param <T> element type
|
||||
* @param collection the collection
|
||||
* @param count number of elements to pick
|
||||
* @return random element
|
||||
* @throws IllegalArgumentException if count exceeds the number of unique elements
|
||||
*/
|
||||
public static <T> Set<T> randomEleSet(Collection<T> collection, int count) {
|
||||
ArrayList<T> source = new ArrayList<>(new HashSet<>(collection));
|
||||
@@ -325,41 +324,41 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个随机的字符串(只包含数字和字符)
|
||||
* Returns a random alphanumeric string
|
||||
*
|
||||
* @param length 字符串的长度
|
||||
* @return 随机字符串
|
||||
* @param length string length
|
||||
* @return random string
|
||||
*/
|
||||
public static String randomString(int length) {
|
||||
return randomString(BASE_CHAR_NUMBER, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个随机的字符串(只包含数字和大写字符)
|
||||
* Returns a random string of digits and uppercase letters
|
||||
*
|
||||
* @param length 字符串的长度
|
||||
* @return 随机字符串
|
||||
* @param length string length
|
||||
* @return random string
|
||||
*/
|
||||
public static String randomStringUpper(int length) {
|
||||
return randomString(BASE_CHAR_NUMBER, length).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个只包含数字的字符串
|
||||
* Returns a random numeric string
|
||||
*
|
||||
* @param length 字符串的长度
|
||||
* @return 随机字符串
|
||||
* @param length string length
|
||||
* @return random string
|
||||
*/
|
||||
public static String randomNumbers(int length) {
|
||||
return randomString(StringUtils.ARAB_NUMBER, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个随机的字符串
|
||||
* Returns a random string
|
||||
*
|
||||
* @param baseString 随机字符选取的样本
|
||||
* @param length 字符串的长度
|
||||
* @return 随机字符串
|
||||
* @param baseString character pool
|
||||
* @param length string length
|
||||
* @return random string
|
||||
*/
|
||||
public static String randomString(String baseString, int length) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
@@ -376,28 +375,28 @@ public abstract class RandomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机数字,数字为0~9单个数字
|
||||
* Random digit character (0-9)
|
||||
*
|
||||
* @return 随机数字字符
|
||||
* @return random character
|
||||
*/
|
||||
public static int randomNumber() {
|
||||
return randomChar(StringUtils.ARAB_NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机字母或数字,小写
|
||||
* Random lowercase alphanumeric character
|
||||
*
|
||||
* @return 随机字符
|
||||
* @return random character
|
||||
*/
|
||||
public static char randomChar() {
|
||||
return randomChar(BASE_CHAR_NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机字符
|
||||
* Random character
|
||||
*
|
||||
* @param baseString 随机字符选取的样本
|
||||
* @return 随机字符
|
||||
* @param baseString character pool
|
||||
* @return random character
|
||||
*/
|
||||
public static char randomChar(String baseString) {
|
||||
return baseString.charAt(getRandom().nextInt(baseString.length()));
|
||||
|
||||
@@ -31,18 +31,18 @@ import java.util.function.Predicate;
|
||||
|
||||
|
||||
/**
|
||||
* 反射工具类
|
||||
* Reflection utility class
|
||||
*
|
||||
* @author godotg
|
||||
*/
|
||||
public abstract class ReflectionUtils {
|
||||
|
||||
/**
|
||||
* 如果field符合FieldFilter过滤条件,则执行回调方法
|
||||
* If the field matches the FieldFilter, invoke the callback
|
||||
*
|
||||
* @param field 属性
|
||||
* @param fieldFilter 属性过滤器
|
||||
* @param fieldCallback 属性回调方法
|
||||
* @param field the field
|
||||
* @param fieldFilter field filter
|
||||
* @param fieldCallback field callback
|
||||
*/
|
||||
public static void filterField(Field field, Predicate<Field> fieldFilter, Consumer<Field> fieldCallback) {
|
||||
if (fieldFilter != null && !fieldFilter.test(field)) {
|
||||
@@ -52,7 +52,7 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将clazz通过filter过滤,过滤后的field执行callback方法
|
||||
* Filter fields of clazz using filter and invoke callback on matched fields
|
||||
* <p>
|
||||
* Invoke the given callback on all fields in the target class, going up the
|
||||
* class hierarchy to get all declared fields.
|
||||
@@ -80,7 +80,7 @@ public abstract class ReflectionUtils {
|
||||
|
||||
public static void assertIsPojoClass(Class<?> clazz) {
|
||||
if (!isPojoClass(clazz)) {
|
||||
throw new RunException("[class:{}]不是简单的javabean(POJO类不能继承别的类,但是可以继承其它接口)", clazz.getName());
|
||||
throw new RunException("[class:{}] is not a simple JavaBean (POJO cannot extend a class, but can implement interfaces)", clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准的属性名称更加通用,前缀不能是is,否则属性名称在不同语言很难去统一get和set方法
|
||||
* Standard field names are more portable; the 'is' prefix should be avoided to keep getters/setters consistent across languages
|
||||
*/
|
||||
public static void assertIsStandardFieldName(Field field) {
|
||||
var fieldName = field.getName();
|
||||
@@ -112,11 +112,11 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个指定的POJO的Class中获得具有指定注解的Field,只获取子类的Field,不获取父类的Field
|
||||
* Get fields with the specified annotation from a POJO class; only current class fields, not inherited
|
||||
*
|
||||
* @param clazz 指定的Class
|
||||
* @param annotation 指定注解的Class
|
||||
* @return 数组,可能长度为0
|
||||
* @param clazz the target class
|
||||
* @param annotation the annotation class
|
||||
* @return array, possibly empty
|
||||
*/
|
||||
public static Field[] getFieldsByAnnoInPOJOClass(Class<?> clazz, Class<? extends Annotation> annotation) {
|
||||
var list = new ArrayList<Field>();
|
||||
@@ -156,11 +156,11 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个Class中获得具有指定注解的Method,只获取子类的Method,不获取父类的Method
|
||||
* Get methods with the specified annotation from a class; only current class methods, not inherited
|
||||
*
|
||||
* @param clazz 指定的Class
|
||||
* @param annotation 指定注解的Class
|
||||
* @return 数组,可能长度为0
|
||||
* @param clazz the target class
|
||||
* @param annotation the annotation class
|
||||
* @return array, possibly empty
|
||||
*/
|
||||
public static Method[] getMethodsByAnnoInPOJOClass(Class<?> clazz, Class<? extends Annotation> annotation) {
|
||||
var list = new ArrayList<Method>();
|
||||
@@ -187,7 +187,7 @@ public abstract class ReflectionUtils {
|
||||
* Searches all superclasses up to {@code Object}.
|
||||
*
|
||||
* @param clazz the class to introspect
|
||||
* @return 数组,可能长度为0
|
||||
* @return array, possibly empty
|
||||
*/
|
||||
public static Method[] getAllMethods(Class<?> clazz) {
|
||||
AssertionUtils.notNull(clazz, "Class must not be null");
|
||||
@@ -200,7 +200,7 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
return ArrayUtils.listToArray(list, Method.class);
|
||||
}
|
||||
//*************************************操作Class*********************************
|
||||
//*********************************** Class operations ***********************************
|
||||
|
||||
public static <T> T newInstance(Class<T> clazz) {
|
||||
try {
|
||||
@@ -228,7 +228,7 @@ public abstract class ReflectionUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 等于{@link Field#get(Object)}
|
||||
* Equivalent to {@link Field#get(Object)}
|
||||
* <p>
|
||||
* In accordance with {@link Field#get(Object)}
|
||||
* semantics, the returned value is automatically wrapped if the underlying field
|
||||
@@ -248,7 +248,7 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 等于{@link Field#set(Object, Object)}
|
||||
* Equivalent to {@link Field#set(Object, Object)}
|
||||
* <p>
|
||||
* In accordance with {@link Field#set(Object, Object)} semantics, the new value
|
||||
* is automatically unwrapped if the underlying field has a primitive type.
|
||||
@@ -286,7 +286,7 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 让私有变量可访问,在必要的情况下调用
|
||||
* Make a private field accessible; call only when necessary
|
||||
* <p>
|
||||
* Make the given field accessible, explicitly setting it accessible if necessary.
|
||||
* </p>
|
||||
@@ -327,7 +327,7 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取class中的普通field属性字段
|
||||
// Get regular (non-static, non-synthetic) fields from class
|
||||
public static List<Field> notStaticAndTransientFields(Class<?> clazz) {
|
||||
return Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(it -> !Modifier.isStatic(it.getModifiers()))
|
||||
|
||||
@@ -35,48 +35,48 @@ public abstract class StringUtils {
|
||||
public static final String TAB = " ";
|
||||
public static final String TAB_ASCII = "\t";
|
||||
|
||||
public static final String COMMA = ","; // [com·ma || 'kɒmə] n. 逗点; 逗号
|
||||
public static final String COMMA = ","; // [com·ma || 'kɒmə] n. comma
|
||||
public static final String COMMA_REGEX = ",|,";
|
||||
public static final String COMMON_SPLIT_REGEX = "[,,\\s]+";
|
||||
|
||||
public static final String PERIOD = "."; // 句号
|
||||
public static final String PERIOD = "."; // period
|
||||
public static final String PERIOD_REGEX = "\\.";
|
||||
|
||||
public static final String LEFT_SQUARE_BRACKET = "["; // 左方括号
|
||||
public static final String LEFT_SQUARE_BRACKET = "["; // left square bracket
|
||||
|
||||
public static final String RIGHT_SQUARE_BRACKET = "]"; // 右方括号
|
||||
public static final String RIGHT_SQUARE_BRACKET = "]"; // right square bracket
|
||||
|
||||
public static final String COLON = ":"; // 冒号[co·lon || 'kəʊlən]
|
||||
public static final String COLON = ":"; // colon [co·lon || 'kəʊlən]
|
||||
public static final String COLON_REGEX = ":|:";
|
||||
|
||||
public static final String SEMICOLON = ";"; // 分号['semi'kәulәn]
|
||||
public static final String SEMICOLON = ";"; // semicolon ['semi'kәulәn]
|
||||
public static final String SEMICOLON_REGEX = ";|;";
|
||||
|
||||
public static final String QUOTATION_MARK = "\""; // 引号[quo·ta·tion || kwəʊ'teɪʃn]
|
||||
public static final String QUOTATION_MARK = "\""; // quotation mark [quo·ta·tion || kwəʊ'teɪʃn]
|
||||
|
||||
public static final String ELLIPSIS = "..."; // 省略号
|
||||
public static final String ELLIPSIS = "..."; // ellipsis
|
||||
|
||||
public static final String EXCLAMATION_POINT = "!"; // 感叹号
|
||||
public static final String EXCLAMATION_POINT = "!"; // exclamation point
|
||||
|
||||
public static final String DASH = "-"; // 破折号
|
||||
public static final String DASH = "-"; // dash
|
||||
|
||||
public static final String QUESTION_MARK = "?"; // 问号
|
||||
public static final String QUESTION_MARK = "?"; // question mark
|
||||
|
||||
public static final String HYPHEN = "-"; // 连接号,连接号与破折号的区别是,连接号的两头不用空格
|
||||
public static final String HYPHEN = "-"; // hyphen, the difference from DASH is that hyphen has no spaces on either side
|
||||
|
||||
public static final String SLASH = "/"; // 斜线号
|
||||
public static final String SLASH = "/"; // slash
|
||||
|
||||
public static final String EQUAL = "="; // 等于号
|
||||
public static final String EQUAL = "="; // equal sign
|
||||
|
||||
public static final String BACK_SLASH = "\\"; //反斜线号
|
||||
public static final String BACK_SLASH = "\\"; // back slash
|
||||
|
||||
public static final String VERTICAL_BAR = "|"; // 竖线
|
||||
public static final String VERTICAL_BAR = "|"; // vertical bar
|
||||
public static final String VERTICAL_BAR_REGEX = "\\|";
|
||||
|
||||
public static final String SHARP = "#";
|
||||
public static final String SHARP_REGEX = "\\#";
|
||||
|
||||
public static final String DOLLAR = "$"; // 美元符号
|
||||
public static final String DOLLAR = "$"; // dollar sign
|
||||
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
|
||||
@@ -90,11 +90,11 @@ public abstract class StringUtils {
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_CHARSET_NAME);
|
||||
|
||||
/**
|
||||
* 用于随机选的数字
|
||||
* Numbers for random selection
|
||||
*/
|
||||
public static final String ARAB_NUMBER = "0123456789";
|
||||
/**
|
||||
* 用于随机选的字符
|
||||
* Characters for random selection
|
||||
*/
|
||||
public static final String ENGLISH_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
@@ -135,8 +135,8 @@ public abstract class StringUtils {
|
||||
* StringUtils.isBlank(" ")=true
|
||||
* StringUtils.isBlank(" b ")=false
|
||||
*
|
||||
* @param cs 要检查的字符串
|
||||
* @return 是否为空的字符串
|
||||
* @param cs the string to check
|
||||
* @return true if the string is empty
|
||||
*/
|
||||
public static boolean isBlank(final CharSequence cs) {
|
||||
if (isEmpty(cs)) {
|
||||
@@ -166,10 +166,10 @@ public abstract class StringUtils {
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 首字母小写(capitalize ['kæpɪtlaɪz] vt.以大写字母写,【根】cap=head(头))
|
||||
* Lowercase the first letter (capitalize ['kæpɪtlaɪz] vt.- capitalize first letter)
|
||||
*
|
||||
* @param str 被转换的字符串,可以为null
|
||||
* @return 首字母小写的字符串,如果str为null,则返回null
|
||||
* @param str the string to convert; may be null
|
||||
* @return string with first letter lowercased; null if input is null
|
||||
*/
|
||||
public static String uncapitalize(String str) {
|
||||
return changeFirstCharacterCase(str, false);
|
||||
@@ -207,7 +207,7 @@ public abstract class StringUtils {
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 从第一个分隔符开始分割
|
||||
* Split from the first separator
|
||||
* <p>Gets the substring before the first occurrence of a separator.
|
||||
* The separator is not returned.</p>
|
||||
*
|
||||
@@ -259,7 +259,7 @@ public abstract class StringUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从第一个分隔符开始分割
|
||||
* Split from the first separator
|
||||
* <p>Gets the substring after the first occurrence of a separator.
|
||||
* The separator is not returned.</p>
|
||||
*
|
||||
@@ -350,7 +350,7 @@ public abstract class StringUtils {
|
||||
* Replaces each substring of the source String that matches the given regular expression with the given
|
||||
* replacement using the {@link Pattern#DOTALL} option.
|
||||
* <p>
|
||||
* 在 DOTALL 模式中,表达式 .可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。
|
||||
* In DOTALL mode, '.' matches any character including line terminators. By default it does not match them.
|
||||
*
|
||||
* @param source the source string
|
||||
* @param regex the regular expression to which this string is to be matched
|
||||
@@ -447,31 +447,31 @@ public abstract class StringUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化字符串
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数
|
||||
* 例:
|
||||
* 通常使用:format("this is {} for {}", "a", "b") =》 this is a for b
|
||||
* Format a string
|
||||
* Replaces {} placeholders with arguments in order
|
||||
* Example:
|
||||
* Typical usage: format("this is {} for {}", "a", "b") => this is a for b
|
||||
*
|
||||
* @param template 字符串模板
|
||||
* @param args 参数列表
|
||||
* @return 结果
|
||||
* @param template string template
|
||||
* @param args argument list
|
||||
* @return result
|
||||
*/
|
||||
public static String format(final String template, final Object... args) {
|
||||
if (isBlank(template) || ArrayUtils.isEmpty(args)) {
|
||||
return template;
|
||||
}
|
||||
|
||||
// 初始化定义好的长度以获得更好的性能
|
||||
// Initialize with a predefined length for better performance
|
||||
var builder = new StringBuilder(template.length() + 50);
|
||||
|
||||
// 记录已经处理到的位置
|
||||
// Track the current read position
|
||||
var readIndex = 0;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
// 占位符所在位置
|
||||
// Position of the placeholder
|
||||
var placeholderIndex = template.indexOf(EMPTY_JSON, readIndex);
|
||||
// 剩余部分无占位符
|
||||
// No placeholder remaining
|
||||
if (placeholderIndex == -1) {
|
||||
// 不带占位符的模板直接返回
|
||||
// Return template directly if it has no placeholder at all
|
||||
if (readIndex == 0) {
|
||||
return template;
|
||||
}
|
||||
@@ -483,13 +483,13 @@ public abstract class StringUtils {
|
||||
readIndex = placeholderIndex + 2;
|
||||
}
|
||||
|
||||
// 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
|
||||
// Append the remaining part of the template which contains no more placeholders
|
||||
builder.append(template, readIndex, template.length());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化字符串,类似JavaScript的占位符${name}替换
|
||||
* Format a string using JavaScript-style placeholder substitution like ${name}
|
||||
*/
|
||||
public static String render(String template, Map<String, String> params) {
|
||||
var pattern = Pattern.compile("\\$\\{(\\w+)\\}");
|
||||
@@ -510,11 +510,11 @@ public abstract class StringUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定的词是否是不处理的词。
|
||||
* 如果参数为空,则返回true,因为空也属于不处理的字符。
|
||||
* Check whether the given character is a stop character.
|
||||
* Returns true if the argument is null, because null is also treated as a stop character.
|
||||
*
|
||||
* @param ch 指定的词
|
||||
* @return 是否是不处理的词
|
||||
* @param ch the character to check
|
||||
* @return true if the character is a stop character
|
||||
*/
|
||||
public static boolean isStopChar(char ch) {
|
||||
return Character.isWhitespace(ch) || STOP_WORD.contains(ch);
|
||||
|
||||
@@ -82,7 +82,7 @@ public abstract class ThreadUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用kill退出的方式,不能调用这个停止方法
|
||||
* When the process is killed externally, this stop method will not be called
|
||||
*/
|
||||
public static void shutdownApplication() {
|
||||
new Thread(() -> {
|
||||
|
||||
@@ -23,7 +23,7 @@ public abstract class UuidUtils {
|
||||
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* 获取本地int的唯一id,如果达到最大值则重新从最小值重新计算,线程安全
|
||||
* Get a locally unique int ID; wraps around when reaching max; thread-safe
|
||||
*/
|
||||
public static int getLocalIntId() {
|
||||
return ATOMIC_INTEGER.incrementAndGet();
|
||||
@@ -31,23 +31,23 @@ public abstract class UuidUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获得分布式环境下唯一id
|
||||
* Get a unique ID in a distributed environment
|
||||
*
|
||||
* @return String UUID
|
||||
*/
|
||||
public static String getUUID() {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
//去掉“-”符号
|
||||
// Strip the “-”characters
|
||||
return uuid.replaceAll("-", "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 小的id在前 - 大的id在后
|
||||
* Smaller ID first, larger ID second
|
||||
*
|
||||
* @param a 第一个数字
|
||||
* @param b 第二个数字
|
||||
* @return 生成的id
|
||||
* @param a first number
|
||||
* @param b second number
|
||||
* @return generated ID
|
||||
*/
|
||||
public static String generateStringId(long a, long b) {
|
||||
return Math.min(a, b) + "-" + Math.max(a, b);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class ByteBuffer {
|
||||
|
||||
|
||||
public void adjustPadding(int predictionLength, int beforewriteIndex) {
|
||||
// 因为写入的是可变长的int,如果预留的位置过多,则清除多余的位置
|
||||
// Variable-length int; clear any over-reserved positions
|
||||
var currentwriteIndex = writeOffset;
|
||||
var predictionCount = writeIntCount(predictionLength);
|
||||
var length = currentwriteIndex - beforewriteIndex - predictionCount;
|
||||
@@ -225,7 +225,7 @@ public class ByteBuffer {
|
||||
return 5;
|
||||
}
|
||||
|
||||
// 写入没有压缩的int
|
||||
// Write uncompressed int
|
||||
public void writeRawInt(int value) {
|
||||
ensureCapacity(4);
|
||||
buffer[writeOffset++] = (byte) (value >>> 24);
|
||||
@@ -234,7 +234,7 @@ public class ByteBuffer {
|
||||
buffer[writeOffset++] = (byte) value;
|
||||
}
|
||||
|
||||
// 读取没有压缩的int
|
||||
// Read uncompressed int
|
||||
public int readRawInt() {
|
||||
return (buffer[readOffset++] & 255) << 24 | (buffer[readOffset++] & 255) << 16 | (buffer[readOffset++] & 255) << 8 | buffer[readOffset++] & 255;
|
||||
}
|
||||
|
||||
@@ -52,14 +52,14 @@ public class BenchmarkTesting {
|
||||
public static int benchmark = 10_0000;
|
||||
|
||||
/**
|
||||
* 单线程性能测试
|
||||
* Single-threaded performance test
|
||||
* <p>
|
||||
* 不使用任何JVM参数:zfoo比protobuf快20%,zfoo比kryo快40%
|
||||
* Without JVM tuning: zfoo is ~20% faster than protobuf and ~40% faster than kryo
|
||||
* <p>
|
||||
* 包体大小:
|
||||
* 简单对象,zfoo包体大小8,kryo包体大小5,protobuf包体大小8
|
||||
* 常规对象,zfoo包体大小430,kryo包体大小516,protobuf包体大小793
|
||||
* 复杂对象,zfoo包体大小2191,kryo包体大小2611,protobuf包体大小5083
|
||||
* Packet sizes:
|
||||
* Simple object: zfoo=8B, kryo=5B, protobuf=8B
|
||||
* Normal object: zfoo=430B, kryo=516B, protobuf=793B
|
||||
* Complex object: zfoo=2191B, kryo=2611B, protobuf=5083B
|
||||
*/
|
||||
@Test
|
||||
public void singleThreadBenchmarks() {
|
||||
@@ -68,7 +68,7 @@ public class BenchmarkTesting {
|
||||
return;
|
||||
}
|
||||
System.out.println(StringUtils.MULTIPLE_HYPHENS);
|
||||
System.out.println(StringUtils.format("[单线程性能测试-->[benchmark:{}]]", benchmark));
|
||||
System.out.println(StringUtils.format("[Single-threaded performance test-->[benchmark:{}]]", benchmark));
|
||||
|
||||
zfooTest();
|
||||
jsonbTest();
|
||||
@@ -77,13 +77,13 @@ public class BenchmarkTesting {
|
||||
kryoTest();
|
||||
protobufTest();
|
||||
|
||||
// 递归执行,多跑几遍
|
||||
// Run recursively to warm up / repeat runs
|
||||
benchmark = benchmark * 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多线程性能测试
|
||||
* Multi-threaded performance test
|
||||
*/
|
||||
@Test
|
||||
public void multipleThreadBenchmarks() throws InterruptedException {
|
||||
@@ -92,7 +92,7 @@ public class BenchmarkTesting {
|
||||
return;
|
||||
}
|
||||
System.out.println(StringUtils.MULTIPLE_HYPHENS);
|
||||
System.out.println(StringUtils.format("[多线程性能测试-->[benchmark:{}]]", benchmark));
|
||||
System.out.println(StringUtils.format("[Multi-threaded performance test-->[benchmark:{}]]", benchmark));
|
||||
|
||||
zfooMultipleThreadTest();
|
||||
furyMultipleThreadTest();
|
||||
@@ -106,27 +106,27 @@ public class BenchmarkTesting {
|
||||
|
||||
@Test
|
||||
public void zfooTest() {
|
||||
// netty的ByteBuf做了更多的安全检测,java自带的ByteBuffer并没有做安全检测,为了公平,把不需要的检测去掉
|
||||
// java通过ByteBuffer.allocate(1024 * 8)构造出来的是使用了unsafe的HeapByteBuffer,为了公平,使用netty中带有unsafe操作的UnpooledUnsafeHeapByteBuf
|
||||
// Netty ByteBuf has extra safety checks; Java ByteBuffer does not. Remove unnecessary checks for a fair comparison
|
||||
// ByteBuffer.allocate() creates an unsafe HeapByteBuffer; for a fair comparison, use Netty's UnpooledUnsafeHeapByteBuf
|
||||
System.setProperty("io.netty.buffer.checkAccessible", "false");
|
||||
System.setProperty("io.netty.buffer.checkBounds", "false");
|
||||
|
||||
ByteBuf buffer = new UnpooledHeapByteBuf(ByteBufAllocator.DEFAULT, 100, 1_0000);
|
||||
|
||||
// 序列化和反序列化简单对象
|
||||
// Serialize and deserialize a simple object
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
buffer.clear();
|
||||
// 把对象序列化到buffer中
|
||||
// Serialize object into buffer
|
||||
ProtocolManager.write(buffer, simpleObject);
|
||||
|
||||
// 从buffer中反序列化出对象
|
||||
// Deserialize object from buffer
|
||||
var packet = ProtocolManager.read(buffer);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[zfoo] [简单对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[zfoo] [SimpleObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化常规对象
|
||||
// Serialize and deserialize a normal object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
buffer.clear();
|
||||
@@ -134,9 +134,9 @@ public class BenchmarkTesting {
|
||||
var packet = ProtocolManager.read(buffer);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[zfoo] [常规对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[zfoo] [NormalObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化复杂对象
|
||||
// Serialize and deserialize a complex object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
buffer.clear();
|
||||
@@ -144,13 +144,13 @@ public class BenchmarkTesting {
|
||||
var packet = ProtocolManager.read(buffer);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[zfoo] [复杂对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[zfoo] [ComplexObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void furyTest() {
|
||||
var buffer = MemoryBuffer.newHeapBuffer(1_0000);
|
||||
// 序列化和反序列化简单对象
|
||||
// Serialize and deserialize a simple object
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
buffer.writerIndex(0);
|
||||
@@ -159,9 +159,9 @@ public class BenchmarkTesting {
|
||||
var obj = fury.deserialize(buffer);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[fury] [简单对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fury] [SimpleObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化常规对象
|
||||
// Serialize and deserialize a normal object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
buffer.writerIndex(0);
|
||||
@@ -170,9 +170,9 @@ public class BenchmarkTesting {
|
||||
var obj = fury.deserialize(buffer);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[fury] [常规对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fury] [NormalObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化复杂对象
|
||||
// Serialize and deserialize a complex object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
buffer.writerIndex(0);
|
||||
@@ -180,7 +180,7 @@ public class BenchmarkTesting {
|
||||
fury.serialize(buffer, complexObject);
|
||||
var obj = fury.deserialize(buffer);
|
||||
}
|
||||
System.out.println(StringUtils.format("[fury] [复杂对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fury] [ComplexObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), buffer.writerIndex(), System.currentTimeMillis() - startTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -189,35 +189,35 @@ public class BenchmarkTesting {
|
||||
var output = new Output(1024 * 8);
|
||||
var input = new Input(output.getBuffer());
|
||||
|
||||
// 序列化和反序列化简单对象
|
||||
// Serialize and deserialize a simple object
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var bytes = JSONB.toBytes(simpleObject);
|
||||
var mess = JSONB.parseObject(bytes, SimpleObject.class);
|
||||
|
||||
// 这种通过流写入的方式速度奇慢
|
||||
// writing via stream is extremely slow
|
||||
// JSONB.writeTo(output, normalObject);
|
||||
// var mess = JSONB.parseObject(input, NormalObject.class);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[fastjsonb] [简单对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSONB.toBytes(simpleObject).length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fastjsonb] [SimpleObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSONB.toBytes(simpleObject).length, System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化常规对象
|
||||
// Serialize and deserialize a normal object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var bytes = JSONB.toBytes(normalObject);
|
||||
var mess = JSONB.parseObject(bytes, NormalObject.class);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[fastjsonb] [常规对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSONB.toBytes(normalObject).length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fastjsonb] [NormalObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSONB.toBytes(normalObject).length, System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化复杂对象
|
||||
// Serialize and deserialize a complex object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var bytes = JSONB.toBytes(complexObject);
|
||||
var mess = JSONB.parseObject(bytes, ComplexObject.class);
|
||||
}
|
||||
System.out.println(StringUtils.format("[fastjsonb] [复杂对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSONB.toBytes(complexObject).length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fastjsonb] [ComplexObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSONB.toBytes(complexObject).length, System.currentTimeMillis() - startTime));
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
@@ -226,35 +226,35 @@ public class BenchmarkTesting {
|
||||
@Test
|
||||
public void fastJson2Test() {
|
||||
try {
|
||||
// 序列化和反序列化简单对象
|
||||
// Serialize and deserialize a simple object
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var str = JSON.toJSONString(simpleObject);
|
||||
var mess = JSON.parseObject(str, SimpleObject.class);
|
||||
|
||||
// 这种通过流写入的方式速度奇慢
|
||||
// writing via stream is extremely slow
|
||||
// JSONB.writeTo(output, normalObject);
|
||||
// var mess = JSONB.parseObject(input, NormalObject.class);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[fastjson2] [简单对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSON.toJSONString(simpleObject).getBytes().length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fastjson2] [SimpleObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSON.toJSONString(simpleObject).getBytes().length, System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化常规对象
|
||||
// Serialize and deserialize a normal object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var str = JSON.toJSONString(normalObject);
|
||||
var mess = JSON.parseObject(str, NormalObject.class);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[fastjson2] [常规对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(),JSON.toJSONString(normalObject).getBytes().length,System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fastjson2] [NormalObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(),JSON.toJSONString(normalObject).getBytes().length,System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化复杂对象
|
||||
// Serialize and deserialize a complex object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var str = JSON.toJSONString(complexObject);
|
||||
var mess = JSON.parseObject(str, ComplexObject.class);
|
||||
}
|
||||
System.out.println(StringUtils.format("[fastjson2] [复杂对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSON.toJSONString(complexObject).getBytes().length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[fastjson2] [ComplexObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), JSON.toJSONString(complexObject).getBytes().length, System.currentTimeMillis() - startTime));
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
@@ -268,7 +268,7 @@ public class BenchmarkTesting {
|
||||
var output = new Output(1024 * 8);
|
||||
var input = new Input(output.getBuffer());
|
||||
|
||||
// 序列化和反序列化简单对象
|
||||
// Serialize and deserialize a simple object
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
input.reset();
|
||||
@@ -277,9 +277,9 @@ public class BenchmarkTesting {
|
||||
var mess = kryo.readObject(input, SimpleObject.class);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[kryo] [简单对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), output.position(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[kryo] [SimpleObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), output.position(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化常规对象
|
||||
// Serialize and deserialize a normal object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
input.reset();
|
||||
@@ -288,9 +288,9 @@ public class BenchmarkTesting {
|
||||
var mess = kryo.readObject(input, NormalObject.class);
|
||||
}
|
||||
|
||||
System.out.println(StringUtils.format("[kryo] [常规对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), output.position(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[kryo] [NormalObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), output.position(), System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化复杂对象
|
||||
// Serialize and deserialize a complex object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
input.reset();
|
||||
@@ -298,10 +298,10 @@ public class BenchmarkTesting {
|
||||
kryo.writeObject(output, complexObject);
|
||||
var mess = kryo.readObject(input, ComplexObject.class);
|
||||
}
|
||||
System.out.println(StringUtils.format("[kryo] [复杂对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), output.position(), System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[kryo] [ComplexObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), output.position(), System.currentTimeMillis() - startTime));
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
System.err.println("JDK17 运行kryo会报错,等kryo修复bug");
|
||||
System.err.println("JDK17 causes Kryo errors; waiting for Kryo bug fix");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ public class BenchmarkTesting {
|
||||
var buffer = new byte[1024 * 8];
|
||||
var length = 0;
|
||||
|
||||
// 序列化和反序列化简单对象
|
||||
// Serialize and deserialize a simple object
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var codedOutputStream = CodedOutputStream.newInstance(buffer);
|
||||
@@ -320,9 +320,9 @@ public class BenchmarkTesting {
|
||||
var codeInput = CodedInputStream.newInstance(buffer, 0, length);
|
||||
var mess = ProtobufObject.ProtobufSimpleObject.parseFrom(codeInput);
|
||||
}
|
||||
System.out.println(StringUtils.format("[protobuf] [简单对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[protobuf] [SimpleObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), length, System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化常规对象
|
||||
// Serialize and deserialize a normal object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var codedOutputStream = CodedOutputStream.newInstance(buffer);
|
||||
@@ -331,9 +331,9 @@ public class BenchmarkTesting {
|
||||
var codeInput = CodedInputStream.newInstance(buffer, 0, length);
|
||||
var mess = ProtobufObject.ProtobufNormalObject.parseFrom(codeInput);
|
||||
}
|
||||
System.out.println(StringUtils.format("[protobuf] [常规对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[protobuf] [NormalObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), length, System.currentTimeMillis() - startTime));
|
||||
|
||||
// 序列化和反序列化复杂对象
|
||||
// Serialize and deserialize a complex object
|
||||
startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
var codedOutputStream = CodedOutputStream.newInstance(buffer);
|
||||
@@ -342,7 +342,7 @@ public class BenchmarkTesting {
|
||||
var codeInput = CodedInputStream.newInstance(buffer, 0, length);
|
||||
var mess = ProtobufObject.ProtobufComplexObject.parseFrom(codeInput);
|
||||
}
|
||||
System.out.println(StringUtils.format("[protobuf] [复杂对象] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), length, System.currentTimeMillis() - startTime));
|
||||
System.out.println(StringUtils.format("[protobuf] [ComplexObject] [thread:{}] [size:{}] [time:{}]", Thread.currentThread().getName(), length, System.currentTimeMillis() - startTime));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -419,7 +419,7 @@ public class BenchmarkTesting {
|
||||
public static final int threadNum = Runtime.getRuntime().availableProcessors() / 2;
|
||||
public static final ExecutorService[] executors = new ExecutorService[threadNum];
|
||||
|
||||
// kryo协议注册
|
||||
// Kryo protocol registration
|
||||
public static final ThreadLocal<Kryo> kryos = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected Kryo initialValue() {
|
||||
@@ -451,14 +451,14 @@ public class BenchmarkTesting {
|
||||
kryo.register(ArrayList.class);
|
||||
kryo.register(HashSet.class);
|
||||
kryo.register(HashMap.class);
|
||||
// 关闭循环引用,提高性能
|
||||
// Disable circular reference tracking to improve performance
|
||||
kryo.setReferences(false);
|
||||
return kryo;
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
// zfoo协议注册(其实就是:将Set里面的协议号和对应的类注册好,这样子就可以根据协议号知道是反序列化为哪个类)
|
||||
// zfoo protocol registration: maps protocol IDs to classes for deserialization
|
||||
ProtocolManager.initProtocolAuto(List.of(ComplexObject.class, NormalObject.class, SimpleObject.class, EmptyObject.class, VeryBigObject.class), GenerateOperation.NO_OPERATION);
|
||||
|
||||
for (int i = 0; i < executors.length; i++) {
|
||||
@@ -482,8 +482,8 @@ public class BenchmarkTesting {
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------以下为测试用例---------------------------------------------------------------
|
||||
// 简单类型
|
||||
// -------------------------------------------Test cases below---------------------------------------------------------------
|
||||
// Simple types
|
||||
public static final byte byteValue = 99;
|
||||
public static final short shortValue = 9999;
|
||||
public static final int intValue = 99999999;
|
||||
@@ -492,7 +492,7 @@ public class BenchmarkTesting {
|
||||
public static final double doubleValue = 99999999.9D;
|
||||
public static final String stringValue = "hello";
|
||||
|
||||
// 数组类型
|
||||
// Array types
|
||||
public static final boolean[] booleanArray = new boolean[]{true, false, true, false, true};
|
||||
public static final byte[] byteArray = new byte[]{Byte.MIN_VALUE, -99, 0, 99, Byte.MAX_VALUE};
|
||||
public static final short[] shortArray = new short[]{Short.MIN_VALUE, -99, 0, 99, Short.MAX_VALUE};
|
||||
@@ -625,7 +625,7 @@ public class BenchmarkTesting {
|
||||
simpleObject.setC(intValue);
|
||||
simpleObject.setG(true);
|
||||
|
||||
// protobuf相关
|
||||
// protobuf related
|
||||
var protobufObjectB = ProtobufObject.ObjectB.newBuilder().setFlag(false).build();
|
||||
var protobufObjectA = ProtobufObject.ObjectA.newBuilder()
|
||||
.setA(Integer.MAX_VALUE)
|
||||
|
||||
@@ -106,7 +106,7 @@ public class ByteBufUtilsTest {
|
||||
var beforeWriteIndex = byteBuf.writerIndex();
|
||||
var predictionLength = 1000;
|
||||
|
||||
// padding等于0的情况
|
||||
// case where padding equals 0
|
||||
ByteBufUtils.writeInt(byteBuf, predictionLength);
|
||||
for (int i = 0; i < predictionLength; i++) {
|
||||
ByteBufUtils.writeByte(byteBuf, (byte) 1);
|
||||
@@ -132,7 +132,7 @@ public class ByteBufUtilsTest {
|
||||
var predictionLength = 1000;
|
||||
var length = predictionLength / 100;
|
||||
|
||||
// padding等于0的情况
|
||||
// case where padding equals 0
|
||||
ByteBufUtils.writeInt(byteBuf, predictionLength);
|
||||
for (int i = 0; i < length; i++) {
|
||||
ByteBufUtils.writeByte(byteBuf, (byte) 1);
|
||||
@@ -162,7 +162,7 @@ public class ByteBufUtilsTest {
|
||||
var predictionLength = 1000;
|
||||
var length = predictionLength * 10;
|
||||
|
||||
// padding等于0的情况
|
||||
// case where padding equals 0
|
||||
ByteBufUtils.writeInt(byteBuf, predictionLength);
|
||||
for (int i = 0; i < length; i++) {
|
||||
ByteBufUtils.writeByte(byteBuf, (byte) 1);
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ByteBufUtilsTesting {
|
||||
|
||||
|
||||
/**
|
||||
* 测试所有int值,运行时间太长,放弃测试
|
||||
* Testing all int values takes too long; test skipped
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.zfoo.protocol.BenchmarkTesting.complexObject;
|
||||
import static com.zfoo.protocol.BenchmarkTesting.normalObject;
|
||||
@@ -41,7 +40,7 @@ public class CompatibleTesting {
|
||||
* EN: The order of the bytecode-enhanced Map traversal order will be different, so the order of the serialized content will change.
|
||||
* You can see that the different bytes are not consecutive.
|
||||
* <p>
|
||||
* CN: 字节码增强的Map遍历顺序会出现不一样,所以序列化的内容顺序会改变,可以看到不相同的字节并不是连续的
|
||||
* Bytecode-enhanced Map traversal order may differ, so serialized byte order can change; non-matching bytes are not consecutive
|
||||
*/
|
||||
@Test
|
||||
public void compatibleTest() throws IOException {
|
||||
@@ -77,15 +76,15 @@ public class CompatibleTesting {
|
||||
@Test
|
||||
public void enhanceNormalTest() {
|
||||
var buffer = new UnpooledHeapByteBuf(ByteBufAllocator.DEFAULT, 100, 1_0000);
|
||||
// 简单对象序列化和反序列化测试
|
||||
// 序列化:把normalObject序列化一下写到buffer中
|
||||
// Simple object serialization and deserialization test
|
||||
// Serialize: write normalObject into buffer
|
||||
ProtocolManager.write(buffer, normalObject);
|
||||
// 反序列化:从buffer中反序列化为协议包
|
||||
// Deserialize: read protocol object from buffer
|
||||
var packet = ProtocolManager.read(buffer);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
// 复杂对象序列化和反序列化测试
|
||||
// Complex object serialization and deserialization test
|
||||
ProtocolManager.write(buffer, complexObject);
|
||||
packet = ProtocolManager.read(buffer);
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 测试各种不同特征的数据类型的序列化和反序列化时间
|
||||
* 细致比较性能
|
||||
* Tests serialization/deserialization time for various field types
|
||||
* Detailed performance comparison
|
||||
*/
|
||||
@Ignore
|
||||
public class FieldSpeedTesting {
|
||||
@@ -96,7 +96,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -106,12 +106,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.IntObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [int类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [int类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [int类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [int类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [int类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [int类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [int type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [int type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [int type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [int type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [int type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [int type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,7 +156,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -166,12 +166,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.IntObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [Integer类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [Integer类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [int类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [Integer类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [Integer类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [int类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [Integer type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [Integer type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [int type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [Integer type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [Integer type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [int type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -216,7 +216,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -226,12 +226,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.FloatObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [float类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [float类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [float类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [float类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [float类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [float类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [float type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [float type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [float type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [float type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [float type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [float type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -276,7 +276,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -286,12 +286,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.BytesObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [byte数组类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [byte数组类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [byte数组类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [byte数组类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [byte数组类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [byte数组类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [byte array type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [byte array type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [byte array type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [byte array type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [byte array type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [byte array type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -336,7 +336,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -346,12 +346,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.StringObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [string类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [string类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [string类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [string类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [string类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [string类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [string type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [string type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [string type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [string type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [string type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [string type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -395,7 +395,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -405,12 +405,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.ListIntegerObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [list类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [list类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [list类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [list类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [list类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [list类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [list type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [list type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [list type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [list type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [list type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [list type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -443,10 +443,10 @@ public class FieldSpeedTesting {
|
||||
}
|
||||
kryoDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
output.close();
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [set类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [set类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [set类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [set类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [set type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [set type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [set type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [set type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -491,7 +491,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -501,12 +501,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.MapObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [map类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [map类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [map类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [map类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [map类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [map类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [map type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [map type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [map type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [map type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [map type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [map type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -551,7 +551,7 @@ public class FieldSpeedTesting {
|
||||
protobufSerializationSize = length;
|
||||
var singleLength = length / benchmark;
|
||||
var l = 0;
|
||||
//在循环中去掉创建CodedInputStream的代码,保证比较耗时公平性
|
||||
//Remove CodedInputStream creation from loop to ensure fair timing
|
||||
var codeInputs = new CodedInputStream[benchmark];
|
||||
for (int i = 0; i < benchmark; i++) {
|
||||
codeInputs[i] = CodedInputStream.newInstance(protobufBuff, l + singleLength, singleLength);
|
||||
@@ -561,12 +561,12 @@ public class FieldSpeedTesting {
|
||||
var mess = FieldProtobufObject.InnerObjectObject.parseFrom(codeInputs[i]);
|
||||
}
|
||||
protobufDeserializationTime = System.currentTimeMillis() - startTime;
|
||||
System.out.println(StringUtils.format("序列化 [zfoo] [内嵌Object类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [kryo] [内嵌Object类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("序列化 [protobuf] [内嵌Object类型] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("反序列化 [zfoo] [内嵌Object类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [kryo] [内嵌Object类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("反序列化 [protobuf] [内嵌Object类型] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
System.out.println(StringUtils.format("Serialize [zfoo] [nested Object type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), zfooSerializationTime, zfooSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [kryo] [nested Object type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), kryoSerializationTime, kryoSerializationSize));
|
||||
System.out.println(StringUtils.format("Serialize [protobuf] [nested Object type] [thread:{}] [time:{}] [size:{}]", Thread.currentThread().getName(), protobufSerializationTime, protobufSerializationSize));
|
||||
System.out.println(StringUtils.format("Deserialize [zfoo] [nested Object type] [thread:{}] [time:{}]", Thread.currentThread().getName(), zfooDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [kryo] [nested Object type] [thread:{}] [time:{}]", Thread.currentThread().getName(), kryoDeserializationTime));
|
||||
System.out.println(StringUtils.format("Deserialize [protobuf] [nested Object type] [thread:{}] [time:{}]", Thread.currentThread().getName(), protobufDeserializationTime));
|
||||
}
|
||||
|
||||
public static final int intAValue = -100000000;
|
||||
@@ -765,7 +765,7 @@ public class FieldSpeedTesting {
|
||||
kryo.register(ArrayList.class);
|
||||
kryo.register(HashSet.class);
|
||||
kryo.register(HashMap.class);
|
||||
// 关闭循环引用,提高性能
|
||||
// Disable circular reference tracking to improve performance
|
||||
kryo.setReferences(false);
|
||||
return kryo;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@ import javassist.*;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/*
|
||||
测试javassist生成的类和普通用new关键字创建出来的类之间的区别<hr/>
|
||||
测试发现两者从访问方法和访问变量的速度几乎没有什么区别,可以用javassist代理其它的类,不会影响速度<hr/>
|
||||
Tests the difference between Javassist-generated classes and those created with new<hr/>
|
||||
Results show virtually no speed difference; Javassist proxies can be used without performance impact<hr/>
|
||||
*/
|
||||
@Ignore
|
||||
public class JavassistTesting {
|
||||
@@ -37,7 +37,7 @@ public class JavassistTesting {
|
||||
|
||||
|
||||
/**
|
||||
* 直接访问public的访问速度
|
||||
* Speed of direct public field access
|
||||
*/
|
||||
public void testA() {
|
||||
A a = new A();
|
||||
@@ -55,7 +55,7 @@ public class JavassistTesting {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用get和set方法访问成员变量的速度
|
||||
* Speed of accessing fields via getters and setters
|
||||
*/
|
||||
public void testB() {
|
||||
B b = new B();
|
||||
@@ -73,7 +73,7 @@ public class JavassistTesting {
|
||||
}
|
||||
|
||||
/*
|
||||
通过反射直接操作属性访问变量的速度
|
||||
Speed of accessing fields via reflection
|
||||
*/
|
||||
public void testC() throws NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
|
||||
Field aField = C.class.getDeclaredField("a");
|
||||
@@ -111,7 +111,7 @@ public class JavassistTesting {
|
||||
}
|
||||
|
||||
/*
|
||||
通过javassist访问成员变量的速度
|
||||
Speed of accessing fields via Javassist
|
||||
*/
|
||||
@Test
|
||||
public void testD() throws NotFoundException, CannotCompileException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
|
||||
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user