优化代码

This commit is contained in:
zhuyijun 2023-03-27 17:01:01 +08:00
parent 3d2ad88116
commit 9a37a14157
21 changed files with 2741 additions and 4 deletions

1
.gitignore vendored
View File

@ -70,7 +70,6 @@ gen
*.rar
hs_err_pid*
.metadata
bin/
tmp/
*.tmp
*.bak

View File

@ -70,7 +70,6 @@ gen
*.rar
hs_err_pid*
.metadata
bin/
tmp/
*.tmp
*.bak

View File

@ -0,0 +1,39 @@
<assembly>
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<unpack>false</unpack>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.md</include>
</includes>
</fileSet>
<!-- 配置文件,静态资源 -->
<fileSet>
<directory>src/main/resources/</directory>
<outputDirectory>config/${project.artifactId}</outputDirectory>
<!--权限控制与linux权限写法一致自行百度-->
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
</assembly>

View File

@ -70,7 +70,6 @@ gen
*.rar
hs_err_pid*
.metadata
bin/
tmp/
*.tmp
*.bak

View File

@ -0,0 +1,39 @@
<assembly>
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<unpack>false</unpack>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.md</include>
</includes>
</fileSet>
<!-- 配置文件,静态资源-->
<fileSet>
<directory>src/main/resources/</directory>
<outputDirectory>config/${project.artifactId}</outputDirectory>
<!--权限控制与linux权限写法一致自行百度-->
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
</assembly>

View File

@ -70,7 +70,6 @@ gen
*.rar
hs_err_pid*
.metadata
bin/
tmp/
*.tmp
*.bak

View File

@ -0,0 +1,39 @@
<assembly>
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<unpack>false</unpack>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.md</include>
</includes>
</fileSet>
<!-- 配置文件,静态资源 -->
<fileSet>
<directory>src/main/resources/</directory>
<outputDirectory>config/${project.artifactId}</outputDirectory>
<!--权限控制与linux权限写法一致自行百度-->
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
</assembly>

View File

@ -34,6 +34,10 @@
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>

View File

@ -0,0 +1,117 @@
package cn.zyjblogs.starter.common.collections.map;
import java.util.AbstractSet;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* ConcurrentHashSet
*/
public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E>, java.io.Serializable {
private static final long serialVersionUID = -8672117787651310382L;
private static final Object PRESENT = new Object();
private final ConcurrentMap<E, Object> map;
public ConcurrentHashSet() {
map = new ConcurrentHashMap<E, Object>();
}
public ConcurrentHashSet(int initialCapacity) {
map = new ConcurrentHashMap<E, Object>(initialCapacity);
}
/**
* Returns an iterator over the elements in this set. The elements are
* returned in no particular order.
*
* @return an Iterator over the elements in this set
* @see ConcurrentModificationException
*/
@Override
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
* Returns the number of elements in this set (its cardinality).
*
* @return the number of elements in this set (its cardinality)
*/
@Override
public int size() {
return map.size();
}
/**
* Returns <tt>true</tt> if this set contains no elements.
*
* @return <tt>true</tt> if this set contains no elements
*/
@Override
public boolean isEmpty() {
return map.isEmpty();
}
/**
* Returns <tt>true</tt> if this set contains the specified element. More
* formally, returns <tt>true</tt> if and only if this set contains an
* element <tt>e</tt> such that
* <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
*
* @param o element whose presence in this set is to be tested
* @return <tt>true</tt> if this set contains the specified element
*/
@Override
public boolean contains(Object o) {
return map.containsKey(o);
}
/**
* Adds the specified element to this set if it is not already present. More
* formally, adds the specified element <tt>e</tt> to this set if this set
* contains no element <tt>e2</tt> such that
* <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>. If this
* set already contains the element, the call leaves the set unchanged and
* returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
@Override
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
/**
* Removes the specified element from this set if it is present. More
* formally, removes an element <tt>e</tt> such that
* <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>, if this
* set contains such an element. Returns <tt>true</tt> if this set contained
* the element (or equivalently, if this set changed as a result of the
* call). (This set will not contain the element once the call returns.)
*
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if the set contained the specified element
*/
@Override
public boolean remove(Object o) {
return map.remove(o) == PRESENT;
}
/**
* Removes all elements from this set. The set will be empty after
* this call returns.
*/
@Override
public void clear() {
map.clear();
}
}

View File

@ -34,4 +34,14 @@ public class AbstractBusinessException extends RuntimeException {
this.message = message;
}
/**
* 创建业务异常对象
*
* @param message 错误消息
*/
public AbstractBusinessException(String message) {
super(message);
this.responseCode = HttpCode.INTERNAL_SERVER_ERROR;
this.message = message;
}
}

View File

@ -0,0 +1,21 @@
package cn.zyjblogs.starter.common.exception;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
/**
* 工具类异常
*
* @author lingyi
*/
public class UtilException extends AbstractBusinessException {
private static final long serialVersionUID = 8247610319171014183L;
public UtilException(String message) {
super(message);
}
public UtilException(HttpCode code, String message) {
super(code, message);
}
}

View File

@ -0,0 +1,15 @@
package cn.zyjblogs.starter.common.function;
/**
* 过滤器接口
*/
@FunctionalInterface
public interface Filter<T> {
/**
* 是否接受对象
*
* @param t 检查的对象
* @return 是否接受对象
*/
boolean accept(T t);
}

View File

