Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/java/org/codelibs/core/CoreLibConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.codelibs.core;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
* Constants class.
Expand All @@ -33,7 +34,7 @@ public class CoreLibConstants {
/**
* UTF-8 Charset.
*/
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
public static final Charset CHARSET_UTF_8 = StandardCharsets.UTF_8;

/**
* ISO 8601 basic format: yyyyMMdd'T'HHmmss.SSSZ
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.codelibs.core.beans.BeanDesc;
import org.codelibs.core.beans.impl.BeanDescImpl;
import org.codelibs.core.misc.Disposable;
import org.codelibs.core.misc.DisposableUtil;

/**
Expand Down Expand Up @@ -56,6 +57,9 @@ protected BeanDescFactory() {
/** Cache of {@link BeanDesc} */
private static final ConcurrentMap<Class<?>, BeanDesc> beanDescCache = newConcurrentHashMap(1024);

/** Disposable that clears the cache; a single stable instance so it can be deregistered. */
private static final Disposable DISPOSABLE = BeanDescFactory::clear;

static {
initialize();
}
Expand Down Expand Up @@ -86,7 +90,7 @@ public static BeanDesc getBeanDesc(final Class<?> clazz) {
public static void initialize() {
synchronized (BeanDescFactory.class) {
if (!initialized) {
DisposableUtil.add(BeanDescFactory::clear);
DisposableUtil.add(DISPOSABLE);
initialized = true;
}
}
Expand All @@ -97,6 +101,7 @@ public static void initialize() {
*/
public static void clear() {
beanDescCache.clear();
DisposableUtil.remove(DISPOSABLE);
initialized = false;
}

Expand Down
48 changes: 42 additions & 6 deletions src/main/java/org/codelibs/core/beans/impl/BeanDescImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
Expand Down Expand Up @@ -286,7 +287,7 @@ public MethodDesc[] getMethodDescs(final String methodName) {
if (methodDescs == null) {
throw new MethodNotFoundRuntimeException(beanClass, methodName, null);
}
return methodDescs;
return methodDescs.clone();
}

@Override
Expand Down Expand Up @@ -404,18 +405,33 @@ protected boolean isSuitable(final Class<?>[] paramTypes, final Object[] args, f
if (paramTypes.length != args.length) {
return false;
}
for (int i = 0; i < args.length; ++i) {
if (args[i] == null) {
// When number adjustment is enabled, evaluate the match against a copy of the arguments.
// This prevents a partially-converted, non-matching candidate from polluting the evaluation
// of later candidates or leaving the caller's array modified, and lets a failed conversion be
// treated as a non-match instead of being propagated. The successful conversions are copied
// back to the caller's array only once a candidate has fully matched.
final Object[] work = adjustNumber ? args.clone() : args;
for (int i = 0; i < work.length; ++i) {
if (work[i] == null) {
continue;
}
if (ClassUtil.isAssignableFrom(paramTypes[i], args[i].getClass())) {
if (ClassUtil.isAssignableFrom(paramTypes[i], work[i].getClass())) {
continue;
}
if (adjustNumber && adjustNumber(paramTypes, args, i)) {
continue;
if (adjustNumber) {
try {
if (adjustNumber(paramTypes, work, i)) {
continue;
}
} catch (final RuntimeException e) {
return false;
}
}
return false;
}
if (adjustNumber) {
System.arraycopy(work, 0, args, 0, args.length);
}
return true;
}

Expand Down Expand Up @@ -479,6 +495,9 @@ protected static boolean adjustNumber(final Class<?>[] paramTypes, final Object[
* Prepares {@link PropertyDesc}.
*/
protected void setupPropertyDescs() {
if (beanClass.isRecord()) {
setupRecordPropertyDescs();
}
for (final Method m : beanClass.getMethods()) {
if (m.isBridge() || m.isSynthetic()) {
continue;
Expand Down Expand Up @@ -511,6 +530,23 @@ protected void setupPropertyDescs() {
invalidPropertyNames.clear();
}

/**
* Prepares read-only {@link PropertyDesc}s for the components of a {@link Record}.
* <p>
* Each record component is registered as a read-only property whose read method is the component's
* accessor (the prefix-less {@code name()} form). Records are immutable, so no write method is set.
* </p>
*/
protected void setupRecordPropertyDescs() {
for (final RecordComponent component : beanClass.getRecordComponents()) {
final Method accessor = component.getAccessor();
if (accessor == null) {
continue;
}
setupReadMethod(accessor, component.getName());
}
}

/**
* Prepares the getter method.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public <T> Constructor<T> getConstructor() {

@Override
public Class<?>[] getParameterTypes() {
return parameterTypes;
return parameterTypes.clone();
}

@Override
Expand All @@ -96,12 +96,12 @@ public boolean isPublic() {
public boolean isParameterized(final int index) {
assertArgumentArrayIndex("index", index, parameterTypes.length);

return parameterizedClassDescs[index].isParameterizedClass();
return parameterizedClassDescs[index] != null && parameterizedClassDescs[index].isParameterizedClass();
}

@Override
public ParameterizedClassDesc[] getParameterizedClassDescs() {
return parameterizedClassDescs;
return parameterizedClassDescs.clone();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public String getMethodName() {

@Override
public Class<?>[] getParameterTypes() {
return parameterTypes;
return parameterTypes.clone();
}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -131,17 +131,17 @@ public boolean isAbstract() {
public boolean isParameterized(final int index) {
assertArgumentArrayIndex("index", index, parameterTypes.length);

return parameterizedClassDescs[index].isParameterizedClass();
return parameterizedClassDescs[index] != null && parameterizedClassDescs[index].isParameterizedClass();
}

@Override
public boolean isParameterized() {
return parameterizedClassDesc.isParameterizedClass();
return parameterizedClassDesc != null && parameterizedClassDesc.isParameterizedClass();
}

@Override
public ParameterizedClassDesc[] getParameterizedClassDescs() {
return parameterizedClassDescs;
return parameterizedClassDescs.clone();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void setRawClass(final Class<?> rawClass) {

@Override
public ParameterizedClassDesc[] getArguments() {
return arguments;
return arguments == null ? null : arguments.clone();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private void setupValueOfMethod() {
}

private void setUpParameterizedClassDesc() {
final Map<TypeVariable<?>, Type> typeVariables = ((BeanDescImpl) beanDesc).getTypeVariables();
final Map<TypeVariable<?>, Type> typeVariables = beanDesc.getTypeVariables();
if (field != null) {
parameterizedClassDesc = ParameterizedClassDescFactory.createParameterizedClassDesc(field, typeVariables);
} else if (readMethod != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public CopyOptions excludeWhitespace() {
* @return This instance itself
*/
public CopyOptions prefix(final CharSequence prefix) {
assertArgumentNotEmpty("propertyNames", prefix);
assertArgumentNotEmpty("prefix", prefix);

this.prefix = prefix.toString();
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public static <T> Iterable<T> iterable(final T... items) {
* @param items the array of elements to iterate (must not be {@literal null})
*/
public ArrayIterator(final T... items) {
assertArgumentNotNull("items", items);
this.items = items;
}

Expand Down
14 changes: 8 additions & 6 deletions src/main/java/org/codelibs/core/collection/ArrayMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import java.lang.reflect.Array;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
Expand Down Expand Up @@ -371,6 +370,9 @@ public Iterator<Entry<K, V>> iterator() {

@Override
public boolean contains(final Object o) {
if (!(o instanceof Entry)) {
return false;
}
final Entry<K, V> entry = (Entry<K, V>) o;
final int index = (entry.hashCode & 0x7FFFFFFF) % mapTable.length;
for (Entry<K, V> e = mapTable[index]; e != null; e = e.next) {
Expand Down Expand Up @@ -431,11 +433,11 @@ public void readExternal(final ObjectInput in) throws IOException, ClassNotFound

@Override
public Object clone() {
final ArrayMap<K, V> copy = new ArrayMap<>();
copy.threshold = threshold;
copy.mapTable = Arrays.copyOf(mapTable, size);
copy.listTable = Arrays.copyOf(listTable, size);
copy.size = size;
final ArrayMap<K, V> copy = new ArrayMap<>(mapTable.length);
for (int i = 0; i < size; i++) {
final Entry<K, V> entry = listTable[i];
copy.put(entry.key, entry.value);
}
return copy;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.codelibs.core.collection;

import java.util.Locale;
import java.util.Map;

/**
Expand Down Expand Up @@ -80,7 +81,10 @@ public boolean containsKey(final Object key) {
}

private static String convertKey(final Object key) {
return key.toString().toLowerCase();
if (key == null) {
return null;
}
return key.toString().toLowerCase(Locale.ROOT);
}

}
35 changes: 35 additions & 0 deletions src/main/java/org/codelibs/core/collection/CaseInsensitiveSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package org.codelibs.core.collection;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.Collection;
Expand Down Expand Up @@ -96,4 +99,36 @@ public void clear() {
map.clear();
}

/**
* Serializes this set. The backing map is {@code transient} and its values are a
* non-serializable sentinel, so only the elements are written.
*
* @param s the stream to write to
* @throws IOException if an I/O error occurs
*/
private void writeObject(final ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(map.size());
for (final String e : map.keySet()) {
s.writeObject(e);
}
}

/**
* Deserializes this set by rebuilding the backing map from the elements written by
* {@link #writeObject(ObjectOutputStream)}.
*
* @param s the stream to read from
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if the class of a serialized element cannot be found
*/
private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
map = new CaseInsensitiveMap<>();
final int size = s.readInt();
for (int i = 0; i < size; i++) {
map.put((String) s.readObject(), PRESENT);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*/
public class IteratorEnumeration<T> implements Enumeration<T> {

/** 反復子 */
/** The iterator */
protected final Iterator<T> iterator;

/**
Expand Down
Loading
Loading