annotationType) {
A annotation = element.getAnnotation(annotationType);
if (annotation == null) {
for (Annotation metaAnn : element.getAnnotations()) {
annotation = metaAnn.annotationType().getAnnotation(annotationType);
if (annotation != null) {
return metaAnn;
}
}
}
return annotation;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/ArrayUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import java.util.Arrays;
/**
* 数组相关操作工具类.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class ArrayUtils {
/**
* 一个空的字符串数组.
*/
public static final String[] EMPTY_STRING_ARRAY = {};
/**
*
* Adds all the elements of the given arrays into a new array.
*
*
* The new array contains all of the element of {@code array1} followed by
* all of the elements {@code array2}. When an array is returned, it is
* always a new array.
*
*
*
* ArrayUtils.addAll([], []) = []
*
*
* @param array1 the first array whose elements are added to the new array.
* @param array2 the second array whose elements are added to the new array.
* @return The new long[] array.
*/
public static long[] addAll(final long[] array1, final long... array2) {
final long[] joinedArray = new long[array1.length + array2.length];
System.arraycopy(array1, 0, joinedArray, 0, array1.length);
System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
return joinedArray;
}
/**
* 判定Object数组是否为空(null或长度为0).
*
* @param array Object数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final Object[] array) {
return array == null || array.length == 0;
}
/**
* 判定Object数组是否不为空(null或长度为0).
*
* @param array Object数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final Object[] array) {
return !isEmpty(array);
}
/**
* 判定byte数组是否为空(null或长度为0).
*
* @param array byte数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final byte[] array) {
return array == null || array.length == 0;
}
/**
* 判定byte数组是否不为空(null或长度为0).
*
* @param array byte数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final byte[] array) {
return !isEmpty(array);
}
/**
* 判定boolean数组是否为空(null或长度为0).
*
* @param array boolean数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final boolean[] array) {
return array == null || array.length == 0;
}
/**
* 判定boolean数组是否不为空(null或长度为0).
*
* @param array boolean数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final boolean[] array) {
return !isEmpty(array);
}
/**
* 判定int数组是否为空(null或长度为0).
*
* @param array int数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final int[] array) {
return array == null || array.length == 0;
}
/**
* 判定int数组是否不为空(null或长度为0).
*
* @param array int数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final int[] array) {
return !isEmpty(array);
}
/**
* 判定long数组是否为空(null或长度为0).
*
* @param array long数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final long[] array) {
return array == null || array.length == 0;
}
/**
* 判定long数组是否不为空(null或长度为0).
*
* @param array long数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final long[] array) {
return !isEmpty(array);
}
/**
* 判定float数组是否为空(null或长度为0).
*
* @param array float数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final float[] array) {
return array == null || array.length == 0;
}
/**
* 判定float数组是否不为空(null或长度为0).
*
* @param array float数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final float[] array) {
return !isEmpty(array);
}
/**
* 判定double数组是否为空(null或长度为0).
*
* @param array double数组
* @return 如果为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isEmpty(final double[] array) {
return array == null || array.length == 0;
}
/**
* 判定double数组是否不为空(null或长度为0).
*
* @param array double数组
* @return 如果不为空(null或长度为0)则返回true, 否则返回false.
*/
public static boolean isNotEmpty(final double[] array) {
return !isEmpty(array);
}
/**
* String数组转化为int数组
*
* @param array String数组
* @return int数组
*/
public static int[] toIntArray(String[] array) {
return Arrays.stream(array).mapToInt(s -> Integer.parseInt(s)).toArray();
}
/**
* Integer数组转化为int数组
*
* @param array Integer数组
* @return int数组
*/
public static int[] toIntArray(Integer[] array) {
return Arrays.stream(array).mapToInt(Integer::intValue).toArray();
}
/**
* String数组转化为long数组
*
* @param array String数组
* @return long数组
*/
public static long[] toLongArray(String[] array) {
return Arrays.stream(array).mapToLong(s -> Long.parseLong(s)).toArray();
}
/**
* Long数组转化为long数组
*
* @param array Long数组
* @return long数组
*/
public static long[] toLongArray(Long[] array) {
return Arrays.stream(array).mapToLong(Long::longValue).toArray();
}
/**
* 字符串组数转化为字节数组.
*
* 默认使用10进制解析
*
* @param array 字符串组数
* @return 转化后的字节数组
*/
public static byte[] toByteArray(String[] array) {
return toByteArray(array, 10);
}
/**
* 字符串组数转化为字节数组.
*
* @param array 字符串组数
* @param radix 角色字符串数组{@code array}时所用的进制
* @return 转化后的字节数组
*/
public static byte[] toByteArray(String[] array, int radix) {
final byte[] data = new byte[array.length];
for (int i = 0; i < array.length; i++) {
data[i] = (byte) Integer.parseInt(array[i], radix);
}
return data;
}
/**
* 求数组长度.
*
* 如果数组为null,则返回0
*
* @param array 字符串数组
* @return 返回字符串数组长度
*/
public static int length(String[] array) {
return array == null ? 0 : array.length;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/Assert.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import org.apache.commons.lang3.ObjectUtils;
import java.util.Collection;
import java.util.Map;
/**
* some description
*
* @author Allen Jiang
* @since 1.0.0
*/
public class Assert {
/**
* Assert a boolean expression, throwing {@code IllegalArgumentException}
* if the test result is {@code false}.
*
Assert.isTrue(i > 0, "The value must be greater than zero");
*
* @param expression a boolean expression
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if expression is {@code false}
*/
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert a boolean expression, throwing {@code IllegalArgumentException}
* if the test result is {@code false}.
* Assert.isTrue(i > 0);
*
* @param expression a boolean expression
* @throws IllegalArgumentException if expression is {@code false}
*/
public static void isTrue(boolean expression) {
isTrue(expression, "[Assertion failed] - this expression must be true");
}
/**
* Assert that an object is {@code null} .
* Assert.isNull(value, "The value must be null");
*
* @param object the object to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object is not {@code null}
*/
public static void isNull(Object object, String message) {
if (object != null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an object is {@code null} .
* Assert.isNull(value);
*
* @param object the object to check
* @throws IllegalArgumentException if the object is not {@code null}
*/
public static void isNull(Object object) {
isNull(object, "[Assertion failed] - the object argument must be null");
}
/**
* Assert that an object is not {@code null} .
* Assert.notNull(clazz, "The class must not be null");
*
* @param object the object to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object is {@code null}
*/
public static void notNull(Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an object is not {@code null} .
* Assert.notNull(clazz);
*
* @param object the object to check
* @throws IllegalArgumentException if the object is {@code null}
*/
public static void notNull(Object object) {
notNull(object, "[Assertion failed] - this argument is required; it must not be null");
}
/**
* Assert that the given String is not empty; that is,
* it must not be {@code null} and not the empty String.
* Assert.hasLength(name, "Name must not be empty");
*
* @param text the String to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the text is empty
* @see StringUtils#hasLength
*/
public static void hasLength(String text, String message) {
if (!StringUtils.hasLength(text)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the given String is not empty; that is,
* it must not be {@code null} and not the empty String.
* Assert.hasLength(name);
*
* @param text the String to check
* @throws IllegalArgumentException if the text is empty
* @see StringUtils#hasLength
*/
public static void hasLength(String text) {
hasLength(text,
"[Assertion failed] - this String argument must have length; it must not be null or empty");
}
/**
* Assert that the given String has valid text content; that is, it must not
* be {@code null} and must contain at least one non-whitespace character.
* Assert.hasText(name, "'name' must not be empty");
*
* @param text the String to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the text does not contain valid text content
* @see StringUtils#hasText
*/
public static void hasText(String text, String message) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the given String has valid text content; that is, it must not
* be {@code null} and must contain at least one non-whitespace character.
* Assert.hasText(name, "'name' must not be empty");
*
* @param text the String to check
* @throws IllegalArgumentException if the text does not contain valid text content
* @see StringUtils#hasText
*/
public static void hasText(String text) {
hasText(text,
"[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
}
/**
* Assert that the given text does not contain the given substring.
* Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");
*
* @param textToSearch the text to search
* @param substring the substring to find within the text
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the text contains the substring
*/
public static void doesNotContain(String textToSearch, String substring, String message) {
if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) &&
textToSearch.contains(substring)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the given text does not contain the given substring.
* Assert.doesNotContain(name, "rod");
*
* @param textToSearch the text to search
* @param substring the substring to find within the text
* @throws IllegalArgumentException if the text contains the substring
*/
public static void doesNotContain(String textToSearch, String substring) {
doesNotContain(textToSearch, substring,
"[Assertion failed] - this String argument must not contain the substring [" + substring + "]");
}
/**
* Assert that an array has elements; that is, it must not be
* {@code null} and must have at least one element.
* Assert.notEmpty(array, "The array must have elements");
*
* @param array the array to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object array is {@code null} or has no elements
*/
public static void notEmpty(Object[] array, String message) {
if (ObjectUtils.isEmpty(array)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an array has elements; that is, it must not be
* {@code null} and must have at least one element.
* Assert.notEmpty(array);
*
* @param array the array to check
* @throws IllegalArgumentException if the object array is {@code null} or has no elements
*/
public static void notEmpty(Object[] array) {
notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
}
/**
* Assert that an array has no null elements.
* Note: Does not complain if the array is empty!
* Assert.noNullElements(array, "The array must have non-null elements");
*
* @param array the array to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static void noNullElements(Object[] array, String message) {
if (array != null) {
for (Object element : array) {
if (element == null) {
throw new IllegalArgumentException(message);
}
}
}
}
/**
* Assert that an array has no null elements.
* Note: Does not complain if the array is empty!
* Assert.noNullElements(array);
*
* @param array the array to check
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static void noNullElements(Object[] array) {
noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
}
/**
* Assert that a collection has elements; that is, it must not be
* {@code null} and must have at least one element.
* Assert.notEmpty(collection, "Collection must have elements");
*
* @param collection the collection to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the collection is {@code null} or has no elements
*/
public static void notEmpty(Collection> collection, String message) {
if (collection == null || collection.isEmpty()) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that a collection has elements; that is, it must not be
* {@code null} and must have at least one element.
* Assert.notEmpty(collection, "Collection must have elements");
*
* @param collection the collection to check
* @throws IllegalArgumentException if the collection is {@code null} or has no elements
*/
public static void notEmpty(Collection> collection) {
notEmpty(collection,
"[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
}
/**
* Assert that a Map has entries; that is, it must not be {@code null}
* and must have at least one entry.
* Assert.notEmpty(map, "Map must have entries");
*
* @param map the map to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the map is {@code null} or has no entries
*/
public static void notEmpty(Map, ?> map, String message) {
if (map == null || map.isEmpty()) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that a Map has entries; that is, it must not be {@code null}
* and must have at least one entry.
* Assert.notEmpty(map);
*
* @param map the map to check
* @throws IllegalArgumentException if the map is {@code null} or has no entries
*/
public static void notEmpty(Map, ?> map) {
notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
}
/**
* Assert that the provided object is an instance of the provided class.
* Assert.instanceOf(Foo.class, foo);
*
* @param clazz the required class
* @param obj the object to check
* @throws IllegalArgumentException if the object is not an instance of clazz
* @see Class#isInstance
*/
public static void isInstanceOf(Class> clazz, Object obj) {
isInstanceOf(clazz, obj, "");
}
/**
* Assert that the provided object is an instance of the provided class.
* Assert.instanceOf(Foo.class, foo);
*
* @param type the type to check against
* @param obj the object to check
* @param message a message which will be prepended to the message produced by
* the function itself, and which may be used to provide context. It should
* normally end in ":" or "." so that the generated message looks OK when
* appended to it.
* @throws IllegalArgumentException if the object is not an instance of clazz
* @see Class#isInstance
*/
public static void isInstanceOf(Class> type, Object obj, String message) {
notNull(type, "Type to check against must not be null");
if (!type.isInstance(obj)) {
throw new IllegalArgumentException(
(StringUtils.hasLength(message) ? message + " " : "") +
"Object of class [" + (obj != null ? obj.getClass().getName() : "null") +
"] must be an instance of " + type);
}
}
/**
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
* Assert.isAssignable(Number.class, myClass);
*
* @param superType the super type to check
* @param subType the sub type to check
* @throws IllegalArgumentException if the classes are not assignable
*/
public static void isAssignable(Class> superType, Class> subType) {
isAssignable(superType, subType, "");
}
/**
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
* Assert.isAssignable(Number.class, myClass);
*
* @param superType the super type to check against
* @param subType the sub type to check
* @param message a message which will be prepended to the message produced by
* the function itself, and which may be used to provide context. It should
* normally end in ":" or "." so that the generated message looks OK when
* appended to it.
* @throws IllegalArgumentException if the classes are not assignable
*/
public static void isAssignable(Class> superType, Class> subType, String message) {
notNull(superType, "Type to check against must not be null");
if (subType == null || !superType.isAssignableFrom(subType)) {
throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "")
+ subType + " is not assignable to " + superType);
}
}
/**
* Assert a boolean expression, throwing {@code IllegalStateException}
* if the test result is {@code false}. Call isTrue if you wish to
* throw IllegalArgumentException on an assertion failure.
* Assert.state(id == null, "The id property must not already be initialized");
*
* @param expression a boolean expression
* @param message the exception message to use if the assertion fails
* @throws IllegalStateException if expression is {@code false}
*/
public static void state(boolean expression, String message) {
if (!expression) {
throw new IllegalStateException(message);
}
}
/**
* Assert a boolean expression, throwing {@link IllegalStateException}
* if the test result is {@code false}.
* Call {@link #isTrue(boolean)} if you wish to
* throw {@link IllegalArgumentException} on an assertion failure.
*
Assert.state(id == null);
*
* @param expression a boolean expression
* @throws IllegalStateException if the supplied expression is {@code false}
*/
public static void state(boolean expression) {
state(expression, "[Assertion failed] - this state invariant must be true");
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/ByteArrayUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
/**
* 字节数组操作工具类.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class ByteArrayUtils {
/**
* 一个空的字节数组.
*/
public static final byte[] EMPTY_BYTE_ARRAY = {};
/**
* 一个short类型的数字转化为2位byte数组
*
* @param a short类型的数字
* @return byte数组
*/
public static byte[] toByteArray(short a) {
return new byte[]{(byte) ((a >> 8) & 0xFF), (byte) (a & 0xFF)};
}
/**
* 一个int类型的数字转化为4位byte数组
*
* @param num int类型的数字
* @return byte数组
*/
public static byte[] toByteArray(int num) {
return new byte[]{(byte) ((num >> 24) & 0xFF), (byte) ((num >> 16) & 0xFF), (byte) ((num >> 8) & 0xFF), (byte) (num & 0xFF)};
}
/**
* 4位byte数组转化为一个int类型的数字
*
* @param bytes byte数组
* @return int类型的数字
*/
public static int toInt(byte[] bytes) {
return bytes[3] & 0xFF | (bytes[2] & 0xFF) << 8 | (bytes[1] & 0xFF) << 16 | (bytes[0] & 0xFF) << 24;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/CharsetUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import java.nio.charset.Charset;
/**
* 字符集工具类
*
* @author Allen Jiang
* @since 1.0.0
*/
public class CharsetUtils {
/**
* ISO-8859-1
*/
public static final String ISO_8859_1 = "ISO-8859-1";
/**
* UTF-8
*/
public static final String UTF_8 = "UTF-8";
/**
* GBK
*/
public static final String GBK = "GBK";
/**
* ISO-8859-1
*/
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
/**
* UTF-8
*/
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
/**
* GBK
*/
public static final Charset CHARSET_GBK = Charset.forName(GBK);
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/ClassUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
/**
* some description
*
* @author Allen Jiang
* @since 1.0.0
*/
public class ClassUtils {
/**
* Return the default ClassLoader to use: typically the thread context
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
* class will be used as fallback.
* Call this method if you intend to use the thread context ClassLoader
* in a scenario where you clearly prefer a non-null ClassLoader reference:
* for example, for class path resource loading (but not necessarily for
* {@code Class.forName}, which accepts a {@code null} ClassLoader
* reference as well).
* @return the default ClassLoader (only {@code null} if even the system
* ClassLoader isn't accessible)
* @see Thread#getContextClassLoader()
* @see ClassLoader#getSystemClassLoader()
*/
public static ClassLoader getDefaultClassLoader() {
ClassLoader ret = null;
try {
ret = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (ret == null) {
// No thread context class loader -> use class loader of this class.
ret = ClassUtils.class.getClassLoader();
if (ret == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
ret = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return ret;
}
/**
* 使用当前线程的ClassLoader加载给定的类
*
* @param className 类的全称
* @return 给定的类
*/
public static Class> loadClass(String className) {
// ClassLoader#loadClass(String):将.class文件加载到JVM中,不会执行static块,只有在创建实例时才会去执行static块
try {
return Thread.currentThread().getContextClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
}
// Class#forName(String):将.class文件加载到JVM中,还会对类进行解释,并执行类中的static块
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
}
throw new RuntimeException("无法加载指定类名的Class=" + className);
}
/**
* 创建一个指定类的对象,调用默认的构造函数.
*
* @param Class
* @param klass 类
* @return 指定类的对象
*/
public static T newInstance(final Class klass) {
try {
return klass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("无法创建实例. Class=" + klass.getName(), e);
}
}
/**
* 根据ClassName和构造方法的参数列表来创建一个对象
*
* @param Class
* @param className 指定类全名(包含包名称的那种)
* @param parameters 参数列表
* @return 指定ClassName的对象
*/
@SuppressWarnings("unchecked")
public static T newInstance(String className, Object... parameters) {
Class> klass = (Class>) loadClass(className);
try {
Class>[] parameterTypes = new Class>[parameters.length];
for (int i = 0, len = parameters.length; i < len; i++) {
parameterTypes[i] = parameters[i].getClass();
}
return (T) klass.getConstructor(parameterTypes).newInstance(parameters);
} catch (Exception e) {
throw new RuntimeException("无法创建实例. Class=" + klass.getName(), e);
}
}
// /**
// * 尝试运行一个带有Main方法的类.
// *
// * @param mainClass 带有Main方法类的名称
// * @param args 启动参数数组
// */
// public static void invokeMain(String mainClass, String[] args) {
// final Class> klass = ClassUtils.loadClass(mainClass);
// Method mainMethod = MethodUtils.getMethod(klass, "main", String[].class);
// MethodUtils.invoke(null, mainMethod, new Object[]{args});
// }
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/FieldUtils.java
================================================
package io.gamioo.common.util;
import io.gamioo.common.exception.NoPublicFieldException;
import io.gamioo.common.exception.ServerBootstrapException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
* 属性工具类.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class FieldUtils {
private static final int PREFIX_IS_METHOD_INDEX = 2;
private static final int PREFIX_GET_METHOD_INDEX = 3;
private static final int PREFIX_SET_METHOD_INDEX = 3;
/**
* 强制给一个属性{@link Field}写入值.
*
* @param target 目标对象
* @param field 要写入的属性
* @param value 要写入的值
*/
public static void writeField(final Object target, final Field field, final Object value) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
field.set(target, value);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new ServerBootstrapException(target.getClass() + " 的 " + field.getName() + " 属性无法注入.", e);
}
}
/**
* 强制给一个指定名称的属性写入值.
*
* 这个基本是留言给脚本调用的,方便修改一些配置错误而生的方法(自动找父类的属性)
*
* @param target 目标对象
* @param fieldName 要写入的属性名称
* @param value 要写入的值
*/
public static void writeField(final Object target, final String fieldName, final Object value) {
Field field = FieldUtils.getField(target.getClass(), fieldName);
if (field == null) {
throw new NoPublicFieldException("Class={},field={} not found",target.getClass().getName(),fieldName);
}
FieldUtils.writeField(target, field, value);
}
/**
* 强制读取一个属性{@link Field}的值.
*
* @param target 目标类对象,如果是静态方法,目标为null
* @param field 对象的属性
* @return 返回对象属性的值
*/
public static Object readField(final Object target, final Field field) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
return field.get(target);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new ServerBootstrapException(e,"{} 的 {} 属性无法读取.",target.getClass(), field.getName());
}
}
/**
* 获取指定名称的属性.
*
* @param klass 指定类
* @param fieldName 指定名称
* @return 指定名称的属性
*/
public static Field getField(final Class> klass, String fieldName) {
for (Class> target = klass; target != Object.class; target = target.getSuperclass()) {
for (Field field : target.getDeclaredFields()) {
if (field.getName().equals(fieldName)) {
return field;
}
}
}
return null;
}
/**
* 获取指定类的所有属性,包含父类的属性.
*
* 包含私有属性,包含静态属性等等....
*
* @param klass 指定类
* @return 指定类的属性集合.
*/
public static List getFieldList(final Class> klass) {
List result = new ArrayList<>();
for (Class> target = klass; target != Object.class; target = target.getSuperclass()) {
result.addAll(Arrays.asList(target.getDeclaredFields()));
}
return result;
}
/**
* 生成Get方法名.
*
*
* @param field 属性
* @return Get方法名
*/
public static String genGetMethodName(Field field) {
int len = field.getName().length();
StringBuilder sb;
if (field.getType() == boolean.class) {
sb = new StringBuilder(len + 2);
sb.append("is").append(field.getName());
if (Character.isLowerCase(sb.charAt(PREFIX_IS_METHOD_INDEX))) {
sb.setCharAt(PREFIX_IS_METHOD_INDEX, Character.toUpperCase(sb.charAt(PREFIX_IS_METHOD_INDEX)));
}
} else {
sb = new StringBuilder(len + 3);
sb.append("get").append(field.getName());
if (Character.isLowerCase(sb.charAt(PREFIX_GET_METHOD_INDEX))) {
sb.setCharAt(PREFIX_GET_METHOD_INDEX, Character.toUpperCase(sb.charAt(PREFIX_GET_METHOD_INDEX)));
}
}
return sb.toString();
}
/**
* 生成Set方法名.
*
* @param field 属性
* @return Set方法名
*/
public static String genSetMethodName(Field field) {
int len = field.getName().length();
StringBuilder sb = new StringBuilder(len + 3);
sb.append("set").append(field.getName());
if (Character.isLowerCase(sb.charAt(PREFIX_SET_METHOD_INDEX))) {
sb.setCharAt(PREFIX_SET_METHOD_INDEX, Character.toUpperCase(sb.charAt(PREFIX_SET_METHOD_INDEX)));
}
return sb.toString();
}
/**
* 利用反射,扫描出此类所有属性(包含父类中子类没有重写的属性)
*
* @param klass 指定类.
* @param annotations 标识属性的注解
* @return 返回此类所有属性.
*/
public static Field[] scanAllField(final Class> klass, List> annotations) {
// 为了返回是有序的添加过程,这里使用LinkedHashMap
Map fieldMap = new LinkedHashMap<>();
scanField(klass, fieldMap, annotations);
return fieldMap.values().toArray(new Field[0]);
}
/**
* 递归的方式拉取属性,这样父类的属性就在上面了...
*
* @param klass 类
* @param fieldMap 所有属性集合
* @param annotations 标识属性的注解
*/
private static void scanField(final Class> klass, Map fieldMap, List> annotations) {
Class> superClass = klass.getSuperclass();
if (!Object.class.equals(superClass)) {
scanField(superClass, fieldMap, annotations);
}
// 属性判定
for (Field f : klass.getDeclaredFields()) {
// Static和Final的不要
if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers())) {
continue;
}
// 子类已重写或内部类中的不要
if (fieldMap.containsKey(f.getName()) || f.getName().startsWith("this$")) {
continue;
}
// 没有指定的注解不要
for (Annotation a : f.getAnnotations()) {
if (annotations.contains(a.annotationType())) {
fieldMap.put(f.getName(), f);
break;
}
}
}
}
/**
* 获取Map类型的属性Key的Class对象.
*
* @param field Map类型的属性
* @return Key的Class对象
*/
public static Class> getMapFieldKeyClass(Field field) {
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
// Key是第0位
return (Class>) pt.getActualTypeArguments()[0];
}
return Object.class;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/FileUtils.java
================================================
package io.gamioo.common.util;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Optional;
/**
* 文件操作工具类.
*
* @author Allen Jiang
*/
public class FileUtils {
private static final Logger logger = LogManager.getLogger(FileUtils.class);
private static final String URL_PROTOCOL_JAR = "jar";
/**
* 可读大小的单位
*/
private static final String[] UNITS = new String[]{"B", "KB", "MB", "GB", "TB", "EB"};
/**
* 加载类路径下指定名称文件中的文本.
*
* @param fileName 文件名称
* @return 返回文件中的文本
*/
public static Optional getFileText(String fileName) {
try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) {
return Optional.ofNullable(StringUtils.readString(is));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
// 文件不存在等其他情况返回null
return Optional.empty();
}
/**
* 读取指定名称文件中的文本.
*
* @param fileName 文件名称
* @return 返回文件中的文本
* @throws IOException If an I/O error occurs
* @throws FileNotFoundException 文件未找到会抛出此异常
*/
public static String readFileText(String fileName) throws FileNotFoundException, IOException {
try (FileReader reader = new FileReader(fileName)) {
return StringUtils.readString(reader);
}
}
/**
* 读取指定名称文件中的文本.
* TODO(fix): 当fileName不存在的情况下,会导致空指针异常。
*
* @param fileName 文件名称
* @return 返回文件中的文本
*/
public static File getFile(String fileName) {
// 通过url获取File的绝对路径
URL url = Thread.currentThread().getContextClassLoader().getResource(fileName);
if (url != null) {
return new File(url.getFile());
} else {
return null;
}
}
/**
* 读取指定名称文件中的文本.
* 获取jar包内的资源
*
* @param fileName 文件名称
* @return 返回文件中的文本
*/
public static File getFileFromJar(String fileName) {
// 通过url获取File的绝对路径
URL url = FileUtils.class.getResource(fileName);
if (url != null) {
return new File(url.getFile());
} else {
return null;
}
}
/**
* 读取指定名称文件中的文本.
*
* @param fileName 文件名称
* @return 返回文件中的文本
*/
public static InputStream getInputStream(String fileName) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
}
public static InputStream getInputStreamFromAll(String fileName) throws IOException {
InputStream ret;
URL url = ClassUtils.getDefaultClassLoader().getResource(fileName);
File file = null;
if (url != null && StringUtils.equals(url.getProtocol(), URL_PROTOCOL_JAR)) {
ret = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
} else {
file = new File(url.getFile());
ret = openInputStream(file);
}
return ret;
}
/**
* 写入指定文本到文件中.
*
* 文件不存在,则会自动创建,默认是覆盖原文件
*
* @param fileName 文件名称
* @param content 要写入的内容
* @throws IOException If an I/O error occurs
*/
public static void writeFileText(String fileName, String content) throws IOException {
writeFileText(fileName, false, content);
}
/**
* 写入指定文本到文件中.
*
* 文件不存在,则会自动创建
*
* @param fileName 文件名称
* @param append 是否追加写入
* @param content 要写入的内容
* @throws IOException If an I/O error occurs
*/
public static void writeFileText(String fileName, boolean append, String content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(fileName, append); OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
osw.write(content);
osw.flush();
}
}
/**
* 可读的文件大小
*
* @param file 文件
* @return 大小
*/
public static String readableFileSize(File file) {
return readableFileSize(file.length());
}
/**
* 可读的文件大小
*
* @param size Long类型大小
* @return 大小
*/
public static String readableFileSize(long size) {
if (size <= 0) {
return "0";
}
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + UNITS[digitGroups];
}
/**
* 创建指定文件,目录不存在则自动创建
*
* 如果目标文件不存在并且创建成功,则为true;如果目标文件存在,则为false
*
* @param file 文件对象
* @return 如果目标文件不存在并且创建成功,则为true;如果目标文件存在,则为false
* @throws IOException IO异常
*/
public static boolean createNewFile(File file) throws IOException {
// 目标文件存在,直接返回false.
if (file.exists()) {
return false;
}
// 如果目录不存在则创建此父目录
final File parentDir = file.getParentFile();
if (parentDir != null && !parentDir.exists()) {
parentDir.mkdirs();
}
// 目录有了,直接调用JDK的创建新文件命令
return file.createNewFile();
}
public static List readLines(final File file) throws IOException {
return readLines(file, Charset.defaultCharset());
}
/**
* Reads the contents of a file line by line to a List of Strings.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @param charset the charset to use, {@code null} means platform default
* @return the list of Strings representing each line in the file, never {@code null}
* @throws IOException in case of an I/O error
* @since 2.3
*/
public static List readLines(final File file, final Charset charset) throws IOException {
try (InputStream in = openInputStream(file)) {
return IOUtils.readLines(in, Charsets.toCharset(charset));
}
}
/**
* Opens a {@link FileInputStream} for the specified file, providing better
* error messages than simply calling new FileInputStream(file).
*
* At the end of the method either the stream will be successfully opened,
* or an exception will have been thrown.
*
*
* An exception is thrown if the file does not exist.
* An exception is thrown if the file object exists but is a directory.
* An exception is thrown if the file exists but cannot be read.
*
*
* @param file the file to open for input, must not be {@code null}
* @return a new {@link FileInputStream} for the specified file
* @throws FileNotFoundException if the file does not exist
* @throws IOException if the file object is a directory
* @throws IOException if the file cannot be read
* @since 1.3
*/
public static FileInputStream openInputStream(final File file) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (!file.canRead()) {
throw new IOException("File '" + file + "' cannot be read");
}
} else {
throw new FileNotFoundException("File '" + file + "' does not exist");
}
return new FileInputStream(file);
}
/**
* Reads the contents of a file into a String using the default encoding for the VM.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @since 1.3.1
*/
public static String readFileToString(final File file) throws IOException {
return readFileToString(file, Charset.defaultCharset());
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @param charsetName the name of the requested charset, {@code null} means platform default
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @since 2.3
*/
public static String readFileToString(final File file, final Charset charsetName) throws IOException {
try (InputStream in = openInputStream(file)) {
return IOUtils.toString(in, Charsets.toCharset(charsetName));
}
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @since 2.3
*/
public static byte[] readFileToByteArray(final File file) throws IOException {
try (InputStream in = openInputStream(file)) {
return IOUtils.toByteArray(in);
}
}
/**
* Reads the contents of a file into a String. The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @param charsetName the name of the requested charset, {@code null} means platform default
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link java.io
* .UnsupportedEncodingException} in version 2.2 if the encoding is not supported.
* @since 2.3
*/
public static String readFileToString(final File file, final String charsetName) throws IOException {
return readFileToString(file, Charsets.toCharset(charsetName));
}
/**
* 读取指定名称文件中的文本.
* 获取jar包内的资源
*
* @param fileName 文件名称
* @return 返回文件中的文本
* @throws IOException 文件不存在
*/
public static String getStringFromJar(String fileName) throws IOException {
// 通过url获取File的绝对路径
InputStream input = FileUtils.class.getResourceAsStream(fileName);
if (input != null) {
return StringUtils.readString(input);
} else {
throw new IOException(fileName + " not exist");
}
}
/**
* 读取指定名称文件中的文本.
* 获取jar包内的资源
*
* @param fileName 文件名称
* @return 返回文件中的文本
* @throws IOException 文件不存在
*/
public static byte[] getByteArrayFromJar(String fileName) throws IOException {
// 通过url获取File的绝对路径
// InputStream input = FileUtils.class.getResourceAsStream(fileName);
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
if (input != null) {
return IOUtils.toByteArray(input);
} else {
throw new IOException(fileName + " not exist");
}
}
/**
* 加载类路径下指定名称文件中的文本. 包括jar 里的还是在jar外的resource
*
* @param fileName 文件名称
* @return 返回文件中的文本
* @throws IOException 文件不存在
*/
public static byte[] getByteArrayFromFile(String fileName) throws IOException {
byte[] ret;
URL url = ClassUtils.getDefaultClassLoader().getResource(fileName);
File file = null;
if (url != null && StringUtils.equals(url.getProtocol(), URL_PROTOCOL_JAR)) {
// file = new File(url.getFile());
ret = getByteArrayFromJar(fileName);
} else {
file = getFile(fileName);
ret = FileUtils.readFileToByteArray(file);
}
return ret;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/JSONUtils.java
================================================
package io.gamioo.common.util;
import com.alibaba.fastjson2.JSONObject;
import io.gamioo.common.exception.ServiceException;
import org.dom4j.Document;
import org.dom4j.Element;
/**
* some description
*
* @author Allen Jiang
* @since 1.0.0
*/
public class JSONUtils {
public static JSONObject loadFromXMLFile(String fileName) throws ServiceException {
JSONObject ret = new JSONObject();
Document document = XMLUtil.loadFromFile(fileName);
Element root = document.getRootElement();
JsonXmlUtil.xml2Json(root, ret);
return ret;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/JVMUtil.java
================================================
package io.gamioo.common.util;
import java.lang.management.ManagementFactory;
import java.util.List;
/**
* @author Allen Jiang
*/
public class JVMUtil {
public static String getStartArgs() {
String ret = "";
List list = ManagementFactory.getRuntimeMXBean().getInputArguments();
for (String e : list) {
ret += e + " ";
}
return ret;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/JsonXmlUtil.java
================================================
package io.gamioo.common.util;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* xml 和 json的转换器
*
* @author Allen Jiang
* @since 1.0.0
*/
public class JsonXmlUtil {
private static final Logger logger = LogManager.getLogger(JsonXmlUtil.class);
private static DocumentBuilderFactory documentBuilderFactory;
static {
documentBuilderFactory = DocumentBuilderFactory.newInstance();
//XML外部实体注入漏洞 https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_5
try {
// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all
// XML entity attacks are prevented
// Xerces 2 only -
// http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
documentBuilderFactory.setFeature(FEATURE, true);
// If you can't completely disable DTDs, then at least do the following:
// Xerces 1 -
// http://xerces.apache.org/xerces-j/features.html#external-general-entities
// Xerces 2 -
// http://xerces.apache.org/xerces2-j/features.html#external-general-entities
// JDK7+ - http://xml.org/sax/features/external-general-entities
FEATURE = "http://xml.org/sax/features/external-general-entities";
documentBuilderFactory.setFeature(FEATURE, false);
// Xerces 1 -
// http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
// Xerces 2 -
// http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
// JDK7+ - http://xml.org/sax/features/external-parameter-entities
FEATURE = "http://xml.org/sax/features/external-parameter-entities";
documentBuilderFactory.setFeature(FEATURE, false);
// Disable external DTDs as well
FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
documentBuilderFactory.setFeature(FEATURE, false);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and
// Entity Attacks"
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
// And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are
// a requirement, then
// ensure the entity settings are disabled (as shown above) and beware that SSRF
// attacks
// (http://cwe.mitre.org/data/definitions/918.html) and denial
// of service attacks (such as billion laughs or decompression bombs via "jar:")
// are a risk."
// remaining parser logic
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* xml转json
*
* @param element 元素
* @param json json
*/
public static void xml2Json(Element element, JSONObject json) {
//如果是属性
for (Attribute e : element.attributes()) {
json.put(e.getName(), e.getValue());
}
List list = element.elements();
for (Element e : list) {
JSONObject object = new JSONObject();
xml2Json(e, object);
Object obj = json.get(e.getName());
if (obj == null) {
json.put(e.getName(), object);
} else {
JSONArray array = null;
if (obj instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) obj;
json.remove(e.getName());
array = new JSONArray();
array.add(jsonObject);
array.add(object);
} else if (obj instanceof JSONArray) {
array = (JSONArray) obj;
array.add(object);
}
json.put(e.getName(), array);
}
}
}
public static boolean isEmpty(String str) {
if (str == null || str.trim().isEmpty() || "null".equals(str)) {
return true;
}
return false;
}
/**
* 将Map转换为XML格式的字符串
*
* @param store Map类型数据
* @return XML格式的字符串
*/
public static String mapToXml(Map store) {
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key : store.keySet()) {
Object obj = store.get(key);
String value = "";
if (obj != null) {
value = String.valueOf(obj).trim();
}
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); // .replaceAll("\n|\r",
writer.close();
return output;
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return null;
}
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
*/
public static Map xmlToMap(String strXML) {
try {
Map data = new HashMap<>();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
stream.close();
return data;
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return null;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/MathUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import io.gamioo.common.shape.Point;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数学计算相关的工具类库.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class MathUtils {
/**
* 一
*/
public static final double ONE = 1.0;
/**
* 百
*/
public static final double HUNDRED = 100.0;
/**
* 千
*/
public static final double THOUSAND = 1000.0;
/**
* 万
*/
public static final double TEN_THOUSAND = 1_0000.0;
/**
* 百万
*/
public static final double MILLION = 100_0000.0;
/**
* 计算两个参数的和,如果相加出现溢出那就返回{@code int}的最大值.
*
* 区别于JDK的方法,仅仅认同判定方案,游戏世界,溢出时那就修正一个合理的值,一般调用此方法的游戏逻辑决不能因异常而中断
*
* @param x 第一个参数
* @param y 第二个参数
* @return 两个参数的和
* @see Math#addExact(int, int)
*/
public static int addExact(int x, int y) {
try {
return Math.addExact(x, y);
} catch (ArithmeticException e) {
return Integer.MAX_VALUE;
}
}
/**
* 计算两个参数的和,如果相加出现溢出那就返回{@code long}的最大值.
*
* 区别于JDK的方法,仅仅认同判定方案,游戏世界,溢出时那就修正一个合理的值,一般调用此方法的游戏逻辑决不能因异常而中断
*
* @param x 第一个参数
* @param y 第二个参数
* @return 两个参数的和
* @see Math#addExact(long, long)
*/
public static long addExact(long x, long y) {
try {
return Math.addExact(x, y);
} catch (ArithmeticException e) {
return Long.MAX_VALUE;
}
}
/**
* 计算两个参数的乘积,如果相乘出现溢出那就返回{@code int}的最大值.
*
* 区别于JDK的方法,仅仅认同判定方案,游戏世界,溢出时那就修正一个合理的值,一般调用此方法的游戏逻辑决不能因异常而中断
*
* @param x 第一个参数
* @param y 第二个参数
* @return 两个参数的乘积
* @see Math#multiplyExact(int, int)
*/
public static int multiplyExact(int x, int y) {
try {
return Math.multiplyExact(x, y);
} catch (ArithmeticException e) {
return Integer.MAX_VALUE;
}
}
/**
* 计算两个参数的乘积,如果相乘出现溢出那就返回{@code long}的最大值.
*
* 区别于JDK的方法,仅仅认同判定方案,游戏世界,溢出时那就修正一个合理的值,一般调用此方法的游戏逻辑决不能因异常而中断
*
* @param x 第一个参数
* @param y 第二个参数
* @return 两个参数的乘积
* @see Math#multiplyExact(long, long)
*/
public static long multiplyExact(long x, long y) {
try {
return Math.multiplyExact(x, y);
} catch (ArithmeticException e) {
return Long.MAX_VALUE;
}
}
/**
* 计算两点(x1,y1)到(x2,y2)的距离.
*
* Math.sqrt(|x1-x2|² + |y1-y2|²)
*
* @param x1 坐标X1
* @param y1 坐标Y1
* @param x2 坐标X2
* @param y2 坐标Y2
* @return 两点的距离
*/
public static double distance(int x1, int y1, int x2, int y2) {
final double x = Math.abs(x1 - x2);
final double y = Math.abs(y1 - y2);
return Math.sqrt(x * x + y * y);
}
/**
* 计算两点(x1,y1)到(x2,y2)的距离.
*
* Math.sqrt(|x1-x2|² + |y1-y2|²)
*
* @param x1 坐标X1
* @param y1 坐标Y1
* @param x2 坐标X2
* @param y2 坐标Y2
* @return 两点的距离
*/
public static double distance(double x1, double y1, double x2, double y2) {
final double x = Math.abs(x1 - x2);
final double y = Math.abs(y1 - y2);
return Math.sqrt(x * x + y * y);
}
/**
* 计算两点P1(x1,y1)到P2(x2,y2)的距离.
*
* Math.sqrt(|x1-x2|² + |y1-y2|²)
*
* @param p1 坐标1
* @param p2 坐标2
* @return 两点的距离
*/
public static double distance(Point p1, Point p2) {
return distance(p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
/**
* 判定两点(x1,y1)和(x2,y2)是否相邻.
*
* 可用于两个AOI是否相邻判定
*
* @param x1 坐标X1
* @param y1 坐标Y1
* @param x2 坐标X2
* @param y2 坐标Y2
* @return 如果两坐标相邻返回true, 否则返回false
*/
public static boolean adjacent(int x1, int y1, int x2, int y2) {
return Math.abs(x1 - x2) <= 1 && Math.abs(y1 - y2) <= 1;
}
/**
* 判定两点P1(x1,y1)和P2(x2,y2)是否相邻.
*
* 可用于两个AOI是否相邻判定
*
* @param p1 坐标1
* @param p2 坐标2
* @return 如果两坐标相邻返回true, 否则返回false
*/
public static boolean adjacent(Point p1, Point p2) {
return adjacent(p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
/**
* 向下取整,并返回int值.
*
* @param a 一个带有小数的数值
* @return 返回向下取整后的int值
*/
public static int floorInt(double a) {
return (int) Math.floor(a);
}
/**
* 向下取整,并返回long值.
*
* @param a 一个带有小数的数值
* @return 返回向下取整后的long值
*/
public static long floorLong(double a) {
return (long) Math.floor(a);
}
/**
* 向上取整,并返回int值.
*
* @param a 一个带有小数的数值
* @return 返回向上取整后的int值
*/
public static int ceilInt(double a) {
return (int) Math.ceil(a);
}
/**
* 向上取整,并返回long值.
*
* @param a 一个带有小数的数值
* @return 返回向上取整后的long值
*/
public static long ceilLong(double a) {
return (long) Math.ceil(a);
}
/**
* 4舍5入取整,并返回int值.
*
* @param a 一个带有小数的数值
* @return 返回向上取整后的int值
*/
public static int roundInt(double a) {
return (int) Math.round(a);
}
public static int min(List array) {
int ret = Integer.MAX_VALUE;
for (int e : array) {
if (e < ret) {
ret = e;
}
}
return ret;
}
public static int min(int... array) {
int ret = Integer.MAX_VALUE;
for (int e : array) {
if (e < ret) {
ret = e;
}
}
return ret;
}
public static int max(int... array) {
int ret = Integer.MIN_VALUE;
for (int e : array) {
if (e > ret) {
ret = e;
}
}
return ret;
}
public static int max(List array) {
int ret = Integer.MIN_VALUE;
for (int e : array) {
if (e > ret) {
ret = e;
}
}
return ret;
}
/**
* 4舍5入取整,并返回long值.
*
* @param a 一个带有小数的数值
* @return 返回向上取整后的long值
*/
public static long roundLong(double a) {
return (long) Math.round(a);
}
/**
* 格式化小数位数的方法.
*
* 采用了{@link BigDecimal#setScale(int, RoundingMode)}方式来保留小数位数
* 默认舍入方式为4舍5入, 参考{@link RoundingMode#HALF_UP}
*
* @param value 原始值
* @param newScale 保留小数位数
* @return 返回要被保留指定小数位数的值.
*/
public static float formatScale(float value, int newScale) {
return formatScale(value, newScale, RoundingMode.HALF_UP);
}
/**
* 格式化小数位数的方法.
*
* 采用了{@link BigDecimal#setScale(int, RoundingMode)}方式来保留小数位数
*
* @param value 原始值
* @param newScale 保留小数位数
* @param mode 被保留位数后舍入方式,参考{@link RoundingMode}
* @return 返回要被保留指定小数位数的值.
*/
public static float formatScale(float value, int newScale, RoundingMode mode) {
return BigDecimal.valueOf(value).setScale(newScale, mode).floatValue();
}
/**
* 格式化小数位数的方法.
*
* 采用了{@link BigDecimal#setScale(int, RoundingMode)}方式来保留小数位数
* 默认舍入方式为4舍5入, 参考{@link RoundingMode#HALF_UP}
*
* @param value 原始值
* @param newScale 保留小数位数
* @return 返回要被保留指定小数位数的值.
*/
public static double formatScale(double value, int newScale) {
return formatScale(value, newScale, RoundingMode.HALF_UP);
}
/**
* 格式化小数位数的方法.
*
* 采用了{@link BigDecimal#setScale(int, RoundingMode)}方式来保留小数位数
*
* @param value 原始值
* @param newScale 保留小数位数
* @param mode 被保留位数后舍入方式,参考{@link RoundingMode}
* @return 返回要被保留指定小数位数的值.
*/
public static double formatScale(double value, int newScale, RoundingMode mode) {
return BigDecimal.valueOf(value).setScale(newScale, mode).doubleValue();
}
/**
* N种资源掠夺最优计算方案.
*
* 有N种资源,尝试抢其他的部分,但各种资源有一定的比例...
* 使用场景:SLG的城池掠夺资源计算
*
* @param 资源类型
* @param resources N种资源(参数选用LinkedHashMap,就是想按顺序优先扣前面的...)
* @param max 掠夺的最大值
* @param ratio 掠夺比例建议:比例总和在100以内
* @return 一种最优的掠夺结果
*/
public static Map plunder(Map resources, long max, Map ratio) {
final Map result = new HashMap<>(resources.size());
final int sum = ratio.values().stream().reduce(0, (a, b) -> a + b);
final long step = max >= sum ? max / Math.min(1000, Math.max(10, ratio.values().stream().reduce(0, (a, b) -> a + b))) : max;
// 总计要抢的资源量
long total = max;
while (total > 0) {
// 标识是否还有资源可以抢...
boolean flag = false;
for (Map.Entry e : resources.entrySet()) {
if (e.getValue() <= 0) {
continue;
}
// 只要有一种资源大于0都算还有资源
flag = true;
// 比例+小步长随便,让抢出来的资源效果更好些...
long selfStep = step * ratio.getOrDefault(e.getKey(), 1) + RandomUtils.nextLong(step);
long temp = Math.min(selfStep, total);
// 如果资源不足,就以当前有的抢光就好了...
if (e.getValue() < temp) {
temp = e.getValue();
}
// 最终本次抢的值
final long value = temp;
e.setValue(e.getValue() - value);
total -= temp;
result.compute(e.getKey(), (k, v) -> v == null ? value : v + value);
// 抢满了就退出啦...
if (total <= 0) {
break;
}
}
// 没有资源可以抢时,就直接退出了...
if (!flag) {
break;
}
}
return result;
}
/**
* long类型的数值按比率转化为double类型的值.
*
* 由于配置表在转化中精度丢失问题,建议策划配置的是long类型的数值,所以就有了这个转化方法。
* 比如:约定XX列为百分比,那配置50,就是50%,等于0.5
*
* @param value long类型的数值
* @param ratio 比率
* @return double类型的值
*/
public static double longToDouble(long value, double ratio) {
return value / ratio;
}
/**
* long类型的数值以千分比转化为double类型的值.
*
* 参考 {@link MathUtils#longToDouble(long, double)}
*
* @param value long类型的数值
* @return double类型的值
*/
public static double permillage(long value) {
return MathUtils.longToDouble(value, MathUtils.THOUSAND);
}
/**
* long类型的数值以百分比转化为double类型的值.
*
* 参考 {@link MathUtils#longToDouble(long, double)}
*
* @param value long类型的数值
* @return double类型的值
*/
public static double percentage(long value) {
return MathUtils.longToDouble(value, MathUtils.HUNDRED);
}
/**格式化乘百分比
* @param value 待格式化的数值
* @return 格式化后的字符串*/
public static String prettyPercentage(double value){
NumberFormat nf = NumberFormat.getPercentInstance();
nf.setMinimumFractionDigits(2);
return nf.format(value);
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/MethodUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import io.gamioo.common.exception.NoPublicMethodException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 方法工具类.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class MethodUtils {
/**
* 强制调用一个方法{@link Method}.
*
* @param target 目标对象
* @param method 要调用的方法
* @param args 方法参数
* @return 返回方法的返回值
*/
public static Object invoke(final Object target, final Method method, final Object... args) {
if (!method.isAccessible()) {
method.setAccessible(true);
}
try {
return method.invoke(target, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("反射调用方式时出现了异常情况...", e);
}
}
/**
* 获取指定类的所有方法,包含父类的方法.
*
* @param klass 指定类
* @return 指定类的方法集合.
*/
public static List getMethodList(final Class> klass) {
Set result = new HashSet<>();
for (Class> target = klass; target != Object.class; target = target.getSuperclass()) {
for (Method method : target.getDeclaredMethods()) {
result.add(method);
}
}
return new ArrayList<>(result);
}
/**
* 获取指定类中的指定名称和参数的方法
*
* @param klass 类
* @param name 方法名称
* @param parameterTypes 方法参数
* @return 方法
*/
public static Method getMethod(Class> klass, String name, Class>... parameterTypes) {
try {
return klass.getMethod(name, parameterTypes);
} catch (NoSuchMethodException | SecurityException e) {
throw new NoPublicMethodException(e.getMessage());
}
}
/**
* 判定指定类是否存在Set方法.
*
* @param klass 指定类
* @return 如果存在则返回true, 否则返回false.
*/
public static boolean existSetMethod(Class> klass) {
for (Class> target = klass; target != Object.class; target = target.getSuperclass()) {
for (Method method : target.getDeclaredMethods()) {
if (method.getName().startsWith("set")) {
return true;
}
}
}
return false;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/NativeUtils.java
================================================
package io.gamioo.common.util;
import org.apache.commons.lang3.SystemUtils;
import java.io.*;
/**
* 用于加载native dll的工具类
*/
public class NativeUtils {
public static void loadLibrary(String name) throws IOException {
String suffix = "";
//TODO 暂时只为两种系统服务
if (SystemUtils.IS_OS_LINUX) {
suffix += ".so";
} else {
suffix += ".dll";
}
try (InputStream inputStream = FileUtils.getInputStream(name + suffix); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int n = 0;
while (-1 != (n = inputStream.read(buffer))) {
out.write(buffer, 0, n);
}
File file = File.createTempFile(name, suffix);
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(out.toByteArray());
}
System.load(file.getAbsolutePath());
}
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/RandomUtils.java
================================================
/*
* Copyright 2015-2020 gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.ToIntFunction;
/**
* 随机数相关操作工具类.
*
* 本工具类中统一以{@link ThreadLocalRandom}为基础的封装
*
* @author Allen Jiang
* @since 1.0.0
*/
public class RandomUtils {
private RandomUtils() {
}
/**
* 返回一个随机Boolean值.
*
* @return 随机Boolean值
*/
public static boolean nextBoolean() {
return ThreadLocalRandom.current().nextBoolean();
}
/**
* 返回一个0到指定区间的随机数字.
*
* 0 <= random < bound
*
* @param bound 最大值(不包含)
* @return 返回一个0到指定区间的随机数字
*/
public static int nextInt(int bound) {
return ThreadLocalRandom.current().nextInt(bound);
}
/**
* 返回一个指定区间的随机数字.
*
* origin <= random < bound
*
* @param origin 最小值(包含)
* @param bound 最大值(不包含)
* @return 返回一个指定区间的随机数字
*/
public static int nextInt(int origin, int bound) {
return ThreadLocalRandom.current().nextInt(origin, bound);
}
/**
* 返回一个0到指定区间的随机数字.
*
* 0 <= random < bound
*
* @param bound 最大值(不包含)
* @return 返回一个0到指定区间的随机数字
*/
public static long nextLong(long bound) {
return ThreadLocalRandom.current().nextLong(bound);
}
/**
* 返回一个指定区间的随机数字.
*
* origin <= random < bound
*
* @param origin 最小值(包含)
* @param bound 最大值(不包含)
* @return 返回一个指定区间的随机数字
*/
public static long nextLong(long origin, long bound) {
return ThreadLocalRandom.current().nextLong(origin, bound);
}
/**
* 返回一个随机Double值.
*
* @return 随机Double值
*/
public static double nextDouble() {
return ThreadLocalRandom.current().nextDouble();
}
/**
* 判定一次随机事件是否成功.
*
*
* 如果rate>=1,则百分百返回true.
* 如果rate<=0,则百分百返回false.
*
*
* @param rate 成功率
* @return 如果成功返回true, 否则返回false.
*/
public static boolean isSuccess(float rate) {
return ThreadLocalRandom.current().nextFloat() < rate;
}
/**
* 判定一次随机事件是否成功.
*
*
* 如果rate>=1,则百分百返回true.
* 如果rate<=0,则百分百返回false.
*
*
* @param rate 成功率
* @return 如果成功返回true, 否则返回false.
*/
public static boolean isSuccess(double rate) {
return ThreadLocalRandom.current().nextDouble() < rate;
}
/**
* 判定一次百分比的随机事件是否成功.
*
* 参数自动转化为百分比单位,就是除100
*
*
* RandomUtils.isSuccessByPercentage(rate) = RandomUtils.isSuccess(rate / 100D)
*
*
* @param rate 成功率/100D
* @return 如果成功返回true, 否则返回false.
*/
public static boolean isSuccessByPercentage(long rate) {
return isSuccess(rate / MathUtils.HUNDRED);
}
/**
* 判定一次千分比的随机事件是否成功.
*
* 参数自动转化为千分比单位,就是除1000
*
*
* RandomUtils.isSuccessByPermillage(rate) = RandomUtils.isSuccess(rate / 1000D)
*
*
* @param rate 成功率/1000D
* @return 如果成功返回true, 否则返回false.
*/
public static boolean isSuccessByPermillage(long rate) {
return isSuccess(rate / MathUtils.THOUSAND);
}
/**
* 在指定集合中随机出一个元素.
*
* 所以元素无权重的随机.
*
* @param 要随机集合里的元素类型
* @param list 指定集合
* @return 随机返回集合中的一个元素.
*/
public static T randomList(List list) {
// 没有东东的集合,随机个毛线啊...
if (list == null || list.isEmpty()) {
return null;
}
return list.get(nextInt(list.size()));
}
/**
* 从一个List集合中随机出指定数量的元素.
*
*
* source = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* random(source, 5) = [5, 3, 6, 7, 2]
*
*
* @param 要随机集合里的元素类型
* @param source List集合
* @param num 指定数量
* @return 如果源为空或指定数量小于1,则返回空集合,否则随机抽取元素组装新集合并返回
*/
@SuppressWarnings("unchecked")
public static List randomList(final List source, int num) {
// 没有源或要取的数小于1个就直接返回空列表
if (source == null || num < 1) {
return Collections.emptyList();
}
// 数量刚刚好
if (source.size() <= num) {
List result = new ArrayList<>(source);
Collections.shuffle(source);
return result;
}
// 随机位,最后一个元素向前移动的方式
Object[] rs = source.toArray();
List result = new ArrayList<>(num);
for (int i = 0; i < num; i++) {
int index = nextInt(rs.length - i);
result.add((T) rs[index]);
rs[index] = rs[rs.length - 1 - i];
}
return result;
}
/**
* 在指定集合中按权重随机出一个元素.
*
* K为元素,如果是自定义对象记得重写HashCode和equals.
* V为权重,机率为V/(sum(All))
*
* @param 要随机的元素类型,也是Map的Key
* @param data 随机集合
* @return 按权重随机返回集合中的一个元素.
*/
public static K randomByWeight(Map data) {
final int sum = data.values().stream().reduce(0, (a, b) -> a + b);
if (sum <= 0) {
return randomList(new ArrayList<>(data.keySet()));
}
final int random = nextInt(sum);
int step = 0;
for (Map.Entry e : data.entrySet()) {
step += e.getValue().intValue();
if (step > random) {
return e.getKey();
}
}
throw new RuntimeException("randomByWeight的实现有Bug:" + random);
}
/**
* 在指定集合中按权重随机出一个元素.
*
* 权重,机率为V/(sum(All))
*
* @param 要随机的元素类型
* @param data 随机集合
* @param weightFunction 元素中权重方法
* @return 按权重随机返回集合中的一个元素
*/
public static T randomByWeight(List data, ToIntFunction super T> weightFunction) {
final int sum = data.stream().mapToInt(weightFunction).reduce(0, (a, b) -> a + b);
if (sum <= 0) {
return randomList(data);
}
final int random = nextInt(sum);
int step = 0;
for (T e : data) {
step += weightFunction.applyAsInt(e);
if (step > random) {
return e;
}
}
throw new RuntimeException("randomByWeight的实现有Bug:" + random);
}
/**
* 在指定集合中按权重随机出指定数量个元素.
*
* 权重,机率为V/(sum(All))
*
* @param 要随机的元素类型
* @param data 随机集合
* @param weightFunction 元素中权重方法
* @param num 指定数量
* @return 按权重随机返回集合中的指定数量个元素
*/
public static List randomByWeight(List data, ToIntFunction super T> weightFunction, int num) {
if (num <= 0) {
return Collections.emptyList();
}
final int sum = data.stream().mapToInt(weightFunction).reduce(0, (a, b) -> a + b);
if (sum <= 0) {
return randomList(data, num);
}
List result = new ArrayList<>(num);
for (int i = 1; i <= num; i++) {
final int random = nextInt(sum);
int step = 0;
for (T e : data) {
step += weightFunction.applyAsInt(e);
if (step > random) {
result.add(e);
break;
}
}
}
return result;
}
/**
* 区间随机
*
* max - min
*
* @param min 起始值
* @param max 结束值
* @return 随机数
*/
public static int randomBetween(int min, int max) {
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
}
/**
* 以 numerator/denominator的概率随机触发
*
* @param numerator 分子
* @param denominator 分母
* @return 是否触发
* @throws IllegalArgumentException 分母为0时抛出异常,无法判断此时期望的return
*/
public static boolean trigger(int numerator, int denominator) {
if (denominator <= 0) {
throw new IllegalArgumentException("denominator=" + denominator);
}
if (numerator <= 0) {
return false;
}
if (numerator > denominator) {
return true;
}
return nextInt(0, denominator) < numerator;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/ResourceUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.*;
/**
* some description
*
* @author Allen Jiang
* @since 1.0.0
*/
public class ResourceUtils {
/** Pseudo URL prefix for loading from the class path: "classpath:" */
public static final String CLASSPATH_URL_PREFIX = "classpath:";
/** URL prefix for loading from the file system: "file:" */
public static final String FILE_URL_PREFIX = "file:";
/** URL prefix for loading from the file system: "jar:" */
public static final String JAR_URL_PREFIX = "jar:";
/** URL protocol for a file in the file system: "file" */
public static final String URL_PROTOCOL_FILE = "file";
/** URL protocol for an entry from a jar file: "jar" */
public static final String URL_PROTOCOL_JAR = "jar";
/** URL protocol for an entry from a zip file: "zip" */
public static final String URL_PROTOCOL_ZIP = "zip";
/** URL protocol for an entry from a WebSphere jar file: "wsjar" */
public static final String URL_PROTOCOL_WSJAR = "wsjar";
/** URL protocol for an entry from a JBoss jar file: "vfszip" */
public static final String URL_PROTOCOL_VFSZIP = "vfszip";
/** URL protocol for a JBoss file system resource: "vfsfile" */
public static final String URL_PROTOCOL_VFSFILE = "vfsfile";
/** URL protocol for a general JBoss VFS resource: "vfs" */
public static final String URL_PROTOCOL_VFS = "vfs";
/** File extension for a regular jar file: ".jar" */
public static final String JAR_FILE_EXTENSION = ".jar";
/** Separator between JAR URL and file path within the JAR: "!/" */
public static final String JAR_URL_SEPARATOR = "!/";
/**
* Return whether the given resource location is a URL:
* either a special "classpath" pseudo URL or a standard URL.
* @param resourceLocation the location String to check
* @return whether the location qualifies as a URL
* @see #CLASSPATH_URL_PREFIX
* @see java.net.URL
*/
public static boolean isUrl(String resourceLocation) {
if (resourceLocation == null) {
return false;
}
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
return true;
}
try {
new URL(resourceLocation);
return true;
}
catch (MalformedURLException ex) {
return false;
}
}
/**
* Resolve the given resource location to a {@code java.net.URL}.
*
Does not check whether the URL actually exists; simply returns
* the URL that the given location would correspond to.
* @param resourceLocation the resource location to resolve: either a
* "classpath:" pseudo URL, a "file:" URL, or a plain file path
* @return a corresponding URL object
* @throws FileNotFoundException if the resource cannot be resolved to a URL
*/
public static URL getURL(String resourceLocation) throws FileNotFoundException {
Assert.notNull(resourceLocation, "Resource location must not be null");
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
ClassLoader cl = ClassUtils.getDefaultClassLoader();
URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
if (url == null) {
String description = "class path resource [" + path + "]";
throw new FileNotFoundException(description +
" cannot be resolved to URL because it does not exist");
}
return url;
}
try {
// try URL
return new URL(resourceLocation);
}
catch (MalformedURLException ex) {
// no URL -> treat as file path
try {
return new File(resourceLocation).toURI().toURL();
}
catch (MalformedURLException ex2) {
throw new FileNotFoundException("Resource location [" + resourceLocation +
"] is neither a URL not a well-formed file path");
}
}
}
/**
* Resolve the given resource location to a {@code java.io.File},
* i.e. to a file in the file system.
*
Does not check whether the file actually exists; simply returns
* the File that the given location would correspond to.
* @param resourceLocation the resource location to resolve: either a
* "classpath:" pseudo URL, a "file:" URL, or a plain file path
* @return a corresponding File object
* @throws FileNotFoundException if the resource cannot be resolved to
* a file in the file system
*/
public static File getFile(String resourceLocation) throws FileNotFoundException {
Assert.notNull(resourceLocation, "Resource location must not be null");
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
String description = "class path resource [" + path + "]";
ClassLoader cl = ClassUtils.getDefaultClassLoader();
URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
if (url == null) {
throw new FileNotFoundException(description +
" cannot be resolved to absolute file path because it does not exist");
}
return getFile(url, description);
}
try {
// try URL
return getFile(new URL(resourceLocation));
}
catch (MalformedURLException ex) {
// no URL -> treat as file path
return new File(resourceLocation);
}
}
/**
* Resolve the given resource URL to a {@code java.io.File},
* i.e. to a file in the file system.
* @param resourceUrl the resource URL to resolve
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URL resourceUrl) throws FileNotFoundException {
return getFile(resourceUrl, "URL");
}
/**
* Resolve the given resource URL to a {@code java.io.File},
* i.e. to a file in the file system.
* @param resourceUrl the resource URL to resolve
* @param description a description of the original resource that
* the URL was created for (for example, a class path location)
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
Assert.notNull(resourceUrl, "Resource URL must not be null");
if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {
throw new FileNotFoundException(
description + " cannot be resolved to absolute file path " +
"because it does not reside in the file system: " + resourceUrl);
}
try {
return new File(toURI(resourceUrl).getSchemeSpecificPart());
}
catch (URISyntaxException ex) {
// Fallback for URLs that are not valid URIs (should hardly ever happen).
return new File(resourceUrl.getFile());
}
}
/**
* Resolve the given resource URI to a {@code java.io.File},
* i.e. to a file in the file system.
* @param resourceUri the resource URI to resolve
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URI resourceUri) throws FileNotFoundException {
return getFile(resourceUri, "URI");
}
/**
* Resolve the given resource URI to a {@code java.io.File},
* i.e. to a file in the file system.
* @param resourceUri the resource URI to resolve
* @param description a description of the original resource that
* the URI was created for (for example, a class path location)
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URI resourceUri, String description) throws FileNotFoundException {
Assert.notNull(resourceUri, "Resource URI must not be null");
if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) {
throw new FileNotFoundException(
description + " cannot be resolved to absolute file path " +
"because it does not reside in the file system: " + resourceUri);
}
return new File(resourceUri.getSchemeSpecificPart());
}
/**
* Determine whether the given URL points to a resource in the file system,
* that is, has protocol "file", "vfsfile" or "vfs".
* @param url the URL to check
* @return whether the URL has been identified as a file system URL
*/
public static boolean isFileURL(URL url) {
String protocol = url.getProtocol();
return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) ||
URL_PROTOCOL_VFS.equals(protocol));
}
/**
* Determine whether the given URL points to a resource in a jar file,
* that is, has protocol "jar", "zip", "vfszip" or "wsjar".
* @param url the URL to check
* @return whether the URL has been identified as a JAR URL
*/
public static boolean isJarURL(URL url) {
String protocol = url.getProtocol();
return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) ||
URL_PROTOCOL_VFSZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol));
}
/**
* Determine whether the given URL points to a jar file itself,
* that is, has protocol "file" and ends with the ".jar" extension.
* @param url the URL to check
* @return whether the URL has been identified as a JAR file URL
* @since 4.1
*/
public static boolean isJarFileURL(URL url) {
return (URL_PROTOCOL_FILE.equals(url.getProtocol()) &&
url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION));
}
/**
* Extract the URL for the actual jar file from the given URL
* (which may point to a resource in a jar file or to a jar file itself).
* @param jarUrl the original URL
* @return the URL for the actual jar file
* @throws MalformedURLException if no valid jar file URL could be extracted
*/
public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
String urlFile = jarUrl.getFile();
int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
String jarFile = urlFile.substring(0, separatorIndex);
try {
return new URL(jarFile);
}
catch (MalformedURLException ex) {
// Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
// This usually indicates that the jar file resides in the file system.
if (!jarFile.startsWith("/")) {
jarFile = "/" + jarFile;
}
return new URL(FILE_URL_PREFIX + jarFile);
}
}
else {
return jarUrl;
}
}
/**
* Create a URI instance for the given URL,
* replacing spaces with "%20" URI encoding first.
*
Furthermore, this method works on JDK 1.4 as well,
* in contrast to the {@code URL.toURI()} method.
* @param url the URL to convert into a URI instance
* @return the URI instance
* @throws URISyntaxException if the URL wasn't a valid URI
* @see java.net.URL#toURI()
*/
public static URI toURI(URL url) throws URISyntaxException {
return toURI(url.toString());
}
/**
* Create a URI instance for the given location String,
* replacing spaces with "%20" URI encoding first.
* @param location the location String to convert into a URI instance
* @return the URI instance
* @throws URISyntaxException if the location wasn't a valid URI
*/
public static URI toURI(String location) throws URISyntaxException {
return new URI(StringUtils.replace(location, " ", "%20"));
}
/**
* Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
* given connection, preferring {@code false} but leaving the
* flag at {@code true} for JNLP based resources.
* @param con the URLConnection to set the flag on
*/
public static void useCachesIfNecessary(URLConnection con) {
con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/StringUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 字符串工具类.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class StringUtils {
private static Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+");
/**
* 空字符串 {@code ""}.
*/
public static final String EMPTY = "";
/**
* 一个空格字符串 {@code " "}
*/
public static final String SPACE = " ";
/**
* 一个换行字符串 {@code "\n"}
*/
public static final String LF = "\n";
/**
* 一个回车字符串 {@code "\r"}
*/
public static final String CR = "\r";
/**
* 一个英文逗号字符串 {@code ","}
*/
public static final String COMMA = ",";
/**
* 一个英文连字符字符串 {@code "-"}
*/
public static final String HYPHEN = "-";
/**
* 一个英文左括号字符串 {@code "("}
*/
public static final String LPAREN = "(";
/**
* 一个英文右括号字符串 {@code ")"}
*/
public static final String RPAREN = ")";
/**
* 一个英文左大括号字符串 "{"
*/
public static final String LBRACE = "{";
/**
* 一个英文右大括号字符串 "}"
*/
public static final String RBRACE = "}";
/**
* 一个英文左中括号字符串 {@code "["}
*/
public static final String LBRACKET = "[";
/**
* 一个英文右中括号字符串 {@code "]"}
*/
public static final String RBRACKET = "]";
/**
* 一个英文冒号字符串 {@code ":"}
*/
public static final String COLON = ":";
/**
* 一个英文星号字符串 {@code "*"}
*/
public static final String ASTERISK = "*";
/**
* 一个空字符串数组.
*/
public static final String[] EMPTY_STRING_ARRAY = {};
/**
* 检测字符串是否为 null或"".
*
*
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("test") = false
* StringUtils.isEmpty(" test ") = false
*
*
* @param text 被检测字符串
* @return 如果字符为null或""则返回true,否则返回false.
*/
public static boolean isEmpty(final String text) {
return text == null || text.length() == 0;
}
/**
* 检测字符串是否不为 null且不为"".
*
*
* StringUtils.isNotEmpty(null) = false
* StringUtils.isNotEmpty("") = false
* StringUtils.isNotEmpty(" ") = true
* StringUtils.isNotEmpty("test") = true
* StringUtils.isNotEmpty(" test ") = true
*
*
* @param text 被检测字符串
* @return 如果字符不为 null且不为""则返回true,否则返回false.
*/
public static boolean isNotEmpty(final String text) {
return !isEmpty(text);
}
/**
* 检测字符串是否为空,null或Java空白字符。
*
* Java空白字符的定义请参考{@link Character#isWhitespace(char)}.
*
*
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
*
*
* @param text 被检测字符串
* @return 如果字符串为空,null或Java空白字符则返回true,否则返回false.
*/
public static boolean isBlank(final String text) {
if (text == null) {
return true;
}
// 只要有一个字符不为空白,那就肯定不是空白
for (int i = 0, len = text.length(); i < len; i++) {
if (!Character.isWhitespace(text.charAt(i))) {
return false;
}
}
return true;
}
/**
* 检测字符串是否不为空,null或Java空白字符。
*
* Java空白字符的定义请参考{@link Character#isWhitespace(char)}.
*
*
* StringUtils.isNotBlank(null) = false
* StringUtils.isNotBlank("") = false
* StringUtils.isNotBlank(" ") = false
* StringUtils.isNotBlank("bob") = true
* StringUtils.isNotBlank(" bob ") = true
*
*
* @param text 被检测字符串
* @return 如果字符串不为空,null或Java空白字符则返回true,否则返回false.
*/
public static boolean isNotBlank(final String text) {
return !isBlank(text);
}
/**
* 检测一个字符串长度.
*
* 字符串有可能包含中文等其他文字,中文应该算2个长度.
*
* @param text 被检测字符串
* @return 字符串长度
*/
public static int length(final String text) {
if (text == null) {
return 0;
}
int sum = 0;
for (int i = 0, len = text.length(); i < len; i++) {
sum += text.charAt(i) > 127 ? 2 : 1;
}
return sum;
}
/**
*
* Splits the provided text into an array, separators specified. This is an alternative to using StringTokenizer.
*
*
*
* The separator is not included in the returned String array. Adjacent separators are treated as one separator. For more control over the split use the StrTokenizer class.
*
*
*
* A {@code null} input String returns {@code null}. A {@code null} separatorChars splits on whitespace.
*
*
*
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("abc def", null) = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
*
*
* @param str the String to parse, may be null
* @param separatorChars the characters used as the delimiters, {@code null} splits on whitespace
* @return an array of parsed Strings, {@code null} if null String input
*/
public static String[] split(final String str, final String separatorChars) {
return splitWorker(str, separatorChars, -1, false);
}
/**
* Performs the logic for the {@code split} and {@code splitPreserveAllTokens} methods that return a maximum array length.
*
* @param str the String to parse, may be {@code null}
* @param separatorChars the separate character
* @param max the maximum number of elements to include in the array. A zero or negative value implies no limit.
* @param preserveAllTokens if {@code true}, adjacent separators are treated as empty token separators; if {@code false}, adjacent separators are treated as one separator.
* @return an array of parsed Strings, {@code null} if null String input
*/
private static String[] splitWorker(final String str, final String separatorChars, final int max, final boolean preserveAllTokens) {
if (str == null) {
return EMPTY_STRING_ARRAY;
}
final int len = str.length();
if (len == 0) {
return EMPTY_STRING_ARRAY;
}
final List list = new ArrayList<>();
int sizePlus1 = 1;
int i = 0, start = 0;
boolean match = false;
boolean lastMatch = false;
if (separatorChars == null) {
while (i < len) {
if (Character.isWhitespace(str.charAt(i))) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
} else if (separatorChars.length() == 1) {
final char sep = separatorChars.charAt(0);
while (i < len) {
if (str.charAt(i) == sep) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
} else {
while (i < len) {
if (separatorChars.indexOf(str.charAt(i)) >= 0) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
}
final boolean flag = preserveAllTokens && lastMatch;
if (match || flag) {
list.add(str.substring(start, i));
}
return list.toArray(new String[0]);
}
/**
* 将一个字符串由驼峰式命名变成分割符分隔单词
*
*
* lowerWord("helloWorld", '_') => "hello_world"
*
*
* @param cs 字符串
* @param c 分隔符
* @return 转换后字符串
*/
public static String lowerWord(CharSequence cs, char c) {
int len = cs.length();
StringBuilder sb = new StringBuilder(len + 5);
for (int i = 0; i < len; i++) {
char ch = cs.charAt(i);
if (Character.isUpperCase(ch)) {
if (i > 0) {
sb.append(c);
}
sb.append(Character.toLowerCase(ch));
} else {
sb.append(ch);
}
}
return sb.toString();
}
/**
* 计算一个Long类型的数字的文本长度.
*
* 包含负数计算
*
* @param v Long类型的数字
* @return 数字的文本长度
*/
public static int asciiSizeInBytes(long v) {
if (v == 0) {
return 1;
}
if (v == Long.MIN_VALUE) {
return 20;
}
boolean negative = false;
if (v < 0) {
v = -v;
negative = true;
}
int width = v < 100000000L
//
? v < 10000L
//
? v < 100L ? v < 10L ? 1 : 2 : v < 1000L ? 3 : 4
//
: v < 1000000L ? v < 100000L ? 5 : 6 : v < 10000000L ? 7 : 8
//
: v < 1000000000000L
//
? v < 10000000000L ? v < 1000000000L ? 9 : 10 : v < 100000000000L ? 11 : 12
//
: v < 1000000000000000L
//
? v < 10000000000000L ? 13 : v < 10000000000000L ? 14 : 15
//
: v < 100000000000000000L
//
? v < 10000000000000000L ? 16 : 17
//
: v < 1000000000000000000L ? 18 : 19;
//
return negative ? width + 1 : width;
}
/**
* 拼接字符串.
*
* @param strings 需要拼接的字串
* @return 拼接后的字符串
*/
public static String join(String... strings) {
int len = 0;
for (String str : strings) {
len += str.length();
}
final StringBuilder result = new StringBuilder(len);
for (String str : strings) {
result.append(str);
}
return result.toString();
}
/**
* 拼接路径字符串.
*
* @param paths 需要拼接的路径字串
* @return 拼接后的路径字符串
*/
public static String pathJoin(String... paths) {
int len = 0;
for (String str : paths) {
len += str.length() + 1;
}
final StringBuilder sb = new StringBuilder(len);
for (String str : paths) {
sb.append(str);
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '/') {
sb.append('/');
}
}
return sb.toString();
}
/**
* 拼接字符串.
*
* 在比较长或多的情况计算长度比StringJoiner性能好.
*
*
* StringJoiner result = new StringJoiner(delimiter, prefix, suffix);
* for (String str : strings) {
* result.add(str);
* }
* return result.toString();
*
* Stream.of(strings).collect(Collectors.joining(delimiter, prefix, suffix))
*
*
* @param delimiter 分隔符
* @param prefix 前缀
* @param suffix 后缀
* @param strings 需要拼接的字串
* @return 拼接后的字符串
*/
public static String build(String delimiter, String prefix, String suffix, String... strings) {
int len = prefix.length() + suffix.length() + (strings.length - 1) * delimiter.length();
for (String str : strings) {
len += str.length();
}
StringBuilder result = new StringBuilder(len);
result.append(prefix);
for (String str : strings) {
result.append(str).append(delimiter);
}
if (result.length() > prefix.length()) {
result.deleteCharAt(result.length() - 1);
}
result.append(suffix);
return result.toString();
}
/**
* 编码字符串,编码为UTF-8
*
* @param str 字符串
* @return 编码后的字节数组
*/
public static byte[] utf8Bytes(CharSequence str) {
return bytes(str, CharsetUtils.CHARSET_UTF_8);
}
/**
* 编码字符串
* 使用系统默认编码
*
* @param str 字符串
* @return 编码后的字节码
*/
public static byte[] bytes(CharSequence str) {
return bytes(str, null);
}
/**
* 编码字符串
*
* @param str 字符串
* @param charset 字符集,如果此字段为空,则编码的结果取决于平台
* @return 编码后的字节数组
*/
public static byte[] bytes(CharSequence str, Charset charset) {
if (str == null) {
return ByteArrayUtils.EMPTY_BYTE_ARRAY;
}
if (null == charset) {
return str.toString().getBytes();
}
return str.toString().getBytes(charset);
}
// /**
// * 常规的格式化一个带有占位符的字符串.
// *
// * 区别于JDK的{@link MessageFormat#format(String, Object...)},这个方法只是简单的按位填充
// *
// * @param messagePattern 带有占位符的字符串
// * @param arguments 参数列表
// * @return 格式化以后的字符串
// */
// public static String format(String messagePattern, Object... arguments) {
// if (arguments.length == 0) {
// return messagePattern;
// }
// // FIXME 这个方法基本用于日志,邮件内容拼接,可以参考日志中使用的方案做一个缓存策略
// for (int i = 0, len = arguments.length; i < len; i++) {
// messagePattern = messagePattern.replace(join("{", String.valueOf(i), "}"), String.valueOf(arguments[i]));
// }
// return messagePattern;
// }
/**
* Replace placeholders in the given messagePattern with arguments.
*
* @param messagePattern the message pattern containing placeholders.
* @param arguments the arguments to be used to replace placeholders.
* @return the formatted message.
*/
public static String format(String messagePattern, Object... arguments) {
if (messagePattern == null || arguments == null || arguments.length == 0) {
return messagePattern;
}
final StringBuilder result = new StringBuilder();
int currentArgument = 0;
for (int i = 0; i < messagePattern.length(); i++) {
final char curChar = messagePattern.charAt(i);
if (curChar == DELIM_START && i < messagePattern.length() - 1 && messagePattern.charAt(i + 1) == DELIM_STOP) {
if (currentArgument < arguments.length) {
result.append(arguments[currentArgument]);
} else {
result.append(DELIM_START).append(DELIM_STOP);
}
currentArgument++;
i++;
} else {
result.append(curChar);
}
}
return result.toString();
}
/**
* 从输入流中读出所有文本.
*
* @param inputStream 输入流
* @return 返回流中的文本
* @throws IOException If an I/O error occurs
*/
public static String readString(InputStream inputStream) throws IOException {
return readString(inputStream, CharsetUtils.CHARSET_UTF_8);
}
/**
* 从输入流中读出所有文本.
*
* @param inputStream 输入流
* @param charset 文本的编码方式
* @return 返回流中的文本
* @throws IOException If an I/O error occurs
*/
public static String readString(InputStream inputStream, Charset charset) throws IOException {
try (InputStreamReader isr = new InputStreamReader(inputStream, charset)) {
return readString(isr);
}
}
/**
* 读出所有文本。
*
* 这里没有选择BufferedReader就是不想一行一行的读,浪费字符串拼接性能
* 正常用于读HTTP的响应,配置文件内容,小文件等情况
*
* @param reader 抽象的文本流
* @return 返回流中所有文本
* @throws IOException If an I/O error occurs
*/
public static String readString(Reader reader) throws IOException {
final StringBuilder sb = new StringBuilder(1024);
// 申明一次读取缓冲区
final char[] array = new char[256];
// 这里并没有使用while(true),如果一个文本超过100W,还是放弃后面的算了
while (sb.length() < MathUtils.MILLION) {
int n = reader.read(array);
// 读结束了,就GG了
if (n < 0) {
break;
}
sb.append(array, 0, n);
}
return sb.toString();
}
/**
* 从左边使用空格(' ')补齐指定位位数的字符串.
*
* StringUtils.leftPad(null, *) = null
* StringUtils.leftPad("", 3) = " "
* StringUtils.leftPad("bat", 3) = "bat"
* StringUtils.leftPad("bat", 5) = " bat"
* StringUtils.leftPad("bat", 1) = "bat"
* StringUtils.leftPad("bat", -1) = "bat"
*
*
* @param str 字符串
* @param size 补齐后的位数
* @return 返回补齐后的字符串{@code null}
*/
public static String leftPad(final String str, final int size) {
return leftPad(str, size, ' ');
}
/**
* 从左边使用指定字符补齐指定位位数的字符串.
*
*
* StringUtils.leftPad(null, *, *) = null
* StringUtils.leftPad("", 3, 'b') = "bbb"
* StringUtils.leftPad("bat", 3, 'b') = "bat"
* StringUtils.leftPad("bat", 5, ' ') = " bat"
* StringUtils.leftPad("bat", 1, 'b') = "bat"
* StringUtils.leftPad("bat", -1, 'b') = "bat"
*
*
* @param str 字符串
* @param size 补齐后的位数
* @param padChar 补齐字符
* @return 返回补齐后的字符串{@code null}
*/
public static String leftPad(final String str, final int size, final char padChar) {
if (str == null) {
return null;
}
// 不需要补位,那就返回原来的字符串
final int pads = size - str.length();
if (pads <= 0) {
return str;
}
// 创建返回数组,前面填充指定字符,后面复制原来的字符串
final char[] array = new char[size];
Arrays.fill(array, 0, pads, padChar);
System.arraycopy(str.toCharArray(), 0, array, pads, str.length());
return new String(array);
}
private static final String FOLDER_SEPARATOR = "/";
private static final char DELIM_START = '{';
private static final char DELIM_STOP = '}';
private static final int GB = 1024 * 1024 * 1024;// 定义GB的计算常量
private static final int MB = 1024 * 1024;// 定义MB的计算常量
private static final int KB = 1024;// 定义KB的计算常量
public static boolean isEmpty(String... array) {
for (String str : array) {
if (org.apache.commons.lang3.StringUtils.isEmpty(str)) {
return true;
}
}
return false;
}
public static String format2Percent(double value) {
String p = String.valueOf(value * 100D);
int ix = p.indexOf(".") + 1;
String percent = p.substring(0, ix) + p.substring(ix, ix + 1);
return percent + "%";
}
public static String byte2GBMBKB(long value) {
if (value / GB >= 1) // 如果当前Byte的值大于等于1GB
{
return String.format("%.2f", value / (float) GB) + " GB";// 将其转换成GB
} else if (value / MB >= 1) // 如果当前Byte的值大于等于1MB
{
return String.format("%.2f", value / (float) MB) + " MB";// 将其转换成MB
} else if (value / KB >= 1) // 如果当前Byte的值大于等于1KB
{
return String.format("%.2f", value / (float) KB) + " KB";// 将其转换成KB
} else {
return String.valueOf(value) + " Byte";// 显示Byte值
}
}
public static String stringToAscii(String value) {
StringBuffer ret = new StringBuffer();
char[] chars = value.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (i != chars.length - 1) {
ret.append((int) chars[i]);
} else {
ret.append((int) chars[i]);
}
}
return ret.toString();
}
/**
* 解析字符串中所有数字,注意:这里只是把数字字符串切割出来了,转换成数字时注意边界
*
* @param content 需要解析的字符串
* @return 所有数字字符串集合
*/
public static ArrayList incisionNumber(String content) {
ArrayList ret = new ArrayList<>();
if (org.apache.commons.lang3.StringUtils.isEmpty(content)) {
return ret;
}
Matcher m = NUMBER_PATTERN.matcher(content);
while (m.find()) {
ret.add(m.group());
}
return ret;
}
/**
* 判断字符串是否是同一个字符。kkk全是k;22222全是2
*
* @param arg 需要解析的字符串
* @return 返回判断结果
*/
public static boolean isSameChar(String arg) {
boolean flag = true;
if (org.apache.commons.lang3.StringUtils.isEmpty(arg)) {
return flag;
}
char str = arg.charAt(0);
for (int i = 1; i < arg.length(); i++) {
if (str != arg.charAt(i)) {
flag = false;
break;
}
}
return flag;
}
/**
* 判断一个数字是否是连续的数字,例如,12345和54321就是连续的
*
* @param str 要判断的数字字符串
* @return 返回结果
*/
public static boolean strNumberIsContinue(String str) {
if (isEmpty(str)) {
return false;
}
char[] chars = str.toCharArray();
char c = chars[0];
for (int i = 1; i < chars.length; i++) {
int diff = chars[i] - c;
if (diff != 1 && diff != -1) {
return false;
}
c = chars[i];
}
return true;
}
/**
* 获取字符串中去重后剩下多少个字符
*
* @param str 需要解析的字符串
* @return 返回去重后的字符串
*/
public static int getStringCharCount(String str) {
if (isEmpty(str)) {
return 0;
}
HashSet set = new HashSet<>();
for (char b : str.toCharArray()) {
set.add(b);
}
return set.size();
}
public static boolean hasLength(String str) {
return hasLength((CharSequence) str);
}
public static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
public static boolean hasText(CharSequence str) {
if (!hasLength(str)) {
return false;
}
int strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return true;
}
}
return false;
}
/**
* Check whether the given {@code String} contains actual text.
* More specifically, this method returns {@code true} if the
* {@code String} is not {@code null}, its length is greater than 0,
* and it contains at least one non-whitespace character.
*
* @param str the {@code String} to check (may be {@code null})
* @return {@code true} if the {@code String} is not {@code null}, its
* length is greater than 0, and it does not contain whitespace only
* @see #hasText(CharSequence)
*/
public static boolean hasText(String str) {
return hasText((CharSequence) str);
}
/**
* Apply the given relative path to the given path,
* assuming standard Java folder separation (i.e. "/" separators).
*
* @param path the path to start from (usually a full file path)
* @param relativePath the relative path to apply
* (relative to the full file path above)
* @return the full file path that results from applying the relative path
*/
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
}
return newPath + relativePath;
} else {
return relativePath;
}
}
public static String uncapitalized(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))) {
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
public static boolean equals(final CharSequence cs1, final CharSequence cs2) {
if (cs1 == cs2) {
return true;
}
if (cs1 == null || cs2 == null) {
return false;
}
if (cs1.length() != cs2.length()) {
return false;
}
if (cs1 instanceof String && cs2 instanceof String) {
return cs1.equals(cs2);
}
// Step-wise comparison
final int length = cs1.length();
for (int i = 0; i < length; i++) {
if (cs1.charAt(i) != cs2.charAt(i)) {
return false;
}
}
return true;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/TelnetUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import org.apache.commons.net.telnet.TelnetClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* some description
*
* @author Allen Jiang
* @since 1.0.0
*/
public class TelnetUtils {
private static final Logger logger = LogManager.getLogger(TelnetUtils.class);
/**
* 模拟Telnet 连接
*
* @param checkNum 验证几个
* @param ip 地址
* @param port 端口
* @return 返回是否连接成功
*
* 检测是否能连上
*/
public static boolean isConnected(int checkNum, String ip, int port) {
boolean ret = false;
for (int i = 0; i < checkNum; i++) {
try {
TelnetClient telnet = new TelnetClient();
telnet.connect(ip, port);
ret = telnet.isConnected();
telnet.disconnect();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (ret) {
return ret;
} else {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}
return ret;
}
public static boolean isConnected(String ip, int port) {
boolean ret = false;
try {
TelnetClient telnet = new TelnetClient();
telnet.setConnectTimeout(5000);//连接超时时间
// TelnetClient telnetClient = new TelnetClient("vt200"); //指明Telnet终端类型,否则会返回来的数据中文会乱码
// telnet.setDefaultTimeout(5000);//打开端口的超时时间
telnet.connect(ip, port);
ret = telnet.isConnected();
if (ret) {
logger.info("该连接可以用 {}:{}", ip, port);
telnet.disconnect();
}
} catch (Exception e) {
// logger.warn("该连接无法使用 {}:{}",ip,port);
// logger.error(e.getMessage(), e);
}
return ret;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/ThreadUtils.java
================================================
/*
* Copyright 2015-2020 Gamioo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gamioo.common.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* 线程工具类.
*
* @author Allen Jiang
* @since 1.0.0
*/
public class ThreadUtils {
private static final Logger logger = LogManager.getLogger(ThreadUtils.class);
/**
* 暂停执行.
*
* 就是JDK的{@link Thread#sleep(long)}包装一下就不用管这个异常了.
* 这个方法只用于写一些测试用例时使用...
*
* @param millis 暂停毫秒数
*/
public static void sleep(long millis) {
try {
if (millis > 0) {
Thread.sleep(millis);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 输出当前线程正在运行的堆栈信息.
*
* @param thread 当前线程
* @return 当前线程正在运行的堆栈信息
*/
public static String printStackTrace(Thread thread) {
final StackTraceElement[] st = thread.getStackTrace();
StringBuffer sb = new StringBuffer(2048);
sb.append("\n");
for (StackTraceElement e : st) {
sb.append("\tat ").append(e).append("\n");
}
return sb.toString();
}
/**
* 输出当前线程正在运行的堆栈信息.
* 当前线程正在运行的堆栈信息
*/
public static void printStackTrace() {
try {
throw new Exception();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/util/XMLUtil.java
================================================
package io.gamioo.common.util;
import io.gamioo.common.exception.ServiceException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.rmi.ServerException;
/**
* @author Guilong Jiang
*/
public class XMLUtil {
public static boolean saveXML(Element ele, String sFilePathName) throws ServerException {
return saveXML(ele, sFilePathName, "UTF-8");
}
/**
* save xml to file with special encode
*
* @param ele 元素
* @param sFilePathName 文件路径
* @param encode XMLUtil.ENCODE_UTF_8 or XMLUtil.ENCODE_GBK, default is
* former
* @return 保存成功与否
*/
public static boolean saveXML(Element ele, String sFilePathName, String encode) throws ServiceException {
Document dom = ele.getDocument();
if (dom == null) {
dom = DocumentHelper.createDocument(ele);
}
return saveXML(dom, sFilePathName, encode);
}
/**
* save xml to file with special encode
*
* @param dom 节点
* @param sFilePathName 文件路径
* @param encode XMLUtil.ENCODE_UTF_8 or XMLUtil.ENCODE_GBK, default is
* former
* @return 保存成功与否
*/
public static boolean saveXML(Document dom, String sFilePathName, String encode) throws ServiceException {
File file = new File(sFilePathName);
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
try {
OutputFormat format = OutputFormat.createPrettyPrint();
FileOutputStream out = new FileOutputStream(file);
// if(!encode.equals(ENCODE_UTF_8)){
format.setEncoding(encode);
XMLWriter xmlWriter = new XMLWriter(out, format);
xmlWriter.write(dom);
xmlWriter.flush();
xmlWriter.close();
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("XUT-PSX10003", "write element to file error:{} {}", sFilePathName, e.getMessage());
}
return true;
}
public static Document loadFromFile(String filePathName) throws ServiceException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePathName);
return loadDocument(is);
}
public static Document loadDocument(InputStream is) throws ServiceException {
SAXReader rd = new SAXReader();
Document document = null;
try {
document = rd.read(is);
} catch (Exception e) {
throw new ServiceException("parsing document failed:" + e.getMessage());
}
return document;
}
public static int attributeValueInt(Element element, String attr) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
throw new ServiceException("缺少属性{}: {}", attr, element.asXML());
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ServiceException("属性{}不是数值类型: {}", attr, element.asXML());
}
}
public static float attributeValueFloat(Element element, String attr) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
throw new ServiceException("缺少属性{}: {}", attr, element.asXML());
}
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
throw new ServiceException("属性{}不是数值类型: {}", attr, element.asXML());
}
}
public static int attributeValueInt(Element element, String attr, int defaultValue) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
return defaultValue;
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ServiceException("属性{}不是数值类型: {}", attr, element.asXML());
}
}
public static String attributeValueString(Element element, String attr) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
throw new ServiceException("缺少属性{}: element={}", attr, element.asXML());
}
return value;
}
public static String attributeValueString(Element element, String attr, String defaultValue) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
return defaultValue;
}
return value;
}
public static boolean attributeValueBoolean(Element element, String attr, boolean defaultValue) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
return defaultValue;
}
try {
return Boolean.parseBoolean(value);
} catch (NumberFormatException e) {
throw new ServiceException("属性{}不是布尔类型: {}", attr, element.asXML());
}
}
public static boolean attributeValueBoolean(Element element, String attr) throws ServiceException {
if (element == null) {
throw new ServiceException("element == null");
}
String value = element.attributeValue(attr);
if (value == null) {
throw new ServiceException("缺少属性{}: {}", attr, element.asXML());
}
try {
return Boolean.parseBoolean(value);
} catch (NumberFormatException e) {
throw new ServiceException("属性{}不是布尔类型: {}", attr, element.asXML());
}
}
public static Element subElement(Element parent, String name) throws ServiceException {
if (parent == null) {
throw new ServiceException("parent == null");
}
Element result = parent.element(name);
if (result == null) {
throw new ServiceException("找不到{}节点的子节点{}", parent.getName(), name);
}
return result;
}
public static Element parseText(String content) {
Element ret = null;
try {
Document doc = DocumentHelper.parseText(content);
ret = doc.getRootElement(); // 获取根节点
} catch (DocumentException e) {
throw new ServiceException("parsing content failed:{}", e.getMessage());
}
return ret;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/vector/Vector2f.java
================================================
package io.gamioo.common.vector;
import io.gamioo.common.shape.Point;
/**
* @author Allen Jiang
*/
public class Vector2f {
private final float x;
private final float y;
public Vector2f(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public double getRadians() {
return Math.atan2(y, x);
}
public Point toPoint() {
return Point.valueOf(x, y);
}
public double getAngle() {
return Math.toDegrees(this.getRadians());
}
public double getLength() {
return Math.sqrt(x * x + y * y);
}
public static Vector2f valueOf(int length, int angle) {
double radians = Math.toRadians(angle);
float y = (float) Math.sin(radians) * length;
float x = (float) Math.cos(radians) * length;
return new Vector2f(x, y);
}
public static Vector2f valueOf(float x, float y) {
return new Vector2f(x, y);
}
public static Vector2f add(Vector2f a, Vector2f b) {
return new Vector2f(a.x + b.x, a.y + b.y);
}
public Vector2f add(Vector2f src) {
return new Vector2f(x + src.x, y + src.y);
}
public Point add(Point srcPoint) {
return Point.valueOf(x + srcPoint.getX(), y + srcPoint.getY());
}
/**
* 单位化
*
* @param a 向量
* @return 调整后的向量
*/
public static Vector2f unitization(Vector2f a) {
float len = (float) a.getLength();
return new Vector2f(a.getX() / len, a.getY() / len);
}
public Vector2f unitization() {
return unitization(this);
}
/**
* 调整长度
*
* @param newLength 新长度
* @return 调整后的向量
*/
public Vector2f resizeLength(float newLength) {
float len = (float) getLength();
return Vector2f.valueOf(x / len * newLength, y / len * newLength);
}
/**
* 旋转
*
* @param angle 顺时针 旋转角度 [0 - 360]
* @return 旋转后的向量
*/
public Vector2f rotate(int angle) {
double radians = Math.toRadians(angle);
double sin = Math.sin(radians);
double cos = Math.cos(radians);
return Vector2f.valueOf((float) (sin * y + cos * x), (float) (cos * y - sin * x));
}
public Vector2f rotate(double radians) {
double sin = Math.sin(radians);
double cos = Math.cos(radians);
return Vector2f.valueOf((float) (sin * y + cos * x), (float) (cos * y - sin * x));
}
/**
* 绕着point 旋转 radian弧度后得到的点
*
* @param point 点
* @param radians 弧度
* @return 旋转后的向量
*/
public Vector2f rotate(Vector2f point, double radians) {
double sin = Math.sin(radians);
double cos = Math.cos(radians);
int x = (int) ((this.x - point.getX()) * cos - (this.y - point.getY()) * sin + point.getX());
int y = (int) ((this.x - point.getX()) * sin + (this.y - point.getY()) * cos + point.getY());
return Vector2f.valueOf(x, y);
}
public static Vector2f getVectorFromPointToPoint(Point srcPoint, Point endPoint) {
return new Vector2f(endPoint.getX() - srcPoint.getX(), endPoint.getY() - srcPoint.getY());
}
public static Vector2f getVectorFromPointToPoint(int srcX, int srcY, int endX, int endY) {
return new Vector2f(endX - srcX, endY - srcY);
}
/**
* 向量点乘
*
* @param a 向量1
* @param b 向量2
* @return 返回结果
*/
public static float dotProduct(Vector2f a, Vector2f b) {
return a.getX() * b.getX() + a.getY() * b.getY();
}
/**
* 向量叉乘
*
* @param a 向量1
* @param b 向量2
* @return 返回结果
*/
public static float crossProduct(Vector2f a, Vector2f b) {
return a.getX() * b.getY() - a.getY() * b.getX();
}
/**
* 获取两个向量的夹角
*
* @param a 向量1
* @param b 向量2
* @return 返回值 [0 - 180]
*/
public static float getIntersectionAngle(Vector2f a, Vector2f b) {
float v = dotProduct(a, b);
double value = v / (a.getLength() * b.getLength());
double angle = Math.toDegrees(Math.acos(value));
return (float) angle;
}
/**
* 获取两个向量的夹角
* 以a为基准,顺时针计算到b的夹角
*
* @param a 向量1
* @param b 向量2
* @return 返回值 [0 - 360]
*/
public static float getIntersectionAngle2(Vector2f a, Vector2f b) {
double v = Math.atan2(a.getY(), a.getX()) - Math.atan2(b.getY(), b.getX());
double angle = Math.toDegrees(v);
if (angle > 360) {
angle -= 360;
} else if (angle < 0) {
angle += 360;
}
return (float) angle;
}
}
================================================
FILE: gamioo-common/src/main/java/io/gamioo/common/vector/Vector3f.java
================================================
package io.gamioo.common.vector;
/**
* 向量
*
* @author Allen Jiang
*/
public class Vector3f implements Cloneable {
private float x;
private float y;
private float z;
public Vector3f() {
}
public Vector3f(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getZ() {
return z;
}
public void setZ(float z) {
this.z = z;
}
}
================================================
FILE: gamioo-common/src/test/java/io/gamioo/JsonXmlUtilTest.java
================================================
package io.gamioo;
import com.alibaba.fastjson2.JSONObject;
import io.gamioo.common.util.JsonXmlUtil;
import io.gamioo.common.util.XMLUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.junit.jupiter.api.*;
/**
* some description
*
* @author Allen Jiang
* @since 1.0.0
*/
@DisplayName("IOC测试")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JsonXmlUtilTest {
private static final Logger logger = LogManager.getLogger(JsonXmlUtilTest.class);
//private final Benchmark benchmark=new Benchmark(10000);
private static Element root;
@BeforeAll
public static void beforeAll() throws Exception {
Document document = XMLUtil.loadFromFile("gate-config.xml");
root = document.getRootElement();
}
@Test
@Order(1)
public void test() throws Exception {
JSONObject obj = new JSONObject();
JsonXmlUtil.xml2Json(root, obj);
logger.debug(obj);
}
@BeforeEach
public void beforeEach() {
}
@AfterEach
public void afterEach() {
}
@AfterAll
public static void afterAll() {
}
}
================================================
FILE: gamioo-common/src/test/java/io/gamioo/MainT.java
================================================
package io.gamioo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
public class MainT {
private static final Logger logger = LogManager.getLogger(MainT.class);
public static void main(String[] args) {
String packager = MainT.class.getPackage().getName();
String[] packages = Arrays.asList(packager, "io.gamioo").toArray(new String[]{});
logger.debug("init ioc, packages={}", packager);
}
}
================================================
FILE: gamioo-common/src/test/resources/gate-config.xml
================================================
================================================
FILE: gamioo-common/src/test/resources/junit-platform.properties
================================================
junit.jupiter.execution.parallel.enabled=true
#\u7c7b\u5185\u90e8\u65b9\u6cd5\u5e76\u884c
junit.jupiter.execution.parallel.mode.default = concurrent
#\u7c7b\u4e4b\u95f4\u4e32\u884c
junit.jupiter.execution.parallel.mode.classes.default = same_thread
# the maximum pool size can be configured using a ParallelExecutionConfigurationStrategy
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=8
================================================
FILE: gamioo-common/src/test/resources/log4j2.component.properties
================================================
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
# default values is 256*1024
AsyncLogger.RingBufferSize=131072
================================================
FILE: gamioo-common/src/test/resources/log4j2.xml
================================================
1
benchmark
/data/log/${SERVER_NAME}/${SERVER_ID}
/data/stat/gamioo/${SERVER_NAME}/${SERVER_ID}
================================================
FILE: gamioo-compress/README.md
================================================
Compress, so easy.
# 简介
📌 压缩相关
* 压缩算法
* zstandard
* zlib
* 如何使用
```bash
implementation group: 'io.gamioo', name: 'gamioo-compress', version: '0.2.11'
```
#### 📄 性能测试结果如下:
```bash
Benchmark Mode Cnt Score Error Units
CompressBenchMark.zlibCompress thrpt 10 42671.817 ± 2112.154 ops/s
CompressBenchMark.zlibDecompress thrpt 10 2366646909.611 ± 43144539.607 ops/s
CompressBenchMark.zstandardCompress thrpt 10 126078.294 ± 10863.591 ops/s
CompressBenchMark.zstandardDecompress thrpt 10 2133946821.515 ± 96154271.597 ops/s
```
在Windows下(4核8线程 Intel Core i7),很明显,
- 压缩API,zstandard比zlib性能达到了 216.8%;
- 解压缩API,zstandard比zlib性能达到了101.8%;
### 依赖&参考
dependncy :
jdk:
```bash
OpenJDK Runtime Environment (Tencent Kona 8.0.12) (build 1.8.0_352-b1)
OpenJDK 64-Bit Server VM (Tencent Kona 8.0.12) (build 25.352-b1, mixed mode, sharing)
```
lib:
```bash
group: 'com.github.luben', name: 'zstd-jni', version: '1.5.4-2'
```
压缩文本的样本(1038 bytes)如下:
展开查看
{code {
flag: 1
id: 1
}
tableId: 936940
ownerId: 143566
createId: 143566
roomTemplateId: 4
configTemplateId: 1101
entryDTO {
key: 1
value: 3
}
entryDTO {
key: 201
value: 0
}
entryDTO {
key: 202
value: 0
}
entryDTO {
key: 204
value: 1
}
entryDTO {
key: 4
value: 6
}
entryDTO {
key: 203
value: 0
}
playerDTO {
playerDTO {
id: 143566
name: "King\345\274\272"
gender: 1
icon: "http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLLMzUbUh9ic7fQlhibCCLnibAIAP838Xge2cmFcStdEaWLL4UdLrgzhZsxrcsYxgJLsDR39vPsfjLibw/132"
city: "\345\256\201\346\263\242\345\270\202"
ip: "39.188.248.167"
longitude: "0.0"
latitude: "0.0"
position: 0
ready: false
online: true
totalPoint: 0.0
lastEnterTime: 1595855143603
win: 0
lose: 0
type: 1
}
sitDownPosition: 0
remain: 0
score: 0
daoNum: 0.0
totalDaoNum: 0.0
rank: 0
}
clubId: 0
status: 0
sitDownPosition: 0
kingBormPokerDTO {
id: 143566
}
}
## TODO list
================================================
FILE: gamioo-compress/build.gradle
================================================
dependencies {
api project(':gamioo-common');
api group: 'com.github.luben', name: 'zstd-jni', version: '1.5.4-2';
api group: 'org.apache.commons', name: 'commons-compress', version: '1.21';
api group: 'commons-io', name: 'commons-io', version: '2.7';
api group: 'org.apache.commons', name: 'commons-collections4', version: '4.4';
api group: 'info.debatty', name: 'java-string-similarity', version: '2.0.0'
}
================================================
FILE: gamioo-compress/src/jmh/java/io/gamioo/compress/CompressBenchMark.java
================================================
package io.gamioo.compress;
import com.github.luben.zstd.Zstd;
import io.gamioo.common.util.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 压缩算法的性能测试
*
* @author Allen Jiang
*/
@State(Scope.Benchmark)
@Fork(value = 1)
public class CompressBenchMark {
private static final Logger logger = LogManager.getLogger(CompressBenchMark.class);
private byte[] array;
private byte[] compressArray;
@Setup(Level.Trial)
public void init() {
try {
array = FileUtils.getByteArrayFromFile("message.txt");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public void zstandardCompress() {
if (compressArray == null) {
compressArray = Zstd.compress(array);
} else {
Zstd.compress(array);
}
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public void zstandardDecompress() {
if (compressArray != null) {
int size = (int) Zstd.decompressedSize(compressArray);
array = new byte[size];
Zstd.decompress(array, compressArray);
}
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public void zlibCompress() {
compressArray = ZlibUtil.compress(array);
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public void zlibDecompress() {
if (compressArray != null) {
array = ZlibUtil.uncompress(compressArray);
}
}
public static void main(String[] args) {
Options opt = new OptionsBuilder()
.include(CompressBenchMark.class.getSimpleName())
.build();
try {
new Runner(opt).run();
} catch (RunnerException e) {
logger.error(e.getMessage(), e);
}
}
}
================================================
FILE: gamioo-compress/src/jmh/java/io/gamioo/compress/SimilarityBenchMark.java
================================================
package io.gamioo.compress;
import info.debatty.java.stringsimilarity.Jaccard;
import info.debatty.java.stringsimilarity.JaroWinkler;
import info.debatty.java.stringsimilarity.RatcliffObershelp;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 压缩算法的性能测试
*
* @author Allen Jiang
*/
@State(Scope.Benchmark)
@Fork(value = 1)
public class SimilarityBenchMark {
private static final Logger logger = LogManager.getLogger(SimilarityBenchMark.class);
private String str1;
private String str2;
private JaroWinkler jaroWinkler;
private Jaccard jaccard;
private RatcliffObershelp ratcliffObershelp;
@Setup(Level.Trial)
public void init() {
jaroWinkler = new JaroWinkler();
jaccard=new Jaccard();
ratcliffObershelp=new RatcliffObershelp();
str1 = "LuaException: Util/StrUtil.lua:222: attempt to compare nil with number";
str2 = "LuaException: c# exception:XLua.LuaException: Util/StrUtil.lua:222: attempt to compare nil with number";
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public double handleJaroWinkler() {
return jaroWinkler.similarity(str1, str2);
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public double handleJaccard() {
return jaccard.similarity(str1, str2);
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 10, time = 2)
public double handleRatcliffObershelp() {
return ratcliffObershelp.similarity(str1, str2);
}
public static void main(String[] args) {
Options opt = new OptionsBuilder()
.include(SimilarityBenchMark.class.getSimpleName())
.build();
try {
new Runner(opt).run();
} catch (RunnerException e) {
logger.error(e.getMessage(), e);
}
}
}
================================================
FILE: gamioo-compress/src/jmh/resources/message.txt
================================================
{code {
flag: 1
id: 1
}
tableId: 936940
ownerId: 143566
createId: 143566
roomTemplateId: 4
configTemplateId: 1101
entryDTO {
key: 1
value: 3
}
entryDTO {
key: 201
value: 0
}
entryDTO {
key: 202
value: 0
}
entryDTO {
key: 204
value: 1
}
entryDTO {
key: 4
value: 6
}
entryDTO {
key: 203
value: 0
}
playerDTO {
playerDTO {
id: 143566
name: "King\345\274\272"
gender: 1
icon: "http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLLMzUbUh9ic7fQlhibCCLnibAIAP838Xge2cmFcStdEaWLL4UdLrgzhZsxrcsYxgJLsDR39vPsfjLibw/132"
city: "\345\256\201\346\263\242\345\270\202"
ip: "39.188.248.167"
longitude: "0.0"
latitude: "0.0"
position: 0
ready: false
online: true
totalPoint: 0.0
lastEnterTime: 1595855143603
win: 0
lose: 0
type: 1
}
sitDownPosition: 0
remain: 0
score: 0
daoNum: 0.0
totalDaoNum: 0.0
rank: 0
}
clubId: 0
status: 0
sitDownPosition: 0
kingBormPokerDTO {
id: 143566
}
}
================================================
FILE: gamioo-compress/src/jmh/resources/mini.txt
================================================
{code {
flag: 1
id: 1
}
tableId: 936940
ownerId: 143566
createId: 143566
roomTemplateId: 4
configTemplateId: 1101
}
================================================
FILE: gamioo-compress/src/jmh/resources/readu.txt
================================================
我不是一个聪明的人,也不算是一个优秀的学生。然而现在已在窗明几净的实验室里跟着导师忙碌地做课题了。偶尔也会掀起窗帘看看外面的风景,看到学弟学妹们因考研而紧张又[猪批]忙碌的身影,除了庆幸自己已站在了这里外,也不免想起去年那段日子,我也是那楼下忙碌身影中的一个,那情形仿佛就在昨天。
在大学期间,我在班级里学习成绩不怎么优秀,属中下游水平;四级考前的3个学期的英语考试我挂过两次;而英语六级考则考了三次;每逢期末考试总会红灯灿烂,到了大三结束时,已有20多个学分弃我而去,离实现无产阶级——一无学位证二无毕业证的目标也就一门课程的学分了,除此之外,大二,大三期间我还无可救药地喜欢上了……电脑游戏,成天在网吧疯狂地玩“星际”,“魔兽”……总之,是那种典型的“反面教材”!同学鄙视,老师唾弃……然而即便是这样,我还是凭实力和毅力挤过了独木桥,很平静,很从容,没有奇迹,也没有意外。当你感到信心不足的时候,当你坚持不下去想要放弃的时候,想想你心爱的人,你在折磨自己时她的心碎,想想你年迈的父母,你就会觉得有天赋的使命让他们自豪,为有你这个儿子自豪,你除了努力还能给他们什么呢?想想当你成功时他们的高兴、他们的兴奋,你就会觉得去上天堂,去下地狱,甚至去死,怎么做都值得……也想想我,连我这样都能考上,那你还担心什么?写这篇文章,不会带给我任何效益,只是出于一种义务,帮助那些在黑暗中没有方向感的同学们,希望你们抱定决心,去面对生命中的风风雨雨,不要放弃,一直这样,坚持下去……
2004年如火如荼的考研大战早已硝烟散去,纵观整个考研之路,审视整个心路历程,虽然考研复习面之繁重,内容之枯燥,要求之高深无与伦比,但其中充满了热血沸腾的力量和畅汗淋漓的激情却让我体会到了进入大学来从未体验过的快感,结局的美让我欣喜若狂,过程的苦让人回味无穷……一旦你全身心地投入到“研”战中去,你就会淹没在汗水,泪水,甚至血水之中,这是不见硝烟的战场。等待考研分数的日子更是痛苦的煎熬,研友们会不断在天堂和地狱间徘徊和挣扎,有的会痛苦地死去,有的会痛快地醒来……如果你做好了准备,愿意轰轰烈烈地拼一回,那就开始吧!
缘起……
四年前,接到大学录取通知书时,虽说是一所不错的大学,我并没有太多的欣喜,甚至感到失落,一种并没有实现自己理想的内心深处的失落,还未进大学校门,我就在心中暗暗地发誓,四年后要考上名牌大学的研究生。当时以为如果能考上名牌大学的研究生还是可以把许多失去的东西追回来的。四年后,我才明白,有些东西错过了就永远错过了,追不回来的,能追回来的,只是心中的一种情结。经过大学四年的沉沦,人也变得现实了,估计考一流的名校也不太现实。高考时未能圆的梦——我那心中的名校,虽然像初恋的对象一样,在心中留下了一种异样的情结,几年之后还是不能释怀,但是在现实面前,我还是妥协了,毕竟你最喜欢的并不一定能成为你的恋人。经过再三考虑,还是打算报考母校了,想想如果能考上,还是不错的。
初涉考研
开始奋战的日子真的很痛苦很累,我根本静不下新来看书。记得去学校图书馆自习的时候,每次都是趴在课桌上睡一觉,然后背几个单词做几道数学题就驱车回寝室打游戏去了,甩下一大群还在图书馆埋头苦干的同学。那时虽然得到了家人亲戚的支持同学朋友的鼓励,但还是有些彷徨,有时甚至想到了退缩,朋友你知道吗?一个还没真正开始准备的我已经开始想着退缩了,可以想象我那时有多么的自卑和不自信了……可是,有件事却让我有了十万个坚持下去的理由。记得那是一个炎热的下午,我刚睡了整整两个小时午觉,也感到羞于跑进图书馆去,毕竟人家同学已经埋头苦干了很长时间,我在图书馆外面的草地上随便翻翻星火单词,正要昏昏欲睡时,几位同班女生正好路过“哟,未来的研究生,很认真嘛……”这让我感到很难受也很尴尬,我一向看不惯瞧不起我的人,虽然我知道,她们为什么瞧不起我,因为毕竟我在她们面前表现一直是个差生,实在无法拿出任何成绩来证明自己的实力来回应,我什么也没说,把书一塞离开了,我发誓一定要考上,为了证明自己的能力,给自己一点慰籍,还自己一份尊严。以后备考的日子虽然还会感觉到周围到处是冷眼和嘲笑,但当我学习厌倦的时候,思想动摇的时候我就会暗示自己,我要让看不起我的人思想破灭,重新正视我,也为了不辜负身边关心和支持我的人,我已经铁了心要赌一把了。现在想来,其种种事情也算是更加坚定了我考研的决心,间接地成就了我最后的辉煌。
放手一搏
我只是想让同学朋友看到我的成功,也让自己明白自己还有机会,我只想这样也只有这样,于是开始了那段疯狂的日子。七月,我已经成了新教楼的常客。六点起床,1点睡觉,中午只在教室趴一会儿,每天休息不到六小时,暴雨天和大热天也从不间断,我承担着太大的心理压力,可是我一点也不累,我只是想我没有退路,我不要完,我要成功,我不要让支持我关心我的人觉得我没用,我不要让一直为我自豪的父母感到任何伤心,怎么样都可以,做什么都可以,就是再也不要失败,再也不要任何的嘲笑!那年杭州的夏天热得简直无法让人忍受,在大教室自修只觉得浑身上下一直在冒汗,全身黏糊糊的,挂在脖子上的湿毛巾不到半小时就干了,有时候额头的汗水滴下来,眼睛眼镜都模糊了,到后来已经不知道这滴下的是汗水还是泪水,但我宁愿去忍受教室里锅炉般的蒸发,我深知考研的人是不能去享受生活的,只能去慢慢咀嚼生活。那段时间一直在背2004考研词汇,星火又重又大的那本,一遍遍地背单词却很少有起色;由于大一大二概率论、线性代数没认真学,所以那段时间知识点只能一个个去啃,对于我来说都是新的,真的很辛苦也很痛苦,说实话,我好想放弃,但是我的倔强告诉我,假如上天决定让我失败,那我就用一次次的努力去感动他,假如没有上帝,我就把知识学到1+1=2那么熟悉,我就不相信我没有任何机会。七月中旬,我报了考研强化班,只报了政治,连续七天,并且还要赶车一小时去听课,没有一点喘息的时间,通常每次上完课回自己的寝室都已经筋疲力尽,倒在床上就迷迷糊糊睡去了,那时很苦却也很充实,我相信这是值得的,虽然别人还在空调的庇护下上网听音乐,享受游山玩水的乐趣,我们这些考研学子却仍然孜孜不倦地沉醉书海。记得那个暴雨倾盆之夜,我们在上政治课,上得很累,老师上完课已经十点多了,她在下课前对我们说:“感谢大家今晚的辛勤合作,愿我们同学明年有个圆满的回报。”不知怎的,听到这句话的那一刹那我的嗓子一下子哽咽了,眼睛一片模糊,十分激动……或许我太渴望成功了吧。
挑战自我
七八月份我的学习效果不错,进度很快,对自己也越来越有信心了我感觉到我已经进入了考研的真空状态,我一直挺着觉得会渐渐习惯的,岂料在八月底,我彻底地累倒了,住进了医院,也许我真的累了……
等住院回来,已经开学了,住院前所订的计划全被打乱,学习任务落下了大半,加上新学期又有新的课业的加入,我感觉不能像前两个月一样全身心地投入了,感到心烦意乱,前途又觉得一片渺茫,心中充满了莫名的恐惧和焦急,我还记得那时想振作却不知该怎么做,我又想到了放弃,幸好,一位同学来找我来谈心,他大概也遇到了生活或学习上的不快了吧,我们互相倾诉,互相鼓励,让我又重燃信心,现在想起来真的很感谢他,否则我可能永远地倒在了漫漫长征路上。
奋战到底
虽然计划有所后延,但我的复习备考还是在有条不紊地进行。我们班10多个同学考研,除了有课的日子,我们早出晚归,奔走于教室与宿舍之间。虽然不敢说自己很刻苦,但是已经够努力了,我觉得我们尽力了,也曾陆续听到中途有人放弃,对此我们并不感到惊讶。有关考研的故事,已在网上听得很多了,也就见怪不怪了;似乎考研过程中发生的一切都是正常的。那段日子,神经几乎麻木了,只有书本才能激活我们的思维;对于考研以外的事情,我们无动于衷,似乎那一切与自己无关。天气越冷,心境也一天比一天苍老,压力,无形中向自己压来,真的是如许多过来人所说的那样,越临近考试心里越没底,已至于到了最后,我竟有种坐以待毙的感觉,巴不得考试快点来临,那个时候已经不再担心考试的结果了,只盼着它尽早结束。要死就死吧,当时已无所谓了。也许就如跑三千米时已将近脱水的状态了,那时我没有快乐,没有欢笑,只有当幻想到美好的前程或在虚幻的梦境中,才能寻找到点点快乐和慰藉,那段日子真的很苦闷,说不清多少个夜晚,做题做得实在很累,真想安静地睡去,但,半夜冻醒,还是那冷清的通宵教室,昏暗的灯光,厚重的书本,纷繁的题目和困倦的我。
我孤身一人趴在窗台,看天上没有星星也没有光,无数遍地听黄家驹的不可一世、我是愤怒还有海阔天空,我经常活在半疯狂和崩溃的边缘。也许老天也真的同情我,最后两个月,一位考研的女生主动提出愿和我在这段日子一起备考,快窒息的我感受到了一股鲜活的空气,以后的日子,不仅多了笑声,多了欢乐,也让我也有激情和力量去最后一搏,我每天都学习10多个小时,即使倦意上来时,我也不甘在她面前倒下(多没面子啊),休息对我来说成为了一种奢侈。有了她和我并肩作战,我再也没有坚持不下来的感觉了,我如饥似渴地看书,效率也不错,心态也平静下来了,心情也很快乐,两个月的时间就这么一溜烟地过去了,感觉很快;元旦前一个星期,我报名参加了冲刺班,记得去上课的时候,本来是晚上七点的课,我下午就去占位了,大约五点的时候整个体育馆已经差不多人满了,三四千人挤得水泄不通,大家顶着零下几度的严寒,只是吃点冷面包喝点冷的矿泉水,用已经冻僵的手不停地把老师讲的重点记下来,此情此景也许只有考研的人才能理解才能体会吧。
走上前线
一转眼就到考试了,元月十日,阴雨绵绵,不过心情很平静,从容地考完政治和英语;第二天来时,有些座位已经空了,也许是他们放弃了吧;我突然觉得能走到今天从某种意义上来说我已算成功了,下午考专业课,我已经无丝毫紧张了,我只想赶快考完让自己解脱出来,由于心情放松,感觉考130多分没问题,11日下午5点我终于身心疲倦地从考场中走了出来,终于结束了!我喉咙一热,哽咽了……
一只被囚禁的小小鸟,带着那么一丝无奈,破笼而出……
半年,半世纪……
从考试到发下通知书跨度足足半年,而我,却如经历了整整半个世纪,在半年里,我真正体味了人生的大喜大悲。虽然考试已经考好,心里却真的没底,很长时间泡在考研论坛里,教学实习也没心情去完成,3月初我在网上查到自己的分数,总分不错,可英语50不到,那一刻我才真正感受到了如坐针毡,因为英语考得低,心情一直很差,而国家线又迟迟不下,网上各种分数线的传闻漫天飞,一会儿一位英语教学专家说保守估计今年的英语分数线要50多分,一会儿一位不知名的网友提供了一个根据历年国家线及今年考试人数和难易度推算出的字字有据的分数线——45分,等等,天堂与地狱之间的徘徊,一个多月的时间内,人都被折腾得支撑不住了。4月初分数线在官方网站公布了,工科英语和去年一样是41,我从鬼门关爬了回来,4月10号收到学校的复试通知书,17号参加复试。去参加复试时才发现自己在已上初试线的人中没一点优势,原来所报专业今年上初试线的人是计划招生人数的好几倍,我虽然比国家线高了四十分左右,可人家却高出了六七十分(真是让我一滴汗),复试一完,我已预感到了那无奈的结果了,22号消息过来——落选,要我自己去找其他学校调剂,虽已做好了心理准备,可真的一下子还是很难接受,想想已经一步一步走到了今天,结果,两个字——落选,就把你全盘否定了,我真的心有不甘,那时许多好的学校已经复试过了,我真的不知道还有没有出路,去找工作?继续考研?申请调剂?那一晚我想了很多很多,第二天一大早,擦干眼泪,心一横,为了让自己不留下丁点遗憾,抱着最后一丝希望去四处奔走,那时已经想好,如果这样还不行,就死心塌地找份工作,今生再也不过问考研之事,有一种不成功便成仁的决心,也许是好事多磨,也许是上苍怜悯我,我还是找到了一所不错的学校,与对方学校协商好回来的路上,我已累得实在不行了,身心俱疲,过几天参加完复试,我想无论结果怎样我可以无愧于自己了。后来打电话给学校,得知被录取了而且还是公费。我终于感到了一阵欣慰,一年来的汗水没白流。我控制住了自己激动的情绪,并没有如预想中的兴奋,也许这半年来考研让我改变可许多,心境也似乎一天比一天成熟。公元2004年6月28日16时23分,一个让我永生难忘的日子,我的硕士录取通知书终于到了,我手捧着它,竟瞬间不知所措,它对我太重要了,这张简简单单的纸张凝聚了我多少心血存储了我多少美好的期盼……
惘然回首
七月,大学毕业了,同学们都将各奔前程。散伙饭上,同学们祝贺我梦想成真。路是自己选的,我淡淡一笑,至少在毕业前,在他们面前我证明了自己的能力,虽然要和朝夕相处的同学们告别了,和四年时间里洒落的欢笑和泪水告别了,但已没什么遗憾。记得当初给自己诠释了几条走上这条路的理由:为了证明自己的能力;为了自己的父母;为了挽回一些漆黑而无奈的回忆;为了自己有更好的发展空间;为了有时间去培养自己的兴趣。考研给我的感觉就如一辆通往成功的末班车,再抓不住,我就会掉入失败的深渊,所以即使车门已经关闭,我也会紧紧抓住车窗不放,即使车窗上那凌厉冰冷的玻璃将我的手心割得血肉模糊痛入心肺,我还是紧咬牙关;我知道,这是我最后的希望,不抓住它,我就会失去一切……
如今,有时候迎着西下落寞的夕阳跑去,长长的身影在身后倾散开去,心中不免生出一股夸父逐日般的悲壮。我,还是一个人在这样一个流水般,平静的日子里追逐着自己的梦想,偶尔想起那篇考研时的文章:“是终点,亦是起点”是啊,一个梦圆了,而新的梦想,正在心中冉冉升起。
朋友,这个充满竞争的社会,你我都无法逃离,就如一句话所说的,有人的地方就有江湖。经常抬头看看天吧,那是你飞翔的地方;去努力吧,待到来年山花烂漫时,希望能听到你的好消息。
[写在最后]:如果你看了这篇文章有什么感想、感悟,就告诉我吧……还有在黑夜中还在摸索的朋友,如果你感到困惑时,对我还抱有一份信任的话,那就来信吧,我会不遗余力地为你排忧解难,做我能做到的,我觉得,这是我应尽的义务,一种义不容辞的责任!
流子
于04年9月
*****Every bird has its own sky *****
* Email:41157121@qq.com
*******************************
================================================
FILE: gamioo-compress/src/jmh/resources/short.txt
================================================
{code {
flag: 1
id: 1
}
tableId: 936940
ownerId: 143566
createId: 143566
roomTemplateId: 4
configTemplateId: 1101
entryDTO {
key: 1
value: 3
}
entryDTO {
key: 201
value: 0
}
entryDTO {
key: 202
value: 0
}
entryDTO {
key: 204
value: 1
}
entryDTO {
key: 4
value: 6
}
entryDTO {
key: 203
value: 0
}
clubId: 0
status: 0
sitDownPosition: 0
kingBormPokerDTO {
id: 143566
}
}
================================================
FILE: gamioo-compress/src/main/java/io/gamioo/compress/Main.java
================================================
package io.gamioo.compress;
import io.gamioo.common.util.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
/**
* @author Allen Jiang
*/
public class Main {
private static final Logger logger = LogManager.getLogger(Main.class);
public static void main(String[] args) throws IOException {
byte[] array = FileUtils.getByteArrayFromFile("message.txt");
logger.debug("size:{}", array.length);
}
}
================================================
FILE: gamioo-compress/src/main/java/io/gamioo/compress/ZlibUtil.java
================================================
package io.gamioo.compress;
import io.gamioo.common.exception.ServiceException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
/**
* @author Allen Jiang
*/
public class ZlibUtil {
private static final Logger logger = LogManager.getLogger(ZlibUtil.class);
public static byte[] compress(byte[] input) throws ServiceException {
int inputLength = input.length;
byte[] ret = null;
Deflater deflater = new Deflater();
ByteArrayOutputStream baos = new ByteArrayOutputStream(inputLength);
try {
deflater.setInput(input);
deflater.finish();
byte[] buf = new byte[1024];
while (!deflater.finished()) {
int got = deflater.deflate(buf);
baos.write(buf, 0, got);
}
ret = baos.toByteArray();
} finally {
deflater.end();
try {
baos.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
return ret;
}
public static byte[] uncompress(byte[] input) throws ServiceException {
if (input.length == 0) {
return input;
}
byte[] ret = null;
Inflater decompresser = new Inflater();
ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length);
try {
decompresser.reset();
decompresser.setInput(input, 0, input.length);
byte[] buf = new byte[1024];
int got = 0;
while (!decompresser.finished()) {
try {
got = decompresser.inflate(buf);
} catch (DataFormatException e) {
throw new ServiceException(e);
}
baos.write(buf, 0, got);
}
ret = baos.toByteArray();
} finally {
decompresser.end();
try {
baos.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
return ret;
}
}
================================================
FILE: gamioo-compress/src/main/resources/message.txt
================================================
{code {
flag: 1
id: 1
}
tableId: 936940
ownerId: 143566
createId: 143566
roomTemplateId: 4
configTemplateId: 1101
entryDTO {
key: 1
value: 3
}
entryDTO {
key: 201
value: 0
}
entryDTO {
key: 202
value: 0
}
entryDTO {
key: 204
value: 1
}
entryDTO {
key: 4
value: 6
}
entryDTO {
key: 203
value: 0
}
playerDTO {
playerDTO {
id: 143566
name: "King\345\274\272"
gender: 1
icon: "http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLLMzUbUh9ic7fQlhibCCLnibAIAP838Xge2cmFcStdEaWLL4UdLrgzhZsxrcsYxgJLsDR39vPsfjLibw/132"
city: "\345\256\201\346\263\242\345\270\202"
ip: "39.188.248.167"
longitude: "0.0"
latitude: "0.0"
position: 0
ready: false
online: true
totalPoint: 0.0
lastEnterTime: 1595855143603
win: 0
lose: 0
type: 1
}
sitDownPosition: 0
remain: 0
score: 0
daoNum: 0.0
totalDaoNum: 0.0
rank: 0
}
clubId: 0
status: 0
sitDownPosition: 0
kingBormPokerDTO {
id: 143566
}
}
================================================
FILE: gamioo-config/build.gradle
================================================
dependencies {
implementation project(':gamioo-common');
implementation project(':gamioo-network');
implementation group: 'com.alibaba.nacos', name: 'nacos-client', version: '2.2.1-RC';
implementation group: 'org.yaml', name: 'snakeyaml', version: '2.0'
// jetcd
implementation 'io.etcd:jetcd-core:0.7.5'
}
================================================
FILE: gamioo-config/src/main/java/io/gamioo/config/NacosUtil.java
================================================
package io.gamioo.config;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Properties;
import java.util.concurrent.Executor;
public class NacosUtil {
private static final Logger logger = LogManager.getLogger(NacosUtil.class);
public static void main(String[] args) {
NacosUtil util = new NacosUtil();
util.initNacos();
}
public void initNacos() {
logger.info("[nacos]:nacosbutton equals 1L");
ServerConfigManager serverConfigManager = new ServerConfigManager();
serverConfigManager.load("game", "game-config.yml");
ServerConfig config = serverConfigManager.getServerConfig();
//指定连接到的服务器地址,可以是本地也可以是其他服务器
String serverAddr = "1.15.9.113";
//自己指定的组
String group = "game";
// 示例
try {
//自己指定的dataId
String dataId = "10003";
//从nacos中获取的开关服务
ConfigService configService = this.getConfigService(serverAddr);
//本方法启动的时候获取内容
String content = configService.getConfig(dataId, group, 5000);
logger.info("content:{}", content);
//获取服务器状态
String status = configService.getServerStatus();
logger.debug("status={}", status);
configService.publishConfig(dataId, group, config.getContent(), ConfigType.YAML.getType());
//监听器,一旦nacos中相应值改变,则进行相应开关状态改变
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
logger.info("[Listener]:{}", configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
} catch (NacosException e) {
logger.error("获取功能开关配置失败:" + e, e);
}
}
/**
* 根据地址获取ConfigService
*
* @param serverAddr 地址
* @return 返回获取到的ConfigService
* @throws NacosException 抛出异常
*/
private ConfigService getConfigService(String serverAddr) throws NacosException {
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
return NacosFactory.createConfigService(properties);
}
}
================================================
FILE: gamioo-config/src/main/java/io/gamioo/config/ServerConfig.java
================================================
package io.gamioo.config;
import com.alibaba.fastjson2.JSON;
import io.gamioo.common.lang.Cache;
import io.gamioo.common.lang.Server;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class ServerConfig {
private String content;
/**
* 服务器类型
*/
private String type;
private int tcpPort;
private int webPort;
private int innerPort;
/**
* 外网IP
*/
private String externalIp;
/**
* 本地策划数据存放地址
*/
private String local;
private boolean debug;
private int saveInterval;
private int offlineInterval;
private int maxConcurrentUser;//单区最高在线人数
private int maxRegisterUser;//单区最大注册人数
private int heartBeat;//网关心跳检测时间(0 不进行心跳检测)
private boolean compress;//网关数据包压缩
private int compressThreshold;//压缩阀值
private boolean crypto;//网关数据包加密
/**
* rpc client: 超时
*/
private int rpcTimeout;
private Map root;
private List cacheList = new ArrayList<>();
private Server server;
private Properties dbConfig = new Properties();
public Server getServer() {
return server;
}
public void setServer(Server server) {
this.server = server;
}
public void add(Cache cache) {
cacheList.add(cache);
}
public List getCacheList() {
return cacheList;
}
public void setCacheList(List cacheList) {
this.cacheList = cacheList;
}
public String getExternalIp() {
return externalIp;
}
public void setExternalIp(String externalIp) {
this.externalIp = externalIp;
}
public int getRpcTimeout() {
return rpcTimeout;
}
public void setRpcTimeout(int rpcTimeout) {
this.rpcTimeout = rpcTimeout;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getTcpPort() {
return tcpPort;
}
public void setTcpPort(int tcpPort) {
this.tcpPort = tcpPort;
}
public int getWebPort() {
return webPort;
}
public void setWebPort(int webPort) {
this.webPort = webPort;
}
public String getLocal() {
return local;
}
public void setLocal(String local) {
this.local = local;
}
public boolean isDebug() {
return debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public int getSaveInterval() {
return saveInterval;
}
public void setSaveInterval(int saveInterval) {
this.saveInterval = saveInterval;
}
public int getOfflineInterval() {
return offlineInterval;
}
public void setOfflineInterval(int offlineInterval) {
this.offlineInterval = offlineInterval;
}
public int getInnerPort() {
return innerPort;
}
public void setInnerPort(int innerPort) {
this.innerPort = innerPort;
}
public Properties getDbConfig() {
return dbConfig;
}
public void setDbConfig(Properties dbConfig) {
this.dbConfig = dbConfig;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public void refreshContent() {
this.content = JSON.toJSONString(root);
}
public Map getRoot() {
return root;
}
public void setRoot(Map root) {
this.root = root;
}
public int getMaxConcurrentUser() {
return maxConcurrentUser;
}
public void setMaxConcurrentUser(int maxConcurrentUser) {
this.maxConcurrentUser = maxConcurrentUser;
}
public int getMaxRegisterUser() {
return maxRegisterUser;
}
public void setMaxRegisterUser(int maxRegisterUser) {
this.maxRegisterUser = maxRegisterUser;
}
public int getHeartBeat() {
return heartBeat;
}
public void setHeartBeat(int heartBeat) {
this.heartBeat = heartBeat;
}
public boolean isCompress() {
return compress;
}
public void setCompress(boolean compress) {
this.compress = compress;
}
public int getCompressThreshold() {
return compressThreshold;
}
public void setCompressThreshold(int compressThreshold) {
this.compressThreshold = compressThreshold;
}
public boolean isCrypto() {
return crypto;
}
public void setCrypto(boolean crypto) {
this.crypto = crypto;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
================================================
FILE: gamioo-config/src/main/java/io/gamioo/config/ServerConfigManager.java
================================================
package io.gamioo.config;
import io.gamioo.common.exception.ServiceException;
import io.gamioo.common.lang.Cache;
import io.gamioo.common.lang.Server;
import io.gamioo.common.util.FileUtils;
import io.gamioo.common.util.JVMUtil;
import io.gamioo.network.util.IPUtil;
import org.apache.commons.collections4.MapUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dom4j.Element;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
public class ServerConfigManager {
private static final Logger logger = LogManager.getLogger(ServerConfigManager.class);
private ServerConfig config = new ServerConfig();
private Map root;
public void load(String type, String fileName) throws ServiceException {
File file = FileUtils.getFile("config/" + fileName);
if (!file.exists()) {
throw new ServiceException("config file not found");
}
try {
load(type, file);
} catch (Exception e) {
throw new ServiceException("load config file failed: " + file.getAbsolutePath(), e);
}
}
public void load(String type, File file) throws ServiceException {
logger.info("load config file to start : {}", file.getAbsolutePath());
Yaml yaml = new Yaml();
try {
root = yaml.load(new FileInputStream(file));
String content = FileUtils.readFileToString(file);
config.setContent(content);
// config.setRoot(root);
// config.setContent( JSON.toJSONString(root));
} catch (IOException e) {
throw new ServiceException("failed to load config file", e);
}
this.parse(type);
logger.info("load config file to end : {}", file.getAbsolutePath());
}
private void parse(String type) {
config.setType(type);
int id = MapUtils.getInteger(root, "id");
Element element = null;
String name = MapUtils.getString(root, "name");
String externalIp = MapUtils.getString(root, "externalIp");
config.setExternalIp(externalIp);
int tcpPort = MapUtils.getInteger(root, "tcpPort");
config.setTcpPort(tcpPort);
int innerPort = MapUtils.getInteger(root, "innerPort");
config.setInnerPort(innerPort);
int webPort = MapUtils.getInteger(root, "webPort");
config.setWebPort(webPort);
String innerIp = IPUtil.getIP();
Server server = new Server();
server.setId(id);
server.setType(type);
server.setArgs(JVMUtil.getStartArgs());
server.setExternalIp(externalIp);
server.setInnerPort(innerPort);
server.setInnerIp(innerIp);
server.setName(name);
server.setTcpPort(tcpPort);
server.setWebPort(webPort);
server.setStartTime(new Date());
config.setServer(server);
// game
{
Map game = (Map) MapUtils.getObject(root, "game");
String local = MapUtils.getString(game, "local");
config.setLocal(local);
boolean debug = MapUtils.getBoolean(game, "debug");
config.setDebug(debug);
int saveInterval = MapUtils.getInteger(game, "saveInterval");
config.setSaveInterval(saveInterval);
int offlineInterval = MapUtils.getInteger(game, "offlineInterval");
config.setOfflineInterval(offlineInterval);
int maxConcurrentUser = MapUtils.getInteger(game, "maxConcurrentUser");
config.setMaxConcurrentUser(maxConcurrentUser);
int maxRegisterUser = MapUtils.getInteger(game, "maxRegisterUser");
config.setMaxRegisterUser(maxRegisterUser);
}
//protocol
{
Map protocol = (Map) MapUtils.getObject(root, "protocol");
int heartBeat = MapUtils.getInteger(protocol, "heartBeat");
config.setHeartBeat(heartBeat);
boolean compress = MapUtils.getBoolean(protocol, "compress");
config.setCompress(compress);
int compressThreshold = MapUtils.getInteger(protocol, "compressThreshold");
config.setCompressThreshold(compressThreshold);
boolean crypto = MapUtils.getBoolean(protocol, "crypto");
config.setCrypto(crypto);
}
//db
{
Map db = (Map) MapUtils.getObject(root, "db");
Properties dbConfig = new Properties();
dbConfig.putAll(db);
config.setDbConfig(dbConfig);
}
// redis
{
List