@ -0,0 +1,427 @@
package cn.zyjblogs.starter.common.io;
import org.springframework.util.Assert;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.LinkedList;
public class FastByteArrayOutputStream extends OutputStream {
private static final int DEFAULT_BLOCK_SIZE = 256;
// The buffers used to store the content bytes
private final LinkedList<byte[]> buffers = new LinkedList<byte[]>();
// The size, in bytes, to use when allocating the first byte[]
private final int initialBlockSize;
// The size, in bytes, to use when allocating the next byte[]
private int nextBlockSize = 0;
// The number of bytes in previous buffers.
// (The number of bytes in the current buffer is in 'index'.)
private int alreadyBufferedSize = 0;
// The index in the byte[] found at buffers.getLast() to be written next
private int index = 0;
// Is the stream closed?
private boolean closed = false;
/**
* Create a new <code>FastByteArrayOutputStream</code>
* with the default initial capacity of 256 bytes.
*/
public FastByteArrayOutputStream() {
this(DEFAULT_BLOCK_SIZE);
}
/**
* Create a new <code>FastByteArrayOutputStream</code>
* with the specified initial capacity.
*
* @param initialBlockSize the initial buffer size in bytes
*/
public FastByteArrayOutputStream(int initialBlockSize) {
if (initialBlockSize <= 0) {
throw new IllegalArgumentException("Initial block size must be greater than 0");
}
this.initialBlockSize = initialBlockSize;
this.nextBlockSize = initialBlockSize;
}
// Overridden methods
@Override
public void write(int datum) throws IOException {
if (this.closed) {
throw new IOException("Stream closed");
} else {
if (this.buffers.peekLast() == null || this.buffers.getLast().length == this.index) {
addBuffer(1);
}
// store the byte
this.buffers.getLast()[this.index++] = (byte) datum;
}
}
@Override
public void write(byte[] data, int offset, int length) throws IOException {
if (data == null) {
throw new NullPointerException();
} else if (offset < 0 || offset + length > data.length || length < 0) {
throw new IndexOutOfBoundsException();
} else if (this.closed) {
throw new IOException("Stream closed");
} else {
if (this.buffers.peekLast() == null || this.buffers.getLast().length == this.index) {
addBuffer(length);
}
if (this.index + length > this.buffers.getLast().length) {
int pos = offset;
do {
if (this.index == this.buffers.getLast().length) {
addBuffer(length);
}
int copyLength = this.buffers.getLast().length - this.index;
if (length < copyLength) {
copyLength = length;
}
System.arraycopy(data, pos, this.buffers.getLast(), this.index, copyLength);
pos += copyLength;
this.index += copyLength;
length -= copyLength;
}
while (length > 0);
} else {
// copy in the sub-array
System.arraycopy(data, offset, this.buffers.getLast(), this.index, length);
this.index += length;
}
}
}
@Override
public void close() {
this.closed = true;
}
@Override
public String toString() {
return new String(toByteArrayUnsafe());
}
public int size() {
return (this.alreadyBufferedSize + this.index);
}
public byte[] toByteArrayUnsafe() {
int totalSize = size();
if (totalSize == 0) {
return new byte[0];
}
resize(totalSize);
return this.buffers.getFirst();
}
public byte[] toByteArray() {
byte[] bytesUnsafe = toByteArrayUnsafe();
byte[] ret = new byte[bytesUnsafe.length];
System.arraycopy(bytesUnsafe, 0, ret, 0, bytesUnsafe.length);
return ret;
}
/**
* Reset the contents of this <code>FastByteArrayOutputStream</code>.
* <p>All currently accumulated output in the output stream is discarded.
* The output stream can be used again.
*/
public void reset() {
this.buffers.clear();
this.nextBlockSize = this.initialBlockSize;
this.closed = false;
this.index = 0;
this.alreadyBufferedSize = 0;
}
public InputStream getInputStream() {
return new FastByteArrayInputStream(this);
}
/**
* Write the buffers content to the given OutputStream.
*
* @param out the OutputStream to write to
*/
public void writeTo(OutputStream out) throws IOException {
Iterator<byte[]> it = this.buffers.iterator();
while (it.hasNext()) {
byte[] bytes = it.next();
if (it.hasNext()) {
out.write(bytes, 0, bytes.length);
} else {
out.write(bytes, 0, this.index);
}
}
}
public void resize(int targetCapacity) {
Assert.isTrue(targetCapacity >= size(), "New capacity must not be smaller than current size");
if (this.buffers.peekFirst() == null) {
this.nextBlockSize = targetCapacity - size();
} else if (size() == targetCapacity && this.buffers.getFirst().length == targetCapacity) {
// do nothing - already at the targetCapacity
} else {
int totalSize = size();
byte[] data = new byte[targetCapacity];
int pos = 0;
Iterator<byte[]> it = this.buffers.iterator();
while (it.hasNext()) {
byte[] bytes = it.next();
if (it.hasNext()) {
System.arraycopy(bytes, 0, data, pos, bytes.length);
pos += bytes.length;
} else {
System.arraycopy(bytes, 0, data, pos, this.index);
}
}
this.buffers.clear();
this.buffers.add(data);
this.index = totalSize;
this.alreadyBufferedSize = 0;
}
}
/**
* Create a new buffer and store it in the LinkedList
* <p>Adds a new buffer that can store at least {@code minCapacity} bytes.
*/
private void addBuffer(int minCapacity) {
if (this.buffers.peekLast() != null) {
this.alreadyBufferedSize += this.index;
this.index = 0;
}
if (this.nextBlockSize < minCapacity) {
this.nextBlockSize = nextPowerOf2(minCapacity);
}
this.buffers.add(new byte[this.nextBlockSize]);
this.nextBlockSize *= 2; // block size doubles each time
}
/**
* Get the next power of 2 of a number (ex, the next power of 2 of 119 is 128).
*/
private static int nextPowerOf2(int val) {
val--;
val = (val >> 1) | val;
val = (val >> 2) | val;
val = (val >> 4) | val;
val = (val >> 8) | val;
val = (val >> 16) | val;
val++;
return val;
}
private static final class FastByteArrayInputStream extends UpdateMessageDigestInputStream {
private final FastByteArrayOutputStream fastByteArrayOutputStream;
private final Iterator<byte[]> buffersIterator;
private byte[] currentBuffer;
private int currentBufferLength = 0;
private int nextIndexInCurrentBuffer = 0;
private int totalBytesRead = 0;
/**
* Create a new <code>FastByteArrayOutputStreamInputStream</code> backed
* by the given <code>FastByteArrayOutputStream</code>.
*/
public FastByteArrayInputStream(FastByteArrayOutputStream fastByteArrayOutputStream) {
this.fastByteArrayOutputStream = fastByteArrayOutputStream;
this.buffersIterator = fastByteArrayOutputStream.buffers.iterator();
if (this.buffersIterator.hasNext()) {
this.currentBuffer = this.buffersIterator.next();
if (this.currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
this.currentBufferLength = fastByteArrayOutputStream.index;
} else {
this.currentBufferLength = this.currentBuffer.length;
}
}
}
@Override
public int read() {
if (this.currentBuffer == null) {
// This stream doesn't have any data in it...
return -1;
} else {
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
this.totalBytesRead++;
return this.currentBuffer[this.nextIndexInCurrentBuffer++];
} else {
if (this.buffersIterator.hasNext()) {
this.currentBuffer = this.buffersIterator.next();
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
this.currentBufferLength = this.fastByteArrayOutputStream.index;
} else {
this.currentBufferLength = this.currentBuffer.length;
}
this.nextIndexInCurrentBuffer = 0;
} else {
this.currentBuffer = null;
}
return read();
}
}
}
@Override
public int read(byte[] b) {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
} else {
if (this.currentBuffer == null) {
// This stream doesn't have any data in it...
return -1;
} else {
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
int bytesToCopy = Math.min(len, this.currentBufferLength - this.nextIndexInCurrentBuffer);
System.arraycopy(this.currentBuffer, this.nextIndexInCurrentBuffer, b, off, bytesToCopy);
this.totalBytesRead += bytesToCopy;
this.nextIndexInCurrentBuffer += bytesToCopy;
int remaining = read(b, off + bytesToCopy, len - bytesToCopy);
return bytesToCopy + Math.max(remaining, 0);
} else {
if (this.buffersIterator.hasNext()) {
this.currentBuffer = this.buffersIterator.next();
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
this.currentBufferLength = this.fastByteArrayOutputStream.index;
} else {
this.currentBufferLength = this.currentBuffer.length;
}
this.nextIndexInCurrentBuffer = 0;
} else {
this.currentBuffer = null;
}
return read(b, off, len);
}
}
}
}
@Override
public long skip(long n) throws IOException {
if (n > Integer.MAX_VALUE) {
throw new IllegalArgumentException("n exceeds maximum (" + Integer.MAX_VALUE + "): " + n);
} else if (n == 0) {
return 0;
} else if (n < 0) {
throw new IllegalArgumentException("n must be 0 or greater: " + n);
}
int len = (int) n;
if (this.currentBuffer == null) {
// This stream doesn't have any data in it...
return 0;
} else {
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
int bytesToSkip = Math.min(len, this.currentBufferLength - this.nextIndexInCurrentBuffer);
this.totalBytesRead += bytesToSkip;
this.nextIndexInCurrentBuffer += bytesToSkip;
return (bytesToSkip + skip(len - bytesToSkip));
} else {
if (this.buffersIterator.hasNext()) {
this.currentBuffer = this.buffersIterator.next();
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
this.currentBufferLength = this.fastByteArrayOutputStream.index;
} else {
this.currentBufferLength = this.currentBuffer.length;
}
this.nextIndexInCurrentBuffer = 0;
} else {
this.currentBuffer = null;
}
return skip(len);
}
}
}
@Override
public int available() {
return (this.fastByteArrayOutputStream.size() - this.totalBytesRead);
}
/**
* Update the message digest with the remaining bytes in this stream.
*
* @param messageDigest The message digest to update
*/
public void updateMessageDigest(MessageDigest messageDigest) {
updateMessageDigest(messageDigest, available());
}
/**
* Update the message digest with the next len bytes in this stream.
* Avoids creating new byte arrays and use internal buffers for performance.
*
* @param messageDigest The message digest to update
* @param len how many bytes to read from this stream and use to update the message digest
*/
public void updateMessageDigest(MessageDigest messageDigest, int len) {
if (this.currentBuffer == null) {
// This stream doesn't have any data in it...
return;
} else if (len == 0) {
return;
} else if (len < 0) {
throw new IllegalArgumentException("len must be 0 or greater: " + len);
} else {
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
int bytesToCopy = Math.min(len, this.currentBufferLength - this.nextIndexInCurrentBuffer);
messageDigest.update(this.currentBuffer, this.nextIndexInCurrentBuffer, bytesToCopy);
this.nextIndexInCurrentBuffer += bytesToCopy;
updateMessageDigest(messageDigest, len - bytesToCopy);
} else {
if (this.buffersIterator.hasNext()) {
this.currentBuffer = this.buffersIterator.next();
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
this.currentBufferLength = this.fastByteArrayOutputStream.index;
} else {
this.currentBufferLength = this.currentBuffer.length;
}
this.nextIndexInCurrentBuffer = 0;
} else {
this.currentBuffer = null;
}
updateMessageDigest(messageDigest, len);
}
}
}
}
}

View File

