private int mur(int a, int h) {
// Helper from Murmur3 for combining two 32-bit values.
a *= c1;
- a = rotate32(a, 17);
+ a = Integer.rotateRight(a, 17);
a *= c2;
h ^= a;
- h = rotate32(h, 19);
+ h = Integer.rotateRight(h, 19);
return h * 5 + 0xe6546b64;
}
private static Number128 weakHashLen32WithSeeds(
final long w, final long x, final long y, final long z, long a, long b) {
a += w;
- b = rotate64(b + a + z, 21);
+ b = Long.rotateRight(b + a + z, 21);
final long c = a;
a += x;
a += y;
- b += rotate64(a, 44);
+ b += Long.rotateRight(a, 44);
return new Number128(a + z, b + c);
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/HashUtil.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/HashUtil.java
index a54873d8d..ea2e9e8a3 100755
--- a/hutool-core/src/main/java/cn/hutool/core/codec/hash/HashUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/HashUtil.java
@@ -1,6 +1,8 @@
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Number128;
+import cn.hutool.core.codec.hash.metro.MetroHash128;
+import cn.hutool.core.codec.hash.metro.MetroHash64;
/**
* Hash算法大全
@@ -554,17 +556,7 @@ public class HashUtil {
* @return hash值
*/
public static long metroHash64(final byte[] data, final long seed) {
- return MetroHash.INSTANCE.hash64(data, seed);
- }
-
- /**
- * MetroHash 算法64-bit实现
- *
- * @param data 数据
- * @return hash值
- */
- public static long metroHash64(final byte[] data) {
- return MetroHash.INSTANCE.hash64(data);
+ return MetroHash64.of(seed).hash64(data);
}
/**
@@ -575,17 +567,7 @@ public class HashUtil {
* @return hash值,long[0]:低位,long[1]:高位
*/
public static long[] metroHash128(final byte[] data, final long seed) {
- return MetroHash.INSTANCE.hash128(data, seed).getLongArray();
- }
-
- /**
- * MetroHash 算法128-bit实现
- *
- * @param data 数据
- * @return hash值,long[0]:低位,long[1]:高位
- */
- public static long[] metroHash128(final byte[] data) {
- return MetroHash.INSTANCE.hash128(data).getLongArray();
+ return MetroHash128.of(seed).hash128(data).getLongArray();
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/MetroHash.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/MetroHash.java
deleted file mode 100644
index 56a9388c7..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/codec/hash/MetroHash.java
+++ /dev/null
@@ -1,237 +0,0 @@
-package cn.hutool.core.codec.hash;
-
-import cn.hutool.core.codec.Number128;
-import cn.hutool.core.util.ByteUtil;
-
-import java.nio.ByteOrder;
-import java.util.Arrays;
-
-/**
- * Apache 发布的MetroHash算法,是一组用于非加密用例的最先进的哈希函数。
- * 除了卓越的性能外,他们还以算法生成而著称。
- *
- *
- * 官方实现:https://github.com/jandrewrogers/MetroHash
- * 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
- * Go语言实现:https://github.com/linvon/cuckoo-filter/blob/main/vendor/github.com/dgryski/go-metro/
- *
- * @author li
- */
-public class MetroHash implements Hash64, Hash128 {
- public static MetroHash INSTANCE = new MetroHash();
-
- @Override
- public Number encode(final byte[] bytes) {
- return hash64(bytes);
- }
-
- @Override
- public long hash64(final byte[] data) {
- return hash64(data, 1337);
- }
-
- /**
- * 计算64位Hash值
- *
- * @param data 数据
- * @param seed 种子
- * @return hash64
- */
- public long hash64(final byte[] data, final long seed) {
- final long k0_64 = 0xD6D018F5;
- final long k1_64 = 0xA2AA033B;
- final long k2_64 = 0x62992FC1;
- final long k3_64 = 0x30BC5B29;
-
- byte[] buffer = data;
- long hash = (seed + k2_64) * k0_64;
-
- long v0, v1, v2, v3;
- v0 = hash;
- v1 = hash;
- v2 = hash;
- v3 = hash;
-
- if (buffer.length >= 32) {
-
- while (buffer.length >= 32) {
- v0 += littleEndian64(buffer, 0) * k0_64;
- v0 = rotateLeft64(v0, -29) + v2;
- v1 += littleEndian64(buffer, 8) * k1_64;
- v1 = rotateLeft64(v1, -29) + v3;
- v2 += littleEndian64(buffer, 24) * k2_64;
- v2 = rotateLeft64(v2, -29) + v0;
- v3 += littleEndian64(buffer, 32) * k3_64;
- v3 = rotateLeft64(v3, -29) + v1;
- buffer = Arrays.copyOfRange(buffer, 32, buffer.length);
- }
-
- v2 ^= rotateLeft64(((v0 + v3) * k0_64) + v1, -37) * k1_64;
- v3 ^= rotateLeft64(((v1 + v2) * k1_64) + v0, -37) * k0_64;
- v0 ^= rotateLeft64(((v0 + v2) * k0_64) + v3, -37) * k1_64;
- v1 ^= rotateLeft64(((v1 + v3) * k1_64) + v2, -37) * k0_64;
- hash += v0 ^ v1;
- }
-
- if (buffer.length >= 16) {
- v0 = hash + littleEndian64(buffer, 0) * k2_64;
- v0 = rotateLeft64(v0, -29) * k3_64;
- v1 = hash + littleEndian64(buffer, 8) * k2_64;
- v1 = rotateLeft64(v1, -29) * k3_64;
- v0 ^= rotateLeft64(v0 * k0_64, -21) + v1;
- v1 ^= rotateLeft64(v1 * k3_64, -21) + v0;
- hash += v1;
- buffer = Arrays.copyOfRange(buffer, 16, buffer.length);
- }
-
- if (buffer.length >= 8) {
- hash += littleEndian64(buffer, 0) * k3_64;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- hash ^= rotateLeft64(hash, -55) * k1_64;
- }
-
- if (buffer.length >= 4) {
- hash += (long) littleEndian32(Arrays.copyOfRange(buffer, 0, 4)) * k3_64;
- hash ^= rotateLeft64(hash, -26) * k1_64;
- buffer = Arrays.copyOfRange(buffer, 4, buffer.length);
- }
-
- if (buffer.length >= 2) {
- hash += (long) littleEndian16(Arrays.copyOfRange(buffer, 0, 2)) * k3_64;
- buffer = Arrays.copyOfRange(buffer, 2, buffer.length);
- hash ^= rotateLeft64(hash, -48) * k1_64;
- }
-
- if (buffer.length >= 1) {
- hash += (long) buffer[0] * k3_64;
- hash ^= rotateLeft64(hash, -38) * k1_64;
- }
-
- hash ^= rotateLeft64(hash, -28);
- hash *= k0_64;
- hash ^= rotateLeft64(hash, -29);
-
- return hash;
- }
-
- @Override
- public Number128 hash128(final byte[] data) {
- return hash128(data, 1337);
- }
-
- /**
- * 计算128位hash值
- *
- * @param data 数据
- * @param seed 种子
- * @return hash128
- */
- public Number128 hash128(final byte[] data, final long seed) {
- final long k0_128 = 0xC83A91E1;
- final long k1_128 = 0x8648DBDB;
- final long k2_128 = 0x7BDEC03B;
- final long k3_128 = 0x2F5870A5;
-
- byte[] buffer = data;
-
- long v0, v1, v2, v3;
-
- v0 = (seed - k0_128) * k3_128;
- v1 = (seed + k1_128) * k2_128;
-
- if (buffer.length >= 32) {
- v2 = (seed + k0_128) * k2_128;
- v3 = (seed - k1_128) * k3_128;
-
- while (buffer.length >= 32) {
- v0 += littleEndian64(buffer, 0) * k0_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v0 = rotateRight(v0, 29) + v2;
- v1 += littleEndian64(buffer, 0) * k1_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v1 = rotateRight(v1, 29) + v3;
- v2 += littleEndian64(buffer, 0) * k2_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v2 = rotateRight(v2, 29) + v0;
- v3 = littleEndian64(buffer, 0) * k3_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v3 = rotateRight(v3, 29) + v1;
- }
-
- v2 ^= rotateRight(((v0 + v3) * k0_128) + v1, 21) * k1_128;
- v3 ^= rotateRight(((v1 + v2) * k1_128) + v0, 21) * k0_128;
- v0 ^= rotateRight(((v0 + v2) * k0_128) + v3, 21) * k1_128;
- v1 ^= rotateRight(((v1 + v3) * k1_128) + v2, 21) * k0_128;
- }
-
- if (buffer.length >= 16) {
- v0 += littleEndian64(buffer, 0) * k2_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v0 = rotateRight(v0, 33) * k3_128;
- v1 += littleEndian64(buffer, 0) * k2_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v1 = rotateRight(v1, 33) * k3_128;
- v0 ^= rotateRight((v0 * k2_128) + v1, 45) + k1_128;
- v1 ^= rotateRight((v1 * k3_128) + v0, 45) + k0_128;
- }
-
- if (buffer.length >= 8) {
- v0 += littleEndian64(buffer, 0) * k2_128;
- buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
- v0 = rotateRight(v0, 33) * k3_128;
- v0 ^= rotateRight((v0 * k2_128) + v1, 27) * k1_128;
- }
-
- if (buffer.length >= 4) {
- v1 += (long) littleEndian32(buffer) * k2_128;
- buffer = Arrays.copyOfRange(buffer, 4, buffer.length);
- v1 = rotateRight(v1, 33) * k3_128;
- v1 ^= rotateRight((v1 * k3_128) + v0, 46) * k0_128;
- }
-
- if (buffer.length >= 2) {
- v0 += (long) littleEndian16(buffer) * k2_128;
- buffer = Arrays.copyOfRange(buffer, 2, buffer.length);
- v0 = rotateRight(v0, 33) * k3_128;
- v0 ^= rotateRight((v0 * k2_128) * v1, 22) * k1_128;
- }
-
- if (buffer.length >= 1) {
- v1 += (long) buffer[0] * k2_128;
- v1 = rotateRight(v1, 33) * k3_128;
- v1 ^= rotateRight((v1 * k3_128) + v0, 58) * k0_128;
- }
-
- v0 += rotateRight((v0 * k0_128) + v1, 13);
- v1 += rotateRight((v1 * k1_128) + v0, 37);
- v0 += rotateRight((v0 * k2_128) + v1, 13);
- v1 += rotateRight((v1 * k3_128) + v0, 37);
-
- return new Number128(v0, v1);
- }
-
-
- // region =========== Private methods
- private static long littleEndian64(final byte[] b, final int start) {
- return ByteUtil.bytesToLong(b, start, ByteOrder.LITTLE_ENDIAN);
- }
-
- private static int littleEndian32(final byte[] b) {
- return (int) b[0] | (int) b[1] << 8 | (int) b[2] << 16 | (int) b[3] << 24;
- }
-
- private static int littleEndian16(final byte[] b) {
- return ByteUtil.bytesToShort(b, ByteOrder.LITTLE_ENDIAN);
- }
-
- private static long rotateLeft64(final long x, final int k) {
- final int n = 64;
- final int s = k & (n - 1);
- return x << s | x >> (n - s);
- }
-
- private static long rotateRight(final long val, final int shift) {
- return (val >> shift) | (val << (64 - shift));
- }
- // endregion =========== Private methods
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/MurmurHash.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/MurmurHash.java
index 0f3065973..e5d69616c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/codec/hash/MurmurHash.java
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/MurmurHash.java
@@ -21,6 +21,9 @@ import java.nio.charset.Charset;
* @since 4.3.3
*/
public class MurmurHash implements Hash32, Hash64, Hash128{
+ /**
+ * 单例
+ */
public static final MurmurHash INSTANCE = new MurmurHash();
// Constants for 32 bit variant
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/AbstractMetroHash.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/AbstractMetroHash.java
new file mode 100644
index 000000000..ac736d053
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/AbstractMetroHash.java
@@ -0,0 +1,71 @@
+package cn.hutool.core.codec.hash.metro;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Apache 发布的MetroHash算法抽象实现,是一组用于非加密用例的最先进的哈希函数。
+ * 除了卓越的性能外,他们还以算法生成而著称。
+ *
+ *
+ * 官方实现:https://github.com/jandrewrogers/MetroHash
+ * 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
+ * 来自:https://github.com/postamar/java-metrohash/
+ *
+ * @author Marius Posta
+ * @param 返回值类型,为this类型
+ */
+public abstract class AbstractMetroHash> implements MetroHash {
+
+ final long seed;
+ long v0, v1, v2, v3;
+ long nChunks;
+
+ /**
+ * 使用指定种子构造
+ *
+ * @param seed 种子
+ */
+ public AbstractMetroHash(final long seed) {
+ this.seed = seed;
+ reset();
+ }
+
+ @Override
+ public R apply(final ByteBuffer input) {
+ reset();
+ while (input.remaining() >= 32) {
+ partialApply32ByteChunk(input);
+ }
+ return partialApplyRemaining(input);
+ }
+
+ /**
+ * 从byteBuffer中计算32-byte块并更新hash状态
+ *
+ * @param partialInput byte buffer,至少有32byte的数据
+ * @return this
+ */
+ abstract R partialApply32ByteChunk(ByteBuffer partialInput);
+
+ /**
+ * 从byteBuffer中计算剩余bytes并更新hash状态
+ *
+ * @param partialInput byte buffer,少于32byte的数据
+ * @return this
+ */
+ abstract R partialApplyRemaining(ByteBuffer partialInput);
+
+ static long grab(final ByteBuffer bb, final int length) {
+ long result = bb.get() & 0xFFL;
+ for (int i = 1; i < length; i++) {
+ result |= (bb.get() & 0xFFL) << (i << 3);
+ }
+ return result;
+ }
+
+ static void writeLittleEndian(final long hash, final ByteBuffer output) {
+ for (int i = 0; i < 8; i++) {
+ output.put((byte) (hash >>> (i*8)));
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash.java
new file mode 100644
index 000000000..ba461a8a5
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash.java
@@ -0,0 +1,55 @@
+package cn.hutool.core.codec.hash.metro;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Apache 发布的MetroHash算法接口,是一组用于非加密用例的最先进的哈希函数。
+ * 除了卓越的性能外,他们还以算法生成而著称。
+ *
+ *
+ * 官方实现:https://github.com/jandrewrogers/MetroHash
+ * 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
+ * 来自:https://github.com/postamar/java-metrohash/
+ *
+ * @param 返回值类型,为this类型
+ * @author Marius Posta
+ */
+public interface MetroHash> {
+
+ /**
+ * 创建 {@code MetroHash}对象
+ *
+ * @param seed 种子
+ * @param is128 是否128位
+ * @return {@code MetroHash}对象
+ */
+ static MetroHash> of(final long seed, final boolean is128) {
+ return is128 ? new MetroHash128(seed) : new MetroHash64(seed);
+ }
+
+ /**
+ * 将给定的{@link ByteBuffer}中的数据追加计算hash值
+ * 此方法会更新hash值状态
+ *
+ * @param input 内容
+ * @return this
+ */
+ R apply(final ByteBuffer input);
+
+ /**
+ * 将结果hash值写出到{@link ByteBuffer}中,可选端序
+ *
+ * @param output 输出
+ * @param byteOrder 端序
+ * @return this
+ */
+ R write(ByteBuffer output, final ByteOrder byteOrder);
+
+ /**
+ * 重置,重置后可复用对象开启新的计算
+ *
+ * @return this
+ */
+ R reset();
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash128.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash128.java
new file mode 100644
index 000000000..045bd4d5a
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash128.java
@@ -0,0 +1,164 @@
+package cn.hutool.core.codec.hash.metro;
+
+import cn.hutool.core.codec.Number128;
+import cn.hutool.core.codec.hash.Hash128;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Apache 发布的MetroHash算法的128位实现,是一组用于非加密用例的最先进的哈希函数。
+ * 除了卓越的性能外,他们还以算法生成而著称。
+ *
+ *
+ * 官方实现:https://github.com/jandrewrogers/MetroHash
+ * 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
+ * 来自:https://github.com/postamar/java-metrohash/
+ * @author Marius Posta
+ */
+public class MetroHash128 extends AbstractMetroHash implements Hash128 {
+
+ /**
+ * 创建 {@code MetroHash128}对象
+ *
+ * @param seed 种子
+ * @return {@code MetroHash128}对象
+ */
+ public static MetroHash128 of(final long seed) {
+ return new MetroHash128(seed);
+ }
+
+ private static final long K0 = 0xC83A91E1L;
+ private static final long K1 = 0x8648DBDBL;
+ private static final long K2 = 0x7BDEC03BL;
+ private static final long K3 = 0x2F5870A5L;
+
+ /**
+ * 使用指定种子构造
+ *
+ * @param seed 种子
+ */
+ public MetroHash128(final long seed) {
+ super(seed);
+ }
+
+ @Override
+ public MetroHash128 reset() {
+ v0 = (seed - K0) * K3;
+ v1 = (seed + K1) * K2;
+ v2 = (seed + K0) * K2;
+ v3 = (seed - K1) * K3;
+ nChunks = 0;
+ return this;
+ }
+
+ /**
+ * 获取结果hash值
+ *
+ * @return hash值
+ */
+ public Number128 get() {
+ return new Number128(v1, v0);
+ }
+
+ @Override
+ public Number128 hash128(final byte[] bytes) {
+ return apply(ByteBuffer.wrap(bytes)).get();
+ }
+
+ @Override
+ public MetroHash128 write(final ByteBuffer output, final ByteOrder byteOrder) {
+ if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
+ writeLittleEndian(v0, output);
+ writeLittleEndian(v1, output);
+ } else {
+ output.asLongBuffer().put(v1).put(v0);
+ }
+ return this;
+ }
+
+ @Override
+ MetroHash128 partialApply32ByteChunk(final ByteBuffer partialInput) {
+ assert partialInput.remaining() >= 32;
+ v0 += grab(partialInput, 8) * K0;
+ v0 = Long.rotateRight(v0, 29) + v2;
+ v1 += grab(partialInput, 8) * K1;
+ v1 = Long.rotateRight(v1, 29) + v3;
+ v2 += grab(partialInput, 8) * K2;
+ v2 = Long.rotateRight(v2, 29) + v0;
+ v3 += grab(partialInput, 8) * K3;
+ v3 = Long.rotateRight(v3, 29) + v1;
+ ++nChunks;
+ return this;
+ }
+
+ @Override
+ MetroHash128 partialApplyRemaining(final ByteBuffer partialInput) {
+ assert partialInput.remaining() < 32;
+ if (nChunks > 0) {
+ metroHash128_32();
+ }
+ if (partialInput.remaining() >= 16) {
+ metroHash128_16(partialInput);
+ }
+ if (partialInput.remaining() >= 8) {
+ metroHash128_8(partialInput);
+ }
+ if (partialInput.remaining() >= 4) {
+ metroHash128_4(partialInput);
+ }
+ if (partialInput.remaining() >= 2) {
+ metroHash128_2(partialInput);
+ }
+ if (partialInput.remaining() >= 1) {
+ metroHash128_1(partialInput);
+ }
+ v0 += Long.rotateRight(v0 * K0 + v1, 13);
+ v1 += Long.rotateRight(v1 * K1 + v0, 37);
+ v0 += Long.rotateRight(v0 * K2 + v1, 13);
+ v1 += Long.rotateRight(v1 * K3 + v0, 37);
+ return this;
+ }
+
+ // region ----- private methods
+ private void metroHash128_32() {
+ v2 ^= Long.rotateRight((v0 + v3) * K0 + v1, 21) * K1;
+ v3 ^= Long.rotateRight((v1 + v2) * K1 + v0, 21) * K0;
+ v0 ^= Long.rotateRight((v0 + v2) * K0 + v3, 21) * K1;
+ v1 ^= Long.rotateRight((v1 + v3) * K1 + v2, 21) * K0;
+ }
+
+ private void metroHash128_16(final ByteBuffer bb) {
+ v0 += grab(bb, 8) * K2;
+ v0 = Long.rotateRight(v0, 33) * K3;
+ v1 += grab(bb, 8) * K2;
+ v1 = Long.rotateRight(v1, 33) * K3;
+ v0 ^= Long.rotateRight(v0 * K2 + v1, 45) * K1;
+ v1 ^= Long.rotateRight(v1 * K3 + v0, 45) * K0;
+ }
+
+ private void metroHash128_8(final ByteBuffer bb) {
+ v0 += grab(bb, 8) * K2;
+ v0 = Long.rotateRight(v0, 33) * K3;
+ v0 ^= Long.rotateRight(v0 * K2 + v1, 27) * K1;
+ }
+
+ private void metroHash128_4(final ByteBuffer bb) {
+ v1 += grab(bb, 4) * K2;
+ v1 = Long.rotateRight(v1, 33) * K3;
+ v1 ^= Long.rotateRight(v1 * K3 + v0, 46) * K0;
+ }
+
+ private void metroHash128_2(final ByteBuffer bb) {
+ v0 += grab(bb, 2) * K2;
+ v0 = Long.rotateRight(v0, 33) * K3;
+ v0 ^= Long.rotateRight(v0 * K2 + v1, 22) * K1;
+ }
+
+ private void metroHash128_1(final ByteBuffer bb) {
+ v1 += grab(bb, 1) * K2;
+ v1 = Long.rotateRight(v1, 33) * K3;
+ v1 ^= Long.rotateRight(v1 * K3 + v0, 58) * K0;
+ }
+ // endregion
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash64.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash64.java
new file mode 100644
index 000000000..9a2e38703
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/MetroHash64.java
@@ -0,0 +1,158 @@
+package cn.hutool.core.codec.hash.metro;
+
+import cn.hutool.core.codec.hash.Hash64;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Apache 发布的MetroHash算法的64位实现,是一组用于非加密用例的最先进的哈希函数。
+ * 除了卓越的性能外,他们还以算法生成而著称。
+ *
+ *
+ * 官方实现:https://github.com/jandrewrogers/MetroHash
+ * 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
+ * 来自:https://github.com/postamar/java-metrohash/
+ * @author Marius Posta
+ */
+public class MetroHash64 extends AbstractMetroHash implements Hash64 {
+
+ /**
+ * 创建 {@code MetroHash64}对象
+ *
+ * @param seed 种子
+ * @return {@code MetroHash64}对象
+ */
+ public static MetroHash64 of(final long seed) {
+ return new MetroHash64(seed);
+ }
+
+ private static final long K0 = 0xD6D018F5L;
+ private static final long K1 = 0xA2AA033BL;
+ private static final long K2 = 0x62992FC1L;
+ private static final long K3 = 0x30BC5B29L;
+
+ private long hash;
+
+ /**
+ * 使用指定种子构造
+ *
+ * @param seed 种子
+ */
+ public MetroHash64(final long seed) {
+ super(seed);
+ }
+
+ @Override
+ public MetroHash64 reset() {
+ v0 = v1 = v2 = v3 = hash = (seed + K2) * K0;
+ nChunks = 0;
+ return this;
+ }
+
+ /**
+ * 获取计算结果hash值
+ *
+ * @return hash值
+ */
+ public long get() {
+ return hash;
+ }
+
+ @Override
+ public long hash64(final byte[] bytes) {
+ return apply(ByteBuffer.wrap(bytes)).get();
+ }
+
+ @Override
+ public MetroHash64 write(final ByteBuffer output, final ByteOrder byteOrder) {
+ if(ByteOrder.LITTLE_ENDIAN == byteOrder){
+ writeLittleEndian(hash, output);
+ } else{
+ output.asLongBuffer().put(hash);
+ }
+ return this;
+ }
+
+ @Override
+ MetroHash64 partialApply32ByteChunk(final ByteBuffer partialInput) {
+ assert partialInput.remaining() >= 32;
+ v0 += grab(partialInput, 8) * K0;
+ v0 = Long.rotateRight(v0, 29) + v2;
+ v1 += grab(partialInput, 8) * K1;
+ v1 = Long.rotateRight(v1, 29) + v3;
+ v2 += grab(partialInput, 8) * K2;
+ v2 = Long.rotateRight(v2, 29) + v0;
+ v3 += grab(partialInput, 8) * K3;
+ v3 = Long.rotateRight(v3, 29) + v1;
+ ++nChunks;
+ return this;
+ }
+
+ @Override
+ MetroHash64 partialApplyRemaining(final ByteBuffer partialInput) {
+ assert partialInput.remaining() < 32;
+ if (nChunks > 0) {
+ metroHash64_32();
+ }
+ if (partialInput.remaining() >= 16) {
+ metroHash64_16(partialInput);
+ }
+ if (partialInput.remaining() >= 8) {
+ metroHash64_8(partialInput);
+ }
+ if (partialInput.remaining() >= 4) {
+ metroHash64_4(partialInput);
+ }
+ if (partialInput.remaining() >= 2) {
+ metroHash64_2(partialInput);
+ }
+ if (partialInput.remaining() >= 1) {
+ metroHash64_1(partialInput);
+ }
+ hash ^= Long.rotateRight(hash, 28);
+ hash *= K0;
+ hash ^= Long.rotateRight(hash, 29);
+ return this;
+ }
+
+ // region ----- private methods
+ private void metroHash64_32() {
+ v2 ^= Long.rotateRight(((v0 + v3) * K0) + v1, 37) * K1;
+ v3 ^= Long.rotateRight(((v1 + v2) * K1) + v0, 37) * K0;
+ v0 ^= Long.rotateRight(((v0 + v2) * K0) + v3, 37) * K1;
+ v1 ^= Long.rotateRight(((v1 + v3) * K1) + v2, 37) * K0;
+ hash += v0 ^ v1;
+ }
+
+ private void metroHash64_16(final ByteBuffer bb) {
+ v0 = hash + grab(bb, 8) * K2;
+ v0 = Long.rotateRight(v0, 29) * K3;
+ v1 = hash + grab(bb, 8) * K2;
+ v1 = Long.rotateRight(v1, 29) * K3;
+ v0 ^= Long.rotateRight(v0 * K0, 21) + v1;
+ v1 ^= Long.rotateRight(v1 * K3, 21) + v0;
+ hash += v1;
+ }
+
+ private void metroHash64_8(final ByteBuffer bb) {
+ hash += grab(bb, 8) * K3;
+ hash ^= Long.rotateRight(hash, 55) * K1;
+ }
+
+ private void metroHash64_4(final ByteBuffer bb) {
+ hash += grab(bb, 4) * K3;
+ hash ^= Long.rotateRight(hash, 26) * K1;
+ }
+
+ private void metroHash64_2(final ByteBuffer bb) {
+ hash += grab(bb, 2) * K3;
+ hash ^= Long.rotateRight(hash, 48) * K1;
+ }
+
+ private void metroHash64_1(final ByteBuffer bb) {
+ hash += grab(bb, 1) * K3;
+ hash ^= Long.rotateRight(hash, 37) * K1;
+ }
+ // endregion
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/package-info.java b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/package-info.java
new file mode 100644
index 000000000..56377cadb
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/codec/hash/metro/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * MetroHash算法实现
+ *
+ * 参考:https://github.com/postamar/java-metrohash
+ *
+ * @author postamar, looly
+ */
+package cn.hutool.core.codec.hash.metro;
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
index 3ac2129fd..531378f2a 100755
--- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
@@ -363,7 +363,7 @@ public class CollUtil {
* 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
* 结果:[a, b, c],此结果中只保留了一个c
*
- * @param 集合元素类型
+ * @param 集合元素类型
* @param colls 集合列表
* @return 交集的集合,返回 {@link LinkedHashSet}
* @since 5.3.9
@@ -1735,21 +1735,7 @@ public class CollUtil {
list.sort(comparator);
}
- return page(pageNo, pageSize, list);
- }
-
- /**
- * 对指定List分页取值
- *
- * @param 集合元素类型
- * @param pageNo 页码,从0开始计数,0表示第一页
- * @param pageSize 每页的条目数
- * @param list 列表
- * @return 分页后的段落内容
- * @since 4.1.20
- */
- public static List page(final int pageNo, final int pageSize, final List list) {
- return ListUtil.page(pageNo, pageSize, list);
+ return ListUtil.page(list, pageNo, pageSize);
}
/**
@@ -2015,7 +2001,7 @@ public class CollUtil {
}
/**
- * 根据元素的指定字段名分组,非Bean都放在第一个分组中
+ * 根据元素的指定字段值分组,非Bean都放在第一个分组中
*
* @param 元素类型
* @param collection 集合
@@ -2023,8 +2009,24 @@ public class CollUtil {
* @return 分组列表
*/
public static List> groupByField(final Collection collection, final String fieldName) {
+ return groupByFunc(collection, t -> BeanUtil.getFieldValue(t, fieldName));
+ }
+
+ /**
+ * 根据元素的指定字段值分组,非Bean都放在第一个分组中
+ * 例如:{@code
+ * CollUtil.groupByFunc(list, TestBean::getAge)
+ * }
+ *
+ * @param 元素类型
+ * @param collection 集合
+ * @param getter getter方法引用
+ * @return 分组列表
+ * @since 6.0.0
+ */
+ public static List> groupByFunc(final Collection collection, final Function getter) {
return group(collection, new Hash32() {
- private final List fieldNameList = new ArrayList<>();
+ private final List hashValList = new ArrayList<>();
@Override
public int hash32(final T t) {
@@ -2032,14 +2034,13 @@ public class CollUtil {
// 非Bean放在同一子分组中
return 0;
}
- final Object value = FieldUtil.getFieldValue(t, fieldName);
- final int hash = fieldNameList.indexOf(value);
+ final Object value = getter.apply(t);
+ int hash = hashValList.indexOf(value);
if (hash < 0) {
- fieldNameList.add(value);
- return fieldNameList.size() - 1;
- } else {
- return hash;
+ hashValList.add(value);
+ hash = hashValList.size() - 1;
}
+ return hash;
}
});
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java
index be30e38d3..d05b98999 100755
--- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java
@@ -8,20 +8,11 @@ import cn.hutool.core.collection.partition.RandomAccessPartition;
import cn.hutool.core.comparator.PinyinComparator;
import cn.hutool.core.comparator.PropertyComparator;
import cn.hutool.core.lang.Assert;
+import cn.hutool.core.math.PageInfo;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.PageUtil;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.RandomAccess;
+import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -34,7 +25,7 @@ import java.util.function.Predicate;
public class ListUtil {
/**
- * 新建一个List
+ * 新建一个{@link ArrayList}
* 如果提供的初始化数组为空,新建默认初始长度的List
*
* @param 集合元素类型
@@ -178,7 +169,7 @@ public class ListUtil {
/**
* 数组转为一个不可变List
* 类似于Java9中的List.of
- * 不同于Arrays.asList,此方法的原数组修改时,并不会影响List。
+ * 不同于Arrays.asList,此方法不允许修改数组
*
* @param ts 对象
* @param 对象类型
@@ -254,42 +245,53 @@ public class ListUtil {
* 对指定List分页取值
*
* @param 集合元素类型
- * @param pageNo 页码,第一页的页码取决于{@link PageUtil#getFirstPageNo()},默认0
+ * @param pageNo 页码,第一页的页码,从0开始
* @param pageSize 每页的条目数
* @param list 列表
* @return 分页后的段落内容
* @since 4.1.20
*/
- public static List page(final int pageNo, final int pageSize, final List list) {
+ public static List page(final List list, final int pageNo, final int pageSize) {
+ if (CollUtil.isEmpty(list)) {
+ return new ArrayList<>(0);
+ }
+ return page(list, PageInfo.of(list.size(), pageSize)
+ .setFirstPageNo(0).setPageNo(pageNo));
+ }
+
+ /**
+ * 对指定List分页取值
+ *
+ * @param 集合元素类型
+ * @param pageInfo 分页信息
+ * @param list 列表
+ * @return 分页后的段落内容
+ * @since 4.1.20
+ */
+ public static List page(final List list, final PageInfo pageInfo) {
if (CollUtil.isEmpty(list)) {
return new ArrayList<>(0);
}
- final int resultSize = list.size();
- // 每页条目数大于总数直接返回所有
- if (resultSize <= pageSize) {
- if (pageNo < (PageUtil.getFirstPageNo() + 1)) {
+ final int total = list.size();
+ final int pageSize = pageInfo.getPageSize();
+ // 不满一页
+ if (total <= pageSize) {
+ if (pageInfo.isFirstPage()) {
+ // 页码为1,返回所有
return view(list);
} else {
// 越界直接返回空
return new ArrayList<>(0);
}
}
- // 相乘可能会导致越界 临时用long
- if (((long) (pageNo - PageUtil.getFirstPageNo()) * pageSize) > resultSize) {
+
+ if (pageInfo.getBeginIndex() > total) {
// 越界直接返回空
return new ArrayList<>(0);
}
- final int[] startEnd = PageUtil.transToStartEnd(pageNo, pageSize);
- if (startEnd[1] > resultSize) {
- startEnd[1] = resultSize;
- if (startEnd[0] > startEnd[1]) {
- return new ArrayList<>(0);
- }
- }
-
- return sub(list, startEnd[0], startEnd[1]);
+ return sub(list, pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude());
}
/**
@@ -307,16 +309,11 @@ public class ListUtil {
}
final int total = list.size();
- final int totalPage = PageUtil.totalPage(total, pageSize);
- for (int pageNo = PageUtil.getFirstPageNo(); pageNo < totalPage + PageUtil.getFirstPageNo(); pageNo++) {
- // 获取当前页在列表中对应的起止序号
- final int[] startEnd = PageUtil.transToStartEnd(pageNo, pageSize);
- if (startEnd[1] > total) {
- startEnd[1] = total;
- }
-
+ final PageInfo pageInfo = PageInfo.of(total, pageSize);
+ while(pageInfo.isValidPage()){
// 返回数据
- pageListConsumer.accept(sub(list, startEnd[0], startEnd[1]));
+ pageListConsumer.accept(sub(list, pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude()));
+ pageInfo.nextPage();
}
}
@@ -346,7 +343,7 @@ public class ListUtil {
if (CollUtil.isEmpty(list)) {
return list;
}
- if(null == c){
+ if (null == c) {
c = Comparator.nullsFirst((Comparator super T>) Comparator.naturalOrder());
}
list.sort(c);
@@ -386,6 +383,9 @@ public class ListUtil {
* @since 4.0.6
*/
public static List reverse(final List list) {
+ if (CollUtil.isEmpty(list)) {
+ return list;
+ }
Collections.reverse(list);
return list;
}
@@ -443,10 +443,10 @@ public class ListUtil {
/**
* 在指定位置设置元素。当index小于List的长度时,替换指定位置的值,否则追加{@code paddingElement}直到到达index后,设置值
*
- * @param 元素类型
- * @param list List列表
- * @param index 位置
- * @param element 新元素
+ * @param 元素类型
+ * @param list List列表
+ * @param index 位置
+ * @param element 新元素
* @param paddingElement 填充的值
* @return 原List
* @since 5.8.4
@@ -468,29 +468,29 @@ public class ListUtil {
/**
* 截取集合的部分
*
- * @param 集合元素类型
- * @param list 被截取的数组
- * @param start 开始位置(包含)
- * @param end 结束位置(不包含)
+ * @param 集合元素类型
+ * @param list 被截取的数组
+ * @param begionInclude 开始位置(包含)
+ * @param endExclude 结束位置(不包含)
* @return 截取后的数组,当开始位置超过最大时,返回空的List
*/
- public static List sub(final List list, final int start, final int end) {
- return sub(list, start, end, 1);
+ public static List sub(final List list, final int begionInclude, final int endExclude) {
+ return sub(list, begionInclude, endExclude, 1);
}
/**
* 截取集合的部分
* 此方法与{@link List#subList(int, int)} 不同在于子列表是新的副本,操作子列表不会影响原列表。
*
- * @param 集合元素类型
- * @param list 被截取的数组
- * @param start 开始位置(包含)
- * @param end 结束位置(不包含)
- * @param step 步进
+ * @param 集合元素类型
+ * @param list 被截取的数组
+ * @param begionInclude 开始位置(包含)
+ * @param endExclude 结束位置(不包含)
+ * @param step 步进
* @return 截取后的数组,当开始位置超过最大时,返回空的List
* @since 4.0.6
*/
- public static List sub(final List list, int start, int end, int step) {
+ public static List sub(final List list, int begionInclude, int endExclude, int step) {
if (list == null) {
return null;
}
@@ -500,25 +500,25 @@ public class ListUtil {
}
final int size = list.size();
- if (start < 0) {
- start += size;
+ if (begionInclude < 0) {
+ begionInclude += size;
}
- if (end < 0) {
- end += size;
+ if (endExclude < 0) {
+ endExclude += size;
}
- if (start == size) {
+ if (begionInclude == size) {
return new ArrayList<>(0);
}
- if (start > end) {
- final int tmp = start;
- start = end;
- end = tmp;
+ if (begionInclude > endExclude) {
+ final int tmp = begionInclude;
+ begionInclude = endExclude;
+ endExclude = tmp;
}
- if (end > size) {
- if (start >= size) {
+ if (endExclude > size) {
+ if (begionInclude >= size) {
return new ArrayList<>(0);
}
- end = size;
+ endExclude = size;
}
if (step < 1) {
@@ -526,7 +526,7 @@ public class ListUtil {
}
final List result = new ArrayList<>();
- for (int i = start; i < end; i += step) {
+ for (int i = begionInclude; i < endExclude; i += step) {
result.add(list.get(i));
}
return result;
@@ -716,8 +716,8 @@ public class ListUtil {
* 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。
* 类似js的splice 函数
*
- * @param 元素类型
- * @param list 列表
+ * @param 元素类型
+ * @param list 列表
* @param start 指定修改的开始位置(从 0 计数), 可以为负数, -1代表最后一个元素
* @param deleteCount 删除个数,必须是正整数
* @param items 放入的元素
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/iter/LineIter.java b/hutool-core/src/main/java/cn/hutool/core/collection/iter/LineIter.java
index c22c4cce7..9933dfb53 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/iter/LineIter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/iter/LineIter.java
@@ -95,6 +95,7 @@ public class LineIter extends ComputeIter implements IterableIter
@@ -18,6 +12,7 @@ import java.util.Objects;
*
* @author looly
* @since 3.0.7
+ * @param 被比较的对象
*/
public class ComparatorChain implements Chain, ComparatorChain>, Comparator, Serializable {
private static final long serialVersionUID = -2426725788913962429L;
@@ -38,11 +33,11 @@ public class ComparatorChain implements Chain, ComparatorChain<
//------------------------------------------------------------------------------------- Static method start
/**
- * 构建 {@link ComparatorChain}
+ * 构建 {@code ComparatorChain}
*
* @param 被比较对象类型
* @param comparator 比较器
- * @return {@link ComparatorChain}
+ * @return {@code ComparatorChain}
* @since 5.4.3
*/
public static ComparatorChain of(final Comparator comparator) {
@@ -50,12 +45,12 @@ public class ComparatorChain implements Chain, ComparatorChain<
}
/**
- * 构建 {@link ComparatorChain}
+ * 构建 {@code ComparatorChain}
*
* @param 被比较对象类型
* @param comparator 比较器
* @param reverse 是否反向
- * @return {@link ComparatorChain}
+ * @return {@code ComparatorChain}
* @since 5.4.3
*/
public static ComparatorChain of(final Comparator comparator, final boolean reverse) {
@@ -63,11 +58,11 @@ public class ComparatorChain implements Chain, ComparatorChain<
}
/**
- * 构建 {@link ComparatorChain}
+ * 构建 {@code ComparatorChain}
*
* @param 被比较对象类型
* @param comparators 比较器数组
- * @return {@link ComparatorChain}
+ * @return {@code ComparatorChain}
* @since 5.4.3
*/
@SafeVarargs
@@ -76,11 +71,11 @@ public class ComparatorChain implements Chain, ComparatorChain<
}
/**
- * 构建 {@link ComparatorChain}
+ * 构建 {@code ComparatorChain}
*
* @param 被比较对象类型
* @param comparators 比较器列表
- * @return {@link ComparatorChain}
+ * @return {@code ComparatorChain}
* @since 5.4.3
*/
public static ComparatorChain of(final List> comparators) {
@@ -88,12 +83,12 @@ public class ComparatorChain implements Chain, ComparatorChain<
}
/**
- * 构建 {@link ComparatorChain}
+ * 构建 {@code ComparatorChain}
*
* @param 被比较对象类型
* @param comparators 比较器列表
* @param bits {@link Comparator} 列表对应的排序boolean值,true表示正序,false反序
- * @return {@link ComparatorChain}
+ * @return {@code ComparatorChain}
* @since 5.4.3
*/
public static ComparatorChain of(final List> comparators, final BitSet bits) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerException.java b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerException.java
deleted file mode 100644
index 1455a4c7c..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerException.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.hutool.core.compiler;
-
-import cn.hutool.core.exceptions.ExceptionUtil;
-import cn.hutool.core.text.StrUtil;
-
-/**
- * 编译异常
- *
- * @author looly
- * @since 5.5.2
- */
-public class CompilerException extends RuntimeException {
- private static final long serialVersionUID = 1L;
-
- public CompilerException(final Throwable e) {
- super(ExceptionUtil.getMessage(e), e);
- }
-
- public CompilerException(final String message) {
- super(message);
- }
-
- public CompilerException(final String messageTemplate, final Object... params) {
- super(StrUtil.format(messageTemplate, params));
- }
-
- public CompilerException(final String message, final Throwable throwable) {
- super(message, throwable);
- }
-
- public CompilerException(final Throwable throwable, final String messageTemplate, final Object... params) {
- super(StrUtil.format(messageTemplate, params), throwable);
- }
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java
deleted file mode 100644
index 5e50df3d2..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package cn.hutool.core.compiler;
-
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.ToolProvider;
-
-/**
- * 源码编译工具类,主要封装{@link JavaCompiler} 相关功能
- *
- * @author looly
- * @since 5.5.2
- */
-public class CompilerUtil {
-
- /**
- * java 编译器
- */
- public static final JavaCompiler SYSTEM_COMPILER = ToolProvider.getSystemJavaCompiler();
-
- /**
- * 编译指定的源码文件
- *
- * @param sourceFiles 源码文件路径
- * @return 0表示成功,否则其他
- */
- public static boolean compile(final String... sourceFiles) {
- return 0 == SYSTEM_COMPILER.run(null, null, null, sourceFiles);
- }
-
- /**
- * 获取{@link StandardJavaFileManager}
- *
- * @return {@link StandardJavaFileManager}
- */
- public static StandardJavaFileManager getFileManager() {
- return getFileManager(null);
- }
-
- /**
- * 获取{@link StandardJavaFileManager}
- *
- * @param diagnosticListener 异常收集器
- * @return {@link StandardJavaFileManager}
- * @since 5.5.8
- */
- public static StandardJavaFileManager getFileManager(final DiagnosticListener super JavaFileObject> diagnosticListener) {
- return SYSTEM_COMPILER.getStandardFileManager(diagnosticListener, null, null);
- }
-
- /**
- * 新建编译任务
- *
- * @param fileManager {@link JavaFileManager},用于管理已经编译好的文件
- * @param diagnosticListener 诊断监听
- * @param options 选项,例如 -cpXXX等
- * @param compilationUnits 编译单元,即需要编译的对象
- * @return {@link JavaCompiler.CompilationTask}
- */
- public static JavaCompiler.CompilationTask getTask(
- final JavaFileManager fileManager,
- final DiagnosticListener super JavaFileObject> diagnosticListener,
- final Iterable options,
- final Iterable extends JavaFileObject> compilationUnits) {
- return SYSTEM_COMPILER.getTask(null, fileManager, diagnosticListener, options, null, compilationUnits);
- }
-
- /**
- * 获取{@link JavaSourceCompiler}
- *
- * @param parent 父{@link ClassLoader}
- * @return {@link JavaSourceCompiler}
- * @see JavaSourceCompiler#of(ClassLoader)
- */
- public static JavaSourceCompiler getCompiler(final ClassLoader parent) {
- return JavaSourceCompiler.of(parent);
- }
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/DiagnosticUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/DiagnosticUtil.java
deleted file mode 100644
index 07d93f221..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/DiagnosticUtil.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package cn.hutool.core.compiler;
-
-import javax.tools.DiagnosticCollector;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * 诊断工具类
- *
- * @author looly
- * @since 5.5.2
- */
-public class DiagnosticUtil {
-
- /**
- * 获取{@link DiagnosticCollector}收集到的诊断信息,以文本返回
- *
- * @param collector {@link DiagnosticCollector}
- * @return 诊断消息
- */
- public static String getMessages(final DiagnosticCollector> collector) {
- final List> diagnostics = collector.getDiagnostics();
- return diagnostics.stream().map(String::valueOf)
- .collect(Collectors.joining(System.lineSeparator()));
- }
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java
deleted file mode 100644
index 7c8f79314..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package cn.hutool.core.compiler;
-
-import cn.hutool.core.io.resource.FileObjectResource;
-import cn.hutool.core.classloader.ResourceClassLoader;
-import cn.hutool.core.classloader.ClassLoaderUtil;
-import cn.hutool.core.util.ObjUtil;
-
-import javax.tools.FileObject;
-import javax.tools.ForwardingJavaFileManager;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-import javax.tools.JavaFileObject.Kind;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Java 字节码文件对象管理器
- *
- *
- * 正常我们使用javac命令编译源码时会将class文件写入到磁盘中,但在运行时动态编译类不适合保存在磁盘中
- * 我们采取此对象来管理运行时动态编译类生成的字节码。
- *
- *
- * @author lzpeng
- * @since 5.5.2
- */
-class JavaClassFileManager extends ForwardingJavaFileManager {
-
- /**
- * 存储java字节码文件对象映射
- */
- private final Map classFileObjectMap = new HashMap<>();
-
- /**
- * 加载动态编译生成类的父类加载器
- */
- private final ClassLoader parent;
-
- /**
- * 构造
- *
- * @param parent 父类加载器
- * @param fileManager 字节码文件管理器
- */
- protected JavaClassFileManager(final ClassLoader parent, final JavaFileManager fileManager) {
- super(fileManager);
- this.parent = ObjUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader);
- }
-
- /**
- * 获得动态编译生成的类的类加载器
- *
- * @param location 源码位置
- * @return 动态编译生成的类的类加载器
- */
- @Override
- public ClassLoader getClassLoader(final Location location) {
- return new ResourceClassLoader<>(this.parent, this.classFileObjectMap);
- }
-
- /**
- * 获得Java字节码文件对象
- * 编译器编译源码时会将Java源码对象编译转为Java字节码对象
- *
- * @param location 源码位置
- * @param className 类名
- * @param kind 文件类型
- * @param sibling Java源码对象
- * @return Java字节码文件对象
- */
- @Override
- public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
- final JavaFileObject javaFileObject = new JavaClassFileObject(className);
- this.classFileObjectMap.put(className, new FileObjectResource(javaFileObject));
- return javaFileObject;
- }
-
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java
deleted file mode 100644
index 6db6d22f9..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package cn.hutool.core.compiler;
-
-
-import cn.hutool.core.util.CharUtil;
-import cn.hutool.core.net.url.URLUtil;
-
-import javax.tools.SimpleJavaFileObject;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Java 字节码文件对象,用于在内存中暂存class字节码,从而可以在ClassLoader中动态加载。
- *
- * @author lzpeng
- * @since 5.5.2
- */
-class JavaClassFileObject extends SimpleJavaFileObject {
-
- /**
- * 字节码输出流
- */
- private final ByteArrayOutputStream byteArrayOutputStream;
-
- /**
- * 构造
- *
- * @param className 编译后的class文件的类名
- * @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
- */
- protected JavaClassFileObject(final String className) {
- super(URLUtil.getStringURI(className.replace(CharUtil.DOT, CharUtil.SLASH) + Kind.CLASS.extension), Kind.CLASS);
- this.byteArrayOutputStream = new ByteArrayOutputStream();
- }
-
- /**
- * 获得字节码输入流
- * 编译器编辑源码后,我们将通过此输出流获得编译后的字节码,以便运行时加载类
- *
- * @return 字节码输入流
- * @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location)
- */
- @Override
- public InputStream openInputStream() {
- return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
- }
-
- /**
- * 获得字节码输出流
- * 编译器编辑源码时,会将编译结果输出到本输出流中
- *
- * @return 字节码输出流
- */
- @Override
- public OutputStream openOutputStream() {
- return this.byteArrayOutputStream;
- }
-
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaFileObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaFileObjectUtil.java
deleted file mode 100644
index 7e06b3011..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaFileObjectUtil.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package cn.hutool.core.compiler;
-
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.file.FileNameUtil;
-import cn.hutool.core.compress.ZipUtil;
-import cn.hutool.core.io.resource.FileResource;
-import cn.hutool.core.io.resource.Resource;
-
-import javax.tools.JavaFileObject;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipFile;
-
-/**
- * {@link JavaFileObject} 相关工具类封装
- *
- * @author lzpeng, looly
- * @since 5.5.2
- */
-public class JavaFileObjectUtil {
-
- /**
- * 获取指定资源下的所有待编译的java源码文件,并以{@link JavaFileObject}形式返回
- *
- * 如果资源为目录,则遍历目录找到目录中的.java或者.jar等文件加载之
- * 如果资源为.jar或.zip等,解压读取其中的.java文件加载之
- * 其他情况直接读取资源流并加载之
- *
- *
- * @param resource 资源,可以为目录、文件或流
- * @return 所有待编译的 {@link JavaFileObject}
- */
- public static List getJavaFileObjects(final Resource resource) {
- final List result = new ArrayList<>();
-
- if (resource instanceof FileResource) {
- final File file = ((FileResource) resource).getFile();
- result.addAll(JavaFileObjectUtil.getJavaFileObjects(file));
- } else {
- result.add(new JavaSourceFileObject(resource.getName(), resource.getStream()));
- }
-
- return result;
- }
-
- /**
- * 获取指定文件下的所有待编译的java文件,并以{@link JavaFileObject}形式返回
- *
- * 如果文件为目录,则遍历目录找到目录中的.java或者.jar等文件加载之
- * 如果文件为.jar或.zip等,解压读取其中的.java文件加载之
- *
- *
- * @param file 文件或目录,文件支持.java、.jar和.zip文件
- * @return 所有待编译的 {@link JavaFileObject}
- */
- public static List getJavaFileObjects(final File file) {
- final List result = new ArrayList<>();
- if (file.isDirectory()) {
- FileUtil.walkFiles(file, (subFile) -> result.addAll(getJavaFileObjects(file)));
- } else {
- final String fileName = file.getName();
- if (isJavaFile(fileName)) {
- result.add(new JavaSourceFileObject(file.toURI()));
- } else if (isJarOrZipFile(fileName)) {
- result.addAll(getJavaFileObjectByZipOrJarFile(file));
- }
- }
-
- return result;
- }
-
- /**
- * 是否是jar 或 zip 文件
- * 通过扩展名判定
- *
- * @param fileName 文件名
- * @return 是否是jar 或 zip 文件
- */
- public static boolean isJarOrZipFile(final String fileName) {
- return FileNameUtil.isType(fileName, "jar", "zip");
- }
-
- /**
- * 是否是java文件
- * 通过扩展名判定
- *
- * @param fileName 文件名
- * @return 是否是.java文件
- */
- public static boolean isJavaFile(final String fileName) {
- return FileNameUtil.isType(fileName, "java");
- }
-
- /**
- * 通过zip包或jar包创建Java文件对象
- *
- * @param file 压缩文件
- * @return Java文件对象
- */
- private static List getJavaFileObjectByZipOrJarFile(final File file) {
- final List collection = new ArrayList<>();
- final ZipFile zipFile = ZipUtil.toZipFile(file, null);
- ZipUtil.read(zipFile, (zipEntry) -> {
- final String name = zipEntry.getName();
- if (isJavaFile(name)) {
- collection.add(new JavaSourceFileObject(name, ZipUtil.getStream(zipFile, zipEntry)));
- }
- });
- return collection;
- }
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java
deleted file mode 100644
index af7faace9..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package cn.hutool.core.compiler;
-
-import cn.hutool.core.classloader.ClassLoaderUtil;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.io.resource.FileResource;
-import cn.hutool.core.io.resource.Resource;
-import cn.hutool.core.io.resource.StringResource;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.net.url.URLUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.ObjUtil;
-
-import javax.tools.DiagnosticCollector;
-import javax.tools.JavaCompiler.CompilationTask;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardLocation;
-import java.io.File;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Java 源码编译器
- * 通过此类可以动态编译java源码,并加载到ClassLoader,从而动态获取加载的类。
- * JavaSourceCompiler支持加载的源码类型包括:
- *
- * 源码文件
- * 源码文件源码字符串
- *
- *
- * 使用方法如下:
- *
- * ClassLoader classLoader = JavaSourceCompiler.create(null)
- * .addSource(FileUtil.file("test-compile/b/B.java"))
- * .addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java"))
- * // 增加编译依赖的类库
- * .addLibrary(libFile)
- * .compile();
- * Class<?> clazz = classLoader.loadClass("c.C");
- *
- *
- * @author lzpeng
- */
-public class JavaSourceCompiler {
-
- /**
- * 待编译的资源,支持:
- *
- *
- * 源码字符串,使用{@link StringResource}
- * 源码文件、源码jar包或源码zip包,亦或者文件夹,使用{@link FileResource}
- *
- * 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
- */
- private final List sourceList = new ArrayList<>();
-
- /**
- * 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包
- */
- private final List libraryFileList = new ArrayList<>();
-
- /**
- * 编译类时使用的父类加载器
- */
- private final ClassLoader parentClassLoader;
-
- /**
- * 创建Java源码编译器
- *
- * @param parent 父类加载器
- * @return Java源码编译器
- */
- public static JavaSourceCompiler of(final ClassLoader parent) {
- return new JavaSourceCompiler(parent);
- }
-
- /**
- * 构造
- *
- * @param parent 父类加载器,null则使用默认类加载器
- */
- private JavaSourceCompiler(final ClassLoader parent) {
- this.parentClassLoader = ObjUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader);
- }
-
- /**
- * 向编译器中加入待编译的资源
- * 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
- *
- * @param resources 待编译的资源,支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
- * @return Java源码编译器
- */
- public JavaSourceCompiler addSource(final Resource... resources) {
- if (ArrayUtil.isNotEmpty(resources)) {
- this.sourceList.addAll(Arrays.asList(resources));
- }
- return this;
- }
-
- /**
- * 向编译器中加入待编译的文件
- * 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
- *
- * @param files 待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
- * @return Java源码编译器
- */
- public JavaSourceCompiler addSource(final File... files) {
- if (ArrayUtil.isNotEmpty(files)) {
- for (final File file : files) {
- this.sourceList.add(new FileResource(file));
- }
- }
- return this;
- }
-
- /**
- * 向编译器中加入待编译的源码Map
- *
- * @param sourceCodeMap 源码Map key: 类名 value 源码
- * @return Java源码编译器
- */
- public JavaSourceCompiler addSource(final Map sourceCodeMap) {
- if (MapUtil.isNotEmpty(sourceCodeMap)) {
- sourceCodeMap.forEach(this::addSource);
- }
- return this;
- }
-
- /**
- * 向编译器中加入待编译的源码
- *
- * @param className 类名
- * @param sourceCode 源码
- * @return Java文件编译器
- */
- public JavaSourceCompiler addSource(final String className, final String sourceCode) {
- if (className != null && sourceCode != null) {
- this.sourceList.add(new StringResource(sourceCode, className));
- }
- return this;
- }
-
- /**
- * 加入编译Java源码时所需要的jar包,jar包中必须为字节码
- *
- * @param files 编译Java源码时所需要的jar包
- * @return Java源码编译器
- */
- public JavaSourceCompiler addLibrary(final File... files) {
- if (ArrayUtil.isNotEmpty(files)) {
- this.libraryFileList.addAll(Arrays.asList(files));
- }
- return this;
- }
-
- /**
- * 编译所有文件并返回类加载器
- *
- * @return 类加载器
- */
- public ClassLoader compile() {
- // 获得classPath
- final List classPath = getClassPath();
- final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
- final URLClassLoader ucl = URLClassLoader.newInstance(urLs, this.parentClassLoader);
- if (sourceList.isEmpty()) {
- // 没有需要编译的源码文件返回加载zip或jar包的类加载器
- return ucl;
- }
-
- // 创建编译器
- final JavaClassFileManager javaFileManager = new JavaClassFileManager(ucl, CompilerUtil.getFileManager());
-
- // classpath
- final List options = new ArrayList<>();
- if (false == classPath.isEmpty()) {
- final List cp = CollUtil.map(classPath, File::getAbsolutePath, true);
- options.add("-cp");
- options.add(CollUtil.join(cp, FileUtil.isWindows() ? ";" : ":"));
- }
-
- // 编译文件
- final DiagnosticCollector super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
- final List javaFileObjectList = getJavaFileObject();
- final CompilationTask task = CompilerUtil.getTask(javaFileManager, diagnosticCollector, options, javaFileObjectList);
- try {
- if (task.call()) {
- // 加载编译后的类
- return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
- }
- } finally {
- IoUtil.close(javaFileManager);
- }
- //编译失败,收集错误信息
- throw new CompilerException(DiagnosticUtil.getMessages(diagnosticCollector));
- }
-
- /**
- * 获得编译源码时需要的classpath
- *
- * @return 编译源码时需要的classpath
- */
- private List getClassPath() {
- final List classPathFileList = new ArrayList<>();
- for (final File file : libraryFileList) {
- final List jarOrZipFile = FileUtil.loopFiles(file, (subFile) -> JavaFileObjectUtil.isJarOrZipFile(subFile.getName()));
- classPathFileList.addAll(jarOrZipFile);
- if (file.isDirectory()) {
- classPathFileList.add(file);
- }
- }
- return classPathFileList;
- }
-
- /**
- * 获得待编译的Java文件对象
- *
- * @return 待编译的Java文件对象
- */
- private List getJavaFileObject() {
- final List list = new ArrayList<>();
-
- for (final Resource resource : this.sourceList) {
- list.addAll(JavaFileObjectUtil.getJavaFileObjects(resource));
- }
-
- return list;
- }
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java
deleted file mode 100644
index d4d2ea4a7..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package cn.hutool.core.compiler;
-
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.CharUtil;
-import cn.hutool.core.net.url.URLUtil;
-
-import javax.tools.SimpleJavaFileObject;
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.charset.Charset;
-
-/**
- * Java 源码文件对象,支持:
- *
- * 源文件,通过文件的uri传入
- * 代码内容,通过流传入
- *
- *
- * @author lzpeng
- * @since 5.5.2
- */
-class JavaSourceFileObject extends SimpleJavaFileObject {
-
- /**
- * 输入流
- */
- private InputStream inputStream;
-
- /**
- * 构造,支持File等路径类型的源码
- *
- * @param uri 需要编译的文件uri
- */
- protected JavaSourceFileObject(final URI uri) {
- super(uri, Kind.SOURCE);
- }
-
- /**
- * 构造,支持String类型的源码
- *
- * @param className 需要编译的类名
- * @param code 需要编译的类源码
- */
- protected JavaSourceFileObject(final String className, final String code, final Charset charset) {
- this(className, IoUtil.toStream(code, charset));
- }
-
- /**
- * 构造,支持流中读取源码(例如zip或网络等)
- *
- * @param name 需要编译的文件名
- * @param inputStream 输入流
- */
- protected JavaSourceFileObject(final String name, final InputStream inputStream) {
- this(URLUtil.getStringURI(name.replace(CharUtil.DOT, CharUtil.SLASH) + Kind.SOURCE.extension));
- this.inputStream = inputStream;
- }
-
- /**
- * 获得类源码的输入流
- *
- * @return 类源码的输入流
- * @throws IOException IO 异常
- */
- @Override
- public InputStream openInputStream() throws IOException {
- if (inputStream == null) {
- inputStream = toUri().toURL().openStream();
- }
- return new BufferedInputStream(inputStream);
- }
-
- /**
- * 获得类源码
- * 编译器编辑源码前,会通过此方法获取类的源码
- *
- * @param ignoreEncodingErrors 是否忽略编码错误
- * @return 需要编译的类的源码
- * @throws IOException IO异常
- */
- @Override
- public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
- try(final InputStream in = openInputStream()){
- return IoUtil.readUtf8(in);
- }
- }
-
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java b/hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java
deleted file mode 100644
index a0f3ef2b4..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * 运行时编译java源码,动态从字符串或外部文件加载类
- *
- * @author : Lzpeng
- */
-package cn.hutool.core.compiler;
\ No newline at end of file
diff --git a/hutool-core/src/main/java/cn/hutool/core/compress/ZipReader.java b/hutool-core/src/main/java/cn/hutool/core/compress/ZipReader.java
index 3055d8e90..36665ba1a 100755
--- a/hutool-core/src/main/java/cn/hutool/core/compress/ZipReader.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compress/ZipReader.java
@@ -1,7 +1,7 @@
package cn.hutool.core.compress;
import cn.hutool.core.exceptions.ValidateException;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrUtil;
diff --git a/hutool-core/src/main/java/cn/hutool/core/compress/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/compress/ZipUtil.java
index 1c77e9997..4d04508f0 100644
--- a/hutool-core/src/main/java/cn/hutool/core/compress/ZipUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compress/ZipUtil.java
@@ -2,9 +2,10 @@ package cn.hutool.core.compress;
import cn.hutool.core.collection.iter.EnumerationIter;
import cn.hutool.core.exceptions.UtilException;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.file.FileSystemUtil;
import cn.hutool.core.io.file.PathUtil;
import cn.hutool.core.io.resource.Resource;
@@ -172,7 +173,7 @@ public class ZipUtil {
* @throws UtilException IO异常
*/
public static File zip(final File srcFile, final Charset charset) throws UtilException {
- final File zipFile = FileUtil.file(srcFile.getParentFile(), FileUtil.mainName(srcFile) + ".zip");
+ final File zipFile = FileUtil.file(srcFile.getParentFile(), FileNameUtil.mainName(srcFile) + ".zip");
zip(zipFile, charset, false, srcFile);
return zipFile;
}
@@ -468,7 +469,7 @@ public class ZipUtil {
* @since 3.2.2
*/
public static File unzip(final File zipFile, final Charset charset) throws UtilException {
- final File destDir = FileUtil.file(zipFile.getParentFile(), FileUtil.mainName(zipFile));
+ final File destDir = FileUtil.file(zipFile.getParentFile(), FileNameUtil.mainName(zipFile));
return unzip(zipFile, destDir, charset);
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java b/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java
index 3bb70e92d..c34dd9e58 100755
--- a/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compress/ZipWriter.java
@@ -1,6 +1,6 @@
package cn.hutool.core.compress;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.Resource;
@@ -100,6 +100,18 @@ public class ZipWriter implements Closeable {
return this;
}
+ /**
+ * 设置压缩方式
+ *
+ * @param method 压缩方式,支持{@link ZipOutputStream#DEFLATED}和{@link ZipOutputStream#STORED}
+ * @return this
+ * @since 6.0.0
+ */
+ public ZipWriter setMethod(final int method) {
+ this.out.setMethod(method);
+ return this;
+ }
+
/**
* 获取原始的{@link ZipOutputStream}
*
@@ -233,9 +245,10 @@ public class ZipWriter implements Closeable {
* @param filter 文件过滤器,通过实现此接口,自定义要过滤的文件(过滤掉哪些文件或文件夹不加入压缩),{@code null}表示不过滤
* @throws IORuntimeException IO异常
*/
- private ZipWriter _add(final File file, final String srcRootDir, final FileFilter filter) throws IORuntimeException {
+ @SuppressWarnings("resource")
+ private void _add(final File file, final String srcRootDir, final FileFilter filter) throws IORuntimeException {
if (null == file || (null != filter && false == filter.accept(file))) {
- return this;
+ return;
}
// 获取文件相对于压缩文件夹根目录的子路径
@@ -256,7 +269,6 @@ public class ZipWriter implements Closeable {
// 如果是文件或其它符号,则直接压缩该文件
putEntry(subPath, FileUtil.getInputStream(file));
}
- return this;
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java
index abcc4eb32..6962446f0 100755
--- a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java
@@ -830,6 +830,7 @@ public class Convert {
* @param notConvertSet 不替换的字符集合
* @return 替换后的字符
*/
+ @SuppressWarnings("UnnecessaryUnicodeEscape")
public static String toDBC(final String text, final Set notConvertSet) {
if(StrUtil.isBlank(text)) {
return text;
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/ArrayConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/ArrayConverter.java
index 78b8a83d0..b26209fb7 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/ArrayConverter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/ArrayConverter.java
@@ -1,6 +1,6 @@
package cn.hutool.core.convert.impl;
-import cn.hutool.core.codec.BaseN.Base64;
+import cn.hutool.core.codec.binary.Base64;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.Convert;
@@ -24,6 +24,9 @@ import java.util.List;
public class ArrayConverter extends AbstractConverter {
private static final long serialVersionUID = 1L;
+ /**
+ * 单例
+ */
public static final ArrayConverter INSTANCE = new ArrayConverter();
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java
index af481448c..fbbb2f926 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java
@@ -3,6 +3,7 @@ package cn.hutool.core.convert.impl;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.date.format.GlobalCustomFormat;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
@@ -156,7 +157,15 @@ public class TemporalAccessorConverter extends AbstractConverter {
return IsoEra.of(Math.toIntExact(time));
}
- return parseFromInstant(targetClass, Instant.ofEpochMilli(time), null);
+ final Instant instant;
+ if(GlobalCustomFormat.FORMAT_SECONDS.equals(this.format)){
+ // https://gitee.com/dromara/hutool/issues/I6IS5B
+ // Unix时间戳
+ instant = Instant.ofEpochSecond(time);
+ }else{
+ instant = Instant.ofEpochMilli(time);
+ }
+ return parseFromInstant(targetClass, instant, null);
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/BetweenFormatter.java b/hutool-core/src/main/java/cn/hutool/core/date/BetweenFormatter.java
index 51b281a15..b54c05f17 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/BetweenFormatter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/BetweenFormatter.java
@@ -73,7 +73,7 @@ public class BetweenFormatter implements Serializable {
final int level = this.level.ordinal();
int levelCount = 0;
- if (isLevelCountValid(levelCount) && 0 != day && level >= Level.DAY.ordinal()) {
+ if (isLevelCountValid(levelCount) && 0 != day) {
sb.append(day).append(Level.DAY.name);
levelCount++;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java
index efec80bd6..6c042e540 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java
@@ -2,12 +2,12 @@ package cn.hutool.core.date;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.NumberChineseFormatter;
+import cn.hutool.core.date.format.GlobalCustomFormat;
import cn.hutool.core.date.format.parser.DateParser;
import cn.hutool.core.date.format.parser.FastDateParser;
-import cn.hutool.core.date.format.GlobalCustomFormat;
import cn.hutool.core.date.format.parser.PositionDateParser;
-import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.ObjUtil;
import javax.xml.datatype.XMLGregorianCalendar;
import java.text.ParsePosition;
@@ -15,7 +15,6 @@ import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
-import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.TimeZone;
@@ -74,7 +73,7 @@ public class CalendarUtil {
/**
* 转换为Calendar对象
*
- * @param millis 时间戳
+ * @param millis 时间戳
* @param timeZone 时区
* @return Calendar对象
* @since 5.7.22
@@ -105,6 +104,8 @@ public class CalendarUtil {
return Calendar.PM == calendar.get(Calendar.AM_PM);
}
+ // region ----- modify 时间修改
+
/**
* 修改日期为某个时间字段起始时间
*
@@ -355,6 +356,7 @@ public class CalendarUtil {
public static Calendar endOfYear(final Calendar calendar) {
return ceiling(calendar, DateField.YEAR);
}
+ // endregion
/**
* 比较两个日期是否为同一天
@@ -375,8 +377,8 @@ public class CalendarUtil {
/**
* 比较两个日期是否为同一周
*
- * @param cal1 日期1
- * @param cal2 日期2
+ * @param cal1 日期1
+ * @param cal2 日期2
* @param isMon 是否为周一。国内第一天为星期一,国外第一天为星期日
* @return 是否为同一周
* @since 5.7.21
@@ -443,28 +445,6 @@ public class CalendarUtil {
return date1.getTimeInMillis() == date2.getTimeInMillis();
}
- /**
- * 获得指定日期区间内的年份和季度
- *
- * @param startDate 起始日期(包含)
- * @param endDate 结束日期(包含)
- * @return 季度列表 ,元素类似于 20132
- * @since 4.1.15
- */
- public static LinkedHashSet yearAndQuarter(long startDate, final long endDate) {
- final LinkedHashSet quarters = new LinkedHashSet<>();
- final Calendar cal = calendar(startDate);
- while (startDate <= endDate) {
- // 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环
- quarters.add(yearAndQuarter(cal));
-
- cal.add(Calendar.MONTH, 3);
- startDate = cal.getTimeInMillis();
- }
-
- return quarters;
- }
-
/**
* 获得指定日期年份和季度
* 格式:[20131]表示2013年第一季度
@@ -637,51 +617,7 @@ public class CalendarUtil {
return result.toString();
}
- /**
- * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
- *
- * @param birthday 生日
- * @param dateToCompare 需要对比的日期
- * @return 年龄
- */
- protected static int age(final long birthday, final long dateToCompare) {
- if (birthday > dateToCompare) {
- throw new IllegalArgumentException("Birthday is after dateToCompare!");
- }
-
- final Calendar cal = Calendar.getInstance();
- cal.setTimeInMillis(dateToCompare);
-
- final int year = cal.get(Calendar.YEAR);
- final int month = cal.get(Calendar.MONTH);
- final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
- final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
-
- cal.setTimeInMillis(birthday);
- int age = year - cal.get(Calendar.YEAR);
- //当前日期,则为0岁
- if (age == 0){
- return 0;
- }
-
- final int monthBirth = cal.get(Calendar.MONTH);
- if (month == monthBirth) {
-
- final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
- final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
- // issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
- if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) {
- // 如果生日在当月,但是未达到生日当天的日期,年龄减一
- age--;
- }
- } else if (month < monthBirth) {
- // 如果当前月份未达到生日的月份,年龄计算减一
- age--;
- }
-
- return age;
- }
-
+ // region ----- parse
/**
* 通过给定的日期格式解析日期时间字符串。
* 传入的日期格式会逐个尝试,直到解析成功,返回{@link Calendar}对象,否则抛出{@link DateException}异常。
@@ -782,4 +718,50 @@ public class CalendarUtil {
return parser.parse(StrUtil.str(str), new ParsePosition(0), calendar) ? calendar : null;
}
+ // endregion
+
+ /**
+ * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
+ *
+ * @param birthday 生日
+ * @param dateToCompare 需要对比的日期
+ * @return 年龄
+ */
+ protected static int age(final long birthday, final long dateToCompare) {
+ if (birthday > dateToCompare) {
+ throw new IllegalArgumentException("Birthday is after dateToCompare!");
+ }
+
+ final Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(dateToCompare);
+
+ final int year = cal.get(Calendar.YEAR);
+ final int month = cal.get(Calendar.MONTH);
+ final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
+ final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+
+ cal.setTimeInMillis(birthday);
+ int age = year - cal.get(Calendar.YEAR);
+ //当前日期,则为0岁
+ if (age == 0) {
+ return 0;
+ }
+
+ final int monthBirth = cal.get(Calendar.MONTH);
+ if (month == monthBirth) {
+
+ final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
+ final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ // issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
+ if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) {
+ // 如果生日在当月,但是未达到生日当天的日期,年龄减一
+ age--;
+ }
+ } else if (month < monthBirth) {
+ // 如果当前月份未达到生日的月份,年龄计算减一
+ age--;
+ }
+
+ return age;
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java b/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java
index b59eea778..87edc3e2b 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java
@@ -31,7 +31,7 @@ import java.util.regex.Pattern;
* yyyy-MM-dd HH:mm:ss.SSS 示例:2022-08-05 12:59:59.559
* yyyy-MM-dd HH:mm:ss.SSSZ 示例:2022-08-05 12:59:59.559+0800【东八区中国时区】、2022-08-05 04:59:59.559+0000【冰岛0时区】, 年月日 时分秒 毫秒 时区
* yyyy-MM-dd HH:mm:ss.SSSz 示例:2022-08-05 12:59:59.559UTC【世界标准时间=0时区】、2022-08-05T12:59:59.599GMT【冰岛0时区】、2022-08-05T12:59:59.599CST【东八区中国时区】、2022-08-23T03:45:00.599EDT【美国东北纽约时间,-0400】 ,年月日 时分秒 毫秒 时区
- * yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 示例:2022-08-05T12:59:59.559Z, 其中:''单引号表示转义字符,T:分隔符,Z:一般值UTC,0时区的时间含义
+ * yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 示例:2022-08-05T12:59:59.559Z, 其中:''单引号表示转义字符,T:分隔符,Z:一般指UTC,0时区的时间含义
* yyyy-MM-dd'T'HH:mm:ss.SSSZ 示例:2022-08-05T11:59:59.559+0800, 其中:Z,表示时区
* yyyy-MM-dd'T'HH:mm:ss.SSSX 示例:2022-08-05T12:59:59.559+08, 其中:X:两位时区,+08表示:东8区,中国时区
* yyyy-MM-dd'T'HH:mm:ss.SSSXX 示例:2022-08-05T12:59:59.559+0800, 其中:XX:四位时区
@@ -61,7 +61,7 @@ import java.util.regex.Pattern;
* 如:“09:30 UTC”表示为“09:30Z”或“T0930Z”,其中:Z 是 +00:00 的缩写,意思是 UTC(零时分秒的偏移量).
*
*
- * yyyy-MM-dd'T'HH:mm:ssZ
+ * yyyy-MM-dd'T'HH:mm:ss'Z'
* 2022-08-23T15:20:46UTC
* 2022-08-23T15:20:46 UTC
* 2022-08-23T15:20:46+0000
@@ -189,15 +189,15 @@ public class DatePattern {
/**
* ISO8601日期时间格式,精确到毫秒:yyyy-MM-dd HH:mm:ss,SSS
*/
- public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
+ public static final String NORM_DATETIME_COMMA_MS_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
/**
* ISO8601日期时间格式,精确到毫秒 {@link FastDateFormat}:yyyy-MM-dd HH:mm:ss,SSS
*/
- public static final FastDateFormat ISO8601_FORMAT = FastDateFormat.getInstance(ISO8601_PATTERN);
+ public static final FastDateFormat NORM_DATETIME_COMMA_MS_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_COMMA_MS_PATTERN);
/**
* 标准日期格式 {@link DateTimeFormatter}:yyyy-MM-dd HH:mm:ss,SSS
*/
- public static final DateTimeFormatter ISO8601_FORMATTER = createFormatter(ISO8601_PATTERN);
+ public static final DateTimeFormatter NORM_DATETIME_COMMA_MS_FORMATTER = createFormatter(NORM_DATETIME_COMMA_MS_PATTERN);
/**
* 标准日期格式:yyyy年MM月dd日
@@ -286,8 +286,7 @@ public class DatePattern {
.toFormatter();
// endregion
- // region Others
- //================================================== Others ==================================================
+ // region ----- Others
/**
* HTTP头中日期时间格式:EEE, dd MMM yyyy HH:mm:ss z
*/
@@ -307,76 +306,78 @@ public class DatePattern {
public static final FastDateFormat JDK_DATETIME_FORMAT = FastDateFormat.getInstance(JDK_DATETIME_PATTERN, Locale.US);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ss
+ * ISO8601日期时间:yyyy-MM-dd'T'HH:mm:ss
+ * 按照ISO8601规范,默认使用T分隔日期和时间,末尾不加Z表示当地时区
*/
- public static final String UTC_SIMPLE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
+ public static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
/**
- * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss
+ * ISO8601日期时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss
*/
- public static final FastDateFormat UTC_SIMPLE_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_PATTERN, TimeZone.getTimeZone("UTC"));
+ public static final FastDateFormat ISO8601_FORMAT = FastDateFormat.getInstance(ISO8601_PATTERN);
/**
* UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSS
*/
- public static final String UTC_SIMPLE_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
+ public static final String ISO8601_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
/**
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS
*/
- public static final FastDateFormat UTC_SIMPLE_MS_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_MS_PATTERN, TimeZone.getTimeZone("UTC"));
+ public static final FastDateFormat ISO8601_MS_FORMAT = FastDateFormat.getInstance(ISO8601_MS_PATTERN);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z'
+ * UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z'
+ * 按照ISO8601规范,后缀加Z表示UTC时间
*/
public static final String UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/**
- * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss'Z'
+ * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss'Z'
*/
- public static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance(UTC_PATTERN, TimeZone.getTimeZone("UTC"));
+ public static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance(UTC_PATTERN, ZoneUtil.ZONE_UTC);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ssZ
+ * ISO8601时间:yyyy-MM-dd'T'HH:mm:ssZ,Z表示一个时间偏移,如+0800
*/
- public static final String UTC_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";
+ public static final String ISO8601_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";
/**
- * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssZ
+ * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssZ,Z表示一个时间偏移,如+0800
*/
- public static final FastDateFormat UTC_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_ZONE_OFFSET_PATTERN, TimeZone.getTimeZone("UTC"));
+ public static final FastDateFormat ISO8601_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_WITH_ZONE_OFFSET_PATTERN);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ssXXX
+ * ISO8601时间:yyyy-MM-dd'T'HH:mm:ssXXX
*/
- public static final String UTC_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
+ public static final String ISO8601_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
/**
- * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssXXX
+ * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssXXX
*/
- public static final FastDateFormat UTC_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_XXX_OFFSET_PATTERN);
+ public static final FastDateFormat ISO8601_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_WITH_XXX_OFFSET_PATTERN);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
+ * ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
public static final String UTC_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
/**
- * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
+ * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
- public static final FastDateFormat UTC_MS_FORMAT = FastDateFormat.getInstance(UTC_MS_PATTERN, TimeZone.getTimeZone("UTC"));
+ public static final FastDateFormat UTC_MS_FORMAT = FastDateFormat.getInstance(UTC_MS_PATTERN, ZoneUtil.ZONE_UTC);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSSZ
+ * ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSSZ
*/
- public static final String UTC_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+ public static final String ISO8601_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
/**
- * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSZ
+ * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSZ
*/
- public static final FastDateFormat UTC_MS_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_MS_WITH_ZONE_OFFSET_PATTERN, TimeZone.getTimeZone("UTC"));
+ public static final FastDateFormat ISO8601_MS_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_MS_WITH_ZONE_OFFSET_PATTERN);
/**
- * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
+ * ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
*/
- public static final String UTC_MS_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+ public static final String ISO8601_MS_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
/**
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
*/
- public static final FastDateFormat UTC_MS_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_MS_WITH_XXX_OFFSET_PATTERN);
+ public static final FastDateFormat ISO8601_MS_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_MS_WITH_XXX_OFFSET_PATTERN);
// endregion
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java
index dfed3e3db..a20c27a93 100755
--- a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java
@@ -5,12 +5,7 @@ import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.format.DatePrinter;
import cn.hutool.core.date.format.FastDateFormat;
import cn.hutool.core.date.format.GlobalCustomFormat;
-import cn.hutool.core.date.format.parser.CSTDateParser;
-import cn.hutool.core.date.format.parser.NormalDateParser;
-import cn.hutool.core.date.format.parser.PositionDateParser;
-import cn.hutool.core.date.format.parser.PureDateParser;
-import cn.hutool.core.date.format.parser.TimeParser;
-import cn.hutool.core.date.format.parser.UTCDateParser;
+import cn.hutool.core.date.format.parser.*;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.math.NumberUtil;
import cn.hutool.core.regex.PatternPool;
@@ -26,14 +21,7 @@ import java.time.LocalDateTime;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
+import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -513,21 +501,7 @@ public class DateUtil extends CalendarUtil {
return yearAndQuarter(calendar(date));
}
- /**
- * 获得指定日期区间内的年份和季节
- *
- * @param startDate 起始日期(包含)
- * @param endDate 结束日期(包含)
- * @return 季度列表 ,元素类似于 20132
- */
- public static LinkedHashSet yearAndQuarter(final Date startDate, final Date endDate) {
- if (startDate == null || endDate == null) {
- return new LinkedHashSet<>(0);
- }
- return yearAndQuarter(startDate.getTime(), endDate.getTime());
- }
- // ------------------------------------ Format start ----------------------------------------------
-
+ // region ----- format
/**
* 格式化日期时间
* 格式 yyyy-MM-dd HH:mm:ss
@@ -696,10 +670,9 @@ public class DateUtil extends CalendarUtil {
return CalendarUtil.formatChineseDate(CalendarUtil.calendar(date), withTime);
}
- // ------------------------------------ Format end ----------------------------------------------
-
- // ------------------------------------ Parse start ----------------------------------------------
+ // endregion
+ // region ----- parse
/**
* 构建DateTime对象
*
@@ -840,8 +813,8 @@ public class DateUtil extends CalendarUtil {
// Wed Aug 01 00:00:00 CST 2012
return CSTDateParser.INSTANCE.parse(dateStr);
} else if (StrUtil.contains(dateStr, 'T')) {
- // UTC时间
- return UTCDateParser.INSTANCE.parse(dateStr);
+ // ISO8601标准时间
+ return ISO8601DateParser.INSTANCE.parse(dateStr);
}
//标准日期格式(包括单个数字的日期时间)
@@ -853,10 +826,9 @@ public class DateUtil extends CalendarUtil {
// 没有更多匹配的时间格式
throw new DateException("No format fit for date String [{}] !", dateStr);
}
- // ------------------------------------ Parse end ----------------------------------------------
-
- // ------------------------------------ Offset start ----------------------------------------------
+ // endregion
+ // region ----- offset
/**
* 修改日期为某个时间字段起始时间
*
@@ -1096,7 +1068,6 @@ public class DateUtil extends CalendarUtil {
public static DateTime endOfYear(final Date date) {
return new DateTime(endOfYear(calendar(date)));
}
- // --------------------------------------------------- Offset for now
/**
* 昨天
@@ -1243,9 +1214,9 @@ public class DateUtil extends CalendarUtil {
public static DateTime offset(final Date date, final DateField dateField, final int offset) {
return dateNew(date).offset(dateField, offset);
}
+ // endregion
- // ------------------------------------ Offset end ----------------------------------------------
-
+ // region ----- between
/**
* 判断两个日期相差的时长,只保留绝对值
*
@@ -1351,7 +1322,9 @@ public class DateUtil extends CalendarUtil {
public static long betweenYear(final Date beginDate, final Date endDate, final boolean isReset) {
return new DateBetween(beginDate, endDate).betweenYear(isReset);
}
+ // endregion
+ // region ----- formatBetween
/**
* 格式化日期间隔输出
*
@@ -1397,6 +1370,7 @@ public class DateUtil extends CalendarUtil {
public static String formatBetween(final long betweenMs) {
return new BetweenFormatter(betweenMs, BetweenFormatter.Level.MILLISECOND).format();
}
+ // endregion
/**
* 当前日期是否在日期指定范围内
@@ -1992,7 +1966,7 @@ public class DateUtil extends CalendarUtil {
* @return 单位简写名称
* @since 5.7.16
*/
- public static String getShotName(final TimeUnit unit) {
+ public static String getShortName(final TimeUnit unit) {
switch (unit) {
case NANOSECONDS:
return "ns";
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/Quarter.java b/hutool-core/src/main/java/cn/hutool/core/date/Quarter.java
index 454a2f33d..e1920e2bf 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/Quarter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/Quarter.java
@@ -3,23 +3,29 @@ package cn.hutool.core.date;
/**
* 季度枚举
*
+ * @author zhfish(https : / / github.com / zhfish)
* @see #Q1
* @see #Q2
* @see #Q3
* @see #Q4
- *
- * @author zhfish(https://github.com/zhfish)
- *
*/
public enum Quarter {
- /** 第一季度 */
+ /**
+ * 第一季度
+ */
Q1(1),
- /** 第二季度 */
+ /**
+ * 第二季度
+ */
Q2(2),
- /** 第三季度 */
+ /**
+ * 第三季度
+ */
Q3(3),
- /** 第四季度 */
+ /**
+ * 第四季度
+ */
Q4(4);
// ---------------------------------------------------------------
@@ -29,6 +35,11 @@ public enum Quarter {
this.value = value;
}
+ /**
+ * 获取季度值
+ *
+ * @return 季度值
+ */
public int getValue() {
return this.value;
}
@@ -36,13 +47,12 @@ public enum Quarter {
/**
* 将 季度int转换为Season枚举对象
*
+ * @param intValue 季度int表示
+ * @return {@code Quarter}
* @see #Q1
* @see #Q2
* @see #Q3
* @see #Q4
- *
- * @param intValue 季度int表示
- * @return {@link Quarter}
*/
public static Quarter of(final int intValue) {
switch (intValue) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java
index 121d52dda..d748bf201 100755
--- a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java
@@ -1,6 +1,6 @@
package cn.hutool.core.date;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.text.StrUtil;
import java.text.NumberFormat;
@@ -352,7 +352,7 @@ public class StopWatch {
unit = TimeUnit.NANOSECONDS;
}
return StrUtil.format("StopWatch '{}': running time = {} {}",
- this.id, getTotal(unit), DateUtil.getShotName(unit));
+ this.id, getTotal(unit), DateUtil.getShortName(unit));
}
/**
@@ -382,20 +382,27 @@ public class StopWatch {
sb.append("No task info kept");
} else {
sb.append("---------------------------------------------").append(FileUtil.getLineSeparator());
- sb.append(DateUtil.getShotName(unit)).append(" % Task name").append(FileUtil.getLineSeparator());
+ sb.append(DateUtil.getShortName(unit)).append(" % Task name").append(FileUtil.getLineSeparator());
sb.append("---------------------------------------------").append(FileUtil.getLineSeparator());
final NumberFormat nf = NumberFormat.getNumberInstance();
- nf.setMinimumIntegerDigits(9);
nf.setGroupingUsed(false);
final NumberFormat pf = NumberFormat.getPercentInstance();
- pf.setMinimumIntegerDigits(2);
pf.setGroupingUsed(false);
for (final TaskInfo task : getTaskInfo()) {
- sb.append(nf.format(task.getTime(unit))).append(" ");
- sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" ");
+ final String taskTimeStr = nf.format(task.getTime(unit));
+ sb.append(taskTimeStr);
+ if(taskTimeStr.length() < 11){
+ sb.append(StrUtil.repeat(' ', 11 - taskTimeStr.length()));
+ }
+
+ final String percentStr = pf.format((double) task.getTimeNanos() / getTotalTimeNanos());
+ if(percentStr.length() < 4){
+ sb.append(StrUtil.repeat(' ', 4 - percentStr.length()));
+ }
+ sb.append(percentStr).append(" ");
sb.append(task.getTaskName()).append(FileUtil.getLineSeparator());
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/SystemClock.java b/hutool-core/src/main/java/cn/hutool/core/date/SystemClock.java
index 5f9cf30f7..dd6f960e9 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/SystemClock.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/SystemClock.java
@@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit;
* System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)
* System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道
* 后台定时更新时钟,JVM退出时,线程自动回收
- *
+ *
* see: http://git.oschina.net/yu120/sequence
* @author lry, looly
*/
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java
index c737af574..51483bf34 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java
@@ -164,8 +164,6 @@ public class TemporalAccessorUtil extends TemporalUtil{
result = ((OffsetTime) temporalAccessor).atDate(LocalDate.now()).toInstant();
} else {
// issue#1891@Github
- // Instant.from不能完成日期转换
- //result = Instant.from(temporalAccessor);
result = toInstant(TimeUtil.of(temporalAccessor));
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java
index fbc0a95c0..1fd6b8e0e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java
@@ -1,30 +1,18 @@
package cn.hutool.core.date;
import cn.hutool.core.date.format.GlobalCustomFormat;
+import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
-import java.time.DayOfWeek;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.Period;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
+import java.time.*;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoField;
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.Temporal;
-import java.time.temporal.TemporalAccessor;
-import java.time.temporal.TemporalAdjusters;
-import java.time.temporal.TemporalUnit;
-import java.time.temporal.WeekFields;
+import java.time.temporal.*;
import java.util.Date;
import java.util.TimeZone;
+import java.util.function.Function;
/**
* JDK8+中的{@link java.time} 工具类封装
@@ -35,11 +23,6 @@ import java.util.TimeZone;
* @since 6.0.0
*/
public class TimeUtil extends TemporalAccessorUtil {
- /**
- * UTC 的 ZoneID
- */
- public static final ZoneId ZONE_ID_UTC = ZoneId.of("UTC");
-
/**
* 当前时间,默认时区
*
@@ -65,7 +48,7 @@ public class TimeUtil extends TemporalAccessorUtil {
* @return {@link LocalDateTime}
*/
public static LocalDateTime ofUTC(final Instant instant) {
- return of(instant, ZONE_ID_UTC);
+ return of(instant, ZoneUtil.ZONE_ID_UTC);
}
/**
@@ -234,7 +217,7 @@ public class TimeUtil extends TemporalAccessorUtil {
/**
* 解析日期时间字符串为{@link LocalDateTime},格式支持日期时间、日期、时间
- * 如果formatter为{code null},则使用{@link DateTimeFormatter#ISO_LOCAL_DATE_TIME}
+ * 如果formatter为{@code null},则使用{@link DateTimeFormatter#ISO_LOCAL_DATE_TIME}
*
* @param text 日期时间字符串
* @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter}
@@ -355,6 +338,17 @@ public class TimeUtil extends TemporalAccessorUtil {
return format(date, DatePattern.NORM_DATE_FORMATTER);
}
+
+ /**
+ * 格式化时间函数
+ *
+ * @param dateTimeFormatter {@link DateTimeFormatter}
+ * @return 格式化时间的函数
+ */
+ public static Function formatFunc(final DateTimeFormatter dateTimeFormatter) {
+ return LambdaUtil.toFunction(TimeUtil::format, dateTimeFormatter);
+ }
+
/**
* 日期偏移,根据field不同加不同值(偏移会修改传入的对象)
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java
index 008e648cc..2f11af19a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java
@@ -11,6 +11,15 @@ import java.util.TimeZone;
*/
public class ZoneUtil {
+ /**
+ * UTC 的 ZoneID
+ */
+ public static final TimeZone ZONE_UTC = TimeZone.getTimeZone("UTC");
+ /**
+ * UTC 的 TimeZone
+ */
+ public static final ZoneId ZONE_ID_UTC = ZONE_UTC.toZoneId();
+
/**
* {@link ZoneId}转换为{@link TimeZone},{@code null}则返回系统默认值
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java
index 1d23e0389..11170f4ad 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java
@@ -573,17 +573,11 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mValue = value;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return mValue.length();
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
buffer.append(mValue);
@@ -610,9 +604,6 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mValues = values;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
int max = 0;
@@ -625,9 +616,6 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
return max;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
buffer.append(mValues[calendar.get(mField)]);
@@ -651,25 +639,16 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mField = field;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return 4;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
appendTo(buffer, calendar.get(mField));
}
- /**
- * {@inheritDoc}
- */
@Override
public final void appendTo(final Appendable buffer, final int value) throws IOException {
if (value < 10) {
@@ -697,25 +676,16 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
UnpaddedMonthField() {
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return 2;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
}
- /**
- * {@inheritDoc}
- */
@Override
public final void appendTo(final Appendable buffer, final int value) throws IOException {
if (value < 10) {
@@ -750,25 +720,16 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mSize = size;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return mSize;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
appendTo(buffer, calendar.get(mField));
}
- /**
- * {@inheritDoc}
- */
@Override
public final void appendTo(final Appendable buffer, final int value) throws IOException {
appendFullDigits(buffer, value, mSize);
@@ -792,25 +753,16 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mField = field;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return 2;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
appendTo(buffer, calendar.get(mField));
}
- /**
- * {@inheritDoc}
- */
@Override
public final void appendTo(final Appendable buffer, final int value) throws IOException {
if (value < 100) {
@@ -835,25 +787,16 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
TwoDigitYearField() {
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return 2;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
}
- /**
- * {@inheritDoc}
- */
@Override
public final void appendTo(final Appendable buffer, final int value) throws IOException {
appendDigits(buffer, value);
@@ -874,25 +817,16 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
TwoDigitMonthField() {
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return 2;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
}
- /**
- * {@inheritDoc}
- */
@Override
public final void appendTo(final Appendable buffer, final int value) throws IOException {
appendDigits(buffer, value);
@@ -916,17 +850,11 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mRule = rule;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return mRule.estimateLength();
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
int value = calendar.get(Calendar.HOUR);
@@ -936,9 +864,6 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mRule.appendTo(buffer, value);
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final int value) throws IOException {
mRule.appendTo(buffer, value);
@@ -962,17 +887,11 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mRule = rule;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return mRule.estimateLength();
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
int value = calendar.get(Calendar.HOUR_OF_DAY);
@@ -982,9 +901,6 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mRule.appendTo(buffer, value);
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final int value) throws IOException {
mRule.appendTo(buffer, value);
@@ -1103,9 +1019,6 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
// We have no access to the Calendar object that will be passed to
@@ -1114,9 +1027,6 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
return Math.max(mStandard.length(), mDaylight.length());
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
final TimeZone zone = calendar.getTimeZone();
@@ -1148,17 +1058,11 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mColon = colon;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return 5;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
@@ -1227,17 +1131,11 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
this.length = length;
}
- /**
- * {@inheritDoc}
- */
@Override
public int estimateLength() {
return length;
}
- /**
- * {@inheritDoc}
- */
@Override
public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {
int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
@@ -1298,17 +1196,11 @@ public class FastDatePrinter extends SimpleDateBasic implements DatePrinter {
mLocale = locale;
}
- /**
- * {@inheritDoc}
- */
@Override
public int hashCode() {
return (mStyle * 31 + mLocale.hashCode()) * 31 + mTimeZone.hashCode();
}
- /**
- * {@inheritDoc}
- */
@Override
public boolean equals(final Object obj) {
if (this == obj) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java b/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java
index 8fdc29d93..b80b6adb5 100755
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java
@@ -18,7 +18,13 @@ import java.util.function.Function;
*/
public class GlobalCustomFormat {
+ /**
+ * 格式:秒时间戳(Unix时间戳)
+ */
public static final String FORMAT_SECONDS = "#sss";
+ /**
+ * 格式:毫秒时间戳
+ */
public static final String FORMAT_MILLISECONDS = "#SSS";
private static final Map> formatterMap;
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/CSTDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/CSTDateParser.java
index dbe94f8ed..2c13a43b2 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/CSTDateParser.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/CSTDateParser.java
@@ -16,7 +16,10 @@ import cn.hutool.core.date.format.DefaultDateBasic;
* @since 6.0.0
*/
public class CSTDateParser extends DefaultDateBasic implements DateParser {
-
+ private static final long serialVersionUID = 1L;
+ /**
+ * 单例对象
+ */
public static CSTDateParser INSTANCE = new CSTDateParser();
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/UTCDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/ISO8601DateParser.java
similarity index 81%
rename from hutool-core/src/main/java/cn/hutool/core/date/format/parser/UTCDateParser.java
rename to hutool-core/src/main/java/cn/hutool/core/date/format/parser/ISO8601DateParser.java
index a60cb6a0c..77fc2cb0b 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/UTCDateParser.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/ISO8601DateParser.java
@@ -9,7 +9,7 @@ import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharUtil;
/**
- * UTC日期字符串(JDK的Date对象toString默认格式)解析,支持格式;
+ * ISO8601日期字符串(JDK的Date对象toString默认格式)解析,支持格式;
*
* yyyy-MM-dd'T'HH:mm:ss'Z'
* yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
@@ -22,13 +22,13 @@ import cn.hutool.core.util.CharUtil;
* @author looly
* @since 6.0.0
*/
-public class UTCDateParser extends DefaultDateBasic implements DateParser {
+public class ISO8601DateParser extends DefaultDateBasic implements DateParser {
private static final long serialVersionUID = 1L;
/**
* 单例对象
*/
- public static UTCDateParser INSTANCE = new UTCDateParser();
+ public static ISO8601DateParser INSTANCE = new ISO8601DateParser();
@Override
public DateTime parse(String source) {
@@ -61,10 +61,10 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser {
if (StrUtil.contains(source, CharUtil.DOT)) {
// 带毫秒,格式类似:2018-09-13T05:34:31.999+08:00
source = normalizeMillSeconds(source, ".", "+");
- return new DateTime(source, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
+ return new DateTime(source, DatePattern.ISO8601_MS_WITH_XXX_OFFSET_FORMAT);
} else {
// 格式类似:2018-09-13T05:34:31+08:00
- return new DateTime(source, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
+ return new DateTime(source, DatePattern.ISO8601_WITH_XXX_OFFSET_FORMAT);
}
} else if(ReUtil.contains("-\\d{2}:?00", source)){
// Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800
@@ -78,22 +78,22 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser {
if (StrUtil.contains(source, CharUtil.DOT)) {
// 带毫秒,格式类似:2018-09-13T05:34:31.999-08:00
source = normalizeMillSeconds(source, ".", "-");
- return new DateTime(source, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
+ return new DateTime(source, DatePattern.ISO8601_MS_WITH_XXX_OFFSET_FORMAT);
} else {
// 格式类似:2018-09-13T05:34:31-08:00
- return new DateTime(source, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
+ return new DateTime(source, DatePattern.ISO8601_WITH_XXX_OFFSET_FORMAT);
}
} else {
- if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 2) {
+ if (length == DatePattern.ISO8601_PATTERN.length() - 2) {
// 格式类似:2018-09-13T05:34:31
- return new DateTime(source, DatePattern.UTC_SIMPLE_FORMAT);
- } else if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 5) {
+ return new DateTime(source, DatePattern.ISO8601_FORMAT);
+ } else if (length == DatePattern.ISO8601_PATTERN.length() - 5) {
// 格式类似:2018-09-13T05:34
- return new DateTime(source + ":00", DatePattern.UTC_SIMPLE_FORMAT);
+ return new DateTime(source + ":00", DatePattern.ISO8601_FORMAT);
} else if (StrUtil.contains(source, CharUtil.DOT)) {
// 可能为: 2021-03-17T06:31:33.99
source = normalizeMillSeconds(source, ".", null);
- return new DateTime(source, DatePattern.UTC_SIMPLE_MS_FORMAT);
+ return new DateTime(source, DatePattern.ISO8601_MS_FORMAT);
}
}
// 没有更多匹配的时间格式
@@ -109,6 +109,7 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser {
* @param after 毫秒部分的后一个字符
* @return 规范之后的毫秒部分
*/
+ @SuppressWarnings("SameParameterValue")
private static String normalizeMillSeconds(final String dateStr, final CharSequence before, final CharSequence after) {
if (StrUtil.isBlank(after)) {
final String millOrNaco = StrUtil.subPre(StrUtil.subAfter(dateStr, before, true), 3);
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/NormalDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/NormalDateParser.java
index f090b86ac..99c04ae0a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/NormalDateParser.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/NormalDateParser.java
@@ -21,7 +21,11 @@ import cn.hutool.core.util.CharUtil;
* @since 6.0.0
*/
public class NormalDateParser extends DefaultDateBasic implements DateParser {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 单例
+ */
public static NormalDateParser INSTANCE = new NormalDateParser();
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PatternsDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PatternsDateParser.java
index c4332a60b..13e937069 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PatternsDateParser.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PatternsDateParser.java
@@ -16,6 +16,7 @@ import java.util.Locale;
* @since 6.0.0
*/
public class PatternsDateParser extends DefaultDateBasic implements DateParser {
+ private static final long serialVersionUID = 1L;
/**
* 创建 PatternsDateParser
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PureDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PureDateParser.java
index e5dd0f30e..e7646d59d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PureDateParser.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/PureDateParser.java
@@ -18,7 +18,11 @@ import cn.hutool.core.date.format.DefaultDateBasic;
* @since 6.0.0
*/
public class PureDateParser extends DefaultDateBasic implements DateParser {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 单例
+ */
public static PureDateParser INSTANCE = new PureDateParser();
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/TimeParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/TimeParser.java
index c8aec9a26..6961f02d1 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/TimeParser.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/TimeParser.java
@@ -17,7 +17,11 @@ import cn.hutool.core.text.StrUtil;
* @since 6.0.0
*/
public class TimeParser extends DefaultDateBasic implements DateParser {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 单例
+ */
public static TimeParser INSTANCE = new TimeParser();
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/exceptions/InvocationTargetRuntimeException.java b/hutool-core/src/main/java/cn/hutool/core/exceptions/InvocationTargetRuntimeException.java
index 0d2486471..cf503e7bd 100644
--- a/hutool-core/src/main/java/cn/hutool/core/exceptions/InvocationTargetRuntimeException.java
+++ b/hutool-core/src/main/java/cn/hutool/core/exceptions/InvocationTargetRuntimeException.java
@@ -6,28 +6,66 @@ package cn.hutool.core.exceptions;
* @author looly
*/
public class InvocationTargetRuntimeException extends UtilException {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 构造
+ *
+ * @param e 异常
+ */
public InvocationTargetRuntimeException(final Throwable e) {
super(e);
}
+ /**
+ * 构造
+ *
+ * @param message 消息
+ */
public InvocationTargetRuntimeException(final String message) {
super(message);
}
+ /**
+ * 构造
+ *
+ * @param messageTemplate 消息模板
+ * @param params 参数
+ */
public InvocationTargetRuntimeException(final String messageTemplate, final Object... params) {
super(messageTemplate, params);
}
- public InvocationTargetRuntimeException(final String message, final Throwable throwable) {
- super(message, throwable);
+ /**
+ * 构造
+ *
+ * @param message 消息
+ * @param cause 被包装的子异常
+ */
+ public InvocationTargetRuntimeException(final String message, final Throwable cause) {
+ super(message, cause);
}
- public InvocationTargetRuntimeException(final String message, final Throwable throwable, final boolean enableSuppression, final boolean writableStackTrace) {
- super(message, throwable, enableSuppression, writableStackTrace);
+ /**
+ * 构造
+ *
+ * @param message 消息
+ * @param cause 被包装的子异常
+ * @param enableSuppression 是否启用抑制
+ * @param writableStackTrace 堆栈跟踪是否应该是可写的
+ */
+ public InvocationTargetRuntimeException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
}
- public InvocationTargetRuntimeException(final Throwable throwable, final String messageTemplate, final Object... params) {
- super(throwable, messageTemplate, params);
+ /**
+ * 构造
+ *
+ * @param cause 被包装的子异常
+ * @param messageTemplate 消息模板
+ * @param params 参数
+ */
+ public InvocationTargetRuntimeException(final Throwable cause, final String messageTemplate, final Object... params) {
+ super(cause, messageTemplate, params);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/exceptions/UtilException.java b/hutool-core/src/main/java/cn/hutool/core/exceptions/UtilException.java
index e8d96c7d1..3d3bff60e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/exceptions/UtilException.java
+++ b/hutool-core/src/main/java/cn/hutool/core/exceptions/UtilException.java
@@ -10,8 +10,8 @@ import cn.hutool.core.text.StrUtil;
public class UtilException extends RuntimeException {
private static final long serialVersionUID = 8247610319171014183L;
- public UtilException(final Throwable e) {
- super(ExceptionUtil.getMessage(e), e);
+ public UtilException(final Throwable cause) {
+ super(ExceptionUtil.getMessage(cause), cause);
}
public UtilException(final String message) {
@@ -22,15 +22,15 @@ public class UtilException extends RuntimeException {
super(StrUtil.format(messageTemplate, params));
}
- public UtilException(final String message, final Throwable throwable) {
- super(message, throwable);
+ public UtilException(final String message, final Throwable cause) {
+ super(message, cause);
}
- public UtilException(final String message, final Throwable throwable, final boolean enableSuppression, final boolean writableStackTrace) {
- super(message, throwable, enableSuppression, writableStackTrace);
+ public UtilException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
}
- public UtilException(final Throwable throwable, final String messageTemplate, final Object... params) {
- super(StrUtil.format(messageTemplate, params), throwable);
+ public UtilException(final Throwable cause, final String messageTemplate, final Object... params) {
+ super(StrUtil.format(messageTemplate, params), cause);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java b/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java
index 25b966d54..e339f6fce 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java
@@ -19,6 +19,11 @@ public class AppendableWriter extends Writer implements Appendable {
private final boolean flushable;
private boolean closed;
+ /**
+ * 构造
+ *
+ * @param appendable {@link Appendable}
+ */
public AppendableWriter(final Appendable appendable) {
this.appendable = appendable;
this.flushable = appendable instanceof Flushable;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/CharsetDetector.java b/hutool-core/src/main/java/cn/hutool/core/io/CharsetDetector.java
index 615ca4814..2b52deb15 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/CharsetDetector.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/CharsetDetector.java
@@ -1,6 +1,7 @@
package cn.hutool.core.io;
import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import java.io.File;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
index 739b68437..68f624be5 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
@@ -23,7 +23,6 @@ import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.Flushable;
import java.io.IOException;
@@ -38,6 +37,8 @@ import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Collection;
import java.util.Objects;
@@ -525,15 +526,27 @@ public class IoUtil extends NioUtil {
}
/**
- * 文件转为{@link FileInputStream}
+ * 文件转为{@link InputStream}
*
- * @param file 文件
- * @return {@link FileInputStream}
+ * @param file 文件,非空
+ * @return {@link InputStream}
*/
- public static FileInputStream toStream(final File file) {
+ public static InputStream toStream(final File file) {
+ Assert.notNull(file);
+ return toStream(file.toPath());
+ }
+
+ /**
+ * 文件转为{@link InputStream}
+ *
+ * @param path {@link Path},非空
+ * @return {@link InputStream}
+ */
+ public static InputStream toStream(final Path path) {
+ Assert.notNull(path);
try {
- return new FileInputStream(file);
- } catch (final FileNotFoundException e) {
+ return Files.newInputStream(path);
+ } catch (final IOException e) {
throw new IORuntimeException(e);
}
}
@@ -869,6 +882,19 @@ public class IoUtil extends NioUtil {
}
}
+ /**
+ * 关闭
+ * 关闭失败不会抛出异常
+ *
+ * @param closeable 被关闭的对象
+ * @throws IOException IO异常
+ */
+ public static void nullSafeClose(final Closeable closeable) throws IOException {
+ if (null != closeable) {
+ closeable.close();
+ }
+ }
+
/**
* 尝试关闭指定对象
* 判断对象如果实现了{@link AutoCloseable},则调用之
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/checksum/ChecksumUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/checksum/ChecksumUtil.java
index a96bc8f66..dafb03ad0 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/checksum/ChecksumUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/checksum/ChecksumUtil.java
@@ -6,9 +6,9 @@ import cn.hutool.core.io.stream.EmptyOutputStream;
import cn.hutool.core.lang.Assert;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.Checksum;
@@ -57,8 +57,8 @@ public class ChecksumUtil {
throw new IllegalArgumentException("Checksums can't be computed on directories");
}
try {
- return checksum(new FileInputStream(file), checksum);
- } catch (final FileNotFoundException e) {
+ return checksum(Files.newInputStream(file.toPath()), checksum);
+ } catch (final IOException e) {
throw new IORuntimeException(e);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java
deleted file mode 100755
index 7ced5f670..000000000
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java
+++ /dev/null
@@ -1,313 +0,0 @@
-package cn.hutool.core.io.file;
-
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IORuntimeException;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.lang.copier.SrcToDestCopier;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.text.StrUtil;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.CopyOption;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.util.ArrayList;
-
-/**
- * 文件拷贝器
- * 支持以下几种情况:
- *
- * 1、文件复制到文件
- * 2、文件复制到目录
- * 3、目录复制到目录
- * 4、目录下的文件和目录复制到另一个目录
- *
- *
- * @author Looly
- * @since 3.0.9
- */
-public class FileCopier extends SrcToDestCopier {
- private static final long serialVersionUID = 1L;
-
- /**
- * 是否覆盖目标文件
- */
- private boolean isOverride;
- /**
- * 是否拷贝所有属性
- */
- private boolean isCopyAttributes;
- /**
- * 当拷贝来源是目录时是否只拷贝目录下的内容
- */
- private boolean isCopyContentIfDir;
- /**
- * 当拷贝来源是目录时是否只拷贝文件而忽略子目录
- */
- private boolean isOnlyCopyFile;
-
- //-------------------------------------------------------------------------------------------------------- static method start
-
- /**
- * 新建一个文件复制器
- *
- * @param srcPath 源文件路径(相对ClassPath路径或绝对路径)
- * @param destPath 目标文件路径(相对ClassPath路径或绝对路径)
- * @return this
- */
- public static FileCopier of(final String srcPath, final String destPath) {
- return new FileCopier(FileUtil.file(srcPath), FileUtil.file(destPath));
- }
-
- /**
- * 新建一个文件复制器
- *
- * @param src 源文件
- * @param dest 目标文件
- * @return this
- */
- public static FileCopier of(final File src, final File dest) {
- return new FileCopier(src, dest);
- }
- //-------------------------------------------------------------------------------------------------------- static method end
-
- //-------------------------------------------------------------------------------------------------------- Constructor start
-
- /**
- * 构造
- *
- * @param src 源文件
- * @param dest 目标文件
- */
- public FileCopier(final File src, final File dest) {
- this.src = src;
- this.dest = dest;
- }
- //-------------------------------------------------------------------------------------------------------- Constructor end
-
- //-------------------------------------------------------------------------------------------------------- Getters and Setters start
-
- /**
- * 是否覆盖目标文件
- *
- * @return 是否覆盖目标文件
- */
- public boolean isOverride() {
- return isOverride;
- }
-
- /**
- * 设置是否覆盖目标文件
- *
- * @param isOverride 是否覆盖目标文件
- * @return this
- */
- public FileCopier setOverride(final boolean isOverride) {
- this.isOverride = isOverride;
- return this;
- }
-
- /**
- * 是否拷贝所有属性
- *
- * @return 是否拷贝所有属性
- */
- public boolean isCopyAttributes() {
- return isCopyAttributes;
- }
-
- /**
- * 设置是否拷贝所有属性
- *
- * @param isCopyAttributes 是否拷贝所有属性
- * @return this
- */
- public FileCopier setCopyAttributes(final boolean isCopyAttributes) {
- this.isCopyAttributes = isCopyAttributes;
- return this;
- }
-
- /**
- * 当拷贝来源是目录时是否只拷贝目录下的内容
- *
- * @return 当拷贝来源是目录时是否只拷贝目录下的内容
- */
- public boolean isCopyContentIfDir() {
- return isCopyContentIfDir;
- }
-
- /**
- * 当拷贝来源是目录时是否只拷贝目录下的内容
- *
- * @param isCopyContentIfDir 是否只拷贝目录下的内容
- * @return this
- */
- public FileCopier setCopyContentIfDir(final boolean isCopyContentIfDir) {
- this.isCopyContentIfDir = isCopyContentIfDir;
- return this;
- }
-
- /**
- * 当拷贝来源是目录时是否只拷贝文件而忽略子目录
- *
- * @return 当拷贝来源是目录时是否只拷贝文件而忽略子目录
- * @since 4.1.5
- */
- public boolean isOnlyCopyFile() {
- return isOnlyCopyFile;
- }
-
- /**
- * 设置当拷贝来源是目录时是否只拷贝文件而忽略子目录
- *
- * @param isOnlyCopyFile 当拷贝来源是目录时是否只拷贝文件而忽略子目录
- * @return this
- * @since 4.1.5
- */
- public FileCopier setOnlyCopyFile(final boolean isOnlyCopyFile) {
- this.isOnlyCopyFile = isOnlyCopyFile;
- return this;
- }
- //-------------------------------------------------------------------------------------------------------- Getters and Setters end
-
- /**
- * 执行拷贝
- * 拷贝规则为:
- *
- * 1、源为文件,目标为已存在目录,则拷贝到目录下,文件名不变
- * 2、源为文件,目标为不存在路径,则目标以文件对待(自动创建父级目录)比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
- * 3、源为文件,目标是一个已存在的文件,则当{@link #setOverride(boolean)}设为true时会被覆盖,默认不覆盖
- * 4、源为目录,目标为已存在目录,当{@link #setCopyContentIfDir(boolean)}为true时,只拷贝目录中的内容到目标目录中,否则整个源目录连同其目录拷贝到目标目录中
- * 5、源为目录,目标为不存在路径,则自动创建目标为新目录,然后按照规则4复制
- * 6、源为目录,目标为文件,抛出IO异常
- * 7、源路径和目标路径相同时,抛出IO异常
- *
- *
- * @return 拷贝后目标的文件或目录
- * @throws IORuntimeException IO异常
- */
- @Override
- public File copy() throws IORuntimeException {
- final File src = this.src;
- File dest = this.dest;
- // check
- Assert.notNull(src, "Source File is null !");
- if (false == src.exists()) {
- throw new IORuntimeException("File not exist: " + src);
- }
- Assert.notNull(dest, "Destination File or directiory is null !");
- if (FileUtil.equals(src, dest)) {
- throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest);
- }
-
- if (src.isDirectory()) {// 复制目录
- if (dest.exists() && false == dest.isDirectory()) {
- //源为目录,目标为文件,抛出IO异常
- throw new IORuntimeException("Src is a directory but dest is a file!");
- }
- if (FileUtil.isSub(src, dest)) {
- throw new IORuntimeException("Dest is a sub directory of src !");
- }
-
- final File subTarget = isCopyContentIfDir ? dest : FileUtil.mkdir(FileUtil.file(dest, src.getName()));
- internalCopyDirContent(src, subTarget);
- } else {// 复制文件
- dest = internalCopyFile(src, dest);
- }
- return dest;
- }
-
- //----------------------------------------------------------------------------------------- Private method start
-
- /**
- * 拷贝目录内容,只用于内部,不做任何安全检查
- * 拷贝内容的意思为源目录下的所有文件和目录拷贝到另一个目录下,而不拷贝源目录本身
- *
- * @param src 源目录
- * @param dest 目标目录
- * @throws IORuntimeException IO异常
- */
- private void internalCopyDirContent(final File src, final File dest) throws IORuntimeException {
- if (null != copyPredicate && false == copyPredicate.test(src)) {
- //被过滤的目录跳过
- return;
- }
-
- if (false == dest.exists()) {
- //目标为不存在路径,创建为目录
- //noinspection ResultOfMethodCallIgnored
- dest.mkdirs();
- } else if (false == dest.isDirectory()) {
- throw new IORuntimeException(StrUtil.format("Src [{}] is a directory but dest [{}] is a file!", src.getPath(), dest.getPath()));
- }
-
- final String[] files = src.list();
- if (ArrayUtil.isNotEmpty(files)) {
- File srcFile;
- File destFile;
- for (final String file : files) {
- srcFile = new File(src, file);
- destFile = this.isOnlyCopyFile ? dest : new File(dest, file);
- // 递归复制
- if (srcFile.isDirectory()) {
- internalCopyDirContent(srcFile, destFile);
- } else {
- internalCopyFile(srcFile, destFile);
- }
- }
- }
- }
-
- /**
- * 拷贝文件,只用于内部,不做任何安全检查
- * 情况如下:
- *
- * 1、如果目标是一个不存在的路径,则目标以文件对待(自动创建父级目录)比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
- * 2、如果目标是一个已存在的目录,则文件拷贝到此目录下,文件名与原文件名一致
- *
- *
- * @param src 源文件,必须为文件
- * @param dest 目标文件,如果非覆盖模式必须为目录
- * @return 目标文件
- * @throws IORuntimeException IO异常
- */
- private File internalCopyFile(final File src, File dest) throws IORuntimeException {
- if (null != copyPredicate && false == copyPredicate.test(src)) {
- //被过滤的文件跳过
- return dest;
- }
-
- // 如果已经存在目标文件,切为不覆盖模式,跳过之
- if (dest.exists()) {
- if (dest.isDirectory()) {
- //目标为目录,目录下创建同名文件
- dest = new File(dest, src.getName());
- }
-
- if (dest.exists() && false == isOverride) {
- //非覆盖模式跳过
- return dest;
- }
- } else {
- //路径不存在则创建父目录
- FileUtil.mkParentDirs(dest);
- }
-
- final ArrayList optionList = new ArrayList<>(2);
- if (isOverride) {
- optionList.add(StandardCopyOption.REPLACE_EXISTING);
- }
- if (isCopyAttributes) {
- optionList.add(StandardCopyOption.COPY_ATTRIBUTES);
- }
-
- try {
- Files.copy(src.toPath(), dest.toPath(), optionList.toArray(new CopyOption[0]));
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- return dest;
- }
- //----------------------------------------------------------------------------------------- Private method end
-}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileMagicNumber.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileMagicNumber.java
old mode 100755
new mode 100644
similarity index 65%
rename from hutool-core/src/main/java/cn/hutool/core/io/FileMagicNumber.java
rename to hutool-core/src/main/java/cn/hutool/core/io/file/FileMagicNumber.java
index 61c377ffd..bb7a1d02c
--- a/hutool-core/src/main/java/cn/hutool/core/io/FileMagicNumber.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileMagicNumber.java
@@ -1,4 +1,4 @@
-package cn.hutool.core.io;
+package cn.hutool.core.io.file;
import cn.hutool.core.util.ArrayUtil;
@@ -13,25 +13,34 @@ import java.util.Objects;
* @since 5.8.12
*/
public enum FileMagicNumber {
+ /**
+ * 未知类型
+ */
UNKNOWN(null, null) {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return false;
}
},
- //image start---------------------------------------------
+ // region ----- image
+ /**
+ * jpeg
+ */
JPEG("image/jpeg", "jpg") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 2
&& Objects.equals(bytes[0], (byte) 0xff)
&& Objects.equals(bytes[1], (byte) 0xd8)
&& Objects.equals(bytes[2], (byte) 0xff);
}
},
+ /**
+ * jxr
+ */
JXR("image/vnd.ms-photo", "jxr") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
//file magic number https://www.iana.org/assignments/media-types/image/jxr
return bytes.length > 2
&& Objects.equals(bytes[0], (byte) 0x49)
@@ -39,10 +48,13 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[2], (byte) 0xbc);
}
},
+ /**
+ * apng
+ */
APNG("image/apng", "apng") {
@Override
- public boolean match(byte[] bytes) {
- boolean b = bytes.length > 8
+ public boolean match(final byte[] bytes) {
+ final boolean b = bytes.length > 8
&& Objects.equals(bytes[0], (byte) 0x89)
&& Objects.equals(bytes[1], (byte) 0x50)
&& Objects.equals(bytes[2], (byte) 0x4e)
@@ -56,10 +68,10 @@ public enum FileMagicNumber {
int i = 8;
while (i < bytes.length) {
try {
- int dataLength = new BigInteger(1, Arrays.copyOfRange(bytes, i, i + 4)).intValue();
+ final int dataLength = new BigInteger(1, Arrays.copyOfRange(bytes, i, i + 4)).intValue();
i += 4;
- byte[] bytes1 = Arrays.copyOfRange(bytes, i, i + 4);
- String chunkType = new String(bytes1);
+ final byte[] bytes1 = Arrays.copyOfRange(bytes, i, i + 4);
+ final String chunkType = new String(bytes1);
i += 4;
if (Objects.equals(chunkType, "IDAT") || Objects.equals(chunkType, "IEND")) {
return false;
@@ -67,7 +79,7 @@ public enum FileMagicNumber {
return true;
}
i += dataLength + 4;
- } catch (Exception e) {
+ } catch (final Exception e) {
return false;
}
}
@@ -75,9 +87,12 @@ public enum FileMagicNumber {
return false;
}
},
+ /**
+ * png
+ */
PNG("image/png", "png") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x89)
&& Objects.equals(bytes[1], (byte) 0x50)
@@ -85,34 +100,43 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x47);
}
},
+ /**
+ * gif
+ */
GIF("image/gif", "gif") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 2
&& Objects.equals(bytes[0], (byte) 0x47)
&& Objects.equals(bytes[1], (byte) 0x49)
&& Objects.equals(bytes[2], (byte) 0x46);
}
},
+ /**
+ * bmp
+ */
BMP("image/bmp", "bmp") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 1
&& Objects.equals(bytes[0], (byte) 0x42)
&& Objects.equals(bytes[1], (byte) 0x4d);
}
},
+ /**
+ * tiff
+ */
TIFF("image/tiff", "tiff") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 4) {
return false;
}
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x49)
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x49)
&& Objects.equals(bytes[1], (byte) 0x49)
&& Objects.equals(bytes[2], (byte) 0x2a)
&& Objects.equals(bytes[3], (byte) 0x00);
- boolean flag2 = (Objects.equals(bytes[0], (byte) 0x4d)
+ final boolean flag2 = (Objects.equals(bytes[0], (byte) 0x4d)
&& Objects.equals(bytes[1], (byte) 0x4d)
&& Objects.equals(bytes[2], (byte) 0x00)
&& Objects.equals(bytes[3], (byte) 0x2a));
@@ -120,10 +144,12 @@ public enum FileMagicNumber {
}
},
-
+ /**
+ * dwg
+ */
DWG("image/vnd.dwg", "dwg") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 10
&& Objects.equals(bytes[0], (byte) 0x41)
&& Objects.equals(bytes[1], (byte) 0x43)
@@ -131,10 +157,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x30);
}
},
-
+ /**
+ * webp
+ */
WEBP("image/webp", "webp") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 11
&& Objects.equals(bytes[8], (byte) 0x57)
&& Objects.equals(bytes[9], (byte) 0x45)
@@ -142,9 +170,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[11], (byte) 0x50);
}
},
+ /**
+ * psd
+ */
PSD("image/vnd.adobe.photoshop", "psd") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x38)
&& Objects.equals(bytes[1], (byte) 0x42)
@@ -152,9 +183,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x53);
}
},
+ /**
+ * icon
+ */
ICO("image/x-icon", "ico") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x00)
&& Objects.equals(bytes[1], (byte) 0x00)
@@ -162,9 +196,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x00);
}
},
+ /**
+ * xcf
+ */
XCF("image/x-xcf", "xcf") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 9
&& Objects.equals(bytes[0], (byte) 0x67)
&& Objects.equals(bytes[1], (byte) 0x69)
@@ -178,13 +215,15 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[9], (byte) 0x76);
}
},
- //image end-----------------------------------------------
-
- //audio start---------------------------------------------
+ // endregion
+ // region ----- audio
+ /**
+ * wav
+ */
WAV("audio/x-wav", "wav") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 11
&& Objects.equals(bytes[0], (byte) 0x52)
&& Objects.equals(bytes[1], (byte) 0x49)
@@ -196,9 +235,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[11], (byte) 0x45);
}
},
+ /**
+ * midi
+ */
MIDI("audio/midi", "midi") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x4d)
&& Objects.equals(bytes[1], (byte) 0x54)
@@ -206,22 +248,28 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x64);
}
},
+ /**
+ * mpeg-mp3
+ */
MP3("audio/mpeg", "mp3") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 2) {
return false;
}
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x49) && Objects.equals(bytes[1], (byte) 0x44) && Objects.equals(bytes[2], (byte) 0x33);
- boolean flag2 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xFB);
- boolean flag3 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF3);
- boolean flag4 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF2);
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x49) && Objects.equals(bytes[1], (byte) 0x44) && Objects.equals(bytes[2], (byte) 0x33);
+ final boolean flag2 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xFB);
+ final boolean flag3 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF3);
+ final boolean flag4 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF2);
return flag1 || flag2 || flag3 || flag4;
}
},
+ /**
+ * ogg
+ */
OGG("audio/ogg", "ogg") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x4f)
&& Objects.equals(bytes[1], (byte) 0x67)
@@ -229,9 +277,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x53);
}
},
+ /**
+ * flac
+ */
FLAC("audio/x-flac", "flac") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x66)
&& Objects.equals(bytes[1], (byte) 0x4c)
@@ -239,9 +290,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x43);
}
},
+ /**
+ * mp4
+ */
M4A("audio/mp4", "m4a") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return (bytes.length > 10
&& Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
@@ -256,32 +310,38 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x20));
}
},
+ /**
+ * aac
+ */
AAC("audio/aac", "aac") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 1) {
return false;
}
- boolean flag1 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF1);
- boolean flag2 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF9);
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF1);
+ final boolean flag2 = Objects.equals(bytes[0], (byte) 0xFF) && Objects.equals(bytes[1], (byte) 0xF9);
return flag1 || flag2;
}
},
+ /**
+ * amr
+ */
AMR("audio/amr", "amr") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
//single-channel
if (bytes.length < 11) {
return false;
}
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x23)
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x23)
&& Objects.equals(bytes[1], (byte) 0x21)
&& Objects.equals(bytes[2], (byte) 0x41)
&& Objects.equals(bytes[3], (byte) 0x4d)
&& Objects.equals(bytes[4], (byte) 0x52)
&& Objects.equals(bytes[5], (byte) 0x0A);
//multi-channel:
- boolean flag2 = Objects.equals(bytes[0], (byte) 0x23)
+ final boolean flag2 = Objects.equals(bytes[0], (byte) 0x23)
&& Objects.equals(bytes[1], (byte) 0x21)
&& Objects.equals(bytes[2], (byte) 0x41)
&& Objects.equals(bytes[3], (byte) 0x4d)
@@ -296,17 +356,23 @@ public enum FileMagicNumber {
return flag1 || flag2;
}
},
+ /**
+ * ac3
+ */
AC3("audio/ac3", "ac3") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 2
&& Objects.equals(bytes[0], (byte) 0x0b)
&& Objects.equals(bytes[1], (byte) 0x77);
}
},
+ /**
+ * aiff
+ */
AIFF("audio/x-aiff", "aiff") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 11
&& Objects.equals(bytes[0], (byte) 0x46)
&& Objects.equals(bytes[1], (byte) 0x4f)
@@ -318,59 +384,72 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[11], (byte) 0x46);
}
},
- //audio end-----------------------------------------------
+ // endregion
- //font start---------------------------------------------
- // The existing registration "application/font-woff" is deprecated in favor of "font/woff".
+ // region ----- font
+ /**
+ * woff
+ * The existing registration "application/font-woff" is deprecated in favor of "font/woff".
+ */
WOFF("font/woff", "woff") {
@Override
- public boolean match(byte[] bytes) {
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x77)
+ public boolean match(final byte[] bytes) {
+ if(bytes.length < 8){
+ return false;
+ }
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x77)
&& Objects.equals(bytes[1], (byte) 0x4f)
&& Objects.equals(bytes[2], (byte) 0x46)
&& Objects.equals(bytes[3], (byte) 0x46);
- boolean flag2 = Objects.equals(bytes[4], (byte) 0x00)
+ final boolean flag2 = Objects.equals(bytes[4], (byte) 0x00)
&& Objects.equals(bytes[5], (byte) 0x01)
&& Objects.equals(bytes[6], (byte) 0x00)
&& Objects.equals(bytes[7], (byte) 0x00);
- boolean flag3 = Objects.equals(bytes[4], (byte) 0x4f)
+ final boolean flag3 = Objects.equals(bytes[4], (byte) 0x4f)
&& Objects.equals(bytes[5], (byte) 0x54)
&& Objects.equals(bytes[6], (byte) 0x54)
&& Objects.equals(bytes[7], (byte) 0x4f);
- boolean flag4 = Objects.equals(bytes[4], (byte) 0x74)
+ final boolean flag4 = Objects.equals(bytes[4], (byte) 0x74)
&& Objects.equals(bytes[5], (byte) 0x72)
&& Objects.equals(bytes[6], (byte) 0x75)
&& Objects.equals(bytes[7], (byte) 0x65);
- return bytes.length > 7
- && (flag1 && (flag2 || flag3 || flag4));
+ return flag1 && (flag2 || flag3 || flag4);
}
},
+ /**
+ * woff2
+ */
WOFF2("font/woff2", "woff2") {
@Override
- public boolean match(byte[] bytes) {
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x77)
+ public boolean match(final byte[] bytes) {
+ if(bytes.length < 8){
+ return false;
+ }
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x77)
&& Objects.equals(bytes[1], (byte) 0x4f)
&& Objects.equals(bytes[2], (byte) 0x46)
&& Objects.equals(bytes[3], (byte) 0x32);
- boolean flag2 = Objects.equals(bytes[4], (byte) 0x00)
+ final boolean flag2 = Objects.equals(bytes[4], (byte) 0x00)
&& Objects.equals(bytes[5], (byte) 0x01)
&& Objects.equals(bytes[6], (byte) 0x00)
&& Objects.equals(bytes[7], (byte) 0x00);
- boolean flag3 = Objects.equals(bytes[4], (byte) 0x4f)
+ final boolean flag3 = Objects.equals(bytes[4], (byte) 0x4f)
&& Objects.equals(bytes[5], (byte) 0x54)
&& Objects.equals(bytes[6], (byte) 0x54)
&& Objects.equals(bytes[7], (byte) 0x4f);
- boolean flag4 = Objects.equals(bytes[4], (byte) 0x74)
+ final boolean flag4 = Objects.equals(bytes[4], (byte) 0x74)
&& Objects.equals(bytes[5], (byte) 0x72)
&& Objects.equals(bytes[6], (byte) 0x75)
&& Objects.equals(bytes[7], (byte) 0x65);
- return bytes.length > 7
- && (flag1 && (flag2 || flag3 || flag4));
+ return flag1 && (flag2 || flag3 || flag4);
}
},
+ /**
+ * ttf
+ */
TTF("font/ttf", "ttf") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 4
&& Objects.equals(bytes[0], (byte) 0x00)
&& Objects.equals(bytes[1], (byte) 0x01)
@@ -379,9 +458,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[4], (byte) 0x00);
}
},
+ /**
+ * otf
+ */
OTF("font/otf", "otf") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 4
&& Objects.equals(bytes[0], (byte) 0x4f)
&& Objects.equals(bytes[1], (byte) 0x54)
@@ -390,13 +472,15 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[4], (byte) 0x00);
}
},
+ // endregion
- //font end-----------------------------------------------
-
- //archive start-----------------------------------------
+ // region ----- archive
+ /**
+ * epub
+ */
EPUB("application/epub+zip", "epub") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 58
&& Objects.equals(bytes[0], (byte) 0x50) && Objects.equals(bytes[1], (byte) 0x4b)
&& Objects.equals(bytes[2], (byte) 0x03) && Objects.equals(bytes[3], (byte) 0x04)
@@ -416,21 +500,27 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[56], (byte) 0x69) && Objects.equals(bytes[57], (byte) 0x70);
}
},
+ /**
+ * zip
+ */
ZIP("application/zip", "zip") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 4) {
return false;
}
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x50) && Objects.equals(bytes[1], (byte) 0x4b);
- boolean flag2 = Objects.equals(bytes[2], (byte) 0x03) || Objects.equals(bytes[2], (byte) 0x05) || Objects.equals(bytes[2], (byte) 0x07);
- boolean flag3 = Objects.equals(bytes[3], (byte) 0x04) || Objects.equals(bytes[3], (byte) 0x06) || Objects.equals(bytes[3], (byte) 0x08);
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x50) && Objects.equals(bytes[1], (byte) 0x4b);
+ final boolean flag2 = Objects.equals(bytes[2], (byte) 0x03) || Objects.equals(bytes[2], (byte) 0x05) || Objects.equals(bytes[2], (byte) 0x07);
+ final boolean flag3 = Objects.equals(bytes[3], (byte) 0x04) || Objects.equals(bytes[3], (byte) 0x06) || Objects.equals(bytes[3], (byte) 0x08);
return flag1 && flag2 && flag3;
}
},
+ /**
+ * tar
+ */
TAR("application/x-tar", "tar") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 261
&& Objects.equals(bytes[257], (byte) 0x75)
&& Objects.equals(bytes[258], (byte) 0x73)
@@ -439,9 +529,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[261], (byte) 0x72);
}
},
+ /**
+ * rar
+ */
RAR("application/x-rar-compressed", "rar") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 6
&& Objects.equals(bytes[0], (byte) 0x52)
&& Objects.equals(bytes[1], (byte) 0x61)
@@ -452,27 +545,36 @@ public enum FileMagicNumber {
&& (Objects.equals(bytes[6], (byte) 0x00) || Objects.equals(bytes[6], (byte) 0x01));
}
},
+ /**
+ * gz
+ */
GZ("application/gzip", "gz") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 2
&& Objects.equals(bytes[0], (byte) 0x1f)
&& Objects.equals(bytes[1], (byte) 0x8b)
&& Objects.equals(bytes[2], (byte) 0x08);
}
},
+ /**
+ * bz2
+ */
BZ2("application/x-bzip2", "bz2") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 2
&& Objects.equals(bytes[0], (byte) 0x42)
&& Objects.equals(bytes[1], (byte) 0x5a)
&& Objects.equals(bytes[2], (byte) 0x68);
}
},
+ /**
+ * 7z
+ */
SevenZ("application/x-7z-compressed", "7z") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 6
&& Objects.equals(bytes[0], (byte) 0x37)
&& Objects.equals(bytes[1], (byte) 0x7a)
@@ -483,6 +585,9 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[6], (byte) 0x00);
}
},
+ /**
+ * pdf
+ */
PDF("application/pdf", "pdf") {
@Override
public boolean match(byte[] bytes) {
@@ -498,26 +603,35 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x46);
}
},
+ /**
+ * exe
+ */
EXE("application/x-msdownload", "exe") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 1
&& Objects.equals(bytes[0], (byte) 0x4d)
&& Objects.equals(bytes[1], (byte) 0x5a);
}
},
+ /**
+ * swf
+ */
SWF("application/x-shockwave-flash", "swf") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 2
&& (Objects.equals(bytes[0], 0x43) || Objects.equals(bytes[0], (byte) 0x46))
&& Objects.equals(bytes[1], (byte) 0x57)
&& Objects.equals(bytes[2], (byte) 0x53);
}
},
+ /**
+ * rtf
+ */
RTF("application/rtf", "rtf") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 4
&& Objects.equals(bytes[0], (byte) 0x7b)
&& Objects.equals(bytes[1], (byte) 0x5c)
@@ -526,9 +640,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[4], (byte) 0x66);
}
},
+ /**
+ * nes
+ */
NES("application/x-nintendo-nes-rom", "nes") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x4e)
&& Objects.equals(bytes[1], (byte) 0x45)
@@ -536,9 +653,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x1a);
}
},
+ /**
+ * crx
+ */
CRX("application/x-google-chrome-extension", "crx") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x43)
&& Objects.equals(bytes[1], (byte) 0x72)
@@ -546,30 +666,39 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x34);
}
},
+ /**
+ * cab
+ */
CAB("application/vnd.ms-cab-compressed", "cab") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 4) {
return false;
}
- boolean flag1 = Objects.equals(bytes[0], (byte) 0x4d) && Objects.equals(bytes[1], (byte) 0x53)
+ final boolean flag1 = Objects.equals(bytes[0], (byte) 0x4d) && Objects.equals(bytes[1], (byte) 0x53)
&& Objects.equals(bytes[2], (byte) 0x43) && Objects.equals(bytes[3], (byte) 0x46);
- boolean flag2 = Objects.equals(bytes[0], (byte) 0x49) && Objects.equals(bytes[1], (byte) 0x53)
+ final boolean flag2 = Objects.equals(bytes[0], (byte) 0x49) && Objects.equals(bytes[1], (byte) 0x53)
&& Objects.equals(bytes[2], (byte) 0x63) && Objects.equals(bytes[3], (byte) 0x28);
return flag1 || flag2;
}
},
+ /**
+ * ps
+ */
PS("application/postscript", "ps") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 1
&& Objects.equals(bytes[0], (byte) 0x25)
&& Objects.equals(bytes[1], (byte) 0x21);
}
},
+ /**
+ * xz
+ */
XZ("application/x-xz", "xz") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 5
&& Objects.equals(bytes[0], (byte) 0xFD)
&& Objects.equals(bytes[1], (byte) 0x37)
@@ -579,9 +708,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[5], (byte) 0x00);
}
},
+ /**
+ * sqlite
+ */
SQLITE("application/x-sqlite3", "sqlite") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 15
&& Objects.equals(bytes[0], (byte) 0x53) && Objects.equals(bytes[1], (byte) 0x51)
&& Objects.equals(bytes[2], (byte) 0x4c) && Objects.equals(bytes[3], (byte) 0x69)
@@ -593,9 +725,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[14], (byte) 0x33) && Objects.equals(bytes[15], (byte) 0x00);
}
},
+ /**
+ * deb
+ */
DEB("application/x-deb", "deb") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 20
&& Objects.equals(bytes[0], (byte) 0x21) && Objects.equals(bytes[1], (byte) 0x3c)
&& Objects.equals(bytes[2], (byte) 0x61) && Objects.equals(bytes[3], (byte) 0x72)
@@ -610,9 +745,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[20], (byte) 0x79);
}
},
+ /**
+ * ar
+ */
AR("application/x-unix-archive", "ar") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 6
&& Objects.equals(bytes[0], (byte) 0x21)
&& Objects.equals(bytes[1], (byte) 0x3c)
@@ -623,9 +761,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[6], (byte) 0x3e);
}
},
+ /**
+ * lzo
+ */
LZOP("application/x-lzop", "lzo") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 7
&& Objects.equals(bytes[0], (byte) 0x89)
&& Objects.equals(bytes[1], (byte) 0x4c)
@@ -637,9 +778,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[7], (byte) 0x1a);
}
},
+ /**
+ * lz
+ */
LZ("application/x-lzip", "lz") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x4c)
&& Objects.equals(bytes[1], (byte) 0x5a)
@@ -647,9 +791,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x50);
}
},
+ /**
+ * elf
+ */
ELF("application/x-executable", "elf") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 52
&& Objects.equals(bytes[0], (byte) 0x7f)
&& Objects.equals(bytes[1], (byte) 0x45)
@@ -657,9 +804,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x46);
}
},
+ /**
+ * lz4
+ */
LZ4("application/x-lz4", "lz4") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 4
&& Objects.equals(bytes[0], (byte) 0x04)
&& Objects.equals(bytes[1], (byte) 0x22)
@@ -667,10 +817,13 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x18);
}
},
- //https://github.com/madler/brotli/blob/master/br-format-v3.txt,brotli 没有固定的file magic number,所以此处只是参考
+ /**
+ * br
+ * https://github.com/madler/brotli/blob/master/br-format-v3.txt,brotli 没有固定的file magic number,所以此处只是参考
+ */
BR("application/x-brotli", "br") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0xce)
&& Objects.equals(bytes[1], (byte) 0xb2)
@@ -678,9 +831,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x81);
}
},
+ /**
+ * dcm
+ */
DCM("application/x-dicom", "dcm") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 128
&& Objects.equals(bytes[128], (byte) 0x44)
&& Objects.equals(bytes[129], (byte) 0x49)
@@ -688,9 +844,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[131], (byte) 0x4d);
}
},
+ /**
+ * rpm
+ */
RPM("application/x-rpm", "rpm") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 4
&& Objects.equals(bytes[0], (byte) 0xed)
&& Objects.equals(bytes[1], (byte) 0xab)
@@ -698,15 +857,18 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0xdb);
}
},
+ /**
+ * zst
+ */
ZSTD("application/x-zstd", "zst") {
@Override
- public boolean match(byte[] bytes) {
- int length = bytes.length;
+ public boolean match(final byte[] bytes) {
+ final int length = bytes.length;
if (length < 5) {
return false;
}
- byte[] buf1 = new byte[]{(byte) 0x22, (byte) 0x23, (byte) 0x24, (byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28};
- boolean flag1 = ArrayUtil.contains(buf1, bytes[0])
+ final byte[] buf1 = new byte[]{(byte) 0x22, (byte) 0x23, (byte) 0x24, (byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28};
+ final boolean flag1 = ArrayUtil.contains(buf1, bytes[0])
&& Objects.equals(bytes[1], (byte) 0xb5)
&& Objects.equals(bytes[2], (byte) 0x2f)
&& Objects.equals(bytes[3], (byte) 0xfd);
@@ -719,15 +881,19 @@ public enum FileMagicNumber {
return false;
}
},
- //archive end------------------------------------------------------------
- //video start------------------------------------------------------------
+ // endregion
+
+ // region ----- video
+ /**
+ * mp4
+ */
MP4("video/mp4", "mp4") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 13) {
return false;
}
- boolean flag1 = Objects.equals(bytes[4], (byte) 0x66)
+ final boolean flag1 = Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
&& Objects.equals(bytes[6], (byte) 0x79)
&& Objects.equals(bytes[7], (byte) 0x70)
@@ -735,7 +901,7 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[9], (byte) 0x53)
&& Objects.equals(bytes[10], (byte) 0x4e)
&& Objects.equals(bytes[11], (byte) 0x56);
- boolean flag2 = Objects.equals(bytes[4], (byte) 0x66)
+ final boolean flag2 = Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
&& Objects.equals(bytes[6], (byte) 0x79)
&& Objects.equals(bytes[7], (byte) 0x70)
@@ -746,9 +912,12 @@ public enum FileMagicNumber {
return flag1 || flag2;
}
},
+ /**
+ * avi
+ */
AVI("video/x-msvideo", "avi") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 11
&& Objects.equals(bytes[0], (byte) 0x52)
&& Objects.equals(bytes[1], (byte) 0x49)
@@ -760,9 +929,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[11], (byte) 0x20);
}
},
+ /**
+ * wmv
+ */
WMV("video/x-ms-wmv", "wmv") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 9
&& Objects.equals(bytes[0], (byte) 0x30)
&& Objects.equals(bytes[1], (byte) 0x26)
@@ -776,13 +948,16 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[9], (byte) 0xd9);
}
},
+ /**
+ * m4v
+ */
M4V("video/x-m4v", "m4v") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 12) {
return false;
}
- boolean flag1 = Objects.equals(bytes[4], (byte) 0x66)
+ final boolean flag1 = Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
&& Objects.equals(bytes[6], (byte) 0x79)
&& Objects.equals(bytes[7], (byte) 0x70)
@@ -790,7 +965,7 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[9], (byte) 0x34)
&& Objects.equals(bytes[10], (byte) 0x56)
&& Objects.equals(bytes[11], (byte) 0x20);
- boolean flag2 = Objects.equals(bytes[4], (byte) 0x66)
+ final boolean flag2 = Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
&& Objects.equals(bytes[6], (byte) 0x79)
&& Objects.equals(bytes[7], (byte) 0x70)
@@ -801,9 +976,12 @@ public enum FileMagicNumber {
return flag1 || flag2;
}
},
+ /**
+ * flv
+ */
FLV("video/x-flv", "flv") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x46)
&& Objects.equals(bytes[1], (byte) 0x4c)
@@ -811,11 +989,14 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x01);
}
},
+ /**
+ * mkv
+ */
MKV("video/x-matroska", "mkv") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
//0x42 0x82 0x88 0x6d 0x61 0x74 0x72 0x6f 0x73 0x6b 0x61
- boolean flag1 = bytes.length > 11
+ final boolean flag1 = bytes.length > 11
&& Objects.equals(bytes[0], (byte) 0x1a)
&& Objects.equals(bytes[1], (byte) 0x45)
&& Objects.equals(bytes[2], (byte) 0xdf)
@@ -823,39 +1004,44 @@ public enum FileMagicNumber {
if (flag1) {
//此处需要判断是否是'\x42\x82\x88matroska',算法类似kmp判断
- byte[] bytes1 = {(byte) 0x42, (byte) 0x82, (byte) 0x88, (byte) 0x6d, (byte) 0x61, (byte) 0x74, (byte) 0x72, (byte) 0x6f, (byte) 0x73, (byte) 0x6b, (byte) 0x61};
- int index = FileMagicNumber.indexOf(bytes, bytes1);
+ final byte[] bytes1 = {(byte) 0x42, (byte) 0x82, (byte) 0x88, (byte) 0x6d, (byte) 0x61, (byte) 0x74, (byte) 0x72, (byte) 0x6f, (byte) 0x73, (byte) 0x6b, (byte) 0x61};
+ final int index = FileMagicNumber.indexOf(bytes, bytes1);
return index > 0;
}
return false;
}
},
-
+ /**
+ * webm
+ */
WEBM("video/webm", "webm") {
@Override
- public boolean match(byte[] bytes) {
- boolean flag1 = bytes.length > 8
+ public boolean match(final byte[] bytes) {
+ final boolean flag1 = bytes.length > 8
&& Objects.equals(bytes[0], (byte) 0x1a)
&& Objects.equals(bytes[1], (byte) 0x45)
&& Objects.equals(bytes[2], (byte) 0xdf)
&& Objects.equals(bytes[3], (byte) 0xa3);
if (flag1) {
//此处需要判断是否是'\x42\x82\x88webm',算法类似kmp判断
- byte[] bytes1 = {(byte) 0x42, (byte) 0x82, (byte) 0x88, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6d};
- int index = FileMagicNumber.indexOf(bytes, bytes1);
+ final byte[] bytes1 = {(byte) 0x42, (byte) 0x82, (byte) 0x88, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6d};
+ final int index = FileMagicNumber.indexOf(bytes, bytes1);
return index > 0;
}
return false;
}
},
- //此文件签名非常复杂,只判断常见的几种
+ /**
+ * mov
+ * 此文件签名非常复杂,只判断常见的几种
+ */
MOV("video/quicktime", "mov") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 12) {
return false;
}
- boolean flag1 = Objects.equals(bytes[4], (byte) 0x66)
+ final boolean flag1 = Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
&& Objects.equals(bytes[6], (byte) 0x79)
&& Objects.equals(bytes[7], (byte) 0x70)
@@ -863,36 +1049,39 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[9], (byte) 0x74)
&& Objects.equals(bytes[10], (byte) 0x20)
&& Objects.equals(bytes[11], (byte) 0x20);
- boolean flag2 = Objects.equals(bytes[4], (byte) 0x6D)
+ final boolean flag2 = Objects.equals(bytes[4], (byte) 0x6D)
&& Objects.equals(bytes[5], (byte) 0x6F)
&& Objects.equals(bytes[6], (byte) 0x6F)
&& Objects.equals(bytes[7], (byte) 0x76);
- boolean flag3 = Objects.equals(bytes[4], (byte) 0x66)
+ final boolean flag3 = Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x72)
&& Objects.equals(bytes[6], (byte) 0x65)
&& Objects.equals(bytes[7], (byte) 0x65);
- boolean flag4 = Objects.equals(bytes[4], (byte) 0x6D)
+ final boolean flag4 = Objects.equals(bytes[4], (byte) 0x6D)
&& Objects.equals(bytes[5], (byte) 0x64)
&& Objects.equals(bytes[6], (byte) 0x61)
&& Objects.equals(bytes[7], (byte) 0x74);
- boolean flag5 = Objects.equals(bytes[4], (byte) 0x77)
+ final boolean flag5 = Objects.equals(bytes[4], (byte) 0x77)
&& Objects.equals(bytes[5], (byte) 0x69)
&& Objects.equals(bytes[6], (byte) 0x64)
&& Objects.equals(bytes[7], (byte) 0x65);
- boolean flag6 = Objects.equals(bytes[4], (byte) 0x70)
+ final boolean flag6 = Objects.equals(bytes[4], (byte) 0x70)
&& Objects.equals(bytes[5], (byte) 0x6E)
&& Objects.equals(bytes[6], (byte) 0x6F)
&& Objects.equals(bytes[7], (byte) 0x74);
- boolean flag7 = Objects.equals(bytes[4], (byte) 0x73)
+ final boolean flag7 = Objects.equals(bytes[4], (byte) 0x73)
&& Objects.equals(bytes[5], (byte) 0x6B)
&& Objects.equals(bytes[6], (byte) 0x69)
&& Objects.equals(bytes[7], (byte) 0x70);
return flag1 || flag2 || flag3 || flag4 || flag5 || flag6 || flag7;
}
},
+ /**
+ * mpg
+ */
MPEG("video/mpeg", "mpg") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 3
&& Objects.equals(bytes[0], (byte) 0x00)
&& Objects.equals(bytes[1], (byte) 0x00)
@@ -900,9 +1089,12 @@ public enum FileMagicNumber {
&& (bytes[3] >= (byte) 0xb0 && bytes[3] <= (byte) 0xbf);
}
},
+ /**
+ * rmvb
+ */
RMVB("video/vnd.rn-realvideo", "rmvb") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 4
&& Objects.equals(bytes[0], (byte) 0x2E)
&& Objects.equals(bytes[1], (byte) 0x52)
@@ -910,9 +1102,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[3], (byte) 0x46);
}
},
+ /**
+ * 3gp
+ */
M3GP("video/3gpp", "3gp") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 10
&& Objects.equals(bytes[4], (byte) 0x66)
&& Objects.equals(bytes[5], (byte) 0x74)
@@ -923,94 +1118,115 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[10], (byte) 0x70);
}
},
- //video end ---------------------------------------------------------------
- //document start ----------------------------------------------------------
+ // endregion
+
+ // region ----- document
+ /**
+ * doc
+ */
DOC("application/msword", "doc") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0xd0, (byte) 0xcf, (byte) 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, (byte) 0x1a, (byte) 0xe1};
- boolean flag1 = bytes.length > 515 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte1);
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0xd0, (byte) 0xcf, (byte) 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, (byte) 0x1a, (byte) 0xe1};
+ final boolean flag1 = bytes.length > 515 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte1);
if (flag1) {
- byte[] byte2 = new byte[]{(byte) 0xec, (byte) 0xa5, (byte) 0xc1, (byte) 0x00};
- boolean flag2 = Arrays.equals(Arrays.copyOfRange(bytes, 512, 516), byte2);
- byte[] byte3 = new byte[]{(byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x4d, (byte) 0x53, (byte) 0x57, (byte) 0x6f, (byte) 0x72, (byte) 0x64
+ final byte[] byte2 = new byte[]{(byte) 0xec, (byte) 0xa5, (byte) 0xc1, (byte) 0x00};
+ final boolean flag2 = Arrays.equals(Arrays.copyOfRange(bytes, 512, 516), byte2);
+ final byte[] byte3 = new byte[]{(byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x4d, (byte) 0x53, (byte) 0x57, (byte) 0x6f, (byte) 0x72, (byte) 0x64
, (byte) 0x44, (byte) 0x6f, (byte) 0x63, (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x57, (byte) 0x6f, (byte) 0x72, (byte) 0x64,
(byte) 0x2e, (byte) 0x44, (byte) 0x6f, (byte) 0x63, (byte) 0x75, (byte) 0x6d, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x2e, (byte) 0x38, (byte) 0x00,
(byte) 0xf4, (byte) 0x39, (byte) 0xb2, (byte) 0x71};
- byte[] range = Arrays.copyOfRange(bytes, 2075, 2142);
- boolean flag3 = bytes.length > 2142 && FileMagicNumber.indexOf(range, byte3) > 0;
+ final byte[] range = Arrays.copyOfRange(bytes, 2075, 2142);
+ final boolean flag3 = bytes.length > 2142 && FileMagicNumber.indexOf(range, byte3) > 0;
return flag2 || flag3;
}
return false;
}
},
-
+ /**
+ * xls
+ */
XLS("application/vnd.ms-excel", "xls") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0xd0, (byte) 0xcf, (byte) 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, (byte) 0x1a, (byte) 0xe1};
- boolean flag1 = bytes.length > 520 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte1);
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0xd0, (byte) 0xcf, (byte) 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, (byte) 0x1a, (byte) 0xe1};
+ final boolean flag1 = bytes.length > 520 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte1);
if (flag1) {
- byte[] byte2 = new byte[]{(byte) 0xfd, (byte) 0xff, (byte) 0xff, (byte) 0xff};
- boolean flag2 = Arrays.equals(Arrays.copyOfRange(bytes, 512, 516), byte2) && (bytes[518] == 0x00 || bytes[518] == 0x02);
- byte[] byte3 = new byte[]{(byte) 0x09, (byte) 0x08, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x05, (byte) 0x00};
- boolean flag3 = Arrays.equals(Arrays.copyOfRange(bytes, 512, 520), byte3);
- byte[] byte4 = new byte[]{(byte) 0xe2, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x70, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x43, (byte) 0x61, (byte) 0x6c, (byte) 0x63};
- boolean flag4 = bytes.length > 2095 && Arrays.equals(Arrays.copyOfRange(bytes, 1568, 2095), byte4);
+ final byte[] byte2 = new byte[]{(byte) 0xfd, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ final boolean flag2 = Arrays.equals(Arrays.copyOfRange(bytes, 512, 516), byte2) && (bytes[518] == 0x00 || bytes[518] == 0x02);
+ final byte[] byte3 = new byte[]{(byte) 0x09, (byte) 0x08, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x05, (byte) 0x00};
+ final boolean flag3 = Arrays.equals(Arrays.copyOfRange(bytes, 512, 520), byte3);
+ final byte[] byte4 = new byte[]{(byte) 0xe2, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x70, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x43, (byte) 0x61, (byte) 0x6c, (byte) 0x63};
+ final boolean flag4 = bytes.length > 2095 && Arrays.equals(Arrays.copyOfRange(bytes, 1568, 2095), byte4);
return flag2 || flag3 || flag4;
}
return false;
}
},
+ /**
+ * ppt
+ */
PPT("application/vnd.ms-powerpoint", "ppt") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0xd0, (byte) 0xcf, (byte) 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, (byte) 0x1a, (byte) 0xe1};
- boolean flag1 = bytes.length > 524 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte1);
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0xd0, (byte) 0xcf, (byte) 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, (byte) 0x1a, (byte) 0xe1};
+ final boolean flag1 = bytes.length > 524 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte1);
if (flag1) {
- byte[] byte2 = new byte[]{(byte) 0xa0, (byte) 0x46, (byte) 0x1d, (byte) 0xf0};
- byte[] byteRange = Arrays.copyOfRange(bytes, 512, 516);
- boolean flag2 = Arrays.equals(byteRange, byte2);
- byte[] byte3 = new byte[]{(byte) 0x00, (byte) 0x6e, (byte) 0x1e, (byte) 0xf0};
- boolean flag3 = Arrays.equals(byteRange, byte3);
- byte[] byte4 = new byte[]{(byte) 0x0f, (byte) 0x00, (byte) 0xe8, (byte) 0x03};
- boolean flag4 = Arrays.equals(byteRange, byte4);
- byte[] byte5 = new byte[]{(byte) 0xfd, (byte) 0xff, (byte) 0xff, (byte) 0xff};
- boolean flag5 = Arrays.equals(byteRange, byte5) && bytes[522] == 0x00 && bytes[523] == 0x00;
- byte[] byte6 = new byte[]{(byte) 0x00, (byte) 0xb9, (byte) 0x29, (byte) 0xe8, (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ final byte[] byte2 = new byte[]{(byte) 0xa0, (byte) 0x46, (byte) 0x1d, (byte) 0xf0};
+ final byte[] byteRange = Arrays.copyOfRange(bytes, 512, 516);
+ final boolean flag2 = Arrays.equals(byteRange, byte2);
+ final byte[] byte3 = new byte[]{(byte) 0x00, (byte) 0x6e, (byte) 0x1e, (byte) 0xf0};
+ final boolean flag3 = Arrays.equals(byteRange, byte3);
+ final byte[] byte4 = new byte[]{(byte) 0x0f, (byte) 0x00, (byte) 0xe8, (byte) 0x03};
+ final boolean flag4 = Arrays.equals(byteRange, byte4);
+ final byte[] byte5 = new byte[]{(byte) 0xfd, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ final boolean flag5 = Arrays.equals(byteRange, byte5) && bytes[522] == 0x00 && bytes[523] == 0x00;
+ final byte[] byte6 = new byte[]{(byte) 0x00, (byte) 0xb9, (byte) 0x29, (byte) 0xe8, (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x4d, (byte) 0x53, (byte) 0x20, (byte) 0x50, (byte) 0x6f, (byte) 0x77, (byte) 0x65, (byte) 0x72, (byte) 0x50, (byte)
0x6f, (byte) 0x69, (byte) 0x6e, (byte) 0x74, (byte) 0x20, (byte) 0x39, (byte) 0x37};
- boolean flag6 = bytes.length > 2096 && Arrays.equals(Arrays.copyOfRange(bytes, 2072, 2096), byte6);
+ final boolean flag6 = bytes.length > 2096 && Arrays.equals(Arrays.copyOfRange(bytes, 2072, 2096), byte6);
return flag2 || flag3 || flag4 || flag5 || flag6;
}
return false;
}
},
+ /**
+ * docx
+ */
DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return Objects.equals(FileMagicNumber.matchDocument(bytes), DOCX);
}
},
+ /**
+ * pptx
+ */
PPTX("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return Objects.equals(FileMagicNumber.matchDocument(bytes), PPTX);
}
},
+ /**
+ * xlsx
+ */
XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return Objects.equals(FileMagicNumber.matchDocument(bytes), XLSX);
}
},
+ // endregion
- //document end ------------------------------------------------------------
- //other start -------------------------------------------------------------
+ // region ----- others
+ /**
+ * wasm
+ */
WASM("application/wasm", "wasm") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 7
&& Objects.equals(bytes[0], (byte) 0x00)
&& Objects.equals(bytes[1], (byte) 0x61)
@@ -1022,10 +1238,13 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[7], (byte) 0x00);
}
},
- // https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
+ /**
+ * dex
+ * https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
+ */
DEX("application/vnd.android.dex", "dex") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 36
&& Objects.equals(bytes[0], (byte) 0x64)
&& Objects.equals(bytes[1], (byte) 0x65)
@@ -1034,9 +1253,12 @@ public enum FileMagicNumber {
&& Objects.equals(bytes[36], (byte) 0x70);
}
},
+ /**
+ * dey
+ */
DEY("application/vnd.android.dey", "dey") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
return bytes.length > 100
&& Objects.equals(bytes[0], (byte) 0x64)
&& Objects.equals(bytes[1], (byte) 0x65)
@@ -1045,113 +1267,172 @@ public enum FileMagicNumber {
DEX.match(Arrays.copyOfRange(bytes, 40, 100));
}
},
+ /**
+ * eml
+ */
EML("message/rfc822", "eml") {
@Override
- public boolean match(byte[] bytes) {
+ public boolean match(final byte[] bytes) {
if (bytes.length < 8) {
return false;
}
- byte[] byte1 = new byte[]{(byte) 0x46, (byte) 0x72, (byte) 0x6F, (byte) 0x6D, (byte) 0x20, (byte) 0x20, (byte) 0x20};
- byte[] byte2 = new byte[]{(byte) 0x46, (byte) 0x72, (byte) 0x6F, (byte) 0x6D, (byte) 0x20, (byte) 0x3F, (byte) 0x3F, (byte) 0x3F};
- byte[] byte3 = new byte[]{(byte) 0x46, (byte) 0x72, (byte) 0x6F, (byte) 0x6D, (byte) 0x3A, (byte) 0x20};
- byte[] byte4 = new byte[]{(byte) 0x52, (byte) 0x65, (byte) 0x74, (byte) 0x75, (byte) 0x72, (byte) 0x6E, (byte) 0x2D, (byte) 0x50, (byte) 0x61, (byte) 0x74, (byte) 0x68, (byte) 0x3A, (byte) 0x20};
+ final byte[] byte1 = new byte[]{(byte) 0x46, (byte) 0x72, (byte) 0x6F, (byte) 0x6D, (byte) 0x20, (byte) 0x20, (byte) 0x20};
+ final byte[] byte2 = new byte[]{(byte) 0x46, (byte) 0x72, (byte) 0x6F, (byte) 0x6D, (byte) 0x20, (byte) 0x3F, (byte) 0x3F, (byte) 0x3F};
+ final byte[] byte3 = new byte[]{(byte) 0x46, (byte) 0x72, (byte) 0x6F, (byte) 0x6D, (byte) 0x3A, (byte) 0x20};
+ final byte[] byte4 = new byte[]{(byte) 0x52, (byte) 0x65, (byte) 0x74, (byte) 0x75, (byte) 0x72, (byte) 0x6E, (byte) 0x2D, (byte) 0x50, (byte) 0x61, (byte) 0x74, (byte) 0x68, (byte) 0x3A, (byte) 0x20};
return Arrays.equals(Arrays.copyOfRange(bytes, 0, 7), byte1)
|| Arrays.equals(Arrays.copyOfRange(bytes, 0, 8), byte2)
|| Arrays.equals(Arrays.copyOfRange(bytes, 0, 6), byte3)
|| bytes.length > 13 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 13), byte4);
}
},
+ /**
+ * mdb
+ */
MDB("application/vnd.ms-access", "mdb") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x6E, (byte) 0x64,
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x6E, (byte) 0x64,
(byte) 0x61, (byte) 0x72, (byte) 0x64, (byte) 0x20, (byte) 0x4A, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x44, (byte) 0x42};
return bytes.length > 18 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 18), byte1);
}
},
- //CHM 49 54 53 46
+ /**
+ * CHM 49 54 53 46
+ */
CHM("application/vnd.ms-htmlhelp", "chm") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0x49, (byte) 0x54, (byte) 0x53, (byte) 0x46};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0x49, (byte) 0x54, (byte) 0x53, (byte) 0x46};
return bytes.length > 4 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 4), byte1);
}
},
- //class CA FE BA BE
+
+ /**
+ * class CA FE BA BE
+ */
CLASS("application/java-vm", "class") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
return bytes.length > 4 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 4), byte1);
}
},
- //torrent 64 38 3A 61 6E 6E 6F 75 6E 63 65
+ /**
+ * torrent 64 38 3A 61 6E 6E 6F 75 6E 63 65
+ */
TORRENT("application/x-bittorrent", "torrent") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0x64, (byte) 0x38, (byte) 0x3A, (byte) 0x61, (byte) 0x6E, (byte) 0x6E, (byte) 0x6F, (byte) 0x75, (byte) 0x6E, (byte) 0x63, (byte) 0x65};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0x64, (byte) 0x38, (byte) 0x3A, (byte) 0x61, (byte) 0x6E, (byte) 0x6E, (byte) 0x6F, (byte) 0x75, (byte) 0x6E, (byte) 0x63, (byte) 0x65};
return bytes.length > 11 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 11), byte1);
}
},
+ /**
+ * wpd
+ */
WPD("application/vnd.wordperfect", "wpd") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0xFF, (byte) 0x57, (byte) 0x50, (byte) 0x43};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0xFF, (byte) 0x57, (byte) 0x50, (byte) 0x43};
return bytes.length > 4 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 4), byte1);
}
},
+ /**
+ * dbx
+ */
DBX("", "dbx") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0xCF, (byte) 0xAD, (byte) 0x12, (byte) 0xFE};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0xCF, (byte) 0xAD, (byte) 0x12, (byte) 0xFE};
return bytes.length > 4 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 4), byte1);
}
},
+ /**
+ * pst
+ */
PST("application/vnd.ms-outlook-pst", "pst") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0x21, (byte) 0x42, (byte) 0x44, (byte) 0x4E};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0x21, (byte) 0x42, (byte) 0x44, (byte) 0x4E};
return bytes.length > 4 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 4), byte1);
}
},
+ /**
+ * ram
+ */
RAM("audio/x-pn-realaudio", "ram") {
@Override
- public boolean match(byte[] bytes) {
- byte[] byte1 = new byte[]{(byte) 0x2E, (byte) 0x72, (byte) 0x61, (byte) 0xFD, (byte) 0x00};
+ public boolean match(final byte[] bytes) {
+ final byte[] byte1 = new byte[]{(byte) 0x2E, (byte) 0x72, (byte) 0x61, (byte) 0xFD, (byte) 0x00};
return bytes.length > 5 && Arrays.equals(Arrays.copyOfRange(bytes, 0, 5), byte1);
}
}
- //other end ---------------------------------------------------------------
+ // endregion
;
- private final String mimeType;
- private final String extension;
- FileMagicNumber(String mimeType, String extension) {
- this.mimeType = mimeType;
- this.extension = extension;
- }
-
- public static FileMagicNumber getMagicNumber(byte[] bytes) {
- FileMagicNumber number = Arrays.stream(values())
+ /**
+ * 根据给定的bytes,获取对应识别到的{@code FileMagicNumber}
+ *
+ * @param bytes bytes魔数
+ * @return {@code FileMagicNumber}
+ */
+ public static FileMagicNumber getMagicNumber(final byte[] bytes) {
+ final FileMagicNumber number = Arrays.stream(values())
.filter(fileMagicNumber -> fileMagicNumber.match(bytes))
.findFirst()
.orElse(UNKNOWN);
- if (number.equals(FileMagicNumber.ZIP)) {
- FileMagicNumber fn = FileMagicNumber.matchDocument(bytes);
+
+ // 压缩文档类型,如office或jar等
+ if (FileMagicNumber.ZIP.equals(number)) {
+ final FileMagicNumber fn = FileMagicNumber.matchDocument(bytes);
return fn == UNKNOWN ? ZIP : fn;
}
return number;
}
+ private final String mimeType;
+ private final String extension;
+
+ /**
+ * 构造
+ *
+ * @param mimeType 媒体类型
+ * @param extension 扩展名
+ */
+ FileMagicNumber(final String mimeType, final String extension) {
+ this.mimeType = mimeType;
+ this.extension = extension;
+ }
+
+ /**
+ * 获取媒体类型
+ *
+ * @return 媒体类型
+ */
public String getMimeType() {
return mimeType;
}
+ /**
+ * 获取扩展名
+ *
+ * @return 扩展名
+ */
public String getExtension() {
return extension;
}
- private static int indexOf(byte[] array, byte[] target) {
+ /**
+ * 是否匹配bytes
+ *
+ * @param bytes bytes
+ * @return 是否匹配
+ */
+ public abstract boolean match(byte[] bytes);
+
+ // region ----- private static methods
+ private static int indexOf(final byte[] array, final byte[] target) {
if (array == null || target == null || array.length < target.length) {
return -1;
}
@@ -1172,19 +1453,26 @@ public enum FileMagicNumber {
}
//处理 Open XML 类型的文件
- private static boolean compareBytes(byte[] buf, byte[] slice, int startOffset) {
- int sl = slice.length;
+ private static boolean compareBytes(final byte[] buf, final byte[] slice, final int startOffset) {
+ final int sl = slice.length;
if (startOffset + sl > buf.length) {
return false;
}
- byte[] sub = Arrays.copyOfRange(buf, startOffset, startOffset + sl);
+ final byte[] sub = Arrays.copyOfRange(buf, startOffset, startOffset + sl);
return Arrays.equals(sub, slice);
}
- private static FileMagicNumber matchOpenXmlMime(byte[] bytes, int offset) {
- byte[] word = new byte[]{'w', 'o', 'r', 'd', '/'};
- byte[] ppt = new byte[]{'p', 'p', 't', '/'};
- byte[] xl = new byte[]{'x', 'l', '/'};
+ /**
+ * 判断OpenXML文档类型
+ *
+ * @param bytes bytes魔数
+ * @param offset 偏移
+ * @return {@code FileMagicNumber}
+ */
+ private static FileMagicNumber matchOpenXmlMime(final byte[] bytes, final int offset) {
+ final byte[] word = new byte[]{'w', 'o', 'r', 'd', '/'};
+ final byte[] ppt = new byte[]{'p', 'p', 't', '/'};
+ final byte[] xl = new byte[]{'x', 'l', '/'};
if (FileMagicNumber.compareBytes(bytes, word, offset)) {
return FileMagicNumber.DOCX;
}
@@ -1197,17 +1485,23 @@ public enum FileMagicNumber {
return FileMagicNumber.UNKNOWN;
}
- private static FileMagicNumber matchDocument(byte[] bytes) {
- FileMagicNumber fileMagicNumber = FileMagicNumber.matchOpenXmlMime(bytes, (byte) 0x1e);
+ /**
+ * 匹配文档类型
+ *
+ * @param bytes bytes魔数
+ * @return 匹配到的文档类型
+ */
+ private static FileMagicNumber matchDocument(final byte[] bytes) {
+ final FileMagicNumber fileMagicNumber = FileMagicNumber.matchOpenXmlMime(bytes, (byte) 0x1e);
if (false == fileMagicNumber.equals(UNKNOWN)) {
return fileMagicNumber;
}
- byte[] bytes1 = new byte[]{0x5B, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x5F, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5D, 0x2E, 0x78, 0x6D, 0x6C};
- byte[] bytes2 = new byte[]{0x5F, 0x72, 0x65, 0x6C, 0x73, 0x2F, 0x2E, 0x72, 0x65, 0x6C, 0x73};
- byte[] bytes3 = new byte[]{0x64, 0x6F, 0x63, 0x50, 0x72, 0x6F, 0x70, 0x73};
- boolean flag1 = FileMagicNumber.compareBytes(bytes, bytes1, (byte) 0x1e);
- boolean flag2 = FileMagicNumber.compareBytes(bytes, bytes2, (byte) 0x1e);
- boolean flag3 = FileMagicNumber.compareBytes(bytes, bytes3, (byte) 0x1e);
+ final byte[] bytes1 = new byte[]{0x5B, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x5F, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5D, 0x2E, 0x78, 0x6D, 0x6C};
+ final byte[] bytes2 = new byte[]{0x5F, 0x72, 0x65, 0x6C, 0x73, 0x2F, 0x2E, 0x72, 0x65, 0x6C, 0x73};
+ final byte[] bytes3 = new byte[]{0x64, 0x6F, 0x63, 0x50, 0x72, 0x6F, 0x70, 0x73};
+ final boolean flag1 = FileMagicNumber.compareBytes(bytes, bytes1, (byte) 0x1e);
+ final boolean flag2 = FileMagicNumber.compareBytes(bytes, bytes2, (byte) 0x1e);
+ final boolean flag3 = FileMagicNumber.compareBytes(bytes, bytes3, (byte) 0x1e);
if (false == (flag1 || flag2 || flag3)) {
return UNKNOWN;
}
@@ -1217,7 +1511,7 @@ public enum FileMagicNumber {
if (index == -1) {
continue;
}
- FileMagicNumber fn = FileMagicNumber.matchOpenXmlMime(bytes, index + 30);
+ final FileMagicNumber fn = FileMagicNumber.matchOpenXmlMime(bytes, index + 30);
if (false == fn.equals(UNKNOWN)) {
return fn;
}
@@ -1225,19 +1519,26 @@ public enum FileMagicNumber {
return UNKNOWN;
}
- private static int searchSignature(byte[] bytes, int start, int rangeNum) {
- byte[] signature = new byte[]{0x50, 0x4B, 0x03, 0x04};
- int length = bytes.length;
+ /**
+ * 查找文档签名(文档标识)
+ *
+ * @param bytes bytes
+ * @param start 开始位置
+ * @param rangeNum 步进长度
+ * @return 签名
+ */
+ @SuppressWarnings("SameParameterValue")
+ private static int searchSignature(final byte[] bytes, final int start, final int rangeNum) {
+ final byte[] signature = new byte[]{0x50, 0x4B, 0x03, 0x04};
+ final int length = bytes.length;
int end = start + rangeNum;
if (end > length) {
end = length;
}
- int index = FileMagicNumber.indexOf(Arrays.copyOfRange(bytes, start, end), signature);
+ final int index = FileMagicNumber.indexOf(Arrays.copyOfRange(bytes, start, end), signature);
return (index == -1)
? -1
: (start + index);
}
-
- public abstract boolean match(byte[] bytes);
-
+ // endregion
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java
index 0b73ad6a4..2542e14d3 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java
@@ -1,9 +1,10 @@
package cn.hutool.core.io.file;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.func.SerConsumer;
+import cn.hutool.core.lang.func.SerFunction;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
@@ -14,6 +15,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -207,14 +209,20 @@ public class FileReader extends FileWrapper {
* @return 从文件中read出的数据
* @throws IORuntimeException IO异常
*/
- public T read(final ReaderHandler readerHandler) throws IORuntimeException {
+ public T read(final SerFunction readerHandler) throws IORuntimeException {
BufferedReader reader = null;
T result;
try {
reader = FileUtil.getReader(this.file, charset);
- result = readerHandler.handle(reader);
- } catch (final IOException e) {
- throw new IORuntimeException(e);
+ result = readerHandler.applying(reader);
+ } catch (final Exception e) {
+ if(e instanceof IOException){
+ throw new IORuntimeException(e);
+ } else if(e instanceof RuntimeException){
+ throw (RuntimeException)e;
+ } else{
+ throw new UtilException(e);
+ }
} finally {
IoUtil.close(reader);
}
@@ -239,7 +247,7 @@ public class FileReader extends FileWrapper {
*/
public BufferedInputStream getInputStream() throws IORuntimeException {
try {
- return new BufferedInputStream(new FileInputStream(this.file));
+ return new BufferedInputStream(Files.newInputStream(this.file.toPath()));
} catch (final IOException e) {
throw new IORuntimeException(e);
}
@@ -277,19 +285,6 @@ public class FileReader extends FileWrapper {
}
}
- // -------------------------------------------------------------------------- Interface start
- /**
- * Reader处理接口
- *
- * @author Luxiaolei
- *
- * @param Reader处理返回结果类型
- */
- public interface ReaderHandler {
- T handle(BufferedReader reader) throws IOException;
- }
- // -------------------------------------------------------------------------- Interface end
-
/**
* 检查文件
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileTypeUtil.java
similarity index 83%
rename from hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java
rename to hutool-core/src/main/java/cn/hutool/core/io/file/FileTypeUtil.java
index cebe88de8..04a7803ff 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileTypeUtil.java
@@ -1,10 +1,11 @@
-package cn.hutool.core.io;
+package cn.hutool.core.io.file;
import cn.hutool.core.codec.HexUtil;
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrUtil;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@@ -32,7 +33,7 @@ public class FileTypeUtil {
* @param extName 文件扩展名
* @return 之前已经存在的文件扩展名
*/
- public static String putFileType(String fileStreamHexHead, String extName) {
+ public static String putFileType(final String fileStreamHexHead, final String extName) {
return FILE_TYPE_MAP.put(fileStreamHexHead, extName);
}
@@ -42,7 +43,7 @@ public class FileTypeUtil {
* @param fileStreamHexHead 文件流头部Hex信息
* @return 移除的文件扩展名
*/
- public static String removeFileType(String fileStreamHexHead) {
+ public static String removeFileType(final String fileStreamHexHead) {
return FILE_TYPE_MAP.remove(fileStreamHexHead);
}
@@ -52,13 +53,13 @@ public class FileTypeUtil {
* @param fileStreamHexHead 文件流头部16进制字符串
* @return 文件类型,未找到为{@code null}
*/
- public static String getType(String fileStreamHexHead) {
- for (Entry fileTypeEntry : FILE_TYPE_MAP.entrySet()) {
+ public static String getType(final String fileStreamHexHead) {
+ for (final Entry fileTypeEntry : FILE_TYPE_MAP.entrySet()) {
if (StrUtil.startWithIgnoreCase(fileStreamHexHead, fileTypeEntry.getKey())) {
return fileTypeEntry.getValue();
}
}
- byte[] bytes = (HexUtil.decodeHex(fileStreamHexHead));
+ final byte[] bytes = (HexUtil.decodeHex(fileStreamHexHead));
return FileMagicNumber.getMagicNumber(bytes).getExtension();
}
@@ -68,8 +69,9 @@ public class FileTypeUtil {
* @param in 文件流
* @param fileHeadSize 自定义读取文件头部的大小
* @return 文件类型,未找到为{@code null}
+ * @throws IORuntimeException IO异常
*/
- public static String getType(InputStream in,int fileHeadSize) throws IORuntimeException {
+ public static String getType(final InputStream in, final int fileHeadSize) throws IORuntimeException {
return getType((IoUtil.readHex(in, fileHeadSize,false)));
}
@@ -82,7 +84,7 @@ public class FileTypeUtil {
* @return 类型,文件的扩展名,未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
*/
- public static String getType(InputStream in,boolean isExact) throws IORuntimeException {
+ public static String getType(final InputStream in, final boolean isExact) throws IORuntimeException {
return isExact
?getType(readHex8192Upper(in))
:getType(readHex64Upper(in));
@@ -96,7 +98,7 @@ public class FileTypeUtil {
* @return 类型,文件的扩展名,未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
*/
- public static String getType(InputStream in) throws IORuntimeException {
+ public static String getType(final InputStream in) throws IORuntimeException {
return getType(in,false);
}
@@ -116,7 +118,7 @@ public class FileTypeUtil {
* @return 类型,文件的扩展名,未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
*/
- public static String getType(InputStream in, String filename) throws IORuntimeException {
+ public static String getType(final InputStream in, final String filename) throws IORuntimeException {
return getType(in,filename,false);
}
@@ -136,14 +138,14 @@ public class FileTypeUtil {
* @return 类型,文件的扩展名,未找到为{@code null}
* @throws IORuntimeException 读取流引起的异常
*/
- public static String getType(InputStream in, String filename,boolean isExact) throws IORuntimeException {
+ public static String getType(final InputStream in, final String filename, final boolean isExact) throws IORuntimeException {
String typeName = getType(in,isExact);
if (null == typeName) {
// 未成功识别类型,扩展名辅助识别
- typeName = FileUtil.extName(filename);
+ typeName = FileNameUtil.extName(filename);
} else if ("zip".equals(typeName)) {
// zip可能为docx、xlsx、pptx、jar、war、ofd等格式,扩展名辅助判断
- final String extName = FileUtil.extName(filename);
+ final String extName = FileNameUtil.extName(filename);
if ("docx".equalsIgnoreCase(extName)) {
typeName = "docx";
} else if ("xlsx".equalsIgnoreCase(extName)) {
@@ -161,7 +163,7 @@ public class FileTypeUtil {
}
} else if ("jar".equals(typeName)) {
// wps编辑过的.xlsx文件与.jar的开头相同,通过扩展名判断
- final String extName = FileUtil.extName(filename);
+ final String extName = FileNameUtil.extName(filename);
if ("xlsx".equalsIgnoreCase(extName)) {
typeName = "xlsx";
} else if ("docx".equalsIgnoreCase(extName)) {
@@ -193,8 +195,8 @@ public class FileTypeUtil {
* @return 类型,文件的扩展名,未找到为{@code null}
* @throws IORuntimeException 读取文件引起的异常
*/
- public static String getType(File file,boolean isExact) throws IORuntimeException {
- FileInputStream in = null;
+ public static String getType(final File file, final boolean isExact) throws IORuntimeException {
+ InputStream in = null;
try {
in = IoUtil.toStream(file);
return getType(in, file.getName(),isExact);
@@ -216,7 +218,7 @@ public class FileTypeUtil {
* @return 类型,文件的扩展名,未找到为{@code null}
* @throws IORuntimeException 读取文件引起的异常
*/
- public static String getType(File file) throws IORuntimeException {
+ public static String getType(final File file) throws IORuntimeException {
return getType(file,false);
}
@@ -228,7 +230,7 @@ public class FileTypeUtil {
* @return 类型
* @throws IORuntimeException 读取文件引起的异常
*/
- public static String getTypeByPath(String path,boolean isExact) throws IORuntimeException {
+ public static String getTypeByPath(final String path, final boolean isExact) throws IORuntimeException {
return getType(FileUtil.file(path),isExact);
}
@@ -239,7 +241,7 @@ public class FileTypeUtil {
* @return 类型
* @throws IORuntimeException 读取文件引起的异常
*/
- public static String getTypeByPath(String path) throws IORuntimeException {
+ public static String getTypeByPath(final String path) throws IORuntimeException {
return getTypeByPath(path,false);
}
@@ -252,7 +254,6 @@ public class FileTypeUtil {
*/
private static String readHex8192Upper(final InputStream in) throws IORuntimeException {
try {
- final int i = in.available();
return IoUtil.readHex(in, Math.min(8192, in.available()), false);
} catch (final IOException e) {
throw new RuntimeException(e);
@@ -266,7 +267,7 @@ public class FileTypeUtil {
* @return 16进制字符串
* @throws IORuntimeException IO异常
*/
- private static String readHex64Upper(InputStream in) throws IORuntimeException {
+ private static String readHex64Upper(final InputStream in) throws IORuntimeException {
return IoUtil.readHex(in, 64, false);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileUtil.java
old mode 100755
new mode 100644
similarity index 87%
rename from hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
rename to hutool-core/src/main/java/cn/hutool/core/io/file/FileUtil.java
index 219831fdf..b2472a9f8
--- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileUtil.java
@@ -1,62 +1,29 @@
-package cn.hutool.core.io;
+package cn.hutool.core.io.file;
import cn.hutool.core.compress.ZipUtil;
-import cn.hutool.core.io.file.FileCopier;
-import cn.hutool.core.io.file.FileMode;
-import cn.hutool.core.io.file.FileNameUtil;
-import cn.hutool.core.io.file.FileReader;
-import cn.hutool.core.io.file.FileReader.ReaderHandler;
-import cn.hutool.core.io.file.FileWriter;
-import cn.hutool.core.io.file.LineSeparator;
-import cn.hutool.core.io.file.PathUtil;
-import cn.hutool.core.io.file.Tailer;
+import cn.hutool.core.io.BomReader;
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.io.stream.BOMInputStream;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.SerConsumer;
+import cn.hutool.core.lang.func.SerFunction;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.thread.ThreadUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.CharUtil;
-import cn.hutool.core.util.CharsetUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.SystemUtil;
+import cn.hutool.core.util.*;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.LineNumberReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-import java.io.Reader;
+import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
-import java.nio.file.DirectoryNotEmptyException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.nio.file.*;
+import java.util.*;
import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
@@ -693,11 +660,10 @@ public class FileUtil extends PathUtil {
* 某个文件删除失败会终止删除操作
*
* @param fullFileOrDirPath 文件或者目录的路径
- * @return 成功与否
* @throws IORuntimeException IO异常
*/
- public static boolean del(final String fullFileOrDirPath) throws IORuntimeException {
- return del(file(fullFileOrDirPath));
+ public static void del(final String fullFileOrDirPath) throws IORuntimeException {
+ del(file(fullFileOrDirPath));
}
/**
@@ -705,42 +671,13 @@ public class FileUtil extends PathUtil {
* 注意:删除文件夹时不会判断文件夹是否为空,如果不空则递归删除子文件或文件夹
* 某个文件删除失败会终止删除操作
*
- *
- * 从5.7.6开始,删除文件使用{@link Files#delete(Path)}代替 {@link File#delete()}
- * 因为前者遇到文件被占用等原因时,抛出异常,而非返回false,异常会指明具体的失败原因。
- *
- *
* @param file 文件对象
- * @return 成功与否
* @throws IORuntimeException IO异常
* @see Files#delete(Path)
*/
- public static boolean del(final File file) throws IORuntimeException {
- if (file == null || false == file.exists()) {
- // 如果文件不存在或已被删除,此处返回true表示删除成功
- return true;
- }
-
- if (file.isDirectory()) {
- // 清空目录下所有文件和目录
- final boolean isOk = clean(file);
- if (false == isOk) {
- return false;
- }
- }
-
- // 删除文件或清空后的目录
- final Path path = file.toPath();
- try {
- delFile(path);
- } catch (final DirectoryNotEmptyException e) {
- // 遍历清空目录没有成功,此时补充删除一次(可能存在部分软链)
- del(path);
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
-
- return true;
+ public static void del(final File file) throws IORuntimeException {
+ Assert.notNull(file, "File must be not null!");
+ PathUtil.del(file.toPath());
}
/**
@@ -749,12 +686,10 @@ public class FileUtil extends PathUtil {
* 某个文件删除失败会终止删除操作
*
* @param dirPath 文件夹路径
- * @return 成功与否
* @throws IORuntimeException IO异常
- * @since 4.0.8
*/
- public static boolean clean(final String dirPath) throws IORuntimeException {
- return clean(file(dirPath));
+ public static void clean(final String dirPath) throws IORuntimeException {
+ clean(file(dirPath));
}
/**
@@ -763,52 +698,11 @@ public class FileUtil extends PathUtil {
* 某个文件删除失败会终止删除操作
*
* @param directory 文件夹
- * @return 成功与否
* @throws IORuntimeException IO异常
- * @since 3.0.6
*/
- public static boolean clean(final File directory) throws IORuntimeException {
- if (directory == null || directory.exists() == false || false == directory.isDirectory()) {
- return true;
- }
-
- final File[] files = directory.listFiles();
- if (null != files) {
- for (final File childFile : files) {
- if (false == del(childFile)) {
- // 删除一个出错则本次删除任务失败
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * 清理空文件夹
- * 此方法用于递归删除空的文件夹,不删除文件
- * 如果传入的文件夹本身就是空的,删除这个文件夹
- *
- * @param directory 文件夹
- * @return 成功与否
- * @throws IORuntimeException IO异常
- * @since 4.5.5
- */
- public static boolean cleanEmpty(final File directory) throws IORuntimeException {
- if (directory == null || false == directory.exists() || false == directory.isDirectory()) {
- return true;
- }
-
- final File[] files = directory.listFiles();
- if (ArrayUtil.isEmpty(files)) {
- // 空文件夹则删除之
- return directory.delete();
- }
-
- for (final File childFile : files) {
- cleanEmpty(childFile);
- }
- return true;
+ public static void clean(final File directory) throws IORuntimeException {
+ Assert.notNull(directory, "File must be not null!");
+ PathUtil.clean(directory.toPath());
}
/**
@@ -986,43 +880,6 @@ public class FileUtil extends PathUtil {
}
}
- /**
- * 通过JDK7+的 Files#copy(Path, Path, CopyOption...) 方法拷贝文件
- *
- * @param src 源文件路径
- * @param dest 目标文件或目录路径,如果为目录使用与源文件相同的文件名
- * @param options {@link StandardCopyOption}
- * @return File
- * @throws IORuntimeException IO异常
- */
- public static File copyFile(final String src, final String dest, final StandardCopyOption... options) throws IORuntimeException {
- Assert.notBlank(src, "Source File path is blank !");
- Assert.notBlank(dest, "Destination File path is blank !");
- return copyFile(Paths.get(src), Paths.get(dest), options).toFile();
- }
-
- /**
- * 通过JDK7+的 Files#copy(Path, Path, CopyOption...) 方法拷贝文件
- *
- * @param src 源文件
- * @param dest 目标文件或目录,如果为目录使用与源文件相同的文件名
- * @param options {@link StandardCopyOption}
- * @return 目标文件
- * @throws IORuntimeException IO异常
- */
- public static File copyFile(final File src, final File dest, final StandardCopyOption... options) throws IORuntimeException {
- // check
- Assert.notNull(src, "Source File is null !");
- if (false == src.exists()) {
- throw new IORuntimeException("File not exist: " + src);
- }
- Assert.notNull(dest, "Destination File or directiory is null !");
- if (equals(src, dest)) {
- throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest);
- }
- return copyFile(src.toPath(), dest.toPath(), options).toFile();
- }
-
/**
* 复制文件或目录
* 如果目标文件为目录,则将源文件以相同文件名拷贝到目标目录
@@ -1048,13 +905,19 @@ public class FileUtil extends PathUtil {
*
*
* @param src 源文件
- * @param dest 目标文件或目录,目标不存在会自动创建(目录、文件都创建)
+ * @param target 目标文件或目录,目标不存在会自动创建(目录、文件都创建)
* @param isOverride 是否覆盖目标文件
* @return 目标目录或文件
* @throws IORuntimeException IO异常
*/
- public static File copy(final File src, final File dest, final boolean isOverride) throws IORuntimeException {
- return FileCopier.of(src, dest).setOverride(isOverride).copy();
+ public static File copy(final File src, final File target, final boolean isOverride) throws IORuntimeException {
+ Assert.notNull(src, "Src file must be not null!");
+ Assert.notNull(target, "target file must be not null!");
+ return PathUtil.copy(
+ src.toPath(),
+ target.toPath(),
+ isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{})
+ .toFile();
}
/**
@@ -1068,65 +931,43 @@ public class FileUtil extends PathUtil {
*
*
* @param src 源文件
- * @param dest 目标文件或目录,目标不存在会自动创建(目录、文件都创建)
+ * @param target 目标文件或目录,目标不存在会自动创建(目录、文件都创建)
* @param isOverride 是否覆盖目标文件
* @return 目标目录或文件
* @throws IORuntimeException IO异常
*/
- public static File copyContent(final File src, final File dest, final boolean isOverride) throws IORuntimeException {
- return FileCopier.of(src, dest).setCopyContentIfDir(true).setOverride(isOverride).copy();
+ public static File copyContent(final File src, final File target, final boolean isOverride) throws IORuntimeException {
+ Assert.notNull(src, "Src file must be not null!");
+ Assert.notNull(target, "target file must be not null!");
+ return PathUtil.copyContent(
+ src.toPath(),
+ target.toPath(),
+ isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{})
+ .toFile();
}
/**
- * 复制文件或目录
- * 情况如下:
+ * 移动文件或目录到目标中,例如:
+ *
+ * 如果src为文件,target为目录,则移动到目标目录下,存在同名文件则按照是否覆盖参数执行。
+ * 如果src为文件,target为文件,则按照是否覆盖参数执行。
+ * 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如moveContent("/a/b", "/c/d"), d不存在,则b变成d。
+ * 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
+ * 如果src为目录,target为目录,则将源目录及其内容移动到目标路径目录中,如move("/a/b", "/c/d"),结果为"/c/d/b"
+ * 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录及其内容移动到目标路径目录中,如move("/a/b", "/c/d"),结果为"/c/d/b"
+ *
*
- *
- * 1、src和dest都为目录,则将src下所有文件(包括子目录)拷贝到dest下
- * 2、src和dest都为文件,直接复制,名字为dest
- * 3、src为文件,dest为目录,将src拷贝到dest目录下
- *
- *
- * @param src 源文件
- * @param dest 目标文件或目录,目标不存在会自动创建(目录、文件都创建)
+ * @param src 源文件或目录路径
+ * @param target 目标路径,如果为目录,则移动到此目录下
* @param isOverride 是否覆盖目标文件
- * @return 目标目录或文件
- * @throws IORuntimeException IO异常
- * @since 4.1.5
- */
- public static File copyFilesFromDir(final File src, final File dest, final boolean isOverride) throws IORuntimeException {
- return FileCopier.of(src, dest).setCopyContentIfDir(true).setOnlyCopyFile(true).setOverride(isOverride).copy();
- }
-
- /**
- * 移动文件或者目录
- *
- * @param src 源文件或者目录
- * @param target 目标文件或者目录
- * @param isOverride 是否覆盖目标,只有目标为文件才覆盖
+ * @return 目标文件或目录
* @throws IORuntimeException IO异常
* @see PathUtil#move(Path, Path, boolean)
*/
- public static void move(final File src, final File target, final boolean isOverride) throws IORuntimeException {
+ public static File move(final File src, final File target, final boolean isOverride) throws IORuntimeException {
Assert.notNull(src, "Src file must be not null!");
Assert.notNull(target, "target file must be not null!");
- move(src.toPath(), target.toPath(), isOverride);
- }
-
- /**
- * 移动文件或者目录
- *
- * @param src 源文件或者目录
- * @param target 目标文件或者目录
- * @param isOverride 是否覆盖目标,只有目标为文件才覆盖
- * @throws IORuntimeException IO异常
- * @see PathUtil#moveContent(Path, Path, boolean)
- * @since 5.7.9
- */
- public static void moveContent(final File src, final File target, final boolean isOverride) throws IORuntimeException {
- Assert.notNull(src, "Src file must be not null!");
- Assert.notNull(target, "target file must be not null!");
- moveContent(src.toPath(), target.toPath(), isOverride);
+ return move(src.toPath(), target.toPath(), isOverride).toFile();
}
/**
@@ -1172,7 +1013,7 @@ public class FileUtil extends PathUtil {
*/
public static File rename(final File file, String newName, final boolean isRetainExt, final boolean isOverride) {
if (isRetainExt) {
- final String extName = FileUtil.extName(file);
+ final String extName = FileNameUtil.extName(file);
if (StrUtil.isNotBlank(extName)) {
newName = newName.concat(".").concat(extName);
}
@@ -1604,129 +1445,6 @@ public class FileUtil extends PathUtil {
return filePath;
}
- // -------------------------------------------------------------------------------------------- name start
-
- /**
- * 返回文件名
- *
- * @param file 文件
- * @return 文件名
- * @see FileNameUtil#getName(File)
- * @since 4.1.13
- */
- public static String getName(final File file) {
- return FileNameUtil.getName(file);
- }
-
- /**
- * 返回文件名
- *
- * "d:/test/aaa" 返回 "aaa"
- * "/test/aaa.jpg" 返回 "aaa.jpg"
- *
- *
- * @param filePath 文件
- * @return 文件名
- * @see FileNameUtil#getName(String)
- * @since 4.1.13
- */
- public static String getName(final String filePath) {
- return FileNameUtil.getName(filePath);
- }
-
- /**
- * 获取文件后缀名,扩展名不带“.”
- *
- * @param file 文件
- * @return 扩展名
- * @see FileNameUtil#getSuffix(File)
- * @since 5.3.8
- */
- public static String getSuffix(final File file) {
- return FileNameUtil.getSuffix(file);
- }
-
- /**
- * 获得文件后缀名,扩展名不带“.”
- *
- * @param fileName 文件名
- * @return 扩展名
- * @see FileNameUtil#getSuffix(String)
- * @since 5.3.8
- */
- public static String getSuffix(final String fileName) {
- return FileNameUtil.getSuffix(fileName);
- }
-
- /**
- * 返回主文件名
- *
- * @param file 文件
- * @return 主文件名
- * @see FileNameUtil#getPrefix(File)
- * @since 5.3.8
- */
- public static String getPrefix(final File file) {
- return FileNameUtil.getPrefix(file);
- }
-
- /**
- * 返回主文件名
- *
- * @param fileName 完整文件名
- * @return 主文件名
- * @see FileNameUtil#getPrefix(String)
- * @since 5.3.8
- */
- public static String getPrefix(final String fileName) {
- return FileNameUtil.getPrefix(fileName);
- }
-
- /**
- * 返回主文件名
- *
- * @param file 文件
- * @return 主文件名
- * @see FileNameUtil#mainName(File)
- */
- public static String mainName(final File file) {
- return FileNameUtil.mainName(file);
- }
-
- /**
- * 返回主文件名
- *
- * @param fileName 完整文件名
- * @return 主文件名
- * @see FileNameUtil#mainName(String)
- */
- public static String mainName(final String fileName) {
- return FileNameUtil.mainName(fileName);
- }
-
- /**
- * 获取文件扩展名(后缀名),扩展名不带“.”
- *
- * @param file 文件
- * @return 扩展名
- * @see FileNameUtil#extName(File)
- */
- public static String extName(final File file) {
- return FileNameUtil.extName(file);
- }
-
- /**
- * 获得文件的扩展名(后缀名),扩展名不带“.”
- *
- * @param fileName 文件名
- * @return 扩展名
- * @see FileNameUtil#extName(String)
- */
- public static String extName(final String fileName) {
- return FileNameUtil.extName(fileName);
- }
- // -------------------------------------------------------------------------------------------- name end
-
/**
* 判断文件路径是否有指定后缀,忽略大小写
* 常用语判断扩展名
@@ -1765,6 +1483,7 @@ public class FileUtil extends PathUtil {
* @param file 文件
* @return 输入流
* @throws IORuntimeException 文件未找到
+ * @see IoUtil#toStream(File)
*/
public static BufferedInputStream getInputStream(final File file) throws IORuntimeException {
return IoUtil.toBuffered(IoUtil.toStream(file));
@@ -1790,8 +1509,7 @@ public class FileUtil extends PathUtil {
*/
public static BOMInputStream getBOMInputStream(final File file) throws IORuntimeException {
try {
- //noinspection IOStreamConstructor
- return new BOMInputStream(new FileInputStream(file));
+ return new BOMInputStream(Files.newInputStream(file.toPath()));
} catch (final IOException e) {
throw new IORuntimeException(e);
}
@@ -2258,10 +1976,9 @@ public class FileUtil extends PathUtil {
* @param path 文件的绝对路径
* @return 从文件中load出的数据
* @throws IORuntimeException IO异常
- * @since 3.1.1
*/
- public static T loadUtf8(final String path, final ReaderHandler readerHandler) throws IORuntimeException {
- return load(path, CharsetUtil.UTF_8, readerHandler);
+ public static T readUtf8(final String path, final SerFunction readerHandler) throws IORuntimeException {
+ return read(path, CharsetUtil.UTF_8, readerHandler);
}
/**
@@ -2275,23 +1992,8 @@ public class FileUtil extends PathUtil {
* @throws IORuntimeException IO异常
* @since 3.1.1
*/
- public static T load(final String path, final String charset, final ReaderHandler readerHandler) throws IORuntimeException {
- return FileReader.of(file(path), CharsetUtil.charset(charset)).read(readerHandler);
- }
-
- /**
- * 按照给定的readerHandler读取文件中的数据
- *
- * @param 集合类型
- * @param readerHandler Reader处理类
- * @param path 文件的绝对路径
- * @param charset 字符集
- * @return 从文件中load出的数据
- * @throws IORuntimeException IO异常
- * @since 3.1.1
- */
- public static T load(final String path, final Charset charset, final ReaderHandler readerHandler) throws IORuntimeException {
- return FileReader.of(file(path), charset).read(readerHandler);
+ public static T read(final String path, final Charset charset, final SerFunction readerHandler) throws IORuntimeException {
+ return read(file(path), charset, readerHandler);
}
/**
@@ -2304,8 +2006,8 @@ public class FileUtil extends PathUtil {
* @throws IORuntimeException IO异常
* @since 3.1.1
*/
- public static T loadUtf8(final File file, final ReaderHandler readerHandler) throws IORuntimeException {
- return load(file, CharsetUtil.UTF_8, readerHandler);
+ public static T readUtf8(final File file, final SerFunction readerHandler) throws IORuntimeException {
+ return read(file, CharsetUtil.UTF_8, readerHandler);
}
/**
@@ -2319,7 +2021,7 @@ public class FileUtil extends PathUtil {
* @throws IORuntimeException IO异常
* @since 3.1.1
*/
- public static T load(final File file, final Charset charset, final ReaderHandler readerHandler) throws IORuntimeException {
+ public static T read(final File file, final Charset charset, final SerFunction readerHandler) throws IORuntimeException {
return FileReader.of(file, charset).read(readerHandler);
}
@@ -2335,8 +2037,7 @@ public class FileUtil extends PathUtil {
public static BufferedOutputStream getOutputStream(final File file) throws IORuntimeException {
final OutputStream out;
try {
- //noinspection IOStreamConstructor
- out = new FileOutputStream(touch(file));
+ out = Files.newOutputStream(touch(file).toPath());
} catch (final IOException e) {
throw new IORuntimeException(e);
}
@@ -3005,30 +2706,6 @@ public class FileUtil extends PathUtil {
return FileWriter.of(file, charset).writeLines(lines, lineSeparator, false);
}
- /**
- * 清除文件名中的在Windows下不支持的非法字符,包括: \ / : * ? " < > |
- *
- * @param fileName 文件名(必须不包括路径,否则路径符将被替换)
- * @return 清理后的文件名
- * @see FileNameUtil#cleanInvalid(String)
- * @since 3.3.1
- */
- public static String cleanInvalid(final String fileName) {
- return FileNameUtil.cleanInvalid(fileName);
- }
-
- /**
- * 文件名中是否包含在Windows下不支持的非法字符,包括: \ / : * ? " < > |
- *
- * @param fileName 文件名(必须不包括路径,否则路径符将被替换)
- * @return 是否包含非法字符
- * @see FileNameUtil#containsInvalid(String)
- * @since 3.3.1
- */
- public static boolean containsInvalid(final String fileName) {
- return FileNameUtil.containsInvalid(fileName);
- }
-
/**
* 获取Web项目下的web root路径
* 原理是首先获取ClassPath路径,由于在web项目中ClassPath位于 WEB-INF/classes/下,故向上获取两级目录即可。
@@ -3051,7 +2728,7 @@ public class FileUtil extends PathUtil {
* getParent(file("d:/aaa/bbb/cc/ddd")) -》 "d:/aaa/bbb/cc"
*
*
- * @param file 目录或文件
+ * @param file 目录或文件
* @return 路径File,如果不存在返回null
* @since 6.0.0
*/
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileWrapper.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileWrapper.java
index 3c712c028..a7405a8b1 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileWrapper.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileWrapper.java
@@ -1,6 +1,5 @@
package cn.hutool.core.io.file;
-import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.func.Wrapper;
import cn.hutool.core.util.CharsetUtil;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java
index 255421399..cd51770c2 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java
@@ -1,11 +1,10 @@
package cn.hutool.core.io.file;
-import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.CharsetUtil;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
@@ -13,9 +12,11 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
+import java.nio.file.Files;
import java.util.Map;
import java.util.Map.Entry;
@@ -336,9 +337,9 @@ public class FileWriter extends FileWrapper {
* @since 5.5.2
*/
public File writeFromStream(final InputStream in, final boolean isCloseIn) throws IORuntimeException {
- FileOutputStream out = null;
+ OutputStream out = null;
try {
- out = new FileOutputStream(FileUtil.touch(file));
+ out = Files.newOutputStream(FileUtil.touch(file).toPath());
IoUtil.copy(in, out);
} catch (final IOException e) {
throw new IORuntimeException(e);
@@ -359,7 +360,7 @@ public class FileWriter extends FileWrapper {
*/
public BufferedOutputStream getOutputStream() throws IORuntimeException {
try {
- return new BufferedOutputStream(new FileOutputStream(FileUtil.touch(file)));
+ return new BufferedOutputStream(Files.newOutputStream(FileUtil.touch(file).toPath()));
} catch (final IOException e) {
throw new IORuntimeException(e);
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/LineReadWatcher.java b/hutool-core/src/main/java/cn/hutool/core/io/file/LineReadWatcher.java
index 3abbb178d..e6acd1ebb 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/LineReadWatcher.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/LineReadWatcher.java
@@ -1,6 +1,5 @@
package cn.hutool.core.io.file;
-import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.lang.func.SerConsumer;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java
new file mode 100755
index 000000000..eda09af2c
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java
@@ -0,0 +1,164 @@
+package cn.hutool.core.io.file;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.file.visitor.CopyVisitor;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.copier.SrcToDestCopier;
+import cn.hutool.core.util.ObjUtil;
+
+import java.io.IOException;
+import java.nio.file.*;
+
+/**
+ * 文件复制封装
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class PathCopier extends SrcToDestCopier {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建文件或目录拷贝器
+ *
+ * @param src 源文件或目录
+ * @param target 目标文件或目录
+ * @param isOverride 是否覆盖目标文件
+ * @return {@code PathCopier}
+ */
+ public static PathCopier of(final Path src, final Path target, final boolean isOverride) {
+ return of(src, target, isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{});
+ }
+
+ /**
+ * 创建文件或目录拷贝器
+ *
+ * @param src 源文件或目录
+ * @param target 目标文件或目录
+ * @param options 拷贝参数
+ * @return {@code PathCopier}
+ */
+ public static PathCopier of(final Path src, final Path target, final CopyOption[] options) {
+ return new PathCopier(src, target, options);
+ }
+
+ private final CopyOption[] options;
+
+ /**
+ * 构造
+ *
+ * @param src 源文件或目录,不能为{@code null}且必须存在
+ * @param target 目标文件或目录
+ * @param options 移动参数
+ */
+ public PathCopier(final Path src, final Path target, final CopyOption[] options) {
+ Assert.notNull(target, "Src path must be not null !");
+ if (false == PathUtil.exists(src, false)) {
+ throw new IllegalArgumentException("Src path is not exist!");
+ }
+ this.src = src;
+ this.target = Assert.notNull(target, "Target path must be not null !");
+ this.options = ObjUtil.defaultIfNull(options, new CopyOption[]{});
+ }
+
+ /**
+ * 复制src到target中
+ *
+ * src路径和target路径相同时,不执行操作
+ * src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
+ * src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
+ * src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
+ * src为目录,target为已存在目录,整个src目录连同其目录拷贝到目标目录中
+ * src为目录,target为不存在路径,则自动创建目标为新目录,并只拷贝src内容到目标目录中,相当于重命名目录。
+ * src为目录,target为文件,抛出{@link IllegalArgumentException}
+ *
+ *
+ * @return 目标Path
+ * @throws IORuntimeException IO异常
+ */
+ @Override
+ public Path copy() throws IORuntimeException {
+ if (PathUtil.isDirectory(src)) {
+ if (PathUtil.exists(target, false)) {
+ if (PathUtil.isDirectory(target)) {
+ return _copyContent(src, target.resolve(src.getFileName()), options);
+ } else {
+ // src目录,target文件,无法拷贝
+ throw new IllegalArgumentException("Can not copy directory to a file!");
+ }
+ } else {
+ // 目标不存在,按照重命名对待
+ return _copyContent(src, target, options);
+ }
+ }
+ return copyFile(src, target, options);
+ }
+
+ /**
+ * 复制src的内容到target中
+ *
+ * src路径和target路径相同时,不执行操作
+ * src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
+ * src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
+ * src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
+ * src为目录,target为已存在目录,整个src目录下的内容拷贝到目标目录中
+ * src为目录,target为不存在路径,则自动创建目标为新目录,整个src目录下的内容拷贝到目标目录中,相当于重命名目录。
+ * src为目录,target为文件,抛出IO异常
+ *
+ *
+ * @return 目标Path
+ * @throws IORuntimeException IO异常
+ */
+ public Path copyContent() throws IORuntimeException {
+ if (PathUtil.isDirectory(src, false)) {
+ return _copyContent(src, target, options);
+ }
+ return copyFile(src, target, options);
+ }
+
+ /**
+ * 拷贝目录下的所有文件或目录到目标目录中,此方法不支持文件对文件的拷贝。
+ *
+ * 源文件为目录,目标也为目录或不存在,则拷贝目录下所有文件和目录到目标目录下
+ * 源文件为文件,目标为目录或不存在,则拷贝文件到目标目录下
+ *
+ *
+ * @param src 源文件路径,如果为目录只在目标中创建新目录
+ * @param target 目标目录,如果为目录使用与源文件相同的文件名
+ * @param options {@link StandardCopyOption}
+ * @return Path
+ * @throws IORuntimeException IO异常
+ */
+ private static Path _copyContent(final Path src, final Path target, final CopyOption... options) throws IORuntimeException {
+ try {
+ Files.walkFileTree(src, new CopyVisitor(src, target, options));
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ return target;
+ }
+
+ /**
+ * 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
+ * 此方法不支持递归拷贝目录,如果src传入是目录,只会在目标目录中创建空目录
+ *
+ * @param src 源文件路径,如果为目录只在目标中创建新目录
+ * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
+ * @param options {@link StandardCopyOption}
+ * @return Path
+ * @throws IORuntimeException IO异常
+ */
+ private static Path copyFile(final Path src, final Path target, final CopyOption... options) throws IORuntimeException {
+ Assert.notNull(src, "Source File is null !");
+ Assert.notNull(target, "Destination File or directory is null !");
+
+ final Path targetPath = PathUtil.isDirectory(target) ? target.resolve(src.getFileName()) : target;
+ // 创建级联父目录
+ PathUtil.mkParentDirs(targetPath);
+ try {
+ return Files.copy(src, targetPath, options);
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathDeleter.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathDeleter.java
new file mode 100755
index 000000000..3b82b0ce1
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathDeleter.java
@@ -0,0 +1,104 @@
+package cn.hutool.core.io.file;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.file.visitor.DelVisitor;
+import cn.hutool.core.lang.Assert;
+
+import java.io.IOException;
+import java.nio.file.*;
+import java.util.stream.Stream;
+
+/**
+ * 文件删除封装
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class PathDeleter {
+
+ /**
+ * 创建文件或目录移动器
+ *
+ * @param src 源文件或目录
+ * @return {@code PathMover}
+ */
+ public static PathDeleter of(final Path src) {
+ return new PathDeleter(src);
+ }
+
+ private final Path path;
+
+ /**
+ * 构造
+ *
+ * @param path 文件或目录,不能为{@code null}且必须存在
+ */
+ public PathDeleter(final Path path) {
+ this.path = Assert.notNull(path, "Path must be not null !");
+ }
+
+ /**
+ * 删除文件或者文件夹,不追踪软链
+ * 注意:删除文件夹时不会判断文件夹是否为空,如果不空则递归删除子文件或文件夹
+ * 某个文件删除失败会终止删除操作
+ *
+ * @throws IORuntimeException IO异常
+ */
+ public void del() throws IORuntimeException {
+ final Path path = this.path;
+ if (Files.notExists(path)) {
+ return;
+ }
+
+ if (PathUtil.isDirectory(path)) {
+ _del(path);
+ } else {
+ delFile(path);
+ }
+ }
+
+ /**
+ * 清空目录
+ */
+ public void clean() {
+ try (final Stream list = Files.list(this.path)){
+ list.forEach(PathUtil::del);
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 删除目录
+ *
+ * @param path 目录路径
+ */
+ private static void _del(final Path path) {
+ try {
+ Files.walkFileTree(path, DelVisitor.INSTANCE);
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 删除文件或空目录,不追踪软链
+ *
+ * @param path 文件对象
+ * @throws IORuntimeException IO异常
+ * @since 5.7.7
+ */
+ private static void delFile(final Path path) throws IORuntimeException {
+ try {
+ Files.delete(path);
+ } catch (final IOException e) {
+ if (e instanceof AccessDeniedException) {
+ // 可能遇到只读文件,无法删除.使用 file 方法删除
+ if (path.toFile().delete()) {
+ return;
+ }
+ }
+ throw new IORuntimeException(e);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java
new file mode 100755
index 000000000..71b36234c
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java
@@ -0,0 +1,166 @@
+package cn.hutool.core.io.file;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.file.visitor.MoveVisitor;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjUtil;
+
+import java.io.IOException;
+import java.nio.file.*;
+
+/**
+ * 文件移动封装
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class PathMover {
+
+ /**
+ * 创建文件或目录移动器
+ *
+ * @param src 源文件或目录
+ * @param target 目标文件或目录
+ * @param isOverride 是否覆盖目标文件
+ * @return {@code PathMover}
+ */
+ public static PathMover of(final Path src, final Path target, final boolean isOverride) {
+ return of(src, target, isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{});
+ }
+
+ /**
+ * 创建文件或目录移动器
+ *
+ * @param src 源文件或目录
+ * @param target 目标文件或目录
+ * @param options 移动参数
+ * @return {@code PathMover}
+ */
+ public static PathMover of(final Path src, final Path target, final CopyOption[] options) {
+ return new PathMover(src, target, options);
+ }
+
+ private final Path src;
+ private final Path target;
+ private final CopyOption[] options;
+
+ /**
+ * 构造
+ *
+ * @param src 源文件或目录,不能为{@code null}且必须存在
+ * @param target 目标文件或目录
+ * @param options 移动参数
+ */
+ public PathMover(final Path src, final Path target, final CopyOption[] options) {
+ Assert.notNull(target, "Src path must be not null !");
+ if(false == PathUtil.exists(src, false)){
+ throw new IllegalArgumentException("Src path is not exist!");
+ }
+ this.src = src;
+ this.target = Assert.notNull(target, "Target path must be not null !");
+ this.options = ObjUtil.defaultIfNull(options, new CopyOption[]{});
+ }
+
+ /**
+ * 移动文件或目录到目标中,例如:
+ *
+ * 如果src和target为同一文件或目录,直接返回target。
+ * 如果src为文件,target为目录,则移动到目标目录下,存在同名文件则按照是否覆盖参数执行。
+ * 如果src为文件,target为文件,则按照是否覆盖参数执行。
+ * 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如move("/a/b", "/c/d"), d不存在,则b变成d。
+ * 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
+ * 如果src为目录,target为目录,则将源目录及其内容移动到目标路径目录中,如move("/a/b", "/c/d"),结果为"/c/d/b"
+ * 如果src为目录,target为不存在的路径,则重命名src到target,如move("/a/b", "/c/d"),结果为"/c/d/",相当于b重命名为d
+ *
+ *
+ * @return 目标文件Path
+ */
+ public Path move() {
+ final Path src = this.src;
+ Path target = this.target;
+ final CopyOption[] options = this.options;
+
+ if (PathUtil.isDirectory(target)) {
+ // 创建子路径的情况,1是目标是目录,需要移动到目录下,2是目标不能存在,自动创建目录
+ target = target.resolve(src.getFileName());
+ }
+
+ // issue#2893 target 不存在导致NoSuchFileException
+ if (Files.exists(target) && PathUtil.equals(src, target)) {
+ // issue#2845,当用户传入目标路径与源路径一致时,直接返回,否则会导致删除风险。
+ return target;
+ }
+
+ // 自动创建目标的父目录
+ PathUtil.mkParentDirs(target);
+ try {
+ return Files.move(src, target, options);
+ } catch (final IOException e) {
+ if (e instanceof FileAlreadyExistsException) {
+ // 目标文件已存在,直接抛出异常
+ // issue#I4QV0L@Gitee
+ throw new IORuntimeException(e);
+ }
+ // 移动失败,可能是跨分区移动导致的,采用递归移动方式
+ walkMove(src, target, options);
+ // 移动后删除空目录
+ PathUtil.del(src);
+ return target;
+ }
+ }
+
+ /**
+ * 移动文件或目录内容到目标中,例如:
+ *
+ * 如果src为文件,target为目录,则移动到目标目录下,存在同名文件则按照是否覆盖参数执行。
+ * 如果src为文件,target为文件,则按照是否覆盖参数执行。
+ * 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如moveContent("/a/b", "/c/d"), d不存在,则b变成d。
+ * 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
+ * 如果src为目录,target为目录,则将源目录下的内容移动到目标路径目录中,源目录不删除。
+ * 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录下的内容移动到目标路径目录中,源目录不删除。
+ *
+ *
+ * @return 目标文件Path
+ */
+ public Path moveContent() {
+ final Path src = this.src;
+ if (PathUtil.isExistsAndNotDirectory(target, false)) {
+ // 文件移动调用move方法
+ return move();
+ }
+
+ final Path target = this.target;
+ if (PathUtil.isExistsAndNotDirectory(target, false)) {
+ // 目标不能为文件
+ throw new IllegalArgumentException("Can not move dir content to a file");
+ }
+
+ // issue#2893 target 不存在导致NoSuchFileException
+ if (PathUtil.equals(src, target)) {
+ // issue#2845,当用户传入目标路径与源路径一致时,直接返回,否则会导致删除风险。
+ return target;
+ }
+
+ final CopyOption[] options = this.options;
+
+ // 移动失败,可能是跨分区移动导致的,采用递归移动方式
+ walkMove(src, target, options);
+ return target;
+ }
+
+ /**
+ * 递归移动
+ *
+ * @param src 源目录
+ * @param target 目标目录
+ * @param options 移动参数
+ */
+ private static void walkMove(final Path src, final Path target, final CopyOption... options) {
+ try {
+ // 移动源目录下的内容而不删除目录
+ Files.walkFileTree(src, new MoveVisitor(src, target, options));
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
index 76888bffe..dcac7bcd9 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
@@ -2,33 +2,12 @@ package cn.hutool.core.io.file;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.io.file.visitor.CopyVisitor;
-import cn.hutool.core.io.file.visitor.DelVisitor;
-import cn.hutool.core.io.file.visitor.MoveVisitor;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
import java.nio.charset.Charset;
-import java.nio.file.AccessDeniedException;
-import java.nio.file.CopyOption;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileAlreadyExistsException;
-import java.nio.file.FileVisitOption;
-import java.nio.file.FileVisitResult;
-import java.nio.file.FileVisitor;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.StandardCopyOption;
+import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
@@ -147,73 +126,32 @@ public class PathUtil {
* 某个文件删除失败会终止删除操作
*
* @param path 文件对象
- * @return 成功与否
* @throws IORuntimeException IO异常
* @since 4.4.2
*/
- public static boolean del(final Path path) throws IORuntimeException {
- if (Files.notExists(path)) {
- return true;
- }
-
- try {
- if (isDirectory(path)) {
- Files.walkFileTree(path, DelVisitor.INSTANCE);
- } else {
- delFile(path);
- }
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- return true;
+ public static void del(final Path path) throws IORuntimeException {
+ PathDeleter.of(path).del();
}
/**
- * 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
- * 此方法不支持递归拷贝目录,如果src传入是目录,只会在目标目录中创建空目录
+ * 清空目录
*
- * @param src 源文件路径,如果为目录只在目标中创建新目录
- * @param dest 目标文件或目录,如果为目录使用与源文件相同的文件名
- * @param options {@link StandardCopyOption}
- * @return Path
- * @throws IORuntimeException IO异常
+ * @param path 目录路径
*/
- public static Path copyFile(final Path src, final Path dest, final StandardCopyOption... options) throws IORuntimeException {
- return copyFile(src, dest, (CopyOption[]) options);
+ public static void clean(final Path path) {
+ PathDeleter.of(path).clean();
}
/**
- * 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
- * 此方法不支持递归拷贝目录,如果src传入是目录,只会在目标目录中创建空目录
- *
- * @param src 源文件路径,如果为目录只在目标中创建新目录
- * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
- * @param options {@link StandardCopyOption}
- * @return Path
- * @throws IORuntimeException IO异常
- * @since 5.4.1
- */
- public static Path copyFile(final Path src, final Path target, final CopyOption... options) throws IORuntimeException {
- Assert.notNull(src, "Source File is null !");
- Assert.notNull(target, "Destination File or directory is null !");
-
- final Path targetPath = isDirectory(target) ? target.resolve(src.getFileName()) : target;
- // 创建级联父目录
- mkParentDirs(targetPath);
- try {
- return Files.copy(src, targetPath, options);
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- }
-
- /**
- * 拷贝文件或目录,拷贝规则为:
- *
+ * 复制src到target中
*
- * 源文件为目录,目标也为目录或不存在,则拷贝整个目录到目标目录下
- * 源文件为文件,目标为目录或不存在,则拷贝文件到目标目录下
- * 源文件为文件,目标也为文件,则在{@link StandardCopyOption#REPLACE_EXISTING}情况下覆盖之
+ * src路径和target路径相同时,不执行操作
+ * src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
+ * src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
+ * src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
+ * src为目录,target为已存在目录,整个src目录连同其目录拷贝到目标目录中
+ * src为目录,target为不存在路径,则自动创建目标为新目录,并只拷贝src内容到目标目录中,相当于重命名目录。
+ * src为目录,target为文件,抛出{@link IllegalArgumentException}
*
*
* @param src 源文件路径,如果为目录会在目标中创建新目录
@@ -221,23 +159,21 @@ public class PathUtil {
* @param options {@link StandardCopyOption}
* @return Path
* @throws IORuntimeException IO异常
- * @since 5.5.1
*/
public static Path copy(final Path src, final Path target, final CopyOption... options) throws IORuntimeException {
- Assert.notNull(src, "Src path must be not null !");
- Assert.notNull(target, "Target path must be not null !");
-
- if (isDirectory(src)) {
- return copyContent(src, target.resolve(src.getFileName()), options);
- }
- return copyFile(src, target, options);
+ return PathCopier.of(src, target, options).copy();
}
/**
- * 拷贝目录下的所有文件或目录到目标目录中,此方法不支持文件对文件的拷贝。
+ * 复制src的内容到target中
*
- * 源文件为目录,目标也为目录或不存在,则拷贝目录下所有文件和目录到目标目录下
- * 源文件为文件,目标为目录或不存在,则拷贝文件到目标目录下
+ * src路径和target路径相同时,不执行操作
+ * src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
+ * src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
+ * src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
+ * src为目录,target为已存在目录,整个src目录下的内容拷贝到目标目录中
+ * src为目录,target为不存在路径,则自动创建目标为新目录,整个src目录下的内容拷贝到目标目录中,相当于重命名目录。
+ * src为目录,target为文件,抛出IO异常
*
*
* @param src 源文件路径,如果为目录只在目标中创建新目录
@@ -245,18 +181,9 @@ public class PathUtil {
* @param options {@link StandardCopyOption}
* @return Path
* @throws IORuntimeException IO异常
- * @since 5.5.1
*/
public static Path copyContent(final Path src, final Path target, final CopyOption... options) throws IORuntimeException {
- Assert.notNull(src, "Src path must be not null !");
- Assert.notNull(target, "Target path must be not null !");
-
- try {
- Files.walkFileTree(src, new CopyVisitor(src, target, options));
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- return target;
+ return PathCopier.of(src, target, options).copyContent();
}
/**
@@ -271,6 +198,22 @@ public class PathUtil {
return isDirectory(path, false);
}
+ /**
+ * 判断是否存在且为非目录
+ *
+ * 如果path为{@code null},返回{@code false}
+ * 如果path不存在,返回{@code false}
+ *
+ *
+ * @param path {@link Path}
+ * @param isFollowLinks 是否追踪到软链对应的真实地址
+ * @return 如果为目录true
+ * @since 3.1.0
+ */
+ public static boolean isExistsAndNotDirectory(final Path path, final boolean isFollowLinks) {
+ return exists(path, isFollowLinks) && false == isDirectory(path, isFollowLinks);
+ }
+
/**
* 判断是否为目录,如果file为null,则返回false
*
@@ -457,6 +400,7 @@ public class PathUtil {
*
*
* FileUtil.rename(file, "aaa.jpg", false) xx/xx.png =》xx/aaa.jpg
+ * FileUtil.rename(dir, "dir2", false) xx/xx/ =》xx/dir2/
*
*
* @param path 被修改的文件
@@ -470,74 +414,44 @@ public class PathUtil {
}
/**
- * 移动文件或目录
- * 当目标是目录时,会将源文件或文件夹整体移动至目标目录下
- * 例如:
+ * 移动文件或目录到目标中,例如:
*
- * move("/usr/aaa/abc.txt", "/usr/bbb")结果为:"/usr/bbb/abc.txt"
- * move("/usr/aaa", "/usr/bbb")结果为:"/usr/bbb/aaa"
+ * 如果src和target为同一文件或目录,直接返回target。
+ * 如果src为文件,target为目录,则移动到目标目录下,存在同名文件则按照是否覆盖参数执行。
+ * 如果src为文件,target为文件,则按照是否覆盖参数执行。
+ * 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如moveContent("/a/b", "/c/d"), d不存在,则b变成d。
+ * 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
+ * 如果src为目录,target为目录,则将源目录及其内容移动到目标路径目录中,如move("/a/b", "/c/d"),结果为"/c/d/b"
+ * 如果src为目录,target为不存在的路径,则重命名src到target,如move("/a/b", "/c/d"),结果为"/c/d/",相当于b重命名为d
*
*
* @param src 源文件或目录路径
* @param target 目标路径,如果为目录,则移动到此目录下
* @param isOverride 是否覆盖目标文件
* @return 目标文件Path
- * @since 5.5.1
*/
- public static Path move(final Path src, Path target, final boolean isOverride) {
- Assert.notNull(src, "Src path must be not null !");
- Assert.notNull(target, "Target path must be not null !");
-
- if (isDirectory(target)) {
- target = target.resolve(src.getFileName());
- }
- return moveContent(src, target, isOverride);
+ public static Path move(final Path src, final Path target, final boolean isOverride) {
+ return PathMover.of(src, target, isOverride).move();
}
/**
- * 移动文件或目录内容到目标目录中,例如:
+ * 移动文件或目录内容到目标中,例如:
*
- * moveContent("/usr/aaa/abc.txt", "/usr/bbb")结果为:"/usr/bbb/abc.txt"
- * moveContent("/usr/aaa", "/usr/bbb")结果为:"/usr/bbb"
+ * 如果src为文件,target为目录,则移动到目标目录下,存在同名文件则按照是否覆盖参数执行。
+ * 如果src为文件,target为文件,则按照是否覆盖参数执行。
+ * 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如moveContent("/a/b", "/c/d"), d不存在,则b变成d。
+ * 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
+ * 如果src为目录,target为目录,则将源目录下的内容移动到目标路径目录中,源目录不删除。
+ * 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录下的内容移动到目标路径目录中,源目录不删除。
*
*
* @param src 源文件或目录路径
* @param target 目标路径,如果为目录,则移动到此目录下
* @param isOverride 是否覆盖目标文件
* @return 目标文件Path
- * @since 5.7.9
*/
public static Path moveContent(final Path src, final Path target, final boolean isOverride) {
- Assert.notNull(src, "Src path must be not null !");
- Assert.notNull(target, "Target path must be not null !");
-
- if(equals(src, target)){
- // issue#2845,当用户传入目标路径与源路径一致时,直接返回,否则会导致删除风险。
- return target;
- }
-
- final CopyOption[] options = isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{};
-
- // 自动创建目标的父目录
- mkParentDirs(target);
- try {
- return Files.move(src, target, options);
- } catch (final IOException e) {
- if(e instanceof FileAlreadyExistsException){
- // 目标文件已存在,直接抛出异常
- // issue#I4QV0L@Gitee
- throw new IORuntimeException(e);
- }
- // 移动失败,可能是跨分区移动导致的,采用递归移动方式
- try {
- Files.walkFileTree(src, new MoveVisitor(src, target, options));
- // 移动后空目录没有删除,
- del(src);
- } catch (final IOException e2) {
- throw new IORuntimeException(e2);
- }
- return target;
- }
+ return PathMover.of(src, target, isOverride).moveContent();
}
/**
@@ -589,12 +503,15 @@ public class PathUtil {
/**
* 判断文件或目录是否存在
*
- * @param path 文件
+ * @param path 文件,{@code null}返回{@code false}
* @param isFollowLinks 是否跟踪软链(快捷方式)
* @return 是否存在
* @since 5.5.3
*/
public static boolean exists(final Path path, final boolean isFollowLinks) {
+ if (null == path) {
+ return false;
+ }
final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
return Files.exists(path, options);
}
@@ -681,22 +598,4 @@ public class PathUtil {
}
return path.getFileName().toString();
}
-
- /**
- * 删除文件或空目录,不追踪软链
- *
- * @param path 文件对象
- * @throws IOException IO异常
- * @since 5.7.7
- */
- protected static void delFile(final Path path) throws IOException {
- try {
- Files.delete(path);
- } catch (final AccessDeniedException e) {
- // 可能遇到只读文件,无法删除.使用 file 方法删除
- if (false == path.toFile().delete()) {
- throw e;
- }
- }
- }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/Tailer.java b/hutool-core/src/main/java/cn/hutool/core/io/file/Tailer.java
index 3bd690fce..e15c504ad 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/Tailer.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/Tailer.java
@@ -2,8 +2,8 @@ package cn.hutool.core.io.file;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.exceptions.UtilException;
-import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.func.SerConsumer;
import cn.hutool.core.util.CharUtil;
@@ -140,7 +140,11 @@ public class Tailer implements Serializable {
* 结束,此方法需在异步模式或
*/
public void stop(){
- this.executorService.shutdown();
+ try{
+ this.executorService.shutdown();
+ } finally {
+ IoUtil.close(this.randomAccessFile);
+ }
}
// ---------------------------------------------------------------------------------------- Private method start
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/DelVisitor.java b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/DelVisitor.java
index 09b456113..a167ad627 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/DelVisitor.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/DelVisitor.java
@@ -15,6 +15,9 @@ import java.nio.file.attribute.BasicFileAttributes;
*/
public class DelVisitor extends SimpleFileVisitor {
+ /**
+ * 单例对象
+ */
public static DelVisitor INSTANCE = new DelVisitor();
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java
index deb7c2c99..4dfe544cd 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java
@@ -1,7 +1,8 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.classloader.ClassLoaderUtil;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
+import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.text.StrUtil;
@@ -67,7 +68,7 @@ public class ClassPathResource extends UrlResource {
final String path = normalizePath(pathBaseClassLoader);
this.path = path;
- this.name = StrUtil.isBlank(path) ? null : FileUtil.getName(path);
+ this.name = StrUtil.isBlank(path) ? null : FileNameUtil.getName(path);
this.classLoader = ObjUtil.defaultIfNull(classLoader, ClassLoaderUtil::getClassLoader);
this.clazz = clazz;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java
index 4f9f133fe..f35d598a5 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java
@@ -1,6 +1,6 @@
package cn.hutool.core.io.resource;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.net.url.URLUtil;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java
index 1ce456083..c746a1715 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java
@@ -2,7 +2,7 @@ package cn.hutool.core.io.resource;
import cn.hutool.core.collection.iter.EnumerationIter;
import cn.hutool.core.collection.iter.IterUtil;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.classloader.ClassLoaderUtil;
@@ -123,6 +123,7 @@ public class ResourceUtil {
*
* @param resource 资源(相对Classpath的路径)
* @return 资源URL
+ * @throws IORuntimeException IO异常
*/
public static URL getResourceUrl(final String resource) throws IORuntimeException {
return getResourceUrl(resource, null);
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
index d80e37958..24903cb21 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
@@ -1,6 +1,7 @@
package cn.hutool.core.io.resource;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
+import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.net.url.URLUtil;
@@ -50,7 +51,7 @@ public class UrlResource implements Resource, Serializable{
if(null != url && URLUtil.URL_PROTOCOL_FILE.equals(url.getProtocol())){
this.lastModified = FileUtil.file(url).lastModified();
}
- this.name = ObjUtil.defaultIfNull(name, () -> (null != url ? FileUtil.getName(url.getPath()) : null));
+ this.name = ObjUtil.defaultIfNull(name, () -> (null != url ? FileNameUtil.getName(url.getPath()) : null));
}
//-------------------------------------------------------------------------------------- Constructor end
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/WebAppResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/WebAppResource.java
index a7b57302a..e4fbecb63 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/WebAppResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/WebAppResource.java
@@ -2,7 +2,7 @@ package cn.hutool.core.io.resource;
import java.io.File;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
/**
* Web root资源访问对象
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/stream/EmptyOutputStream.java b/hutool-core/src/main/java/cn/hutool/core/io/stream/EmptyOutputStream.java
index c6de0ac74..01ecaa7ae 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/stream/EmptyOutputStream.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/stream/EmptyOutputStream.java
@@ -1,6 +1,5 @@
package cn.hutool.core.io.stream;
-import java.io.IOException;
import java.io.OutputStream;
/**
@@ -47,11 +46,10 @@ public class EmptyOutputStream extends OutputStream {
* 什么也不做,写出到 {@code /dev/null}.
*
* @param b 写出的数据
- * @throws IOException 不抛出
*/
@SuppressWarnings("NullableProblems")
@Override
- public void write(final byte[] b) throws IOException {
+ public void write(final byte[] b) {
// to /dev/null
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java b/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java
index 08b22c990..fd2673db3 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java
@@ -48,15 +48,15 @@ public class SyncInputStream extends FilterInputStream {
/**
* 同步数据到内存
+ * @return this
*/
- public void sync() {
- if (false == asyncFlag) {
- // 已经是同步模式
- return;
+ public SyncInputStream sync() {
+ if (asyncFlag) {
+ this.in = new ByteArrayInputStream(readBytes());
+ this.asyncFlag = false;
}
- this.in = new ByteArrayInputStream(readBytes());
- this.asyncFlag = false;
+ return this;
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/watch/WatchMonitor.java b/hutool-core/src/main/java/cn/hutool/core/io/watch/WatchMonitor.java
index 4d9b3e983..d0900753d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/watch/WatchMonitor.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/watch/WatchMonitor.java
@@ -1,6 +1,6 @@
package cn.hutool.core.io.watch;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.watch.watchers.WatcherChain;
import cn.hutool.core.text.StrUtil;
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/DelayWatcher.java b/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/DelayWatcher.java
index b84bb88f2..389b35557 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/DelayWatcher.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/DelayWatcher.java
@@ -33,7 +33,7 @@ public class DelayWatcher implements Watcher {
//---------------------------------------------------------------------------------------------------------- Constructor start
/**
* 构造
- * @param watcher 实际处理触发事件的监视器{@link Watcher},不可以是{@link DelayWatcher}
+ * @param watcher 实际处理触发事件的监视器{@link Watcher},不可以是{@code DelayWatcher}
* @param delay 延迟时间,单位毫秒
*/
public DelayWatcher(final Watcher watcher, final long delay) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/WatcherChain.java b/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/WatcherChain.java
index f94024469..00be41a41 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/WatcherChain.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/watch/watchers/WatcherChain.java
@@ -1,11 +1,11 @@
package cn.hutool.core.io.watch.watchers;
-import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.lang.Chain;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -22,9 +22,9 @@ public class WatcherChain implements Watcher, Chain{
final private List chain;
/**
- * 创建观察者链{@link WatcherChain}
+ * 创建观察者链{@code WatcherChain}
* @param watchers 观察者列表
- * @return {@link WatcherChain}
+ * @return {@code WatcherChain}
*/
public static WatcherChain of(final Watcher... watchers) {
return new WatcherChain(watchers);
@@ -35,7 +35,7 @@ public class WatcherChain implements Watcher, Chain{
* @param watchers 观察者列表
*/
public WatcherChain(final Watcher... watchers) {
- chain = ListUtil.of(watchers);
+ chain = Arrays.asList(watchers);
}
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java b/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java
index 350b9bb65..e51a04738 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/Assert.java
@@ -144,7 +144,6 @@ public class Assert {
/**
* 断言对象是否为{@code null} ,如果不为{@code null} 抛出{@link IllegalArgumentException} 异常
- *
*
* Assert.isNull(value, "The value must be null");
*
@@ -160,7 +159,6 @@ public class Assert {
/**
* 断言对象是否为{@code null} ,如果不为{@code null} 抛出{@link IllegalArgumentException} 异常
- *
*
* Assert.isNull(value);
*
@@ -201,7 +199,6 @@ public class Assert {
/**
* 断言对象是否不为{@code null} ,如果为{@code null} 抛出{@link IllegalArgumentException} 异常 Assert that an object is not {@code null} .
- *
*
* Assert.notNull(clazz, "The class must not be null");
*
@@ -219,7 +216,6 @@ public class Assert {
/**
* 断言对象是否不为{@code null} ,如果为{@code null} 抛出{@link IllegalArgumentException} 异常
- *
*
* Assert.notNull(clazz);
*
@@ -358,8 +354,8 @@ public class Assert {
}
/**
- * 断言给定字符串是否不被另一个字符串包含(即是否为子串)
- * 并使用指定的函数获取错误信息返回
+ * 断言给定字符串是否不被另一个字符串包含(即是否为子串),并使用指定的函数获取错误信息返回
+ * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。
*
* Assert.notContain(name, "rod", ()->{
* // to query relation message
@@ -385,43 +381,42 @@ public class Assert {
}
/**
- * 断言给定字符串是否不被另一个字符串包含(即是否为子串)
- *
+ * 断言给定字符串是否不被另一个字符串包含(即是否为子串)
+ * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。
*
* Assert.notContain(name, "rod", "Name must not contain 'rod'");
*
*
* @param textToSearch 被搜索的字符串
- * @param substring 被检查的子串
+ * @param subString 被检查的子串
* @param errorMsgTemplate 异常时的消息模板
* @param params 参数列表
* @return 被检查的子串
* @throws IllegalArgumentException 非子串抛出异常
*/
- public static String notContain(final String textToSearch, final String substring, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
- return notContain(textToSearch, substring, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
+ public static String notContain(final String textToSearch, final String subString, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
+ return notContain(textToSearch, subString, () -> new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params)));
}
/**
- * 断言给定字符串是否不被另一个字符串包含(即是否为子串)
- *
+ * 断言给定字符串是否不被另一个字符串包含(即是否为子串),即subString是否不是textToSearch的子串。
+ * 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。
*
* Assert.notContain(name, "rod");
*
*
* @param textToSearch 被搜索的字符串
- * @param substring 被检查的子串
+ * @param subString 被检查的子串
* @return 被检查的子串
* @throws IllegalArgumentException 非子串抛出异常
*/
- public static String notContain(final String textToSearch, final String substring) throws IllegalArgumentException {
- return notContain(textToSearch, substring, "[Assertion failed] - this String argument must not contain the substring [{}]", substring);
+ public static String notContain(final String textToSearch, final String subString) throws IllegalArgumentException {
+ return notContain(textToSearch, subString, "[Assertion failed] - this String argument must not contain the substring [{}]", subString);
}
/**
* 断言给定数组是否包含元素,数组必须不为 {@code null} 且至少包含一个元素
* 并使用指定的函数获取错误信息返回
- *
*
* Assert.notEmpty(array, ()->{
* // to query relation message
@@ -447,7 +442,6 @@ public class Assert {
/**
* 断言给定数组是否包含元素,数组必须不为 {@code null} 且至少包含一个元素
- *
*
* Assert.notEmpty(array, "The array must have elements");
*
@@ -465,7 +459,6 @@ public class Assert {
/**
* 断言给定数组是否包含元素,数组必须不为 {@code null} 且至少包含一个元素
- *
*
* Assert.notEmpty(array, "The array must have elements");
*
@@ -507,7 +500,6 @@ public class Assert {
/**
* 断言给定数组是否不包含{@code null}元素,如果数组为空或 {@code null}将被认为不包含
- *
*
* Assert.noNullElements(array, "The array must not have null elements");
*
@@ -525,7 +517,6 @@ public class Assert {
/**
* 断言给定数组是否不包含{@code null}元素,如果数组为空或 {@code null}将被认为不包含
- *
*
* Assert.noNullElements(array);
*
@@ -568,7 +559,6 @@ public class Assert {
/**
* 断言给定集合非空
- *
*
* Assert.notEmpty(collection, "Collection must have elements");
*
@@ -587,7 +577,6 @@ public class Assert {
/**
* 断言给定集合非空
- *
*
* Assert.notEmpty(collection);
*
@@ -632,7 +621,6 @@ public class Assert {
/**
* 断言给定Map非空
- *
*
* Assert.notEmpty(map, "Map must have entries");
*
@@ -652,7 +640,6 @@ public class Assert {
/**
* 断言给定Map非空
- *
*
* Assert.notEmpty(map, "Map must have entries");
*
@@ -670,7 +657,6 @@ public class Assert {
/**
* 断言给定对象是否是给定类的实例
- *
*
* Assert.instanceOf(Foo.class, foo);
*
@@ -688,7 +674,6 @@ public class Assert {
/**
* 断言给定对象是否是给定类的实例
- *
*
* Assert.instanceOf(Foo.class, foo, "foo must be an instance of class Foo");
*
@@ -712,7 +697,6 @@ public class Assert {
/**
* 断言 {@code superType.isAssignableFrom(subType)} 是否为 {@code true}.
- *
*
* Assert.isAssignable(Number.class, myClass);
*
@@ -727,7 +711,6 @@ public class Assert {
/**
* 断言 {@code superType.isAssignableFrom(subType)} 是否为 {@code true}.
- *
*
* Assert.isAssignable(Number.class, myClass, "myClass must can be assignable to class Number");
*
@@ -767,7 +750,6 @@ public class Assert {
/**
* 检查boolean表达式,当检查结果为false时抛出 {@code IllegalStateException}。
- *
*
* Assert.state(id == null, "The id property must not already be initialized");
*
@@ -785,7 +767,6 @@ public class Assert {
/**
* 检查boolean表达式,当检查结果为false时抛出 {@code IllegalStateException}。
- *
*
* Assert.state(id == null);
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java b/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java
index 1e69e28a2..02c8b24fc 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java
@@ -1,5 +1,7 @@
package cn.hutool.core.lang;
+import cn.hutool.core.text.StrUtil;
+
/**
* 片段默认实现
*
@@ -9,26 +11,32 @@ package cn.hutool.core.lang;
*/
public class DefaultSegment implements Segment {
- protected T startIndex;
+ protected T beginIndex;
protected T endIndex;
/**
* 构造
- * @param startIndex 起始位置
- * @param endIndex 结束位置
+ *
+ * @param beginIndex 起始位置
+ * @param endIndex 结束位置
*/
- public DefaultSegment(final T startIndex, final T endIndex) {
- this.startIndex = startIndex;
+ public DefaultSegment(final T beginIndex, final T endIndex) {
+ this.beginIndex = beginIndex;
this.endIndex = endIndex;
}
@Override
- public T getStartIndex() {
- return this.startIndex;
+ public T getBeginIndex() {
+ return this.beginIndex;
}
@Override
public T getEndIndex() {
return this.endIndex;
}
+
+ @Override
+ public String toString() {
+ return StrUtil.format("[{}, {}]", beginIndex, endIndex);
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java
index 66a46269b..cb0d81c5e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java
@@ -28,11 +28,10 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.SerSupplier;
import cn.hutool.core.text.StrUtil;
-import java.util.Objects;
-import java.util.Optional;
import java.util.Collection;
import java.util.NoSuchElementException;
-import java.util.concurrent.Callable;
+import java.util.Objects;
+import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -237,8 +236,8 @@ public class Opt {
* 不满足条件或者元素本身为空时返回一个返回一个空的{@code Opt}
*
* @param predicate 给定的条件
- * @return 如果满足条件则返回本身, 不满足条件或者元素本身为空时返回一个返回一个空的{@code Opt}
- * @throws NullPointerException 如果给定的条件为 {@code null},抛出{@code NPE}
+ * @return 如果满足条件则返回本身, 不满足条件或者元素本身为空时返回一个空的{@code Opt}
+ * @throws NullPointerException 如果给定的条件为 {@code null}抛出{@code NPE}
*/
public Opt filter(final Predicate super T> predicate) {
Objects.requireNonNull(predicate);
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java b/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java
index 92ff0e385..be9a4dbaf 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java
@@ -19,7 +19,7 @@ public interface Segment {
*
* @return 起始位置
*/
- T getStartIndex();
+ T getBeginIndex();
/**
* 获取结束位置
@@ -34,7 +34,7 @@ public interface Segment {
* @return 片段长度
*/
default T length(){
- final T start = Assert.notNull(getStartIndex(), "Start index must be not null!");
+ final T start = Assert.notNull(getBeginIndex(), "Start index must be not null!");
final T end = Assert.notNull(getEndIndex(), "End index must be not null!");
return Convert.convert((Type) start.getClass(), NumberUtil.sub(end, start).abs());
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java b/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java
index fa01f02c9..e1aa07a59 100755
--- a/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java
@@ -3,6 +3,7 @@ package cn.hutool.core.lang;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.regex.PatternPool;
+import cn.hutool.core.regex.RegexPool;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.CreditCodeUtil;
import cn.hutool.core.math.NumberUtil;
@@ -915,7 +916,7 @@ public class Validator {
* @since 5.2.1
*/
public static boolean hasChinese(final CharSequence value) {
- return ReUtil.contains(ReUtil.RE_CHINESES, value);
+ return ReUtil.contains(RegexPool.CHINESES, value);
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiStyle.java b/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiStyle.java
index 9339b0260..17a8987c1 100755
--- a/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiStyle.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiStyle.java
@@ -39,7 +39,7 @@ public enum AnsiStyle implements AnsiElement {
private final int code;
- AnsiStyle(int code) {
+ AnsiStyle(final int code) {
this.code = code;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java b/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java
index 030eb1494..ec143686f 100755
--- a/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java
@@ -19,7 +19,7 @@ public abstract class SrcToDestCopier> implem
/** 源 */
protected T src;
/** 目标 */
- protected T dest;
+ protected T target;
/** 拷贝过滤器,可以过滤掉不需要拷贝的源 */
protected Predicate copyPredicate;
@@ -48,18 +48,18 @@ public abstract class SrcToDestCopier> implem
*
* @return 目标
*/
- public T getDest() {
- return dest;
+ public T getTarget() {
+ return target;
}
/**
* 设置目标
*
- * @param dest 目标
+ * @param target 目标
* @return this
*/
@SuppressWarnings("unchecked")
- public C setDest(final T dest) {
- this.dest = dest;
+ public C setTarget(final T target) {
+ this.target = target;
return (C)this;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java
index b5369995b..38401bc96 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java
@@ -65,11 +65,11 @@ public class LambdaFactory {
* @return 接受Lambda的函数式接口对象
* @param Function类型
*/
+ @SuppressWarnings("unchecked")
public static F build(final Class functionInterfaceType, final Method method) {
Assert.notNull(functionInterfaceType);
Assert.notNull(method);
final MutableEntry, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method);
- //noinspection unchecked
return (F) CACHE.computeIfAbsent(cacheKey, key -> {
final List abstractMethods = Arrays.stream(functionInterfaceType.getMethods())
.filter(m -> Modifier.isAbstract(m.getModifiers()))
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java
index 15f942687..26d6bad8f 100755
--- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java
@@ -13,8 +13,7 @@ import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
+import java.util.function.*;
/**
* Lambda相关工具类
@@ -34,12 +33,12 @@ public class LambdaUtil {
* MyTeacher myTeacher = new MyTeacher();
* Class supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, supplierClass);
- * }
+ * }
*
* 引用静态无参方法:{@code
* Class staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
* Assert.assertEquals(MyTeacher.class, staticSupplierClass);
- * }
+ * }
*
*
* 在以下场景无法获取到正确类型
@@ -123,7 +122,7 @@ public class LambdaUtil {
*