mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
U 加入行为轨迹校验
This commit is contained in:
@@ -16,10 +16,6 @@ public class SliderCaptchaInfo {
|
||||
* x轴
|
||||
*/
|
||||
private Integer x;
|
||||
/**
|
||||
* x轴百分比
|
||||
*/
|
||||
private Float xPercent;
|
||||
/**
|
||||
* y轴
|
||||
*/
|
||||
@@ -32,21 +28,39 @@ public class SliderCaptchaInfo {
|
||||
* 移动图
|
||||
*/
|
||||
private String sliderImage;
|
||||
/** 背景图片宽度. */
|
||||
private Integer bgImageWidth;
|
||||
/** 背景图片高度. */
|
||||
private Integer bgImageHeight;
|
||||
/** 滑块图片宽度. */
|
||||
private Integer sliderImageWidth;
|
||||
/** 滑块图片高度. */
|
||||
private Integer sliderImageHeight;
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
public Object expand;
|
||||
|
||||
public SliderCaptchaInfo(Integer x, Float xPercent, Integer y, String backgroundImage, String sliderImage) {
|
||||
public SliderCaptchaInfo(Integer x, Integer y, String backgroundImage, String sliderImage, Integer bgImageWidth, Integer bgImageHeight, Integer sliderImageWidth, Integer sliderImageHeight) {
|
||||
this.x = x;
|
||||
this.xPercent = xPercent;
|
||||
this.y = y;
|
||||
this.backgroundImage = backgroundImage;
|
||||
this.sliderImage = sliderImage;
|
||||
this.bgImageWidth = bgImageWidth;
|
||||
this.bgImageHeight = bgImageHeight;
|
||||
this.sliderImageWidth = sliderImageWidth;
|
||||
this.sliderImageHeight = sliderImageHeight;
|
||||
}
|
||||
|
||||
public static SliderCaptchaInfo of(Integer x, Float xPercent, Integer y, String backgroundImage, String sliderImage) {
|
||||
return new SliderCaptchaInfo(x, xPercent, y, backgroundImage, sliderImage);
|
||||
public static SliderCaptchaInfo of(Integer x,
|
||||
Integer y,
|
||||
String backgroundImage,
|
||||
String sliderImage,
|
||||
Integer bgImageWidth,
|
||||
Integer bgImageHeight,
|
||||
Integer sliderImageWidth,
|
||||
Integer sliderImageHeight) {
|
||||
return new SliderCaptchaInfo(x, y, backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-5
@@ -151,19 +151,21 @@ public class StandardSliderCaptchaTemplate implements SliderCaptchaTemplate {
|
||||
GenerateParam param) {
|
||||
String backgroundFormatName = param.getBackgroundFormatName();
|
||||
String sliderFormatName = param.getSliderFormatName();
|
||||
// 计算滑块百分比
|
||||
float xPercent = (float) randomX / backgroundImage.getWidth();
|
||||
String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName);
|
||||
String sliderImageBase64 = transform(sliderImage, sliderFormatName);
|
||||
return SliderCaptchaInfo.of(randomX, xPercent, randomY,
|
||||
return SliderCaptchaInfo.of(randomX, randomY,
|
||||
backGroundImageBase64,
|
||||
sliderImageBase64);
|
||||
sliderImageBase64,
|
||||
backgroundImage.getWidth(), backgroundImage.getHeight(),
|
||||
sliderImage.getWidth(), sliderImage.getHeight()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将图片转换成字符串格式
|
||||
*
|
||||
* @param bufferedImage 图片
|
||||
* @param formatType 格式化类型
|
||||
* @param formatType 格式化类型
|
||||
* @return String
|
||||
* @throws IOException
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or 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
|
||||
*
|
||||
* https://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 cloud.tianai.captcha.template.slider.util;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* 拷贝spring
|
||||
* Miscellaneous collection utility methods.
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.1.3
|
||||
*/
|
||||
public abstract class CollectionUtils {
|
||||
|
||||
/**
|
||||
* Return {@code true} if the supplied Collection is {@code null} or empty.
|
||||
* Otherwise, return {@code false}.
|
||||
* @param collection the Collection to check
|
||||
* @return whether the given Collection is empty
|
||||
*/
|
||||
public static boolean isEmpty(Collection<?> collection) {
|
||||
return (collection == null || collection.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the supplied Map is {@code null} or empty.
|
||||
* Otherwise, return {@code false}.
|
||||
* @param map the Map to check
|
||||
* @return whether the given Map is empty
|
||||
*/
|
||||
public static boolean isEmpty(Map<?, ?> map) {
|
||||
return (map == null || map.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Merge the given Properties instance into the given Map,
|
||||
* copying all properties (key-value pairs) over.
|
||||
* <p>Uses {@code Properties.propertyNames()} to even catch
|
||||
* default properties linked into the original Properties instance.
|
||||
* @param props the Properties instance to merge (may be {@code null})
|
||||
* @param map the target Map to merge the properties into
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <K, V> void mergePropertiesIntoMap(Properties props, Map<K, V> map) {
|
||||
if (props != null) {
|
||||
for (Enumeration<?> en = props.propertyNames(); en.hasMoreElements();) {
|
||||
String key = (String) en.nextElement();
|
||||
Object value = props.get(key);
|
||||
if (value == null) {
|
||||
// Allow for defaults fallback or potentially overridden accessor...
|
||||
value = props.getProperty(key);
|
||||
}
|
||||
map.put((K) key, (V) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given Iterator contains the given element.
|
||||
* @param iterator the Iterator to check
|
||||
* @param element the element to look for
|
||||
* @return {@code true} if found, {@code false} otherwise
|
||||
*/
|
||||
public static boolean contains(Iterator<?> iterator, Object element) {
|
||||
if (iterator != null) {
|
||||
while (iterator.hasNext()) {
|
||||
Object candidate = iterator.next();
|
||||
if (ObjectUtils.nullSafeEquals(candidate, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given Enumeration contains the given element.
|
||||
* @param enumeration the Enumeration to check
|
||||
* @param element the element to look for
|
||||
* @return {@code true} if found, {@code false} otherwise
|
||||
*/
|
||||
public static boolean contains(Enumeration<?> enumeration, Object element) {
|
||||
if (enumeration != null) {
|
||||
while (enumeration.hasMoreElements()) {
|
||||
Object candidate = enumeration.nextElement();
|
||||
if (ObjectUtils.nullSafeEquals(candidate, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given Collection contains the given element instance.
|
||||
* <p>Enforces the given instance to be present, rather than returning
|
||||
* {@code true} for an equal element as well.
|
||||
* @param collection the Collection to check
|
||||
* @param element the element to look for
|
||||
* @return {@code true} if found, {@code false} otherwise
|
||||
*/
|
||||
public static boolean containsInstance(Collection<?> collection, Object element) {
|
||||
if (collection != null) {
|
||||
for (Object candidate : collection) {
|
||||
if (candidate == element) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if any element in '{@code candidates}' is
|
||||
* contained in '{@code source}'; otherwise returns {@code false}.
|
||||
* @param source the source Collection
|
||||
* @param candidates the candidates to search for
|
||||
* @return whether any of the candidates has been found
|
||||
*/
|
||||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||
return findFirstMatch(source, candidates) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first element in '{@code candidates}' that is contained in
|
||||
* '{@code source}'. If no element in '{@code candidates}' is present in
|
||||
* '{@code source}' returns {@code null}. Iteration order is
|
||||
* {@link Collection} implementation specific.
|
||||
* @param source the source Collection
|
||||
* @param candidates the candidates to search for
|
||||
* @return the first present object, or {@code null} if not found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E findFirstMatch(Collection<?> source, Collection<E> candidates) {
|
||||
if (isEmpty(source) || isEmpty(candidates)) {
|
||||
return null;
|
||||
}
|
||||
for (Object candidate : candidates) {
|
||||
if (source.contains(candidate)) {
|
||||
return (E) candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single value of the given type in the given Collection.
|
||||
* @param collection the Collection to search
|
||||
* @param type the type to look for
|
||||
* @return a value of the given type found if there is a clear match,
|
||||
* or {@code null} if none or more than one such value found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T findValueOfType(Collection<?> collection, Class<T> type) {
|
||||
if (isEmpty(collection)) {
|
||||
return null;
|
||||
}
|
||||
T value = null;
|
||||
for (Object element : collection) {
|
||||
if (type == null || type.isInstance(element)) {
|
||||
if (value != null) {
|
||||
// More than one value found... no clear single value.
|
||||
return null;
|
||||
}
|
||||
value = (T) element;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single value of one of the given types in the given Collection:
|
||||
* searching the Collection for a value of the first type, then
|
||||
* searching for a value of the second type, etc.
|
||||
* @param collection the collection to search
|
||||
* @param types the types to look for, in prioritized order
|
||||
* @return a value of one of the given types found if there is a clear match,
|
||||
* or {@code null} if none or more than one such value found
|
||||
*/
|
||||
public static Object findValueOfType(Collection<?> collection, Class<?>[] types) {
|
||||
if (isEmpty(collection) || ObjectUtils.isEmpty(types)) {
|
||||
return null;
|
||||
}
|
||||
for (Class<?> type : types) {
|
||||
Object value = findValueOfType(collection, type);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given Collection only contains a single unique object.
|
||||
* @param collection the Collection to check
|
||||
* @return {@code true} if the collection contains a single reference or
|
||||
* multiple references to the same instance, {@code false} otherwise
|
||||
*/
|
||||
public static boolean hasUniqueObject(Collection<?> collection) {
|
||||
if (isEmpty(collection)) {
|
||||
return false;
|
||||
}
|
||||
boolean hasCandidate = false;
|
||||
Object candidate = null;
|
||||
for (Object elem : collection) {
|
||||
if (!hasCandidate) {
|
||||
hasCandidate = true;
|
||||
candidate = elem;
|
||||
}
|
||||
else if (candidate != elem) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the common element type of the given Collection, if any.
|
||||
* @param collection the Collection to check
|
||||
* @return the common element type, or {@code null} if no clear
|
||||
* common type has been found (or the collection was empty)
|
||||
*/
|
||||
public static Class<?> findCommonElementType(Collection<?> collection) {
|
||||
if (isEmpty(collection)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> candidate = null;
|
||||
for (Object val : collection) {
|
||||
if (val != null) {
|
||||
if (candidate == null) {
|
||||
candidate = val.getClass();
|
||||
}
|
||||
else if (candidate != val.getClass()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first element of the given Set, using {@link SortedSet#first()}
|
||||
* or otherwise using the iterator.
|
||||
* @param set the Set to check (may be {@code null} or empty)
|
||||
* @return the first element, or {@code null} if none
|
||||
* @since 5.2.3
|
||||
* @see SortedSet
|
||||
* @see LinkedHashMap#keySet()
|
||||
* @see java.util.LinkedHashSet
|
||||
*/
|
||||
public static <T> T firstElement(Set<T> set) {
|
||||
if (isEmpty(set)) {
|
||||
return null;
|
||||
}
|
||||
if (set instanceof SortedSet) {
|
||||
return ((SortedSet<T>) set).first();
|
||||
}
|
||||
|
||||
Iterator<T> it = set.iterator();
|
||||
T first = null;
|
||||
if (it.hasNext()) {
|
||||
first = it.next();
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first element of the given List, accessing the zero index.
|
||||
* @param list the List to check (may be {@code null} or empty)
|
||||
* @return the first element, or {@code null} if none
|
||||
* @since 5.2.3
|
||||
*/
|
||||
public static <T> T firstElement(List<T> list) {
|
||||
if (isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last element of the given Set, using {@link SortedSet#last()}
|
||||
* or otherwise iterating over all elements (assuming a linked set).
|
||||
* @param set the Set to check (may be {@code null} or empty)
|
||||
* @return the last element, or {@code null} if none
|
||||
* @since 5.0.3
|
||||
* @see SortedSet
|
||||
* @see LinkedHashMap#keySet()
|
||||
* @see java.util.LinkedHashSet
|
||||
*/
|
||||
public static <T> T lastElement(Set<T> set) {
|
||||
if (isEmpty(set)) {
|
||||
return null;
|
||||
}
|
||||
if (set instanceof SortedSet) {
|
||||
return ((SortedSet<T>) set).last();
|
||||
}
|
||||
|
||||
// Full iteration necessary...
|
||||
Iterator<T> it = set.iterator();
|
||||
T last = null;
|
||||
while (it.hasNext()) {
|
||||
last = it.next();
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last element of the given List, accessing the highest index.
|
||||
* @param list the List to check (may be {@code null} or empty)
|
||||
* @return the last element, or {@code null} if none
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public static <T> T lastElement(List<T> list) {
|
||||
if (isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the elements from the given enumeration into an array of the given type.
|
||||
* Enumeration elements must be assignable to the type of the given array. The array
|
||||
* returned will be a different instance than the array given.
|
||||
*/
|
||||
public static <A, E extends A> A[] toArray(Enumeration<E> enumeration, A[] array) {
|
||||
ArrayList<A> elements = new ArrayList<>();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
elements.add(enumeration.nextElement());
|
||||
}
|
||||
return elements.toArray(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt an {@link Enumeration} to an {@link Iterator}.
|
||||
* @param enumeration the original {@code Enumeration}
|
||||
* @return the adapted {@code Iterator}
|
||||
*/
|
||||
public static <E> Iterator<E> toIterator(Enumeration<E> enumeration) {
|
||||
return (enumeration != null ? new EnumerationIterator<>(enumeration) : Collections.emptyIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator wrapping an Enumeration.
|
||||
*/
|
||||
private static class EnumerationIterator<E> implements Iterator<E> {
|
||||
|
||||
private final Enumeration<E> enumeration;
|
||||
|
||||
public EnumerationIterator(Enumeration<E> enumeration) {
|
||||
this.enumeration = enumeration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.enumeration.hasMoreElements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
return this.enumeration.nextElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,909 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or 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
|
||||
*
|
||||
* https://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 cloud.tianai.captcha.template.slider.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* 拷贝spring
|
||||
*
|
||||
* Miscellaneous object utility methods.
|
||||
*
|
||||
* <p>Mainly for internal use within the framework.
|
||||
*
|
||||
* <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Keith Donald
|
||||
* @author Rod Johnson
|
||||
* @author Rob Harrop
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 19.03.2004
|
||||
* @see CollectionUtils
|
||||
*/
|
||||
public abstract class ObjectUtils {
|
||||
|
||||
private static final int INITIAL_HASH = 7;
|
||||
private static final int MULTIPLIER = 31;
|
||||
|
||||
private static final String EMPTY_STRING = "";
|
||||
private static final String NULL_STRING = "null";
|
||||
private static final String ARRAY_START = "{";
|
||||
private static final String ARRAY_END = "}";
|
||||
private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
|
||||
private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
|
||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
|
||||
/**
|
||||
* Return whether the given throwable is a checked exception:
|
||||
* that is, neither a RuntimeException nor an Error.
|
||||
* @param ex the throwable to check
|
||||
* @return whether the throwable is a checked exception
|
||||
* @see java.lang.Exception
|
||||
* @see java.lang.RuntimeException
|
||||
* @see java.lang.Error
|
||||
*/
|
||||
public static boolean isCheckedException(Throwable ex) {
|
||||
return !(ex instanceof RuntimeException || ex instanceof Error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given exception is compatible with the specified
|
||||
* exception types, as declared in a throws clause.
|
||||
* @param ex the exception to check
|
||||
* @param declaredExceptions the exception types declared in the throws clause
|
||||
* @return whether the given exception is compatible
|
||||
*/
|
||||
public static boolean isCompatibleWithThrowsClause(Throwable ex, Class<?>... declaredExceptions) {
|
||||
if (!isCheckedException(ex)) {
|
||||
return true;
|
||||
}
|
||||
if (declaredExceptions != null) {
|
||||
for (Class<?> declaredException : declaredExceptions) {
|
||||
if (declaredException.isInstance(ex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given object is an array:
|
||||
* either an Object array or a primitive array.
|
||||
* @param obj the object to check
|
||||
*/
|
||||
public static boolean isArray(Object obj) {
|
||||
return (obj != null && obj.getClass().isArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given array is empty:
|
||||
* i.e. {@code null} or of zero length.
|
||||
* @param array the array to check
|
||||
* @see #isEmpty(Object)
|
||||
*/
|
||||
public static boolean isEmpty(Object[] array) {
|
||||
return (array == null || array.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given object is empty.
|
||||
* <p>This method supports the following object types.
|
||||
* <ul>
|
||||
* <li>{@code Optional}: considered empty if {@link Optional#empty()}</li>
|
||||
* <li>{@code Array}: considered empty if its length is zero</li>
|
||||
* <li>{@link CharSequence}: considered empty if its length is zero</li>
|
||||
* <li>{@link Collection}: delegates to {@link Collection#isEmpty()}</li>
|
||||
* <li>{@link Map}: delegates to {@link Map#isEmpty()}</li>
|
||||
* </ul>
|
||||
* <p>If the given object is non-null and not one of the aforementioned
|
||||
* supported types, this method returns {@code false}.
|
||||
* @param obj the object to check
|
||||
* @return {@code true} if the object is {@code null} or <em>empty</em>
|
||||
* @since 4.2
|
||||
* @see Optional#isPresent()
|
||||
* @see ObjectUtils#isEmpty(Object[])
|
||||
* @see CollectionUtils#isEmpty(java.util.Collection)
|
||||
* @see CollectionUtils#isEmpty(java.util.Map)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static boolean isEmpty(Object obj) {
|
||||
if (obj == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof Optional) {
|
||||
return !((Optional) obj).isPresent();
|
||||
}
|
||||
if (obj instanceof CharSequence) {
|
||||
return ((CharSequence) obj).length() == 0;
|
||||
}
|
||||
if (obj.getClass().isArray()) {
|
||||
return Array.getLength(obj) == 0;
|
||||
}
|
||||
if (obj instanceof Collection) {
|
||||
return ((Collection) obj).isEmpty();
|
||||
}
|
||||
if (obj instanceof Map) {
|
||||
return ((Map) obj).isEmpty();
|
||||
}
|
||||
|
||||
// else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap the given object which is potentially a {@link java.util.Optional}.
|
||||
* @param obj the candidate object
|
||||
* @return either the value held within the {@code Optional}, {@code null}
|
||||
* if the {@code Optional} is empty, or simply the given object as-is
|
||||
* @since 5.0
|
||||
*/
|
||||
public static Object unwrapOptional(Object obj) {
|
||||
if (obj instanceof Optional) {
|
||||
Optional<?> optional = (Optional<?>) obj;
|
||||
if (!optional.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
Object result = optional.get();
|
||||
return result;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given array contains the given element.
|
||||
* @param array the array to check (may be {@code null},
|
||||
* in which case the return value will always be {@code false})
|
||||
* @param element the element to check for
|
||||
* @return whether the element has been found in the given array
|
||||
*/
|
||||
public static boolean containsElement(Object[] array, Object element) {
|
||||
if (array == null) {
|
||||
return false;
|
||||
}
|
||||
for (Object arrayEle : array) {
|
||||
if (nullSafeEquals(arrayEle, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given array of enum constants contains a constant with the given name,
|
||||
* ignoring case when determining a match.
|
||||
* @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
|
||||
* @param constant the constant name to find (must not be null or empty string)
|
||||
* @return whether the constant has been found in the given array
|
||||
*/
|
||||
public static boolean containsConstant(Enum<?>[] enumValues, String constant) {
|
||||
return containsConstant(enumValues, constant, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given array of enum constants contains a constant with the given name.
|
||||
* @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
|
||||
* @param constant the constant name to find (must not be null or empty string)
|
||||
* @param caseSensitive whether case is significant in determining a match
|
||||
* @return whether the constant has been found in the given array
|
||||
*/
|
||||
public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
|
||||
for (Enum<?> candidate : enumValues) {
|
||||
if (caseSensitive ? candidate.toString().equals(constant) :
|
||||
candidate.toString().equalsIgnoreCase(constant)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
|
||||
* @param <E> the concrete Enum type
|
||||
* @param enumValues the array of all Enum constants in question, usually per {@code Enum.values()}
|
||||
* @param constant the constant to get the enum value of
|
||||
* @throws IllegalArgumentException if the given constant is not found in the given array
|
||||
* of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
|
||||
*/
|
||||
public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {
|
||||
for (E candidate : enumValues) {
|
||||
if (candidate.toString().equalsIgnoreCase(constant)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " +
|
||||
enumValues.getClass().getComponentType().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given object to the given array, returning a new array
|
||||
* consisting of the input array contents plus the given object.
|
||||
* @param array the array to append to (can be {@code null})
|
||||
* @param obj the object to append
|
||||
* @return the new array (of the same component type; never {@code null})
|
||||
*/
|
||||
public static <A, O extends A> A[] addObjectToArray(A[] array, O obj) {
|
||||
Class<?> compType = Object.class;
|
||||
if (array != null) {
|
||||
compType = array.getClass().getComponentType();
|
||||
}
|
||||
else if (obj != null) {
|
||||
compType = obj.getClass();
|
||||
}
|
||||
int newArrLength = (array != null ? array.length + 1 : 1);
|
||||
@SuppressWarnings("unchecked")
|
||||
A[] newArr = (A[]) Array.newInstance(compType, newArrLength);
|
||||
if (array != null) {
|
||||
System.arraycopy(array, 0, newArr, 0, array.length);
|
||||
}
|
||||
newArr[newArr.length - 1] = obj;
|
||||
return newArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given array (which may be a primitive array) to an
|
||||
* object array (if necessary of primitive wrapper objects).
|
||||
* <p>A {@code null} source value will be converted to an
|
||||
* empty Object array.
|
||||
* @param source the (potentially primitive) array
|
||||
* @return the corresponding object array (never {@code null})
|
||||
* @throws IllegalArgumentException if the parameter is not an array
|
||||
*/
|
||||
public static Object[] toObjectArray(Object source) {
|
||||
if (source instanceof Object[]) {
|
||||
return (Object[]) source;
|
||||
}
|
||||
if (source == null) {
|
||||
return EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
if (!source.getClass().isArray()) {
|
||||
throw new IllegalArgumentException("Source is not an array: " + source);
|
||||
}
|
||||
int length = Array.getLength(source);
|
||||
if (length == 0) {
|
||||
return EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
Class<?> wrapperType = Array.get(source, 0).getClass();
|
||||
Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
newArray[i] = Array.get(source, i);
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Convenience methods for content-based equality/hash-code handling
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if the given objects are equal, returning {@code true} if
|
||||
* both are {@code null} or {@code false} if only one is {@code null}.
|
||||
* <p>Compares arrays with {@code Arrays.equals}, performing an equality
|
||||
* check based on the array elements rather than the array reference.
|
||||
* @param o1 first Object to compare
|
||||
* @param o2 second Object to compare
|
||||
* @return whether the given objects are equal
|
||||
* @see Object#equals(Object)
|
||||
* @see java.util.Arrays#equals
|
||||
*/
|
||||
public static boolean nullSafeEquals(Object o1, Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
if (o1 == null || o2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (o1.equals(o2)) {
|
||||
return true;
|
||||
}
|
||||
if (o1.getClass().isArray() && o2.getClass().isArray()) {
|
||||
return arrayEquals(o1, o2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the given arrays with {@code Arrays.equals}, performing an equality
|
||||
* check based on the array elements rather than the array reference.
|
||||
* @param o1 first array to compare
|
||||
* @param o2 second array to compare
|
||||
* @return whether the given objects are equal
|
||||
* @see #nullSafeEquals(Object, Object)
|
||||
* @see java.util.Arrays#equals
|
||||
*/
|
||||
private static boolean arrayEquals(Object o1, Object o2) {
|
||||
if (o1 instanceof Object[] && o2 instanceof Object[]) {
|
||||
return Arrays.equals((Object[]) o1, (Object[]) o2);
|
||||
}
|
||||
if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
|
||||
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
|
||||
}
|
||||
if (o1 instanceof byte[] && o2 instanceof byte[]) {
|
||||
return Arrays.equals((byte[]) o1, (byte[]) o2);
|
||||
}
|
||||
if (o1 instanceof char[] && o2 instanceof char[]) {
|
||||
return Arrays.equals((char[]) o1, (char[]) o2);
|
||||
}
|
||||
if (o1 instanceof double[] && o2 instanceof double[]) {
|
||||
return Arrays.equals((double[]) o1, (double[]) o2);
|
||||
}
|
||||
if (o1 instanceof float[] && o2 instanceof float[]) {
|
||||
return Arrays.equals((float[]) o1, (float[]) o2);
|
||||
}
|
||||
if (o1 instanceof int[] && o2 instanceof int[]) {
|
||||
return Arrays.equals((int[]) o1, (int[]) o2);
|
||||
}
|
||||
if (o1 instanceof long[] && o2 instanceof long[]) {
|
||||
return Arrays.equals((long[]) o1, (long[]) o2);
|
||||
}
|
||||
if (o1 instanceof short[] && o2 instanceof short[]) {
|
||||
return Arrays.equals((short[]) o1, (short[]) o2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as hash code for the given object; typically the value of
|
||||
* {@code Object#hashCode()}}. If the object is an array,
|
||||
* this method will delegate to any of the {@code nullSafeHashCode}
|
||||
* methods for arrays in this class. If the object is {@code null},
|
||||
* this method returns 0.
|
||||
* @see Object#hashCode()
|
||||
* @see #nullSafeHashCode(Object[])
|
||||
* @see #nullSafeHashCode(boolean[])
|
||||
* @see #nullSafeHashCode(byte[])
|
||||
* @see #nullSafeHashCode(char[])
|
||||
* @see #nullSafeHashCode(double[])
|
||||
* @see #nullSafeHashCode(float[])
|
||||
* @see #nullSafeHashCode(int[])
|
||||
* @see #nullSafeHashCode(long[])
|
||||
* @see #nullSafeHashCode(short[])
|
||||
*/
|
||||
public static int nullSafeHashCode(Object obj) {
|
||||
if (obj == null) {
|
||||
return 0;
|
||||
}
|
||||
if (obj.getClass().isArray()) {
|
||||
if (obj instanceof Object[]) {
|
||||
return nullSafeHashCode((Object[]) obj);
|
||||
}
|
||||
if (obj instanceof boolean[]) {
|
||||
return nullSafeHashCode((boolean[]) obj);
|
||||
}
|
||||
if (obj instanceof byte[]) {
|
||||
return nullSafeHashCode((byte[]) obj);
|
||||
}
|
||||
if (obj instanceof char[]) {
|
||||
return nullSafeHashCode((char[]) obj);
|
||||
}
|
||||
if (obj instanceof double[]) {
|
||||
return nullSafeHashCode((double[]) obj);
|
||||
}
|
||||
if (obj instanceof float[]) {
|
||||
return nullSafeHashCode((float[]) obj);
|
||||
}
|
||||
if (obj instanceof int[]) {
|
||||
return nullSafeHashCode((int[]) obj);
|
||||
}
|
||||
if (obj instanceof long[]) {
|
||||
return nullSafeHashCode((long[]) obj);
|
||||
}
|
||||
if (obj instanceof short[]) {
|
||||
return nullSafeHashCode((short[]) obj);
|
||||
}
|
||||
}
|
||||
return obj.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(Object[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (Object element : array) {
|
||||
hash = MULTIPLIER * hash + nullSafeHashCode(element);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(boolean[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (boolean element : array) {
|
||||
hash = MULTIPLIER * hash + Boolean.hashCode(element);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(byte[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (byte element : array) {
|
||||
hash = MULTIPLIER * hash + element;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(char[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (char element : array) {
|
||||
hash = MULTIPLIER * hash + element;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(double[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (double element : array) {
|
||||
hash = MULTIPLIER * hash + Double.hashCode(element);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(float[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (float element : array) {
|
||||
hash = MULTIPLIER * hash + Float.hashCode(element);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(int[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (int element : array) {
|
||||
hash = MULTIPLIER * hash + element;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(long[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (long element : array) {
|
||||
hash = MULTIPLIER * hash + Long.hashCode(element);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code based on the contents of the specified array.
|
||||
* If {@code array} is {@code null}, this method returns 0.
|
||||
*/
|
||||
public static int nullSafeHashCode(short[] array) {
|
||||
if (array == null) {
|
||||
return 0;
|
||||
}
|
||||
int hash = INITIAL_HASH;
|
||||
for (short element : array) {
|
||||
hash = MULTIPLIER * hash + element;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the same value as {@link Boolean#hashCode(boolean)}}.
|
||||
* @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
|
||||
*/
|
||||
@Deprecated
|
||||
public static int hashCode(boolean bool) {
|
||||
return Boolean.hashCode(bool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the same value as {@link Double#hashCode(double)}}.
|
||||
* @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
|
||||
*/
|
||||
@Deprecated
|
||||
public static int hashCode(double dbl) {
|
||||
return Double.hashCode(dbl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the same value as {@link Float#hashCode(float)}}.
|
||||
* @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
|
||||
*/
|
||||
@Deprecated
|
||||
public static int hashCode(float flt) {
|
||||
return Float.hashCode(flt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the same value as {@link Long#hashCode(long)}}.
|
||||
* @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
|
||||
*/
|
||||
@Deprecated
|
||||
public static int hashCode(long lng) {
|
||||
return Long.hashCode(lng);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Convenience methods for toString output
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return a String representation of an object's overall identity.
|
||||
* @param obj the object (may be {@code null})
|
||||
* @return the object's identity as String representation,
|
||||
* or an empty String if the object was {@code null}
|
||||
*/
|
||||
public static String identityToString(Object obj) {
|
||||
if (obj == null) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
String className = obj.getClass().getName();
|
||||
String identityHexString = getIdentityHexString(obj);
|
||||
return className + '@' + identityHexString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hex String form of an object's identity hash code.
|
||||
* @param obj the object
|
||||
* @return the object's identity code in hex notation
|
||||
*/
|
||||
public static String getIdentityHexString(Object obj) {
|
||||
return Integer.toHexString(System.identityHashCode(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a content-based String representation if {@code obj} is
|
||||
* not {@code null}; otherwise returns an empty String.
|
||||
* <p>Differs from {@link #nullSafeToString(Object)} in that it returns
|
||||
* an empty String rather than "null" for a {@code null} value.
|
||||
* @param obj the object to build a display String for
|
||||
* @return a display String representation of {@code obj}
|
||||
* @see #nullSafeToString(Object)
|
||||
*/
|
||||
public static String getDisplayString(Object obj) {
|
||||
if (obj == null) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
return nullSafeToString(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the class name for the given object.
|
||||
* <p>Returns a {@code "null"} String if {@code obj} is {@code null}.
|
||||
* @param obj the object to introspect (may be {@code null})
|
||||
* @return the corresponding class name
|
||||
*/
|
||||
public static String nullSafeClassName(Object obj) {
|
||||
return (obj != null ? obj.getClass().getName() : NULL_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the specified Object.
|
||||
* <p>Builds a String representation of the contents in case of an array.
|
||||
* Returns a {@code "null"} String if {@code obj} is {@code null}.
|
||||
* @param obj the object to build a String representation for
|
||||
* @return a String representation of {@code obj}
|
||||
*/
|
||||
public static String nullSafeToString(Object obj) {
|
||||
if (obj == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
if (obj instanceof String) {
|
||||
return (String) obj;
|
||||
}
|
||||
if (obj instanceof Object[]) {
|
||||
return nullSafeToString((Object[]) obj);
|
||||
}
|
||||
if (obj instanceof boolean[]) {
|
||||
return nullSafeToString((boolean[]) obj);
|
||||
}
|
||||
if (obj instanceof byte[]) {
|
||||
return nullSafeToString((byte[]) obj);
|
||||
}
|
||||
if (obj instanceof char[]) {
|
||||
return nullSafeToString((char[]) obj);
|
||||
}
|
||||
if (obj instanceof double[]) {
|
||||
return nullSafeToString((double[]) obj);
|
||||
}
|
||||
if (obj instanceof float[]) {
|
||||
return nullSafeToString((float[]) obj);
|
||||
}
|
||||
if (obj instanceof int[]) {
|
||||
return nullSafeToString((int[]) obj);
|
||||
}
|
||||
if (obj instanceof long[]) {
|
||||
return nullSafeToString((long[]) obj);
|
||||
}
|
||||
if (obj instanceof short[]) {
|
||||
return nullSafeToString((short[]) obj);
|
||||
}
|
||||
String str = obj.toString();
|
||||
return (str != null ? str : EMPTY_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(Object[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (Object o : array) {
|
||||
stringJoiner.add(String.valueOf(o));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(boolean[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (boolean b : array) {
|
||||
stringJoiner.add(String.valueOf(b));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(byte[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (byte b : array) {
|
||||
stringJoiner.add(String.valueOf(b));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(char[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (char c : array) {
|
||||
stringJoiner.add('\'' + String.valueOf(c) + '\'');
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(double[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (double d : array) {
|
||||
stringJoiner.add(String.valueOf(d));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(float[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (float f : array) {
|
||||
stringJoiner.add(String.valueOf(f));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(int[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (int i : array) {
|
||||
stringJoiner.add(String.valueOf(i));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(long[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (long l : array) {
|
||||
stringJoiner.add(String.valueOf(l));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the contents of the specified array.
|
||||
* <p>The String representation consists of a list of the array's elements,
|
||||
* enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
|
||||
* by the characters {@code ", "} (a comma followed by a space).
|
||||
* Returns a {@code "null"} String if {@code array} is {@code null}.
|
||||
* @param array the array to build a String representation for
|
||||
* @return a String representation of {@code array}
|
||||
*/
|
||||
public static String nullSafeToString(short[] array) {
|
||||
if (array == null) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
int length = array.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
|
||||
for (short s : array) {
|
||||
stringJoiner.add(String.valueOf(s));
|
||||
}
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package cloud.tianai.captcha.template.slider.validator;
|
||||
|
||||
import cloud.tianai.captcha.template.slider.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.template.slider.util.ObjectUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/2/17 11:01
|
||||
* @Description 基本的行为轨迹校验
|
||||
*/
|
||||
public class BasicCaptchaTrackValidator extends SimpleSliderCaptchaValidator {
|
||||
|
||||
public BasicCaptchaTrackValidator() {
|
||||
}
|
||||
|
||||
public BasicCaptchaTrackValidator(float defaultTolerant) {
|
||||
super(defaultTolerant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Float oriPercentage) {
|
||||
// 校验参数
|
||||
checkParam(sliderCaptchaTrack);
|
||||
// 基础校验
|
||||
boolean superValid = super.valid(sliderCaptchaTrack, oriPercentage);
|
||||
if (!superValid) {
|
||||
return false;
|
||||
}
|
||||
// 进行行为轨迹检测
|
||||
long startSlidingTime = sliderCaptchaTrack.getStartSlidingTime().getTime();
|
||||
long endSlidingTime = sliderCaptchaTrack.getEntSlidingTime().getTime();
|
||||
Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
|
||||
List<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList();
|
||||
// 这里只进行基本检测, 用一些简单算法进行校验,如有需要可扩展
|
||||
// 检测1: 滑动时间如果小于300毫秒 返回false
|
||||
// 检测2: 轨迹数据要是少于背景宽度的五分之一,或者大于背景宽度的五分之一 返回false
|
||||
// 检测3: x轴和y轴应该是从0开始的,要是一开始x轴和y轴乱跑,返回false
|
||||
// 检测4: 如果y轴是相同的,必然是机器操作,直接返回false
|
||||
// 检测5: x轴或者y轴直接的区间跳跃过大的话返回 false
|
||||
// 检测6: x轴应该是由快到慢的, 要是速率一致,返回false
|
||||
// 检测7: 如果x轴超过图片宽度的频率过高,返回false
|
||||
|
||||
// 检测1
|
||||
if (startSlidingTime + 300 > endSlidingTime) {
|
||||
return false;
|
||||
}
|
||||
// 检测2
|
||||
if (trackList.size() < bgImageWidth / 5 || trackList.size() > bgImageWidth * 5) {
|
||||
return false;
|
||||
}
|
||||
// 检测3
|
||||
SliderCaptchaTrack.Track firstTrack = trackList.get(0);
|
||||
if (firstTrack.getX() > 1 || firstTrack.getX() < -2 || firstTrack.getY() > 1 || firstTrack.getY() < -2) {
|
||||
return false;
|
||||
}
|
||||
int check4 = 0;
|
||||
int check7 = 0;
|
||||
for (int i = 1; i < trackList.size(); i++) {
|
||||
SliderCaptchaTrack.Track track = trackList.get(i);
|
||||
int x = track.getX();
|
||||
int y = track.getY();
|
||||
// check4
|
||||
if (firstTrack.getY() == y) {
|
||||
check4++;
|
||||
}
|
||||
// check7
|
||||
if (x >= bgImageWidth) {
|
||||
check7++;
|
||||
}
|
||||
// check5
|
||||
SliderCaptchaTrack.Track preTrack = trackList.get(i - 1);
|
||||
if ((track.getX() - preTrack.getX()) > 5 || (track.getY() - preTrack.getY()) > 5) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (check4 > trackList.size() * 0.7 || check7 > 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check6
|
||||
int splitPos = (int) (trackList.size() * 0.7);
|
||||
SliderCaptchaTrack.Track splitPostTrack = trackList.get(splitPos - 1);
|
||||
int posTime = splitPostTrack.getT();
|
||||
float startAvgPosTime = posTime / (float) splitPos;
|
||||
|
||||
SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
|
||||
float endAvgPosTime = lastTrack.getT() / (float) (trackList.size() - splitPos);
|
||||
|
||||
return endAvgPosTime > startAvgPosTime;
|
||||
}
|
||||
|
||||
|
||||
public void checkParam(SliderCaptchaTrack sliderCaptchaTrack) {
|
||||
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getBgImageWidth())) {
|
||||
throw new IllegalArgumentException("bgImageWidth must not be null");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getBgImageHeight())) {
|
||||
throw new IllegalArgumentException("bgImageHeight must not be null");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getSliderImageWidth())) {
|
||||
throw new IllegalArgumentException("sliderImageWidth must not be null");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getSliderImageHeight())) {
|
||||
throw new IllegalArgumentException("sliderImageHeight must not be null");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getStartSlidingTime())) {
|
||||
throw new IllegalArgumentException("startSlidingTime must not be null");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getEntSlidingTime())) {
|
||||
throw new IllegalArgumentException("entSlidingTime must not be null");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(sliderCaptchaTrack.getTrackList())) {
|
||||
throw new IllegalArgumentException("trackList must not be null");
|
||||
}
|
||||
for (SliderCaptchaTrack.Track track : sliderCaptchaTrack.getTrackList()) {
|
||||
Integer x = track.getX();
|
||||
Integer y = track.getY();
|
||||
Integer t = track.getT();
|
||||
if (x == null || y == null || t == null) {
|
||||
throw new IllegalArgumentException("track[x,y,t] must not be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package cloud.tianai.captcha.template.slider.validator;
|
||||
|
||||
import cloud.tianai.captcha.template.slider.util.CollectionUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator {
|
||||
|
||||
public static float DEFAULT_TOLERANT = 0.02f;
|
||||
/** 容错值. */
|
||||
@Getter
|
||||
@Setter
|
||||
public float defaultTolerant = DEFAULT_TOLERANT;
|
||||
|
||||
public SimpleSliderCaptchaValidator() {
|
||||
}
|
||||
|
||||
public SimpleSliderCaptchaValidator(float defaultTolerant) {
|
||||
this.defaultTolerant = defaultTolerant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float calcPercentage(int x, int bgImageWidth) {
|
||||
return (float) x / bgImageWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPercentage(Float newPercentage, Float oriPercentage) {
|
||||
return checkPercentage(newPercentage, oriPercentage, defaultTolerant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPercentage(Float newPercentage, Float oriPercentage, float tolerant) {
|
||||
if (newPercentage == null || Float.isNaN(newPercentage) || Float.isInfinite(newPercentage)
|
||||
|| oriPercentage == null || Float.isNaN(oriPercentage) || Float.isInfinite(oriPercentage)) {
|
||||
return false;
|
||||
}
|
||||
// 容错值
|
||||
float maxTolerant = oriPercentage + tolerant;
|
||||
float minTolerant = oriPercentage - tolerant;
|
||||
return newPercentage >= minTolerant && newPercentage <= maxTolerant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Float oriPercentage) {
|
||||
Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
|
||||
if (bgImageWidth == null || bgImageWidth < 1) {
|
||||
// 没有背景图片宽度
|
||||
return false;
|
||||
}
|
||||
List<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList();
|
||||
if (CollectionUtils.isEmpty(trackList)) {
|
||||
// 没有滑动轨迹
|
||||
return false;
|
||||
}
|
||||
// 取最后一个滑动轨迹
|
||||
SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
|
||||
// 计算百分比
|
||||
float calcPercentage = calcPercentage(lastTrack.getX(), bgImageWidth);
|
||||
// 校验百分比
|
||||
return checkPercentage(calcPercentage, oriPercentage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cloud.tianai.captcha.template.slider.validator;
|
||||
|
||||
import cloud.tianai.captcha.template.slider.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.template.slider.util.ObjectUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/2/17 9:23
|
||||
* @Description 滑块验证码滑动轨迹
|
||||
*/
|
||||
@Data
|
||||
public class SliderCaptchaTrack {
|
||||
|
||||
/** 背景图片宽度. */
|
||||
private Integer bgImageWidth;
|
||||
/** 背景图片高度. */
|
||||
private Integer bgImageHeight;
|
||||
/** 滑块图片宽度. */
|
||||
private Integer sliderImageWidth;
|
||||
/** 滑块图片高度. */
|
||||
private Integer sliderImageHeight;
|
||||
/** 滑动开始时间. */
|
||||
private Date startSlidingTime;
|
||||
/** 滑动结束时间. */
|
||||
private Date entSlidingTime;
|
||||
/** 滑动的轨迹. */
|
||||
private List<Track> trackList;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class Track {
|
||||
private Integer x;
|
||||
private Integer y;
|
||||
private Integer t;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package cloud.tianai.captcha.template.slider.validator;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/2/17 10:54
|
||||
* @Description 滑块验证码校验器
|
||||
*/
|
||||
public interface SliderCaptchaValidator {
|
||||
|
||||
/**
|
||||
* 计算滑块要背景图的百分比,基本校验
|
||||
*
|
||||
* @param x 凹槽的x轴
|
||||
* @param bgImageWidth 背景图片的宽度
|
||||
* @return float
|
||||
*/
|
||||
float calcPercentage(int x, int bgImageWidth);
|
||||
|
||||
/**
|
||||
* 校验滑块百分比
|
||||
*
|
||||
* @param newPercentage 用户滑动的百分比
|
||||
* @param oriPercentage 正确的滑块百分比
|
||||
* @return boolean
|
||||
*/
|
||||
boolean checkPercentage(Float newPercentage, Float oriPercentage);
|
||||
|
||||
/**
|
||||
* 校验滑块百分比
|
||||
*
|
||||
* @param newPercentage 用户滑动的百分比
|
||||
* @param oriPercentage 正确的滑块百分比
|
||||
* @param tolerant 容错值
|
||||
* @return boolean
|
||||
*/
|
||||
boolean checkPercentage(Float newPercentage, Float oriPercentage, float tolerant);
|
||||
|
||||
/**
|
||||
* 校验用户滑动滑块是否正确
|
||||
*
|
||||
* @param sliderCaptchaTrack 包含了滑动轨迹,展示的图片宽高,滑动时间等参数
|
||||
* @param oriPercentage 正确的滑块百分比,用作基础校验
|
||||
* @return boolean
|
||||
*/
|
||||
boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Float oriPercentage);
|
||||
}
|
||||
Reference in New Issue
Block a user