@ -0,0 +1,93 @@
package cn.zyjblogs.starter.common.io;
import java.io.IOException;
import java.io.Writer;
/**
* Thread-unsafe StringWriter.
*
* @author lingyi
*/
public class UnsafeStringWriter extends Writer {
private StringBuilder mBuffer;
public UnsafeStringWriter() {
lock = mBuffer = new StringBuilder();
}
public UnsafeStringWriter(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
lock = mBuffer = new StringBuilder();
}
@Override
public void write(int c) {
mBuffer.append((char) c);
}
@Override
public void write(char[] cs) throws IOException {
mBuffer.append(cs, 0, cs.length);
}
@Override
public void write(char[] cs, int off, int len) throws IOException {
if ((off < 0) || (off > cs.length) || (len < 0) ||
((off + len) > cs.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len > 0) {
mBuffer.append(cs, off, len);
}
}
@Override
public void write(String str) {
mBuffer.append(str);
}
@Override
public void write(String str, int off, int len) {
mBuffer.append(str, off, off + len);
}
@Override
public Writer append(CharSequence csq) {
if (csq == null) {
write("null");
} else {
write(csq.toString());
}
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
@Override
public Writer append(char c) {
mBuffer.append(c);
return this;
}
@Override
public void close() {
}
@Override
public void flush() {
}
@Override
public String toString() {
return mBuffer.toString();
}
}

View File

@ -0,0 +1,27 @@
package cn.zyjblogs.starter.common.io;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
abstract class UpdateMessageDigestInputStream extends InputStream {
public void updateMessageDigest(MessageDigest messageDigest) throws IOException {
int data;
while ((data = read()) != -1) {
messageDigest.update((byte) data);
}
}
public void updateMessageDigest(MessageDigest messageDigest, int len) throws IOException {
int data;
int bytesRead = 0;
while (bytesRead < len && (data = read()) != -1) {
messageDigest.update((byte) data);
bytesRead++;
}
}
}

View File

@ -0,0 +1,179 @@
package cn.zyjblogs.starter.common.utils.collections;
import org.apache.commons.lang3.ArrayUtils;
import java.util.*;
/**
* 集合相关工具类
*
* @author lingyi
*/
public class CollectionUtils {
/**
* 集合是否为非空
*
* @param collection 集合
* @return 是否为非空
*/
public static boolean isNotEmpty(Collection<?> collection) {
return !isEmpty(collection);
}
/**
* 集合是否为空
*
* @param collection 集合
* @return 是否为空
*/
public static boolean isEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
/**
* Enumeration是否为空
*
* @param enumeration {@link Enumeration}
* @return 是否为空
*/
public static boolean isNotEmpty(Enumeration<?> enumeration) {
return null != enumeration && enumeration.hasMoreElements();
}
/**
* 获取集合中指定下标的元素值下标可以为负数例如-1表示最后一个元素<br>
* 如果元素越界返回null
*
* @param <T> 元素类型
* @param collection 集合
* @param index 下标支持负数
* @return 元素值
*/
public static <T> T get(Collection<T> collection, int index) {
if (null == collection) {
return null;
}
final int size = collection.size();
if (0 == size) {
return null;
}
if (index < 0) {
index += size;
}
// 检查越界
if (index >= size) {
return null;
}
if (collection instanceof List) {
final List<T> list = ((List<T>) collection);
return list.get(index);
} else {
int i = 0;
for (T t : collection) {
if (i > index) {
break;
} else if (i == index) {
return t;
}
i++;
}
}
return null;
}
/**
* 加入全部
*
* @param <T> 集合元素类型
* @param collection 被加入的集合 {@link Collection}
* @param enumeration 要加入的内容{@link Enumeration}
* @return 原集合
*/
public static <T> Collection<T> addAll(Collection<T> collection, Enumeration<T> enumeration) {
if (null != collection && null != enumeration) {
while (enumeration.hasMoreElements()) {
collection.add(enumeration.nextElement());
}
}
return collection;
}
/**
* 新建一个ArrayList
*
* @param <T> 集合元素类型
* @param values 数组
* @return ArrayList对象
*/
@SafeVarargs
public static <T> ArrayList<T> newArrayList(T... values) {
return toList(values);
}
/**
* 新建一个ArrayList
*
* @param <T> 集合元素类型
* @param values 数组
* @return ArrayList对象
*/
@SafeVarargs
public static <T> ArrayList<T> toList(T... values) {
return (ArrayList<T>) list(false, values);
}
/**
* 新建一个List
*
* @param <T> 集合元素类型
* @param isLinked 是否新建LinkedList
* @param values 数组
* @return List对象
*/
@SafeVarargs
public static <T> List<T> list(boolean isLinked, T... values) {
if (ArrayUtils.isEmpty(values)) {
return list(isLinked);
}
final List<T> arrayList = isLinked ? new LinkedList<>() : new ArrayList<>(values.length);
Collections.addAll(arrayList, values);
return arrayList;
}
/**
* 新建一个HashSet
*
* @param <T> 集合元素类型
* @param ts 元素数组
* @return HashSet对象
*/
@SafeVarargs
public static <T> HashSet<T> newHashSet(T... ts) {
return set(false, ts);
}
/**
* 新建一个HashSet
*
* @param <T> 集合元素类型
* @param isSorted 是否有序有序返回 {@link LinkedHashSet}否则返回 {@link HashSet}
* @param ts 元素数组
* @return HashSet对象
*/
@SafeVarargs
public static <T> HashSet<T> set(boolean isSorted, T... ts) {
if (null == ts) {
return isSorted ? new LinkedHashSet<>() : new HashSet<>();
}
int initialCapacity = Math.max((int) (ts.length / .75f) + 1, 16);
final HashSet<T> set = isSorted ? new LinkedHashSet<>(initialCapacity) : new HashSet<>(initialCapacity);
Collections.addAll(set, ts);
return set;
}
}

View File

@ -0,0 +1,245 @@
package cn.zyjblogs.starter.common.utils.date;
import cn.zyjblogs.starter.common.exception.UtilException;
import cn.zyjblogs.starter.common.utils.string.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
/**
* 时间工具类
*
* @author xuechao.sxc
*/
public class DateUtils {
/**
* 标准日期格式yyyyMMdd
*/
public final static String PURE_DATE_PATTERN = "yyyyMMdd";
public final static SimpleDateFormat PURE_DATE_FORMAT = new SimpleDateFormat(PURE_DATE_PATTERN);
/**
* 标准日期格式HHmmss
*/
public final static String PURE_TIME_PATTERN = "HHmmss";
public final static SimpleDateFormat PURE_TIME_FORMAT = new SimpleDateFormat(PURE_TIME_PATTERN);
/**
* 标准日期格式yyyyMMddHHmmss
*/
public final static String PURE_DATETIME_PATTERN = "yyyyMMddHHmmss";
public final static SimpleDateFormat PURE_DATETIME_FORMAT = new SimpleDateFormat(PURE_DATETIME_PATTERN);
/**
* 标准日期格式yyyyMMddHHmmssSSS
*/
public final static String PURE_DATETIME_MS_PATTERN = "yyyyMMddHHmmssSSS";
public final static SimpleDateFormat PURE_DATETIME_MS_FORMAT = new SimpleDateFormat(PURE_DATETIME_MS_PATTERN);
/**
* 标准日期格式yyyy-MM-dd
*/
public final static String NORM_DATE_PATTERN = "yyyy-MM-dd";
public final static SimpleDateFormat NORM_DATE_FORMAT = new SimpleDateFormat(NORM_DATE_PATTERN);
/**
* 标准日期时间格式精确到分yyyy-MM-dd HH:mm
*/
public final static String NORM_DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm";
public final static SimpleDateFormat NORM_DATETIME_MINUTE_FORMAT = new SimpleDateFormat(NORM_DATETIME_MINUTE_PATTERN);
/**
* 标准日期时间格式精确到秒yyyy-MM-dd HH:mm:ss
*/
public final static String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public final static SimpleDateFormat NORM_DATETIME_FORMAT = new SimpleDateFormat(NORM_DATETIME_PATTERN);
/**
* {@link TemporalAccessor}类型时间转为{@link Date}<br>
* 始终根据已有{@link TemporalAccessor} 产生新的{@link Date}对象
*
* @param temporalAccessor {@link TemporalAccessor}
* @return 时间对象
*/
public static Date date(TemporalAccessor temporalAccessor) {
Instant instant = toInstant(temporalAccessor);
return new Date(instant.toEpochMilli());
}
/**
* Date对象转换为{@link Instant}对象
*
* @param temporalAccessor Date对象
* @return {@link Instant}对象
*/
public static Instant toInstant(TemporalAccessor temporalAccessor) {
if (null == temporalAccessor) {
return null;
}
Instant result;
if (temporalAccessor instanceof Instant) {
result = (Instant) temporalAccessor;
} else if (temporalAccessor instanceof LocalDateTime) {
result = ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault()).toInstant();
} else if (temporalAccessor instanceof ZonedDateTime) {
result = ((ZonedDateTime) temporalAccessor).toInstant();
} else if (temporalAccessor instanceof OffsetDateTime) {
result = ((OffsetDateTime) temporalAccessor).toInstant();
} else if (temporalAccessor instanceof LocalDate) {
result = ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault()).toInstant();
} else if (temporalAccessor instanceof LocalTime) {
// 指定本地时间转换 为Instant取当天日期
result = ((LocalTime) temporalAccessor).atDate(LocalDate.now()).atZone(ZoneId.systemDefault()).toInstant();
} else if (temporalAccessor instanceof OffsetTime) {
// 指定本地时间转换 为Instant取当天日期
result = ((OffsetTime) temporalAccessor).atDate(LocalDate.now()).toInstant();
} else {
result = Instant.from(temporalAccessor);
}
return result;
}
/**
* 将日期字符串转换为{@link Date}对象格式<br>
* <ol>
* <li>yyyy-MM-dd HH:mm:ss</li>
* <li>yyyy/MM/dd HH:mm:ss</li>
* <li>yyyy.MM.dd HH:mm:ss</li>
* <li>yyyy年MM月dd日 HH时mm分ss秒</li>
* <li>yyyy-MM-dd</li>
* <li>yyyy/MM/dd</li>
* <li>yyyy.MM.dd</li>
* <li>HH:mm:ss</li>
* <li>HH时mm分ss秒</li>
* <li>yyyy-MM-dd HH:mm</li>
* <li>yyyy-MM-dd HH:mm:ss.SSS</li>
* <li>yyyyMMddHHmmss</li>
* <li>yyyyMMddHHmmssSSS</li>
* <li>yyyyMMdd</li>
* <li>EEE, dd MMM yyyy HH:mm:ss z</li>
* <li>EEE MMM dd HH:mm:ss zzz yyyy</li>
* <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li>
* <li>yyyy-MM-dd'T'HH:mm:ssZ</li>
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ</li>
* </ol>
*
* @param dateCharSequence 日期字符串
* @return 日期
*/
public static Date parse(CharSequence dateCharSequence) {
if (StringUtils.isBlank(dateCharSequence)) {
return null;
}
String dateStr = dateCharSequence.toString();
// 去掉两边空格并去掉中文日期中的以规范长度
dateStr = StringUtils.removeAll(dateStr.trim(), '日', '秒');
int length = dateStr.length();
if (NumberUtils.isCreatable(dateStr)) {
// 纯数字形式
if (length == PURE_DATETIME_PATTERN.length()) {
return parse(dateStr, PURE_DATETIME_FORMAT);
} else if (length == PURE_DATETIME_MS_PATTERN.length()) {
return parse(dateStr, PURE_DATETIME_MS_FORMAT);
} else if (length == PURE_DATE_PATTERN.length()) {
return parse(dateStr, PURE_DATE_FORMAT);
} else if (length == PURE_TIME_PATTERN.length()) {
return parse(dateStr, PURE_TIME_FORMAT);
}
}
if (length == NORM_DATETIME_PATTERN.length()) {
// yyyy-MM-dd HH:mm:ss
return parseDateTime(dateStr);
} else if (length == NORM_DATE_PATTERN.length()) {
// yyyy-MM-dd
return parseDate(dateStr);
} else if (length == NORM_DATETIME_MINUTE_PATTERN.length()) {
// yyyy-MM-dd HH:mm
return parse(dateStr, NORM_DATETIME_MINUTE_FORMAT);
}
// 没有更多匹配的时间格式
throw new UtilException("No format fit for date String:" + dateStr);
}
/**
* 转换字符串为Date
*
* @param dateStr 日期字符串
* @param dateFormat {@link SimpleDateFormat}
* @return {@link Date}
*/
private static Date parse(CharSequence dateStr, DateFormat dateFormat) {
if (StringUtils.isBlank(dateStr)) {
throw new UtilException("dataStr is blank");
}
try {
return dateFormat.parse(dateStr.toString());
} catch (Throwable e) {
String pattern;
if (dateFormat instanceof SimpleDateFormat) {
pattern = ((SimpleDateFormat) dateFormat).toPattern();
} else {
pattern = dateFormat.toString();
}
throw new UtilException(StringUtils.format("Parse [{}] with format [{}] error!", dateStr, pattern));
}
}
/**
* 将特定格式的日期转换为Date对象
*
* @param dateStr 特定格式的日期
* @param format 格式例如yyyy-MM-dd
* @return 日期对象
*/
public static Date parse(CharSequence dateStr, String format) {
SimpleDateFormat fmt = new SimpleDateFormat(format);
return parse(dateStr, fmt);
}
/**
* 解析格式为yyyy-MM-dd的日期忽略时分秒
*
* @param dateString 标准形式的日期字符串
* @return 日期对象
*/
public static Date parseDate(CharSequence dateString) {
return parse(dateString, NORM_DATE_FORMAT);
}
/**
* 格式yyyy-MM-dd HH:mm:ss
*
* @param dateString 标准形式的时间字符串
* @return 日期对象
*/
public static Date parseDateTime(CharSequence dateString) {
return parse(dateString, NORM_DATETIME_FORMAT);
}
/**
* 根据特定格式格式化日期
*
* @param date 被格式化的日期
* @param format {@link SimpleDateFormat}
* @return 格式化后的字符串
*/
public static String format(Date date, DateFormat format) {
if (null == format || null == date) {
return null;
}
return format.format(date);
}
}

