优化代码
This commit is contained in:
parent
3d2ad88116
commit
9a37a14157
1
.gitignore
vendored
1
.gitignore
vendored
@ -70,7 +70,6 @@ gen
|
||||
*.rar
|
||||
hs_err_pid*
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
|
1
server/zyjblogs-gateway/.gitignore
vendored
1
server/zyjblogs-gateway/.gitignore
vendored
@ -70,7 +70,6 @@ gen
|
||||
*.rar
|
||||
hs_err_pid*
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
|
39
server/zyjblogs-gateway/bin/assembly.xml
Normal file
39
server/zyjblogs-gateway/bin/assembly.xml
Normal 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>
|
1
server/zyjblogs-oauth/.gitignore
vendored
1
server/zyjblogs-oauth/.gitignore
vendored
@ -70,7 +70,6 @@ gen
|
||||
*.rar
|
||||
hs_err_pid*
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
|
39
server/zyjblogs-oauth/bin/assembly.xml
Normal file
39
server/zyjblogs-oauth/bin/assembly.xml
Normal 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>
|
1
server/zyjblogs-rbac/.gitignore
vendored
1
server/zyjblogs-rbac/.gitignore
vendored
@ -70,7 +70,6 @@ gen
|
||||
*.rar
|
||||
hs_err_pid*
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
|
39
server/zyjblogs-rbac/bin/assembly.xml
Normal file
39
server/zyjblogs-rbac/bin/assembly.xml
Normal 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>
|
@ -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>
|
||||
|
@ -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 ? e==null : 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 ? e2==null : 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 ? e==null : 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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.zyjblogs.starter.common.function;
|
||||
|
||||
/**
|
||||
* 过滤器接口
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Filter<T> {
|
||||
/**
|
||||
* 是否接受对象
|
||||
*
|
||||
* @param t 检查的对象
|
||||
* @return 是否接受对象
|
||||
*/
|
||||
boolean accept(T t);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)地址、非局域网地址(siteLocal)、IPv4地址
|
||||
* 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.255、172.16.0.0 ~ 172.31.255.255、192.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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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归到0,to归到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("") = ""
|
||||
* trim(" ") = ""
|
||||
* trim("abc") = "abc"
|
||||
* trim(" abc ") = "abc"
|
||||
* </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 <= 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 <= 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 <= 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 <= 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 <= 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() + ">";
|
||||
}
|
||||
}
|
||||
}
|
@ -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<T>
|
||||
* class B extends A<String>
|
||||
* </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<T>
|
||||
* class B extends A<String>
|
||||
* </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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user