View File

@ -0,0 +1,212 @@
package cn.zyjblogs.starter.common.utils.net;
import cn.zyjblogs.starter.common.exception.UtilException;
import cn.zyjblogs.starter.common.function.Filter;
import cn.zyjblogs.starter.common.utils.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.net.*;
import java.util.*;
/**
* 网络相关工具
*/
public class NetUtils {
private transient static String currentIp;
public static boolean isLocalhost(String ip) {
return "127.0.0.1".equals(ip)
|| "0:0:0:0:0:0:0:1".equals(ip)
|| "localhost".equals(ip)
|| "::1".equals(ip);
}
/**
* 获取本机网卡IP地址这个地址为所有网卡中非回路地址的第一个<br>
* 如果获取失败调用 {@link InetAddress#getLocalHost()}方法获取<br>
* 此方法不会抛出异常获取失败将返回<code>null</code><br>
* <p>
* 参考http://stackoverflow.com/questions/9481865/getting-the-ip-address-of-the-current-machine-using-java
*
* @return 本机网卡IP地址获取失败返回<code>null</code>
*/
public static String getInstanceIp() {
InetAddress localhost = getLocalhost();
if (null != localhost) {
return localhost.getHostAddress();
}
return null;
}
public static String getInstanceIpWithCache() {
if (Objects.isNull(currentIp)) {
synchronized (NetUtils.class) {
if (Objects.isNull(currentIp)) {
InetAddress localhost = getLocalhost();
currentIp = Objects.nonNull(localhost) ? localhost.getHostAddress() : StringUtils.EMPTY;
}
}
}
return currentIp;
}
/**
* 获取本机网卡IP地址规则如下
*
* <pre>
* 1. 查找所有网卡地址必须非回路loopback地址非局域网地址siteLocalIPv4地址
* 2. 如果无满足要求的地址调用 {@link InetAddress#getLocalHost()} 获取地址
* </pre>
* <p>
* 此方法不会抛出异常获取失败将返回<code>null</code><br>
* <p>
*
* @return 本机网卡IP地址获取失败返回<code>null</code>
*/
public static InetAddress getLocalhost() {
final LinkedHashSet<InetAddress> localAddressList = localAddressList(address -> {
// 非loopback地址指127.*.*.*的地址
return false == address.isLoopbackAddress()
// 非地区本地地址指10.0.0.0 ~ 10.255.255.255172.16.0.0 ~ 172.31.255.255192.168.0.0 ~ 192.168.255.255
&& false == address.isSiteLocalAddress()
// 需为IPV4地址
&& address instanceof Inet4Address;
});
if (CollectionUtils.isNotEmpty(localAddressList)) {
return CollectionUtils.get(localAddressList, 0);
}
try {
return InetAddress.getLocalHost();
} catch (UnknownHostException e) {
// ignore
}
return null;
}
/**
* 获得本机的IP地址列表包括Ipv4和Ipv6<br>
* 返回的IP列表有序按照系统设备顺序
*
* @return IP地址列表 {@link LinkedHashSet}
*/
public static LinkedHashSet<String> localIps() {
final LinkedHashSet<InetAddress> localAddressList = localAddressList(null);
return toIpList(localAddressList);
}
/**
* 地址列表转换为IP地址列表
*
* @param addressList 地址{@link Inet4Address} 列表
* @return IP地址字符串列表
*/
public static LinkedHashSet<String> toIpList(Set<InetAddress> addressList) {
final LinkedHashSet<String> ipSet = new LinkedHashSet<>();
for (InetAddress address : addressList) {
ipSet.add(address.getHostAddress());
}
return ipSet;
}
/**
* 获取所有满足过滤条件的本地IP地址对象
*
* @param addressFilter 过滤器null表示不过滤获取所有地址
* @return 过滤后的地址对象列表
*/
public static LinkedHashSet<InetAddress> localAddressList(Filter<InetAddress> addressFilter) {
Enumeration<NetworkInterface> networkInterfaces;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
throw new UtilException(e.getMessage());
}
if (networkInterfaces == null) {
throw new UtilException("Get network interface error!");
}
final LinkedHashSet<InetAddress> ipSet = new LinkedHashSet<>();
while (networkInterfaces.hasMoreElements()) {
final NetworkInterface networkInterface = networkInterfaces.nextElement();
final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
final InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress != null && (null == addressFilter || addressFilter.accept(inetAddress))) {
ipSet.add(inetAddress);
}
}
}
return ipSet;
}
/**
* 获取指定名称的网卡信息
*
* @param name 网络接口名例如Linux下默认是eth0
* @return 网卡未找到返回<code>null</code>
*/
public static NetworkInterface getNetworkInterface(String name) {
Enumeration<NetworkInterface> networkInterfaces;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
return null;
}
NetworkInterface netInterface;
while (networkInterfaces.hasMoreElements()) {
netInterface = networkInterfaces.nextElement();
if (null != netInterface && name.equals(netInterface.getName())) {
return netInterface;
}
}
return null;
}
/**
* 获取本机所有网卡
*
* @return 所有网卡异常返回<code>null</code>
*/
public static Collection<NetworkInterface> getNetworkInterfaces() {
Enumeration<NetworkInterface> networkInterfaces;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
return null;
}
return CollectionUtils.addAll(new ArrayList<>(), networkInterfaces);
}
public static String getHostName() {
try {
return InetAddress.getLocalHost().getCanonicalHostName();
} catch (Throwable e) {
return null;
}
}
public static boolean isPortAvailable(int port) {
Socket socket;
String host = "localhost";
try {
socket = new Socket(host, port);
return true;
} catch (IOException e) {
return false;
}
}
}

View File

@ -0,0 +1,73 @@
package cn.zyjblogs.starter.common.utils.string;
/**
* 字符工具类<br>
* 部分工具来自于Apache Commons系列
*
* @author lingyi
*/
public class CharUtils {
public static final char DOT = '.';
private static final int ASCII_LENGTH = 128;
private static final String[] CACHE = new String[ASCII_LENGTH];
static {
for (char c = 0; c < ASCII_LENGTH; c++) {
CACHE[c] = String.valueOf(c);
}
}
/**
* 是否空白符<br>
* 空白符包括空格制表符全角空格和不间断空格<br>
*
* @param c 字符
* @return 是否空白符
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
*/
public static boolean isBlankChar(char c) {
return isBlankChar((int) c);
}
/**
* 是否空白符<br>
* 空白符包括空格制表符全角空格和不间断空格<br>
*
* @param c 字符
* @return 是否空白符
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
*/
public static boolean isBlankChar(int c) {
return Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a';
}
/**
* 给定对象对应的类是否为字符类字符类包括
*
* <pre>
* Character.class
* char.class
* </pre>
*
* @param value 被检查的对象
* @return true表示为字符类
*/
public static boolean isChar(Object value) {
//noinspection ConstantConditions
return value instanceof Character || value.getClass() == char.class;
}
/**
* 字符转为字符串<br>
* 如果为ASCII字符使用缓存
*
* @param c 字符
* @return 字符串
*/
public static String toString(char c) {
return c < ASCII_LENGTH ? CACHE[c] : String.valueOf(c);
}
}

View File

@ -0,0 +1,905 @@
package cn.zyjblogs.starter.common.utils.string;
import cn.zyjblogs.starter.common.exception.UtilException;
import cn.zyjblogs.starter.common.io.UnsafeStringWriter;
import com.alibaba.fastjson2.JSON;
import org.apache.commons.lang3.ArrayUtils;
import java.io.PrintWriter;
/**
* StringUtils
*
* @author lingyi
*/
public class StringUtils {
public static final int INDEX_NOT_FOUND = -1;
public static final String EMPTY_JSON = "{}";
public static final char BACKSLASH = '\\';
public static final char DELIMIT_START = '{';
public static final String NULL = "null";
public static final String EMPTY = "";
public static final String DOT = ".";
public static final String COMMA = ",";
public static final String SEMICOLON = ";";
/**
* UTF-8
*/
public static final String UTF_8 = "UTF-8";
/**
* is not empty string.
*
* @param str source string.
* @return is not empty.
*/
public static boolean isNotEmpty(CharSequence str) {
return !isEmpty(str);
}
/**
* is empty string.
*
* @param str source string.
* @return is empty.
*/
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}
public static boolean isNotBlank(CharSequence cs) {
return !isBlank(cs);
}
public static boolean isBlank(CharSequence cs) {
int strLen;
if (cs != null && (strLen = cs.length()) != 0) {
for (int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
} else {
return true;
}
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
* 如果想输出 {} 使用 \\转义 { 即可如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
* <br>
* 通常使用format("this is {} for {}", "a", "b") = this is a for b<br>
* 转义{} format("this is \\{} for {}", "a", "b") = this is \{} for a<br>
* 转义\ format("this is \\\\{} for {}", "a", "b") = this is \a for b<br>
*
* @param template 文本模板被替换的部分用 {} 表示
* @param params 参数值
* @return 格式化后的文本
*/
public static String format(String template, Object... params) {
if (isBlank(template) || ArrayUtils.isEmpty(params)) {
return template;
}
final int strPatternLength = template.length();
// 初始化定义好的长度以获得更好的性能
StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
// 记录已经处理到的位置
int handledPosition = 0;
// 占位符所在位置
int delimIndex;
for (int argIndex = 0; argIndex < params.length; argIndex++) {
delimIndex = template.indexOf(EMPTY_JSON, handledPosition);
// 剩余部分无占位符
if (delimIndex == -1) {
// 不带占位符的模板直接返回
if (handledPosition == 0) {
return template;
}
// 字符串模板剩余部分不再包含占位符加入剩余部分后返回结果
sbuf.append(template, handledPosition, strPatternLength);
return sbuf.toString();
}
// 转义符
if (delimIndex > 0 && template.charAt(delimIndex - 1) == BACKSLASH) {
// 双转义符
if (delimIndex > 1 && template.charAt(delimIndex - 2) == BACKSLASH) {
// 转义符之前还有一个转义符占位符依旧有效
sbuf.append(template, handledPosition, delimIndex - 1);
sbuf.append(toJsonStr(params[argIndex]));
handledPosition = delimIndex + 2;
} else {
// 占位符被转义
argIndex--;
sbuf.append(template, handledPosition, delimIndex - 1);
sbuf.append(DELIMIT_START);
handledPosition = delimIndex + 1;
}
} else {// 正常占位符
sbuf.append(template, handledPosition, delimIndex);
sbuf.append(toJsonStr(params[argIndex]));
handledPosition = delimIndex + 2;
}
}
// append the characters following the last {} pair.
// 加入最后一个占位符后所有的字符
sbuf.append(template, handledPosition, template.length());
return sbuf.toString();
}
/**
* 将对象转为json字符串<br>
*
* @param obj 对象
* @return 字符串
*/
private static String toJsonStr(Object obj) {
if (obj instanceof String) {
return (String) obj;
}
return JSON.toJSONString(obj);
}
/**
* 切割指定位置之前部分的字符串
*
* @param string 字符串
* @param toIndex 切割到的位置不包括
* @return 切割后的剩余的前半部分字符串
*/
public static String subPre(CharSequence string, int toIndex) {
return sub(string, 0, toIndex);
}
/**
* 改进JDK subString<br>
* index从0开始计算最后一个字符为-1<br>
* 如果from和to位置一样返回 "" <br>
* 如果from或to为负数则按照length从后向前数位置如果绝对值大于字符串长度则from归到0to归到length<br>
* 如果经过修正的index中from大于to则互换from和to example: <br>
* abcdefgh 2 3 = c <br>
* abcdefgh 2 -3 = cde <br>
*
* @param str String
* @param fromIndex 开始的index包括
* @param toIndex 结束的index不包括
* @return 字串
*/
public static String sub(CharSequence str, int fromIndex, int toIndex) {
if (isEmpty(str)) {
return str(str);
}
int len = str.length();
if (fromIndex < 0) {
fromIndex = len + fromIndex;
if (fromIndex < 0) {
fromIndex = 0;
}
} else if (fromIndex > len) {
fromIndex = len;
}
if (toIndex < 0) {
toIndex = len + toIndex;
if (toIndex < 0) {
toIndex = len;
}
} else if (toIndex > len) {
toIndex = len;
}
if (toIndex < fromIndex) {
int tmp = fromIndex;
fromIndex = toIndex;
toIndex = tmp;
}
if (fromIndex == toIndex) {
return EMPTY;
}
return str.toString().substring(fromIndex, toIndex);
}
/**
* {@link CharSequence} 转为字符串null安全
*
* @param cs {@link CharSequence}
* @return 字符串
*/
public static String str(CharSequence cs) {
return null == cs ? null : cs.toString();
}
/**
* 比较两个字符串是否相等
*
* @param str1 要比较的字符串1
* @param str2 要比较的字符串2
* @param ignoreCase 是否忽略大小写
* @return 如果两个字符串相同或者都是<code>null</code>则返回<code>true</code>
*/
public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
if (null == str1) {
// 只有两个都为null才判断相等
return str2 == null;
}
if (null == str2) {
// 字符串2空字符串1非空直接false
return false;
}
if (ignoreCase) {
return str1.toString().equalsIgnoreCase(str2.toString());
} else {
return str1.equals(str2);
}
}
/**
* 第一个字符串是否包含第二个字符串
*
* @param str1 字符串1
* @param str2 字符串2
*/
public static boolean contains(CharSequence str1, CharSequence str2) {
if (null == str1 || null == str2) {
return false;
}
return str1.toString().contains(str2);
}
/**
* @param e
* @return string
*/
public static String toString(Throwable e) {
UnsafeStringWriter w = new UnsafeStringWriter();
PrintWriter p = new PrintWriter(w);
p.print(e.getClass().getName());
if (e.getMessage() != null) {
p.print(": " + e.getMessage());
}
p.println();
try {
e.printStackTrace(p);
return w.toString();
} finally {
p.close();
}
}
public static String camelToSplitName(String camelName, String split) {
if (isEmpty(camelName)) {
return camelName;
}
StringBuilder buf = null;
for (int i = 0; i < camelName.length(); i++) {
char ch = camelName.charAt(i);
if (ch >= 'A' && ch <= 'Z') {
if (buf == null) {
buf = new StringBuilder();
if (i > 0) {
buf.append(camelName, 0, i);
}
}
if (i > 0) {
buf.append(split);
}
buf.append(Character.toLowerCase(ch));
} else if (buf != null) {
buf.append(ch);
}
}
return buf == null ? camelName : buf.toString();
}
// ------------------------------------------------------------------------ Trim
/**
* 除去字符串头尾部的空白如果字符串是<code>null</code>依然返回<code>null</code>
*
* <p>
* 注意<code>String.trim</code>不同此方法使用<code>NumberUtil.isBlankChar</code> 来判定空白 因而可以除去英文字符集之外的其它空白如中文空格
*
* <pre>
* trim(null) = null
* trim(&quot;&quot;) = &quot;&quot;
* trim(&quot; &quot;) = &quot;&quot;
* trim(&quot;abc&quot;) = &quot;abc&quot;
* trim(&quot; abc &quot;) = &quot;abc&quot;
* </pre>
*
* @param str 要处理的字符串
* @return 除去头尾空白的字符串如果原字串为<code>null</code>则返回<code>null</code>
*/
public static String trim(CharSequence str) {
return (null == str) ? null : trim(str, 0);
}
/**
* 除去字符串头尾部的空白符如果字符串是<code>null</code>依然返回<code>null</code>
*
* @param str 要处理的字符串
* @param mode <code>-1</code>表示trimStart<code>0</code>表示trim全部 <code>1</code>表示trimEnd
* @return 除去指定字符后的的字符串如果原字串为<code>null</code>则返回<code>null</code>
*/
public static String trim(CharSequence str, int mode) {
if (str == null) {
return null;
}
int length = str.length();
int start = 0;
int end = length;
// 扫描字符串头部
if (mode <= 0) {
while ((start < end) && (CharUtils.isBlankChar(str.charAt(start)))) {
start++;
}
}
// 扫描字符串尾部
if (mode >= 0) {
while ((start < end) && (CharUtils.isBlankChar(str.charAt(end - 1)))) {
end--;
}
}
if ((start > 0) || (end < length)) {
return str.toString().substring(start, end);
}
return str.toString();
}
/**
* <p>Removes control characters (char &lt;= 32) from both
* ends of this String returning <code>null</code> if the String is
* empty ("") after the trim or if it is <code>null</code>.
*
* <p>The String is trimmed using {@link String#trim()}.
* Trim removes start and end characters &lt;= 32.
* To strip whitespace use {@link #stripToNull(String)}.</p>
*
* <pre>
* StringUtils.trimToNull(null) = null
* StringUtils.trimToNull("") = null
* StringUtils.trimToNull(" ") = null
* StringUtils.trimToNull("abc") = "abc"
* StringUtils.trimToNull(" abc ") = "abc"
* </pre>
*
* @param str the String to be trimmed, may be null
* @return the trimmed String,
* <code>null</code> if only chars &lt;= 32, empty or null String input
* @since 2.0
*/
public static String trimToNull(String str) {
String ts = trim(str);
return isEmpty(ts) ? null : ts;
}
/**
* <p>Removes control characters (char &lt;= 32) from both
* ends of this String returning an empty String ("") if the String
* is empty ("") after the trim or if it is <code>null</code>.
*
* <p>The String is trimmed using {@link String#trim()}.
* Trim removes start and end characters &lt;= 32.
* To strip whitespace use {@link #stripToEmpty(String)}.</p>
*
* <pre>
* StringUtils.trimToEmpty(null) = ""
* StringUtils.trimToEmpty("") = ""
* StringUtils.trimToEmpty(" ") = ""
* StringUtils.trimToEmpty("abc") = "abc"
* StringUtils.trimToEmpty(" abc ") = "abc"
* </pre>
*
* @param str the String to be trimmed, may be null
* @return the trimmed String, or an empty String if <code>null</code> input
* @since 2.0
*/
public static String trimToEmpty(String str) {
return str == null ? EMPTY : str.trim();
}
// Stripping
//-----------------------------------------------------------------------
/**
* <p>Strips whitespace from the start and end of a String.</p>
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
*
* <p>A <code>null</code> input String returns <code>null</code>.</p>
*
* <pre>
* StringUtils.strip(null) = null
* StringUtils.strip("") = ""
* StringUtils.strip(" ") = ""
* StringUtils.strip("abc") = "abc"
* StringUtils.strip(" abc") = "abc"
* StringUtils.strip("abc ") = "abc"
* StringUtils.strip(" abc ") = "abc"
* StringUtils.strip(" ab c ") = "ab c"
* </pre>
*
* @param str the String to remove whitespace from, may be null
* @return the stripped String, <code>null</code> if null String input
*/
public static String strip(String str) {
return strip(str, null);
}
/**
* <p>Strips whitespace from the start and end of a String returning
* <code>null</code> if the String is empty ("") after the strip.</p>
*
* <p>This is similar to {@link #trimToNull(String)} but removes whitespace.
* Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
*
* <pre>
* StringUtils.stripToNull(null) = null
* StringUtils.stripToNull("") = null
* StringUtils.stripToNull(" ") = null
* StringUtils.stripToNull("abc") = "abc"
* StringUtils.stripToNull(" abc") = "abc"
* StringUtils.stripToNull("abc ") = "abc"
* StringUtils.stripToNull(" abc ") = "abc"
* StringUtils.stripToNull(" ab c ") = "ab c"
* </pre>
*
* @param str the String to be stripped, may be null
* @return the stripped String,
* <code>null</code> if whitespace, empty or null String input
* @since 2.0
*/
public static String stripToNull(String str) {
if (str == null) {
return null;
}
str = strip(str, null);
return str.length() == 0 ? null : str;
}
/**
* <p>Strips whitespace from the start and end of a String returning
* an empty String if <code>null</code> input.</p>
*
* <p>This is similar to {@link #trimToEmpty(String)} but removes whitespace.
* Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
*
* <pre>
* StringUtils.stripToEmpty(null) = ""
* StringUtils.stripToEmpty("") = ""
* StringUtils.stripToEmpty(" ") = ""
* StringUtils.stripToEmpty("abc") = "abc"
* StringUtils.stripToEmpty(" abc") = "abc"
* StringUtils.stripToEmpty("abc ") = "abc"
* StringUtils.stripToEmpty(" abc ") = "abc"
* StringUtils.stripToEmpty(" ab c ") = "ab c"
* </pre>
*
* @param str the String to be stripped, may be null
* @return the trimmed String, or an empty String if <code>null</code> input
* @since 2.0
*/
public static String stripToEmpty(String str) {
return str == null ? EMPTY : strip(str, null);
}
/**
* <p>Strips any of a set of characters from the start and end of a String.
* This is similar to {@link String#trim()} but allows the characters
* to be stripped to be controlled.</p>
*
* <p>A <code>null</code> input String returns <code>null</code>.
* An empty string ("") input returns the empty string.</p>
*
* <p>If the stripChars String is <code>null</code>, whitespace is
* stripped as defined by {@link Character#isWhitespace(char)}.
* Alternatively use {@link #strip(String)}.</p>
*
* <pre>
* StringUtils.strip(null, *) = null
* StringUtils.strip("", *) = ""
* StringUtils.strip("abc", null) = "abc"
* StringUtils.strip(" abc", null) = "abc"
* StringUtils.strip("abc ", null) = "abc"
* StringUtils.strip(" abc ", null) = "abc"
* StringUtils.strip(" abcyx", "xyz") = " abc"
* </pre>
*
* @param str the String to remove characters from, may be null
* @param stripChars the characters to remove, null treated as whitespace
* @return the stripped String, <code>null</code> if null String input
*/
public static String strip(String str, String stripChars) {
if (isEmpty(str)) {
return str;
}
str = stripStart(str, stripChars);
return stripEnd(str, stripChars);
}
/**
* <p>Strips any of a set of characters from the start of a String.</p>
*
* <p>A <code>null</code> input String returns <code>null</code>.
* An empty string ("") input returns the empty string.</p>
*
* <p>If the stripChars String is <code>null</code>, whitespace is
* stripped as defined by {@link Character#isWhitespace(char)}.</p>
*
* <pre>
* StringUtils.stripStart(null, *) = null
* StringUtils.stripStart("", *) = ""
* StringUtils.stripStart("abc", "") = "abc"
* StringUtils.stripStart("abc", null) = "abc"
* StringUtils.stripStart(" abc", null) = "abc"
* StringUtils.stripStart("abc ", null) = "abc "
* StringUtils.stripStart(" abc ", null) = "abc "
* StringUtils.stripStart("yxabc ", "xyz") = "abc "
* </pre>
*
* @param str the String to remove characters from, may be null
* @param stripChars the characters to remove, null treated as whitespace
* @return the stripped String, <code>null</code> if null String input
*/
public static String stripStart(String str, String stripChars) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return str;
}
int start = 0;
if (stripChars == null) {
while ((start != strLen) && Character.isWhitespace(str.charAt(start))) {
start++;
}
} else if (stripChars.length() == 0) {
return str;
} else {
while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) {
start++;
}
}
return str.substring(start);
}
/**
* <p>Strips any of a set of characters from the end of a String.</p>
*
* <p>A <code>null</code> input String returns <code>null</code>.
* An empty string ("") input returns the empty string.</p>
*
* <p>If the stripChars String is <code>null</code>, whitespace is
* stripped as defined by {@link Character#isWhitespace(char)}.</p>
*
* <pre>
* StringUtils.stripEnd(null, *) = null
* StringUtils.stripEnd("", *) = ""
* StringUtils.stripEnd("abc", "") = "abc"
* StringUtils.stripEnd("abc", null) = "abc"
* StringUtils.stripEnd(" abc", null) = " abc"
* StringUtils.stripEnd("abc ", null) = "abc"
* StringUtils.stripEnd(" abc ", null) = " abc"
* StringUtils.stripEnd(" abcyx", "xyz") = " abc"
* StringUtils.stripEnd("120.00", ".0") = "12"
* </pre>
*
* @param str the String to remove characters from, may be null
* @param stripChars the set of characters to remove, null treated as whitespace
* @return the stripped String, <code>null</code> if null String input
*/
public static String stripEnd(String str, String stripChars) {
int end;
if (str == null || (end = str.length()) == 0) {
return str;
}
if (stripChars == null) {
while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
end--;
}
} else if (stripChars.length() == 0) {
return str;
} else {
while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) {
end--;
}
}
return str.substring(0, end);
}
// StripAll
//-----------------------------------------------------------------------
/**
* <p>Strips whitespace from the start and end of every String in an array.
* Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
*
* <p>A new array is returned each time, except for length zero.
* A <code>null</code> array will return <code>null</code>.
* An empty array will return itself.
* A <code>null</code> array entry will be ignored.</p>
*
* <pre>
* StringUtils.stripAll(null) = null
* StringUtils.stripAll([]) = []
* StringUtils.stripAll(["abc", " abc"]) = ["abc", "abc"]
* StringUtils.stripAll(["abc ", null]) = ["abc", null]
* </pre>
*
* @param strs the array to remove whitespace from, may be null
* @return the stripped Strings, <code>null</code> if null array input
*/
public static String[] stripAll(String[] strs) {
return stripAll(strs, null);
}
/**
* <p>Strips any of a set of characters from the start and end of every
* String in an array.</p>
* Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
*
* <p>A new array is returned each time, except for length zero.
* A <code>null</code> array will return <code>null</code>.
* An empty array will return itself.
* A <code>null</code> array entry will be ignored.
* A <code>null</code> stripChars will strip whitespace as defined by
* {@link Character#isWhitespace(char)}.</p>
*
* <pre>
* StringUtils.stripAll(null, *) = null
* StringUtils.stripAll([], *) = []
* StringUtils.stripAll(["abc", " abc"], null) = ["abc", "abc"]
* StringUtils.stripAll(["abc ", null], null) = ["abc", null]
* StringUtils.stripAll(["abc ", null], "yz") = ["abc ", null]
* StringUtils.stripAll(["yabcz", null], "yz") = ["abc", null]
* </pre>
*
* @param strs the array to remove characters from, may be null
* @param stripChars the characters to remove, null treated as whitespace
* @return the stripped Strings, <code>null</code> if null array input
*/
public static String[] stripAll(String[] strs, String stripChars) {
int strsLen;
if (strs == null || (strsLen = strs.length) == 0) {
return strs;
}
String[] newArr = new String[strsLen];
for (int i = 0; i < strsLen; i++) {
newArr[i] = strip(strs[i], stripChars);
}
return newArr;
}
/**
* 截取分隔字符串之前的字符串不包括分隔字符串<br>
* 如果给定的字符串为空串null或""或者分隔字符串为null返回原字符串<br>
* 如果分隔字符串未找到返回原字符串举例如下
*
* <pre>
* StrUtil.subBefore(null, *) = null
* StrUtil.subBefore("", *) = ""
* StrUtil.subBefore("abc", 'a') = ""
* StrUtil.subBefore("abcba", 'b') = "a"
* StrUtil.subBefore("abc", 'c') = "ab"
* StrUtil.subBefore("abc", 'd') = "abc"
* </pre>
*
* @param string 被查找的字符串
* @param separator 分隔字符串不包括
* @param isLastSeparator 是否查找最后一个分隔字符串多次出现分隔字符串时选取最后一个true为选取最后一个
* @return 切割后的字符串
*/
public static String subBefore(CharSequence string, char separator, boolean isLastSeparator) {
if (isEmpty(string)) {
return null == string ? null : string.toString();
}
final String str = string.toString();
final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
if (INDEX_NOT_FOUND == pos) {
return str;
}
if (0 == pos) {
return EMPTY;
}
return str.substring(0, pos);
}
public static boolean endsWithIgnoreCase(final CharSequence str, final CharSequence suffix) {
return endsWith(str, suffix, true);
}
/**
* 是否以指定字符串结尾<br>
* 如果给定的字符串和开头字符串都为null则返回true否则任意一个值为null返回false
*
* @param str 被监测字符串
* @param suffix 开头字符串
* @param ignoreCase 是否忽略大小写
* @return 是否以指定字符串结尾
*/
private static boolean endsWith(final CharSequence str, final CharSequence suffix, final boolean ignoreCase) {
if (null == str || null == suffix) {
return null == str && null == suffix;
}
if (ignoreCase) {
return str.toString().toLowerCase().endsWith(suffix.toString().toLowerCase());
} else {
return str.toString().endsWith(suffix.toString());
}
}
/**
* 是否以指定字符串开头忽略大小写
*
* @param str 被监测字符串
* @param prefix 开头字符串
* @return 是否以指定字符串开头
*/
public static boolean startWithIgnoreCase(CharSequence str, CharSequence prefix) {
return startWith(str, prefix, true);
}
/**
* 是否以指定字符串开头<br>
* 如果给定的字符串和开头字符串都为null则返回true否则任意一个值为null返回false
*
* @param str 被监测字符串
* @param prefix 开头字符串
* @param isIgnoreCase 是否忽略大小写
* @return 是否以指定字符串开头
*/
public static boolean startWith(CharSequence str, CharSequence prefix, boolean isIgnoreCase) {
if (null == str || null == prefix) {
return null == str && null == prefix;
}
if (isIgnoreCase) {
return str.toString().toLowerCase().startsWith(prefix.toString().toLowerCase());
} else {
return str.toString().startsWith(prefix.toString());
}
}
/**
* 去除字符串中指定的多个字符如有多个则全部去除
*
* @param str 字符串
* @param chars 字符列表
* @return 去除后的字符
*/
public static String removeAll(CharSequence str, char... chars) {
if (null == str || ArrayUtils.isEmpty(chars)) {
return str(str);
}
final int len = str.length();
if (0 == len) {
return str(str);
}
final StringBuilder builder = new StringBuilder(len);
char c;
for (int i = 0; i < len; i++) {
c = str.charAt(i);
if (false == ArrayUtils.contains(chars, c)) {
builder.append(c);
}
}
return builder.toString();
}
public static String padStart(String string, int minLength, char padChar) {
if (string == null) {
throw new UtilException("string is null");
}
if (string.length() >= minLength) {
return string;
}
StringBuilder sb = new StringBuilder(minLength);
for (int i = string.length(); i < minLength; i++) {
sb.append(padChar);
}
sb.append(string);
return sb.toString();
}
public static String padEnd(String string, int minLength, char padChar) {
if (string == null) {
throw new UtilException("string is null");
}
if (string.length() >= minLength) {
return string;
}
StringBuilder sb = new StringBuilder(minLength);
sb.append(string);
for (int i = string.length(); i < minLength; i++) {
sb.append(padChar);
}
return sb.toString();
}
public static String padMiddle(int maxLength, String start, String end, char padChar) {
int leftLength = start.length();
int rightLength = end.length();
int textLength = leftLength + rightLength;
if (maxLength <= textLength) {
return start + end;
}
int padLength = maxLength - rightLength;
return padEnd(start, padLength, padChar) + end;
}
public static String lenientFormat(String template, Object... args) {
template = String.valueOf(template); // null -> "null"
if (args == null) {
args = new Object[]{"(Object[])null"};
} else {
for (int i = 0; i < args.length; i++) {
args[i] = lenientToString(args[i]);
}
}
// start substituting the arguments into the '%s' placeholders
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
int templateStart = 0;
int i = 0;
while (i < args.length) {
int placeholderStart = template.indexOf("%s", templateStart);
if (placeholderStart == -1) {
break;
}
builder.append(template, templateStart, placeholderStart);
builder.append(args[i++]);
templateStart = placeholderStart + 2;
}
builder.append(template, templateStart, template.length());
// if we run out of placeholders, append the extra args in square braces
if (i < args.length) {
builder.append(" [");
builder.append(args[i++]);
while (i < args.length) {
builder.append(", ");
builder.append(args[i++]);
}
builder.append(']');
}
return builder.toString();
}
private static String lenientToString(Object o) {
try {
return String.valueOf(o);
} catch (Throwable e) {
// Default toString() behavior - see Object.toString()
String objectToString =
o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o));
return "<" + objectToString + " threw " + e.getClass().getName() + ">";
}
}
}

View File

@ -0,0 +1,296 @@
package cn.zyjblogs.starter.common.utils.type;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* Type的工具类封装
*
* @author lingyi
*/
public class TypeUtils {
public static List<Type> getGenericParamType(Class source, Class target) {
Type directTargetInterface = findDirectTargetInterface(source, target);
if (directTargetInterface != null) {
return findActualTypeArguments(directTargetInterface);
} else {
Map<String, Type> parentTypeArgumentMap = findParentTypeArgumentMap(source);
Type targetInterface = findTargetInterfaceRecursive(source, target);
List<Type> actualTypeArguments = findActualTypeArguments(targetInterface);
return actualTypeArguments.stream().map(type -> {
if (type instanceof TypeVariable) {
String name = ((TypeVariable) type).getName();
return parentTypeArgumentMap.get(name);
}
return type;
}).collect(Collectors.toList());
}
}
/**
* 获取直接继承目标接口, 此接口是泛型接口
*
* @param source
* @param target
* @return
*/
public static ParameterizedType findDirectTargetInterface(Class source, Class target) {
// 类实现的接口(直接实现), 返回类型包含了泛型信息
Type[] genericInterfaces = source.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) genericInterface).getRawType();
// 是否是目标接口
if (rawType.equals(target)) {
return (ParameterizedType) genericInterface;
}
}
}
return null;
}
public static ParameterizedType findTargetInterfaceRecursive(Class source, Class target) {
while (source != null && !source.equals(Object.class)) {
ParameterizedType directTargetInterface = findDirectTargetInterface(source, target);
if (directTargetInterface != null) {
return directTargetInterface;
}
source = source.getSuperclass();
}
return null;
}
/**
* <pre>
* 获取泛型的实际类型
* 如果泛型是 List<D> 这种, 继续找到D
* Type 可能是实际类型, 也可能是TypeVariableImpl
* </pre>
*
* @param type
* @return
*/
public static List<Type> findActualTypeArguments(Type type) {
List<Type> list = new ArrayList();
if (type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
if (actualTypeArgument instanceof ParameterizedType) {
list.add(findActualTypeArgumentIfRawTypeIfList(actualTypeArgument));
} else {
list.add(actualTypeArgument);
}
}
}
return list;
}
public static Type findActualTypeArgumentIfRawTypeIfList(Type type) {
// 如果是 ParameterizedType, 说明泛型参数是有嵌套的
if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
// 获取本层的泛型, 判断是否List, 是则递归
Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
if (List.class.isAssignableFrom((Class<?>) rawType)) {
return findActualTypeArgumentIfRawTypeIfList(actualTypeArgument);
} else {
return rawType;
}
} else {
return type;
}
}
/**
* @param source
* @return {{"T", ParameterizedType}}
*/
public static Map<String, Type> findParentTypeArgumentMap(Class source) {
Map<String, Type> map = new HashMap(1 << 3);
while (source != null && !source.equals(Object.class)) {
// 返回直接父类, 会带上泛型参数
Type genericSuperclass = source.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
TypeVariable[] typeParameters = source.getSuperclass().getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
Type actualTypeArgument = actualTypeArguments[i];
TypeVariable typeParameter = typeParameters[i];
Type actualTypeArgumentIfRawType = findActualTypeArgumentIfRawTypeIfList(actualTypeArgument);
// 不包含并且类型是Class类
if (!map.containsKey(typeParameter.getName()) && actualTypeArgumentIfRawType instanceof Class) {
map.put(typeParameter.getName(), actualTypeArgumentIfRawType);
}
}
}
source = source.getSuperclass();
}
return map;
}
/**
* 获取所有的字段包括父类
*
* @param clazz
* @return
*/
public static List<Field> getDeclaredFields(Class clazz) {
if (clazz == null) {
throw new IllegalArgumentException("clazz param can not be null");
}
List<Class> classes = new ArrayList<>();
while (clazz != null && clazz != Object.class) {
classes.add(clazz);
clazz = clazz.getSuperclass();
}
List<Field> fields = new ArrayList<>();
for (int i = classes.size() - 1; i >= 0; i--) {
final Class aClass = classes.get(i);
fields.addAll(new ArrayList<>(Arrays.asList(aClass.getDeclaredFields())));
}
return fields;
}
public static Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
Set<Class<?>> classes = new HashSet<>();
classes.add(concreteClass);
while (concreteClass != null && !concreteClass.equals(Object.class)) {
Class<?>[] interfaces = concreteClass.getInterfaces();
if (interfaces != null) {
for (Class<?> anInterface : interfaces) {
classes.addAll(flattenHierarchy(anInterface));
}
}
concreteClass = concreteClass.getSuperclass();
if (concreteClass != null) {
classes.add(concreteClass);
}
}
return classes;
}
// ----------------------------------------------------------------------------------- Type Argument
/**
* 是否未知类型<br>
* type为null或者{@link TypeVariable} 都视为未知类型
*
* @param type Type类型
* @return 是否未知类型
*/
public static boolean isUnknow(Type type) {
return null == type || type instanceof TypeVariable;
}
/**
* 获得给定类的第一个泛型参数
*
* @param type 被检查的类型必须是已经确定泛型类型的类型
* @return {@link Type}可能为{@code null}
*/
public static Type getTypeArgument(Type type) {
return getTypeArgument(type, 0);
}
/**
* 获得给定类的泛型参数
*
* @param type 被检查的类型必须是已经确定泛型类型的类
* @param index 泛型类型的索引号即第几个泛型类型
* @return {@link Type}
*/
public static Type getTypeArgument(Type type, int index) {
final Type[] typeArguments = getTypeArguments(type);
if (null != typeArguments && typeArguments.length > index) {
return typeArguments[index];
}
return null;
}
/**
* 获得指定类型中所有泛型参数类型例如
*
* <pre>
* class A&lt;T&gt;
* class B extends A&lt;String&gt;
* </pre>
* <p>
* 通过此方法传入B.class即可得到String
*
* @param type 指定类型
* @return 所有泛型参数类型
*/
public static Type[] getTypeArguments(Type type) {
if (null == type) {
return null;
}
final ParameterizedType parameterizedType = toParameterizedType(type);
return (null == parameterizedType) ? null : parameterizedType.getActualTypeArguments();
}
/**
* 获得Type对应的原始类
*
* @param type {@link Type}
* @return 原始类如果无法获取原始类返回{@code null}
*/
public static Class<?> getClass(Type type) {
if (null != type) {
if (type instanceof Class) {
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) type).getRawType();
} else if (type instanceof TypeVariable) {
return (Class<?>) ((TypeVariable<?>) type).getBounds()[0];
} else if (type instanceof WildcardType) {
final Type[] upperBounds = ((WildcardType) type).getUpperBounds();
if (upperBounds.length == 1) {
return getClass(upperBounds[0]);
}
}
}
return null;
}
/**
* {@link Type} 转换为{@link ParameterizedType}<br>
* {@link ParameterizedType}用于获取当前类或父类中泛型参数化后的类型<br>
* 一般用于获取泛型参数具体的参数类型例如
*
* <pre>
* class A&lt;T&gt;
* class B extends A&lt;String&gt;
* </pre>
* <p>
* 通过此方法传入B.class即可得到B{@link ParameterizedType}从而获取到String
*
* @param type {@link Type}
* @return {@link ParameterizedType}
*/
public static ParameterizedType toParameterizedType(Type type) {
ParameterizedType result = null;
if (type instanceof ParameterizedType) {
result = (ParameterizedType) type;
} else if (type instanceof Class) {
final Class<?> clazz = (Class<?>) type;
Type genericSuper = clazz.getGenericSuperclass();
if (null == genericSuper || Object.class.equals(genericSuper)) {
// 如果类没有父类而是实现一些定义好的泛型接口则取接口的Type
final Type[] genericInterfaces = clazz.getGenericInterfaces();
if (ArrayUtils.isNotEmpty(genericInterfaces)) {
// 默认取第一个实现接口的泛型Type
genericSuper = genericInterfaces[0];
}
}
result = toParameterizedType(genericSuper);
}
return result;
}
}