diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 6e082fbf5..f14cacd2a 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -24,14 +24,14 @@ jobs: continue-on-error: ${{ matrix.experimental }} strategy: matrix: - java: [ 8, 11, 14 ] + java: [ 8, 11, 15 ] experimental: [false] include: - - java: 15-ea + - java: 16-ea experimental: true steps: - - uses: actions/checkout@v2.3.2 + - uses: actions/checkout@v2.3.4 - uses: actions/cache@v2 with: path: ~/.m2/repository @@ -39,7 +39,7 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1.4.0 + uses: actions/setup-java@v1.4.3 with: java-version: ${{ matrix.java }} - name: Build with Maven diff --git a/.travis.yml b/.travis.yml index a999f9106..9efb7d8f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ jdk: - oraclejdk11 - openjdk8 - openjdk11 - - openjdk14 + - openjdk15 - openjdk-ea after_success: diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..51943ba7b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ + +The Apache Commons security page is [https://commons.apache.org/security.html](https://commons.apache.org/security.html). diff --git a/pom.xml b/pom.xml index 07c426043..17d0a921e 100644 --- a/pom.xml +++ b/pom.xml @@ -43,16 +43,16 @@ (Java 6) commons-beanutils-${commons.release.2.version} - + BEANUTILS 12310460 -Xmx256M - 0.14.3 + 0.14.4 2.8 utf-8 - 0.8.5 + 0.8.6 3.1.1 8.34 @@ -340,201 +340,201 @@ junit junit - 4.13 + 4.13.1 test clean apache-rat:check checkstyle:check verify javadoc:javadoc + + + org.apache.felix + maven-bundle-plugin + + + biz.aQute.bnd + biz.aQute.bndlib + 5.2.0 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + pertest + + ${surefire.argLine} ${argLine} + + **/*TestCase.java + + + + **/*MemoryTestCase.java + + + + true + + org.apache.commons.logging.impl.LogFactoryImpl + org.apache.commons.logging.impl.SimpleLog + WARN + + + + + + maven-assembly-plugin + + + src/main/assembly/bin.xml + src/main/assembly/src.xml + + gnu + + + + maven-checkstyle-plugin + ${checkstyle.plugin.version} + + ${basedir}/src/conf/checkstyle.xml + ${basedir}/src/conf/checkstyle-suppressions.xml + true + false + NOTICE.txt,LICENSE.txt,**/pom.properties,target/**.properties, + + + + - - org.apache.felix - maven-bundle-plugin - - - biz.aQute.bnd - biz.aQute.bndlib - 5.1.2 - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - - pertest - - ${surefire.argLine} ${argLine} - - **/*TestCase.java - - - - **/*MemoryTestCase.java - - - - true - - org.apache.commons.logging.impl.LogFactoryImpl - org.apache.commons.logging.impl.SimpleLog - WARN - - + maven-scm-publish-plugin + + + + + javadocs** + release-notes** + - - maven-assembly-plugin - - - src/main/assembly/bin.xml - src/main/assembly/src.xml - - gnu - - - - maven-checkstyle-plugin - ${checkstyle.plugin.version} - - ${basedir}/src/conf/checkstyle.xml - ${basedir}/src/conf/checkstyle-suppressions.xml - true - false - NOTICE.txt,LICENSE.txt,**/pom.properties,target/**.properties, - - - - - - org.apache.maven.plugins - maven-scm-publish-plugin + + + + + + + maven-checkstyle-plugin + ${checkstyle.plugin.version} + + ${basedir}/src/conf/checkstyle.xml + ${basedir}/src/conf/checkstyle-suppressions.xml + true + false + NOTICE.txt,LICENSE.txt,**/pom.properties,target/**.properties, + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + https://docs.oracle.com/javase/8/docs/api/ + https://commons.apache.org/collections/api-release/ + + + + + org.apache.maven.plugins + maven-changes-plugin + ${commons.changes.version} + + + %URL%/%ISSUE% + + + + + + changes-report + + + + + + maven-pmd-plugin + 3.13.0 + + ${maven.compiler.target} + true + + + + pmd-report + + pmd + + + + pmd-aggregate + false + + pmd + - - - - javadocs** - release-notes** - + true - - - - - - - - - maven-checkstyle-plugin - ${checkstyle.plugin.version} - - ${basedir}/src/conf/checkstyle.xml - ${basedir}/src/conf/checkstyle-suppressions.xml - true - false - NOTICE.txt,LICENSE.txt,**/pom.properties,target/**.properties, - - - - org.apache.maven.plugins - maven-javadoc-plugin - - true - - https://docs.oracle.com/javase/8/docs/api/ - https://commons.apache.org/collections/api-release/ - - - - - org.apache.maven.plugins - maven-changes-plugin - ${commons.changes.version} - - - %URL%/%ISSUE% - - - - - - changes-report - - - - - - maven-pmd-plugin - 3.13.0 - - ${maven.compiler.target} - true - - - - pmd-report - - pmd - - - - pmd-aggregate - false - - pmd - - - true - - - - - - com.github.siom79.japicmp - japicmp-maven-plugin - ${commons.japicmp.version} - + + + + + com.github.siom79.japicmp + japicmp-maven-plugin + ${commons.japicmp.version} + - - org.apache.commons - commons-beanutils2 - 2.0.0-SNAPSHOT - jar - - - - - ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} - - - - - true - - - - - org.codehaus.mojo - findbugs-maven-plugin - ${commons.findbugs.version} - - Normal - Default - - true - -Duser.language=en - - - - + + org.apache.commons + commons-beanutils2 + 2.0.0-SNAPSHOT + jar + + + + + ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} + + + + + true + + + + + org.codehaus.mojo + findbugs-maven-plugin + ${commons.findbugs.version} + + Normal + Default + + true + -Duser.language=en + + + + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4ece28b64..9e18bfc8d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -36,12 +36,6 @@ Double-Checked Locking anti pattern in WeakFastHashMap. - - Update from Java 6 to 7. - - - Update from Java 7 to 8. - Change packaging from org.apache.commons.beanutils to org.apache.commons.beanutils2. @@ -60,9 +54,6 @@ BeanUtils2 mitigate CVE-2014-0114. - - Update Apache Commons Collections from 4.3 to 4.4. - Convert Collections4 to java.util.function. #8. @@ -78,14 +69,27 @@ Log at the debug level instead of info. + + Fix typos; fix error in Javadoc; performance fix; fix code smells #25. + + + + Update Apache Commons Collections from 4.3 to 4.4. + + + Update from Java 6 to 7. + + + Update from Java 7 to 8. + Update JUnit from 4.12 to 4.13. Update Jacoco from 0.8.4 to 0.8.5. - - Update JApiCmp from 0.12.0 to 0.14.3. + + Update JApiCmp from 0.12.0 to 0.14.4, #46. Update maven-surefire-plugin from 2.22.1 to 3.0.0-M5. @@ -99,17 +103,23 @@ Update BC version from 1.9.3 to 1.9.4. - - Fix typos; fix error in Javadoc; performance fix; fix code smells #25. - Update commons-parent from 50 to 51. - Update biz.aQute.bndlib from 5.1.0 to 5.1.2 #29. + Update biz.aQute.bndlib from 5.1.0 to 5.2.0 #29, #45. + + + Update actions/checkout from v2.3.1 to v2.3.4 #33. - Update actions/checkout from v2.3.1 to v2.3.2 #33. + Update actions/setup-java from v1.4.0 to v1.4.3 #35. + + + Update JUnit from 4.13 to 4.13.1. + + + Jacoco 0.8.6 for JDK15 support #55. @@ -668,4 +678,4 @@ src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java."> - + \ No newline at end of file diff --git a/src/main/java/org/apache/commons/beanutils2/BasicDynaBean.java b/src/main/java/org/apache/commons/beanutils2/BasicDynaBean.java index bb8c2fed4..8e72bf0c5 100644 --- a/src/main/java/org/apache/commons/beanutils2/BasicDynaBean.java +++ b/src/main/java/org/apache/commons/beanutils2/BasicDynaBean.java @@ -62,7 +62,6 @@ public class BasicDynaBean implements DynaBean, Serializable { */ public BasicDynaBean(final DynaClass dynaClass) { - super(); this.dynaClass = dynaClass; } diff --git a/src/main/java/org/apache/commons/beanutils2/BasicDynaClass.java b/src/main/java/org/apache/commons/beanutils2/BasicDynaClass.java index afb46a788..c1d1c2838 100644 --- a/src/main/java/org/apache/commons/beanutils2/BasicDynaClass.java +++ b/src/main/java/org/apache/commons/beanutils2/BasicDynaClass.java @@ -69,7 +69,6 @@ public BasicDynaClass(final String name, final Class dynaBeanClass) { public BasicDynaClass(final String name, Class dynaBeanClass, final DynaProperty[] properties) { - super(); if (name != null) { this.name = name; } diff --git a/src/main/java/org/apache/commons/beanutils2/BeanAccessLanguageException.java b/src/main/java/org/apache/commons/beanutils2/BeanAccessLanguageException.java index 411223f0f..0672985e8 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanAccessLanguageException.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanAccessLanguageException.java @@ -35,7 +35,6 @@ public class BeanAccessLanguageException extends IllegalArgumentException { * Constructs a {@code BeanAccessLanguageException} without a detail message. */ public BeanAccessLanguageException() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/BeanComparator.java b/src/main/java/org/apache/commons/beanutils2/BeanComparator.java index 381b26fb0..70a21b4e8 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanComparator.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanComparator.java @@ -229,7 +229,6 @@ private static class NaturalOrderComparator> * Private constructor to prevent instantiation. Only use INSTANCE. */ private NaturalOrderComparator() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java index 0b6a69891..5fc46b7dd 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeConsumer.java @@ -135,8 +135,6 @@ public BeanPropertyValueChangeConsumer(final String propertyName, final V proper * @throws IllegalArgumentException If the propertyName provided is null or empty. */ public BeanPropertyValueChangeConsumer(final String propertyName, final V propertyValue, final boolean ignoreNull) { - super(); - if (propertyName != null && propertyName.length() > 0) { this.propertyName = propertyName; this.propertyValue = propertyValue; diff --git a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java index 442bdc836..9f4112520 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java @@ -166,8 +166,6 @@ public BeanPropertyValueEqualsPredicate(final String propertyName, final V prope */ public BeanPropertyValueEqualsPredicate(final String propertyName, final V propertyValue, final boolean ignoreNull) { - super(); - if (propertyName != null && propertyName.length() > 0) { this.propertyName = propertyName; this.propertyValue = propertyValue; diff --git a/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java b/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java index c4f0ca7f6..f56b9f883 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java @@ -120,8 +120,6 @@ public BeanToPropertyValueTransformer(final String propertyName) { * empty. */ public BeanToPropertyValueTransformer(final String propertyName, final boolean ignoreNull) { - super(); - if (propertyName != null && propertyName.length() > 0) { this.propertyName = propertyName; this.ignoreNull = ignoreNull; diff --git a/src/main/java/org/apache/commons/beanutils2/BeanUtils.java b/src/main/java/org/apache/commons/beanutils2/BeanUtils.java index ba2c0eacf..aba9a3e93 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanUtils.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanUtils.java @@ -436,6 +436,6 @@ public static boolean initCause(final Throwable throwable, final Throwable cause * @since 1.8.0 */ public static Map createCache() { - return new WeakFastHashMap<>(); + return new ConcurrentWeakKeyHashMap<>(); } } diff --git a/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java index 1366b6ffa..8147b1b6d 100644 --- a/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/BeanUtilsBean.java @@ -1038,7 +1038,7 @@ public boolean initCause(final Throwable throwable, final Throwable cause) { INIT_CAUSE_METHOD.invoke(throwable, cause); return true; } catch (final Throwable e) { - return false; // can't initialize cause + // can't initialize cause } } return false; diff --git a/src/main/java/org/apache/commons/beanutils2/ConcurrentWeakKeyHashMap.java b/src/main/java/org/apache/commons/beanutils2/ConcurrentWeakKeyHashMap.java new file mode 100644 index 000000000..7569e3f6e --- /dev/null +++ b/src/main/java/org/apache/commons/beanutils2/ConcurrentWeakKeyHashMap.java @@ -0,0 +1,1449 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.beanutils2; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * An alternative weak-key {@link ConcurrentMap} which is similar to {@link java.util.concurrent.ConcurrentHashMap}. + *

+ * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * @see Creative Commons + * @param the type of keys maintained by this map + * @param the type of mapped values + * @author The Netty Project + * @author Doug Lea + * @author Jason T. Greene + * @author Trustin Lee + * @since 2.0 + */ +public final class ConcurrentWeakKeyHashMap extends AbstractMap implements ConcurrentMap { + + /* + * The basic strategy is to subdivide the table among Segments, + * each of which itself is a concurrently readable hash table. + */ + + /** + * The default initial capacity for this table, used when not otherwise specified in a constructor. + */ + static final int DEFAULT_INITIAL_CAPACITY = 16; + + /** + * The default load factor for this table, used when not otherwise specified in a constructor. + */ + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * The default concurrency level for this table, used when not otherwise specified in a constructor. + */ + static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * The maximum capacity, used if a higher value is implicitly specified by either of the constructors with + * arguments. MUST be a power of two <= 1<<30 to ensure that entries are indexable using integers. + */ + static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The maximum number of segments to allow; used to bound constructor arguments. + */ + static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + + /** + * Number of unsynchronized retries in size and containsValue methods before resorting to locking. This is used to + * avoid unbounded retries if tables undergo continuous modification which would make it impossible to obtain an + * accurate result. + */ + static final int RETRIES_BEFORE_LOCK = 2; + + /* ---------------- Fields -------------- */ + + /** + * Mask value for indexing into segments. The upper bits of a key's hash code are used to choose the segment. + */ + final int segmentMask; + + /** + * Shift value for indexing within segments. + */ + final int segmentShift; + + /** + * The segments, each of which is a specialized hash table + */ + final Segment[] segments; + + Set keySet; + Set> entrySet; + Collection values; + + /* ---------------- Small Utilities -------------- */ + + /** + * Applies a supplemental hash function to a given hashCode, which defends against poor quality hash functions. + * This is critical because ConcurrentReferenceHashMap uses power-of-two length hash tables, that otherwise + * encounter collisions for hashCodes that do not differ in lower or upper bits. + */ + private static int hash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + h += h << 15 ^ 0xffffcd7d; + h ^= h >>> 10; + h += h << 3; + h ^= h >>> 6; + h += (h << 2) + (h << 14); + return h ^ h >>> 16; + } + + /** + * Returns the segment that should be used for key with given hash. + * + * @param hash the hash code for the key + * @return the segment + */ + final Segment segmentFor(int hash) { + return segments[hash >>> segmentShift & segmentMask]; + } + + private int hashOf(Object key) { + return hash(key.hashCode()); + } + + /* ---------------- Inner Classes -------------- */ + + /** + * A weak-key reference which stores the key hash needed for reclamation. + */ + static final class WeakKeyReference extends WeakReference { + + final int hash; + + WeakKeyReference(K key, int hash, ReferenceQueue refQueue) { + super(key, refQueue); + this.hash = hash; + } + + public final int keyHash() { + return hash; + } + + public final Object keyRef() { + return this; + } + } + + /** + * ConcurrentReferenceHashMap list entry. Note that this is never exported out as a user-visible Map.Entry. + * Because the value field is volatile, not final, it is legal wrt the Java Memory Model for an unsynchronized + * reader to see null instead of initial value when read via a data race. Although a reordering leading to this is + * not likely to ever actually occur, the Segment.readValueUnderLock method is used as a backup in case a null + * (pre-initialized) value is ever seen in an unsynchronized access method. + */ + static final class HashEntry { + final Object keyRef; + final int hash; + volatile Object valueRef; + final HashEntry next; + + HashEntry( + K key, int hash, HashEntry next, V value, + ReferenceQueue refQueue) { + this.hash = hash; + this.next = next; + this.keyRef = new WeakKeyReference(key, hash, refQueue); + this.valueRef = value; + } + + @SuppressWarnings("unchecked") + final K key() { + return ((WeakReference) keyRef).get(); + } + + final V value() { + return dereferenceValue(valueRef); + } + + @SuppressWarnings("unchecked") + final V dereferenceValue(Object value) { + if (value instanceof WeakKeyReference) { + return ((Reference) value).get(); + } + + return (V) value; + } + + final void setValue(V value) { + this.valueRef = value; + } + + @SuppressWarnings("unchecked") + static HashEntry[] newArray(int i) { + return new HashEntry[i]; + } + } + + /** + * Segments are specialized versions of hash tables. This subclasses from ReentrantLock opportunistically, just to + * simplify some locking and avoid separate construction. + */ + static final class Segment extends ReentrantLock { + /* + * Segments maintain a table of entry lists that are ALWAYS kept in a + * consistent state, so can be read without locking. Next fields of + * nodes are immutable (final). All list additions are performed at the + * front of each bin. This makes it easy to check changes, and also fast + * to traverse. When nodes would otherwise be changed, new nodes are + * created to replace them. This works well for hash tables since the + * bin lists tend to be short. (The average length is less than two for + * the default load factor threshold.) + * + * Read operations can thus proceed without locking, but rely on + * selected uses of volatiles to ensure that completed write operations + * performed by other threads are noticed. For most purposes, the + * "count" field, tracking the number of elements, serves as that + * volatile variable ensuring visibility. This is convenient because + * this field needs to be read in many read operations anyway: + * + * - All (unsynchronized) read operations must first read the + * "count" field, and should not look at table entries if + * it is 0. + * + * - All (synchronized) write operations should write to + * the "count" field after structurally changing any bin. + * The operations must not take any action that could even + * momentarily cause a concurrent read operation to see + * inconsistent data. This is made easier by the nature of + * the read operations in Map. For example, no operation + * can reveal that the table has grown but the threshold + * has not yet been updated, so there are no atomicity + * requirements for this with respect to reads. + * + * As a guide, all critical volatile reads and writes to the count field + * are marked in code comments. + */ + + private static final long serialVersionUID = -8328104880676891126L; + + /** + * The number of elements in this segment's region. + */ + transient volatile int count; + + /** + * Number of updates that alter the size of the table. This is used during bulk-read methods to make sure they + * see a consistent snapshot: If modCounts change during a traversal of segments computing size or checking + * containsValue, then we might have an inconsistent view of state so (usually) must retry. + */ + int modCount; + + /** + * The table is rehashed when its size exceeds this threshold. (The value of this field is always (capacity + * * loadFactor).) + */ + int threshold; + + /** + * The per-segment table. + */ + transient volatile HashEntry[] table; + + /** + * The load factor for the hash table. Even though this value is same for all segments, it is replicated to + * avoid needing links to outer object. + */ + final float loadFactor; + + /** + * The collected weak-key reference queue for this segment. This should be (re)initialized whenever table is + * assigned, + */ + transient volatile ReferenceQueue refQueue; + + Segment(int initialCapacity, float lf) { + loadFactor = lf; + setTable(HashEntry.newArray(initialCapacity)); + } + + @SuppressWarnings("unchecked") + static Segment[] newArray(int i) { + return new Segment[i]; + } + + private boolean keyEq(Object src, Object dest) { + return src.equals(dest); + } + + /** + * Sets table to new HashEntry array. Call only while holding lock or in constructor. + */ + void setTable(HashEntry[] newTable) { + threshold = (int) (newTable.length * loadFactor); + table = newTable; + refQueue = new ReferenceQueue(); + } + + /** + * Returns properly casted first entry of bin for given hash. + */ + HashEntry getFirst(int hash) { + HashEntry[] tab = table; + return tab[hash & tab.length - 1]; + } + + HashEntry newHashEntry( + K key, int hash, HashEntry next, V value) { + return new HashEntry( + key, hash, next, value, refQueue); + } + + /** + * Reads value field of an entry under lock. Called if value field ever appears to be null. This is possible + * only if a compiler happens to reorder a HashEntry initialization with its table assignment, which is legal + * under memory model but is not known to ever occur. + */ + V readValueUnderLock(HashEntry e) { + lock(); + try { + removeStale(); + return e.value(); + } finally { + unlock(); + } + } + + /* Specialized implementations of map methods */ + + V get(Object key, int hash) { + if (count != 0) { // read-volatile + HashEntry e = getFirst(hash); + while (e != null) { + if (e.hash == hash && keyEq(key, e.key())) { + Object opaque = e.valueRef; + if (opaque != null) { + return e.dereferenceValue(opaque); + } + + return readValueUnderLock(e); // recheck + } + e = e.next; + } + } + return null; + } + + boolean containsKey(Object key, int hash) { + if (count != 0) { // read-volatile + HashEntry e = getFirst(hash); + while (e != null) { + if (e.hash == hash && keyEq(key, e.key())) { + return true; + } + e = e.next; + } + } + return false; + } + + boolean containsValue(Object value) { + if (count != 0) { // read-volatile + HashEntry[] tab = table; + int len = tab.length; + for (int i = 0; i < len; i++) { + for (HashEntry e = tab[i]; e != null; e = e.next) { + Object opaque = e.valueRef; + V v; + + if (opaque == null) { + v = readValueUnderLock(e); // recheck + } else { + v = e.dereferenceValue(opaque); + } + + if (value.equals(v)) { + return true; + } + } + } + } + return false; + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + lock(); + try { + removeStale(); + HashEntry e = getFirst(hash); + while (e != null && (e.hash != hash || !keyEq(key, e.key()))) { + e = e.next; + } + + boolean replaced = false; + if (e != null && oldValue.equals(e.value())) { + replaced = true; + e.setValue(newValue); + } + return replaced; + } finally { + unlock(); + } + } + + V replace(K key, int hash, V newValue) { + lock(); + try { + removeStale(); + HashEntry e = getFirst(hash); + while (e != null && (e.hash != hash || !keyEq(key, e.key()))) { + e = e.next; + } + + V oldValue = null; + if (e != null) { + oldValue = e.value(); + e.setValue(newValue); + } + return oldValue; + } finally { + unlock(); + } + } + + V put(K key, int hash, V value, boolean onlyIfAbsent) { + lock(); + try { + removeStale(); + int c = count; + if (c++ > threshold) { // ensure capacity + int reduced = rehash(); + if (reduced > 0) { + count = (c -= reduced) - 1; // write-volatile + } + } + + HashEntry[] tab = table; + int index = hash & tab.length - 1; + HashEntry first = tab[index]; + HashEntry e = first; + while (e != null && (e.hash != hash || !keyEq(key, e.key()))) { + e = e.next; + } + + V oldValue; + if (e != null) { + oldValue = e.value(); + if (!onlyIfAbsent) { + e.setValue(value); + } + } else { + oldValue = null; + ++modCount; + tab[index] = newHashEntry(key, hash, first, value); + count = c; // write-volatile + } + return oldValue; + } finally { + unlock(); + } + } + + int rehash() { + HashEntry[] oldTable = table; + int oldCapacity = oldTable.length; + if (oldCapacity >= MAXIMUM_CAPACITY) { + return 0; + } + + /* + * Reclassify nodes in each list to new Map. Because we are using + * power-of-two expansion, the elements from each bin must either + * stay at same index, or move with a power of two offset. We + * eliminate unnecessary node creation by catching cases where old + * nodes can be reused because their next fields won't change. + * Statistically, at the default threshold, only about one-sixth of + * them need cloning when a table doubles. The nodes they replace + * will be garbage collectable as soon as they are no longer + * referenced by any reader thread that may be in the midst of + * traversing table right now. + */ + + HashEntry[] newTable = HashEntry.newArray(oldCapacity << 1); + threshold = (int) (newTable.length * loadFactor); + int sizeMask = newTable.length - 1; + int reduce = 0; + for (int i = 0; i < oldCapacity; i++) { + // We need to guarantee that any existing reads of old Map can + // proceed. So we cannot yet null out each bin. + HashEntry e = oldTable[i]; + + if (e != null) { + HashEntry next = e.next; + int idx = e.hash & sizeMask; + + // Single node on list + if (next == null) { + newTable[idx] = e; + } else { + // Reuse trailing consecutive sequence at same slot + HashEntry lastRun = e; + int lastIdx = idx; + for (HashEntry last = next; last != null; last = last.next) { + int k = last.hash & sizeMask; + if (k != lastIdx) { + lastIdx = k; + lastRun = last; + } + } + newTable[lastIdx] = lastRun; + // Clone all remaining nodes + for (HashEntry p = e; p != lastRun; p = p.next) { + // Skip GC'd weak references + K key = p.key(); + if (key == null) { + reduce++; + continue; + } + int k = p.hash & sizeMask; + HashEntry n = newTable[k]; + newTable[k] = newHashEntry(key, p.hash, n, p.value()); + } + } + } + } + table = newTable; + return reduce; + } + + /** + * Remove; match on key only if value null, else match both. + */ + V remove(Object key, int hash, Object value, boolean refRemove) { + lock(); + try { + if (!refRemove) { + removeStale(); + } + int c = count - 1; + HashEntry[] tab = table; + int index = hash & tab.length - 1; + HashEntry first = tab[index]; + HashEntry e = first; + // a reference remove operation compares the Reference instance + while (e != null && key != e.keyRef && + (refRemove || hash != e.hash || !keyEq(key, e.key()))) { + e = e.next; + } + + V oldValue = null; + if (e != null) { + V v = e.value(); + if (value == null || value.equals(v)) { + oldValue = v; + // All entries following removed node can stay in list, + // but all preceding ones need to be cloned. + ++modCount; + HashEntry newFirst = e.next; + for (HashEntry p = first; p != e; p = p.next) { + K pKey = p.key(); + if (pKey == null) { // Skip GC'd keys + c--; + continue; + } + + newFirst = newHashEntry( + pKey, p.hash, newFirst, p.value()); + } + tab[index] = newFirst; + count = c; // write-volatile + } + } + return oldValue; + } finally { + unlock(); + } + } + + @SuppressWarnings("rawtypes") + final void removeStale() { + WeakKeyReference ref; + while ((ref = (WeakKeyReference) refQueue.poll()) != null) { + remove(ref.keyRef(), ref.keyHash(), null, true); + } + } + + void clear() { + if (count != 0) { + lock(); + try { + HashEntry[] tab = table; + for (int i = 0; i < tab.length; i++) { + tab[i] = null; + } + ++modCount; + // replace the reference queue to avoid unnecessary stale + // cleanups + refQueue = new ReferenceQueue(); + count = 0; // write-volatile + } finally { + unlock(); + } + } + } + } + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the specified initial capacity, load factor and concurrency level. + * + * @param initialCapacity the initial capacity. The implementation performs internal sizing to accommodate this + * many elements. + * @param loadFactor the load factor threshold, used to control resizing. Resizing may be performed when the + * average number of elements per bin exceeds this threshold. + * @param concurrencyLevel the estimated number of concurrently updating threads. The implementation performs + * internal sizing to try to accommodate this many threads. + * @throws IllegalArgumentException if the initial capacity is negative or the load factor or concurrencyLevel are + * nonpositive. + */ + public ConcurrentWeakKeyHashMap( + int initialCapacity, float loadFactor, int concurrencyLevel) { + if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) { + throw new IllegalArgumentException(); + } + + if (concurrencyLevel > MAX_SEGMENTS) { + concurrencyLevel = MAX_SEGMENTS; + } + + // Find power-of-two sizes best matching arguments + int sshift = 0; + int ssize = 1; + while (ssize < concurrencyLevel) { + ++sshift; + ssize <<= 1; + } + segmentShift = 32 - sshift; + segmentMask = ssize - 1; + this.segments = Segment.newArray(ssize); + + if (initialCapacity > MAXIMUM_CAPACITY) { + initialCapacity = MAXIMUM_CAPACITY; + } + int c = initialCapacity / ssize; + if (c * ssize < initialCapacity) { + ++c; + } + int cap = 1; + while (cap < c) { + cap <<= 1; + } + + for (int i = 0; i < this.segments.length; ++i) { + this.segments[i] = new Segment(cap, loadFactor); + } + } + + /** + * Creates a new, empty map with the specified initial capacity and load factor and with the default reference types + * (weak keys, strong values), and concurrencyLevel (16). + * + * @param initialCapacity The implementation performs internal sizing to accommodate this many elements. + * @param loadFactor the load factor threshold, used to control resizing. Resizing may be performed when the + * average number of elements per bin exceeds this threshold. + * @throws IllegalArgumentException if the initial capacity of elements is negative or the load factor is + * nonpositive + */ + public ConcurrentWeakKeyHashMap(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL); + } + + /** + * Creates a new, empty map with the specified initial capacity, and with default reference types (weak keys, strong + * values), load factor (0.75) and concurrencyLevel (16). + * + * @param initialCapacity the initial capacity. The implementation performs internal sizing to accommodate this many + * elements. + * @throws IllegalArgumentException if the initial capacity of elements is negative. + */ + public ConcurrentWeakKeyHashMap(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); + } + + /** + * Creates a new, empty map with a default initial capacity (16), reference types (weak keys, strong values), + * default load factor (0.75) and concurrencyLevel (16). + */ + public ConcurrentWeakKeyHashMap() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); + } + + /** + * Creates a new map with the same mappings as the given map. The map is created with a capacity of 1.5 times the + * number of mappings in the given map or 16 (whichever is greater), and a default load factor (0.75) and + * concurrencyLevel (16). + * + * @param m the map + */ + public ConcurrentWeakKeyHashMap(Map m) { + this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, + DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR, + DEFAULT_CONCURRENCY_LEVEL); + putAll(m); + } + + /** + * Returns true if this map contains no key-value mappings. + * + * @return true if this map contains no key-value mappings + */ + @Override + public boolean isEmpty() { + final Segment[] segments = this.segments; + /* + * We keep track of per-segment modCounts to avoid ABA problems in which + * an element in one segment was added and in another removed during + * traversal, in which case the table was never actually empty at any + * point. Note the similar use of modCounts in the size() and + * containsValue() methods, which are the only other methods also + * susceptible to ABA problems. + */ + int[] mc = new int[segments.length]; + int mcsum = 0; + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } else { + mcsum += mc[i] = segments[i].modCount; + } + } + // If mcsum happens to be zero, then we know we got a snapshot before + // any modifications at all were made. This is probably common enough + // to bother tracking. + if (mcsum != 0) { + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0 || mc[i] != segments[i].modCount) { + return false; + } + } + } + return true; + } + + /** + * Returns the number of key-value mappings in this map. If the map contains more than Integer.MAX_VALUE + * elements, returns Integer.MAX_VALUE. + * + * @return the number of key-value mappings in this map + */ + @Override + public int size() { + final Segment[] segments = this.segments; + long sum = 0; + long check = 0; + int[] mc = new int[segments.length]; + // Try a few times to get accurate count. On failure due to continuous + // async changes in table, resort to locking. + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { + check = 0; + sum = 0; + int mcsum = 0; + for (int i = 0; i < segments.length; ++i) { + sum += segments[i].count; + mcsum += mc[i] = segments[i].modCount; + } + if (mcsum != 0) { + for (int i = 0; i < segments.length; ++i) { + check += segments[i].count; + if (mc[i] != segments[i].modCount) { + check = -1; // force retry + break; + } + } + } + if (check == sum) { + break; + } + } + if (check != sum) { // Resort to locking all segments + sum = 0; + for (int i = 0; i < segments.length; ++i) { + segments[i].lock(); + } + try { + for (int i = 0; i < segments.length; ++i) { + sum += segments[i].count; + } + } finally { + for (int i = 0; i < segments.length; ++i) { + segments[i].unlock(); + } + } + } + if (sum > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else { + return (int) sum; + } + } + + /** + * Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the + * key. + *

More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that {@code + * key.equals(k)}, then this method returns {@code v}; otherwise it returns {@code null}. (There can be at most one + * such mapping.)

+ * + * @throws NullPointerException if the specified key is null + */ + @Override + public V get(Object key) { + int hash = hashOf(key); + return segmentFor(hash).get(key, hash); + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key + * @return true if and only if the specified object is a key in this table, as determined by the + * equals method; false otherwise. + * @throws NullPointerException if the specified key is null + */ + @Override + public boolean containsKey(Object key) { + int hash = hashOf(key); + return segmentFor(hash).containsKey(key, hash); + } + + /** + * Returns true if this map maps one or more keys to the specified value. Note: This method requires a + * full internal traversal of the hash table, and so is much slower than method containsKey. + * + * @param value value whose presence in this map is to be tested + * @return true if this map maps one or more keys to the specified value + * @throws NullPointerException if the specified value is null + */ + + @Override + public boolean containsValue(Object value) { + if (value == null) { + throw new NullPointerException(); + } + + // See explanation of modCount use above + + final Segment[] segments = this.segments; + int[] mc = new int[segments.length]; + + // Try a few times without locking + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { + int mcsum = 0; + for (int i = 0; i < segments.length; ++i) { + mcsum += mc[i] = segments[i].modCount; + if (segments[i].containsValue(value)) { + return true; + } + } + boolean cleanSweep = true; + if (mcsum != 0) { + for (int i = 0; i < segments.length; ++i) { + if (mc[i] != segments[i].modCount) { + cleanSweep = false; + break; + } + } + } + if (cleanSweep) { + return false; + } + } + // Resort to locking all segments + for (int i = 0; i < segments.length; ++i) { + segments[i].lock(); + } + boolean found = false; + try { + for (int i = 0; i < segments.length; ++i) { + if (segments[i].containsValue(value)) { + found = true; + break; + } + } + } finally { + for (int i = 0; i < segments.length; ++i) { + segments[i].unlock(); + } + } + return found; + } + + /** + * Legacy method testing if some key maps into the specified value in this table. This method is identical in + * functionality to {@link #containsValue}, and exists solely to ensure full compatibility with class {@link + * Hashtable}, which supported this method prior to introduction of the Java Collections framework. + * + * @param value a value to search for + * @return true if and only if some key maps to the value argument in this table as + * determined by the equals method; false otherwise + * @throws NullPointerException if the specified value is null + */ + public boolean contains(Object value) { + return containsValue(value); + } + + /** + * Maps the specified key to the specified value in this table. Neither the key nor the value can be null. + *

The value can be retrieved by calling the get method with a key that is equal to the + * original key.

+ * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or null if there was no mapping for + * key + * @throws NullPointerException if the specified key or value is null + */ + @Override + public V put(K key, V value) { + if (value == null) { + throw new NullPointerException(); + } + int hash = hashOf(key); + return segmentFor(hash).put(key, hash, value, false); + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, or null if there was no mapping for the + * key + * @throws NullPointerException if the specified key or value is null + */ + @Override + public V putIfAbsent(K key, V value) { + if (value == null) { + throw new NullPointerException(); + } + int hash = hashOf(key); + return segmentFor(hash).put(key, hash, value, true); + } + + /** + * Copies all of the mappings from the specified map to this one. These mappings replace any mappings that this map + * had for any of the keys currently in the specified map. + * + * @param m mappings to be stored in this map + */ + @Override + public void putAll(Map m) { + for (Map.Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + /** + * Removes the key (and its corresponding value) from this map. This method does nothing if the key is not in the + * map. + * + * @param key the key that needs to be removed + * @return the previous value associated with key, or null if there was no mapping for + * key + * @throws NullPointerException if the specified key is null + */ + @Override + public V remove(Object key) { + int hash = hashOf(key); + return segmentFor(hash).remove(key, hash, null, false); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + @Override + public boolean remove(Object key, Object value) { + int hash = hashOf(key); + if (value == null) { + return false; + } + return segmentFor(hash).remove(key, hash, value, false) != null; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null + */ + @Override + public boolean replace(K key, V oldValue, V newValue) { + if (oldValue == null || newValue == null) { + throw new NullPointerException(); + } + int hash = hashOf(key); + return segmentFor(hash).replace(key, hash, oldValue, newValue); + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, or null if there was no mapping for the + * key + * @throws NullPointerException if the specified key or value is null + */ + @Override + public V replace(K key, V value) { + if (value == null) { + throw new NullPointerException(); + } + int hash = hashOf(key); + return segmentFor(hash).replace(key, hash, value); + } + + /** + * Removes all of the mappings from this map. + */ + @Override + public void clear() { + for (int i = 0; i < segments.length; ++i) { + segments[i].clear(); + } + } + + /** + * Removes any stale entries whose keys have been finalized. Use of this method is normally not necessary since + * stale entries are automatically removed lazily, when blocking operations are required. However, there are some + * cases where this operation should be performed eagerly, such as cleaning up old references to a ClassLoader in a + * multi-classloader environment. + *

+ * Note: this method will acquire locks, one at a time, across all segments of this table, so if it is to be used, + * it should be used sparingly.

+ */ + public void purgeStaleEntries() { + for (int i = 0; i < segments.length; ++i) { + segments[i].removeStale(); + } + } + + /** + * Returns a {@link Set} view of the keys contained in this map. The set is backed by the map, so changes to the + * map are reflected in the set, and vice-versa. The set supports element removal, which removes the corresponding + * mapping from this map, via the Iterator.remove, Set.remove, removeAll, + * retainAll, and clear operations. It does not support the add or + * addAll operations. + *

The view's iterator is a "weakly consistent" iterator that will never throw {@link + * ConcurrentModificationException}, and guarantees to traverse elements as they existed upon construction of the + * iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.

+ */ + @Override + public Set keySet() { + Set ks = keySet; + return ks != null ? ks : (keySet = new KeySet()); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. The collection is backed by the map, so + * changes to the map are reflected in the collection, and vice-versa. The collection supports element removal, + * which removes the corresponding mapping from this map, via the Iterator.remove, + * Collection.remove, removeAll, retainAll, and clear operations. + * It does not support the add or addAll operations. + *

The view's iterator is a "weakly consistent" iterator that will never throw {@link + * ConcurrentModificationException}, and guarantees to traverse elements as they existed upon construction of the + * iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.

+ */ + @Override + public Collection values() { + Collection vs = values; + return vs != null ? vs : (values = new Values()); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. The set is backed by the map, so changes to the + * map are reflected in the set, and vice-versa. The set supports element removal, which removes the corresponding + * mapping from the map, via the Iterator.remove, Set.remove, removeAll, + * retainAll, and clear operations. It does not support the add or + * addAll operations. + *

The view's iterator is a "weakly consistent" iterator that will never throw {@link + * ConcurrentModificationException}, and guarantees to traverse elements as they existed upon construction of the + * iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.

+ */ + @Override + public Set> entrySet() { + Set> es = entrySet; + return es != null ? es : (entrySet = new EntrySet()); + } + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() + */ + public Enumeration keys() { + return new KeyIterator(); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() + */ + public Enumeration elements() { + return new ValueIterator(); + } + + /* ---------------- Iterator Support -------------- */ + + abstract class HashIterator { + int nextSegmentIndex; + int nextTableIndex; + HashEntry[] currentTable; + HashEntry nextEntry; + HashEntry lastReturned; + K currentKey; // Strong reference to weak key (prevents gc) + + HashIterator() { + nextSegmentIndex = segments.length - 1; + nextTableIndex = -1; + advance(); + } + + public void rewind() { + nextSegmentIndex = segments.length - 1; + nextTableIndex = -1; + currentTable = null; + nextEntry = null; + lastReturned = null; + currentKey = null; + advance(); + } + + public boolean hasMoreElements() { + return hasNext(); + } + + final void advance() { + if (nextEntry != null && (nextEntry = nextEntry.next) != null) { + return; + } + + while (nextTableIndex >= 0) { + if ((nextEntry = currentTable[nextTableIndex--]) != null) { + return; + } + } + + while (nextSegmentIndex >= 0) { + Segment seg = segments[nextSegmentIndex--]; + if (seg.count != 0) { + currentTable = seg.table; + for (int j = currentTable.length - 1; j >= 0; --j) { + if ((nextEntry = currentTable[j]) != null) { + nextTableIndex = j - 1; + return; + } + } + } + } + } + + public boolean hasNext() { + while (nextEntry != null) { + if (nextEntry.key() != null) { + return true; + } + advance(); + } + + return false; + } + + HashEntry nextEntry() { + do { + if (nextEntry == null) { + throw new NoSuchElementException(); + } + + lastReturned = nextEntry; + currentKey = lastReturned.key(); + advance(); + } while (currentKey == null); // Skip GC'd keys + + return lastReturned; + } + + public void remove() { + if (lastReturned == null) { + throw new IllegalStateException(); + } + ConcurrentWeakKeyHashMap.this.remove(currentKey); + lastReturned = null; + } + } + + final class KeyIterator + extends HashIterator implements ReusableIterator, Enumeration { + + @Override + public K next() { + return super.nextEntry().key(); + } + + @Override + public K nextElement() { + return super.nextEntry().key(); + } + } + + final class ValueIterator + extends HashIterator implements ReusableIterator, Enumeration { + + @Override + public V next() { + return super.nextEntry().value(); + } + + @Override + public V nextElement() { + return super.nextEntry().value(); + } + } + + /* + * This class is needed for JDK5 compatibility. + */ + static class SimpleEntry implements Entry { + + private final K key; + + private V value; + + public SimpleEntry(K key, V value) { + this.key = key; + this.value = value; + + } + + public SimpleEntry(Entry entry) { + this.key = entry.getKey(); + this.value = entry.getValue(); + + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(V value) { + V oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + @SuppressWarnings("rawtypes") + Map.Entry e = (Map.Entry) o; + return eq(key, e.getKey()) && eq(value, e.getValue()); + } + + @Override + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); + } + + @Override + public String toString() { + return key + "=" + value; + } + + private static boolean eq(Object o1, Object o2) { + return o1 == null ? o2 == null : o1.equals(o2); + } + } + + /** + * Custom Entry class used by EntryIterator.next(), that relays setValue changes to the underlying map. + */ + final class WriteThroughEntry extends SimpleEntry { + + WriteThroughEntry(K k, V v) { + super(k, v); + } + + /** + * Set our entry's value and write through to the map. The value to return is somewhat arbitrary here. Since a + * WriteThroughEntry does not necessarily track asynchronous changes, the most recent "previous" value could be + * different from what we return (or could even have been removed in which case the put will re-establish). We + * do not and can not guarantee more. + */ + @Override + public V setValue(V value) { + + if (value == null) { + throw new NullPointerException(); + } + V v = super.setValue(value); + ConcurrentWeakKeyHashMap.this.put(getKey(), value); + return v; + } + + } + + final class EntryIterator extends HashIterator implements + ReusableIterator> { + @Override + public Map.Entry next() { + HashEntry e = super.nextEntry(); + return new WriteThroughEntry(e.key(), e.value()); + } + } + + final class KeySet extends AbstractSet { + @Override + public Iterator iterator() { + + return new KeyIterator(); + } + + @Override + public int size() { + return ConcurrentWeakKeyHashMap.this.size(); + } + + @Override + public boolean isEmpty() { + return ConcurrentWeakKeyHashMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return ConcurrentWeakKeyHashMap.this.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return ConcurrentWeakKeyHashMap.this.remove(o) != null; + + } + + @Override + public void clear() { + ConcurrentWeakKeyHashMap.this.clear(); + } + } + + final class Values extends AbstractCollection { + @Override + public Iterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return ConcurrentWeakKeyHashMap.this.size(); + } + + @Override + public boolean isEmpty() { + return ConcurrentWeakKeyHashMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return ConcurrentWeakKeyHashMap.this.containsValue(o); + } + + @Override + public void clear() { + ConcurrentWeakKeyHashMap.this.clear(); + } + } + + final class EntrySet extends AbstractSet> { + @Override + public Iterator> iterator() { + return new EntryIterator(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry) o; + V v = ConcurrentWeakKeyHashMap.this.get(e.getKey()); + return v != null && v.equals(e.getValue()); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry) o; + return ConcurrentWeakKeyHashMap.this.remove(e.getKey(), e.getValue()); + } + + @Override + public int size() { + return ConcurrentWeakKeyHashMap.this.size(); + } + + @Override + public boolean isEmpty() { + return ConcurrentWeakKeyHashMap.this.isEmpty(); + } + + @Override + public void clear() { + ConcurrentWeakKeyHashMap.this.clear(); + } + } + + public static interface ReusableIterator extends Iterator { + void rewind(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java b/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java index cb61ea930..34083db7b 100644 --- a/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java +++ b/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java @@ -110,7 +110,6 @@ public class ContextClassLoaderLocal { * Construct a context classloader instance */ public ContextClassLoaderLocal() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java index 8dc70c1f1..51942e3e3 100644 --- a/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java @@ -190,8 +190,8 @@ protected static ConvertUtilsBean getInstance() { * The set of {@link Converter}s that can be used to convert Strings * into objects of a specified Class, keyed by the destination Class. */ - private final WeakFastHashMap, Converter> converters = - new WeakFastHashMap<>(); + private final ConcurrentWeakKeyHashMap, Converter> converters = + new ConcurrentWeakKeyHashMap<>(); /** * The {@code Log} instance for this class. @@ -202,9 +202,7 @@ protected static ConvertUtilsBean getInstance() { /** Construct a bean with standard converters registered */ public ConvertUtilsBean() { - converters.setFast(false); deregister(); - converters.setFast(true); } diff --git a/src/main/java/org/apache/commons/beanutils2/DynaProperty.java b/src/main/java/org/apache/commons/beanutils2/DynaProperty.java index 201031981..a86e94dd8 100644 --- a/src/main/java/org/apache/commons/beanutils2/DynaProperty.java +++ b/src/main/java/org/apache/commons/beanutils2/DynaProperty.java @@ -81,7 +81,6 @@ public DynaProperty(final String name) { */ public DynaProperty(final String name, final Class type) { - super(); this.name = name; this.type = type; if (type != null && type.isArray()) { @@ -100,7 +99,6 @@ public DynaProperty(final String name, final Class type) { */ public DynaProperty(final String name, final Class type, final Class contentType) { - super(); this.name = name; this.type = type; this.contentType = contentType; @@ -200,7 +198,7 @@ public boolean isMapped() { @Override public boolean equals(final Object obj) { - boolean result = false; + boolean result; result = obj == this; diff --git a/src/main/java/org/apache/commons/beanutils2/LazyDynaClass.java b/src/main/java/org/apache/commons/beanutils2/LazyDynaClass.java index 9c29af5e7..2205b2eb4 100644 --- a/src/main/java/org/apache/commons/beanutils2/LazyDynaClass.java +++ b/src/main/java/org/apache/commons/beanutils2/LazyDynaClass.java @@ -284,7 +284,7 @@ public void remove(final String name) { final DynaProperty[] oldProperties = getDynaProperties(); final DynaProperty[] newProperties = new DynaProperty[oldProperties.length-1]; int j = 0; - for (DynaProperty oldProperty : oldProperties) { + for (final DynaProperty oldProperty : oldProperties) { if (!name.equals(oldProperty.getName())) { newProperties[j] = oldProperty; j++; diff --git a/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java b/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java index dd213c195..2902eaa6f 100644 --- a/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java +++ b/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java @@ -193,7 +193,6 @@ public class LazyDynaList extends ArrayList { * Default Constructor. */ public LazyDynaList() { - super(); } /** @@ -214,7 +213,6 @@ public LazyDynaList(final int capacity) { * @param elementDynaClass The DynaClass of the List's elements. */ public LazyDynaList(final DynaClass elementDynaClass) { - super(); setElementDynaClass(elementDynaClass); } @@ -225,7 +223,6 @@ public LazyDynaList(final DynaClass elementDynaClass) { * @param elementType The Type of the List's elements. */ public LazyDynaList(final Class elementType) { - super(); setElementType(elementType); } @@ -630,7 +627,7 @@ private DynaBean transform(final Object element) { DynaBean dynaBean = null; Class newDynaBeanType = null; - Class newElementType = null; + Class newElementType; // Create a new element if (element == null) { diff --git a/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java b/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java index c204be0b9..7f955f052 100644 --- a/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java +++ b/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java @@ -161,7 +161,7 @@ public MappedPropertyDescriptor(final String propertyName, final Class beanCl setName(propertyName); // search the mapped get and set methods - Method mappedReadMethod = null; + Method mappedReadMethod; Method mappedWriteMethod = null; mappedReadMethod = getMethod(beanClass, mappedGetterName, STRING_CLASS_PARAMETER); diff --git a/src/main/java/org/apache/commons/beanutils2/MethodUtils.java b/src/main/java/org/apache/commons/beanutils2/MethodUtils.java index 32d86d1d7..6a7b47695 100644 --- a/src/main/java/org/apache/commons/beanutils2/MethodUtils.java +++ b/src/main/java/org/apache/commons/beanutils2/MethodUtils.java @@ -858,7 +858,7 @@ public static Method getAccessibleMethod(Class clazz, Method method) { // Check the implemented interfaces of the parent class final Class[] interfaces = clazz.getInterfaces(); - for (Class anInterface : interfaces) { + for (final Class anInterface : interfaces) { // Is this interface public? if (!Modifier.isPublic(anInterface.getModifiers())) { @@ -1246,7 +1246,6 @@ public static Class toNonPrimitiveClass(final Class clazz) { if (primitiveClazz != null) { return primitiveClazz; } - return clazz; } return clazz; } diff --git a/src/main/java/org/apache/commons/beanutils2/NestedNullException.java b/src/main/java/org/apache/commons/beanutils2/NestedNullException.java index 61c71aed9..60d1c0ecb 100644 --- a/src/main/java/org/apache/commons/beanutils2/NestedNullException.java +++ b/src/main/java/org/apache/commons/beanutils2/NestedNullException.java @@ -34,7 +34,6 @@ public class NestedNullException extends BeanAccessLanguageException { * Constructs a {@code NestedNullException} without a detail message. */ public NestedNullException() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java index fb12188cb..f662d7432 100644 --- a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java @@ -108,8 +108,8 @@ protected static PropertyUtilsBean getInstance() { * The cache of PropertyDescriptor arrays for beans we have already * introspected, keyed by the java.lang.Class of this object. */ - private WeakFastHashMap, BeanIntrospectionData> descriptorsCache = null; - private WeakFastHashMap, Map> mappedDescriptorsCache = null; + private ConcurrentWeakKeyHashMap, BeanIntrospectionData> descriptorsCache = null; + private ConcurrentWeakKeyHashMap, Map> mappedDescriptorsCache = null; /** An empty object array */ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; @@ -124,10 +124,8 @@ protected static PropertyUtilsBean getInstance() { /** Base constructor */ public PropertyUtilsBean() { - descriptorsCache = new WeakFastHashMap<>(); - descriptorsCache.setFast(true); - mappedDescriptorsCache = new WeakFastHashMap<>(); - mappedDescriptorsCache.setFast(true); + descriptorsCache = new ConcurrentWeakKeyHashMap<>(); + mappedDescriptorsCache = new ConcurrentWeakKeyHashMap<>(); introspectors = new CopyOnWriteArrayList<>(); resetBeanIntrospectors(); } @@ -2061,7 +2059,7 @@ private Object invokeMethod( } catch (final NullPointerException | IllegalArgumentException cause) { // JDK 1.3 and JDK 1.4 throw NullPointerException if an argument is // null for a primitive value (JDK 1.5+ throw IllegalArgumentException) - StringBuilder valueString = new StringBuilder(); + final StringBuilder valueString = new StringBuilder(); if (values != null) { for (int i = 0; i < values.length; i++) { if (i>0) { @@ -2074,7 +2072,7 @@ private Object invokeMethod( } } } - StringBuilder expectedString = new StringBuilder(); + final StringBuilder expectedString = new StringBuilder(); final Class[] parTypes = method.getParameterTypes(); if (parTypes != null) { for (int i = 0; i < parTypes.length; i++) { diff --git a/src/main/java/org/apache/commons/beanutils2/WrapDynaClass.java b/src/main/java/org/apache/commons/beanutils2/WrapDynaClass.java index f63084243..8d4393d03 100644 --- a/src/main/java/org/apache/commons/beanutils2/WrapDynaClass.java +++ b/src/main/java/org/apache/commons/beanutils2/WrapDynaClass.java @@ -23,7 +23,6 @@ import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Map; -import java.util.WeakHashMap; /** @@ -116,7 +115,7 @@ private WrapDynaClass(final Class beanClass, final PropertyUtilsBean propUtil new ContextClassLoaderLocal>() { @Override protected Map initialValue() { - return new WeakHashMap<>(); + return new ConcurrentWeakKeyHashMap<>(); } }; diff --git a/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java index dff8337b1..c2b6770f7 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/AbstractConverter.java @@ -423,7 +423,7 @@ String toString(final Class type) { elementType = elementType .getComponentType(); count++; } - StringBuilder typeNameBuilder = new StringBuilder(elementType.getName()); + final StringBuilder typeNameBuilder = new StringBuilder(elementType.getName()); for (int i = 0; i < count; i++) { typeNameBuilder.append("[]"); } diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java index 46a80f5ab..e291fc965 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java @@ -112,7 +112,7 @@ * * // Construct a "Matrix" Converter which converts arrays of integer arrays using * // the preceding ArrayConverter as the element Converter. - * // N.B. Uses a semi-colon (i.e. ";") as the delimiter to separate the different sets of numbers. + * // N.B. Uses a semicolon (i.e. ";") as the delimiter to separate the different sets of numbers. * // Also the delimiter used by the first ArrayConverter needs to be added to the * // "allowed characters" for this one. * ArrayConverter matrixConverter = new ArrayConverter(int[][].class, arrayConverter); @@ -148,7 +148,6 @@ public class ArrayConverter extends AbstractConverter { * individual array elements. */ public ArrayConverter(final Class defaultType, final Converter elementConverter) { - super(); if (defaultType == null) { throw new IllegalArgumentException("Default type is missing"); } diff --git a/src/main/java/org/apache/commons/beanutils2/converters/BooleanConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/BooleanConverter.java index 8a7af0037..110b79788 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/BooleanConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/BooleanConverter.java @@ -56,7 +56,6 @@ public final class BooleanConverter extends AbstractConverter { * not one of the known true strings, nor one of the known false strings. */ public BooleanConverter() { - super(); } @@ -95,7 +94,6 @@ public BooleanConverter(final Object defaultValue) { * @since 1.8.0 */ public BooleanConverter(final String[] trueStrings, final String[] falseStrings) { - super(); this.trueStrings = copyStrings(trueStrings); this.falseStrings = copyStrings(falseStrings); } diff --git a/src/main/java/org/apache/commons/beanutils2/converters/CalendarConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/CalendarConverter.java index b2fdf9d49..feb968523 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/CalendarConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/CalendarConverter.java @@ -39,7 +39,6 @@ public final class CalendarConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public CalendarConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/CharacterConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/CharacterConverter.java index 82365879e..9cc30e1f4 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/CharacterConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/CharacterConverter.java @@ -32,7 +32,6 @@ public final class CharacterConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public CharacterConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ClassConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ClassConverter.java index 7f4a59c4f..96143dd43 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/ClassConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/ClassConverter.java @@ -36,7 +36,6 @@ public final class ClassConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public ClassConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/DateConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/DateConverter.java index 86daae37d..6d05d42e0 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/DateConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/DateConverter.java @@ -39,7 +39,6 @@ public final class DateConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public DateConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java index edc2f17d7..a838849e4 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java @@ -105,7 +105,6 @@ public abstract class DateTimeConverter extends AbstractConverter { * {@code ConversionException} if an error occurs. */ public DateTimeConverter() { - super(); } /** @@ -204,14 +203,7 @@ public String[] getPatterns() { public void setPatterns(final String[] patterns) { this.patterns = patterns; if (patterns != null && patterns.length > 1) { - final StringBuilder buffer = new StringBuilder(); - for (int i = 0; i < patterns.length; i++) { - if (i > 0) { - buffer.append(", "); - } - buffer.append(patterns[i]); - } - displayPatterns = buffer.toString(); + displayPatterns = String.join(", ", patterns); } setUseLocaleFormat(true); } diff --git a/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java index ea7866c65..530346cdf 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java @@ -36,7 +36,6 @@ public final class DurationConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public DurationConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java index bf5386979..33517306f 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java @@ -34,7 +34,6 @@ public final class EnumConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public EnumConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/FileConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/FileConverter.java index 718907003..60fd2c4b3 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/FileConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/FileConverter.java @@ -34,7 +34,6 @@ public final class FileConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public FileConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java index e37fdeacf..2d6861a32 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java @@ -42,7 +42,6 @@ public final class LocalDateConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public LocalDateConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java index 2c355e550..aacc7be10 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java @@ -42,7 +42,6 @@ public final class LocalDateTimeConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public LocalDateTimeConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/LocalTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/LocalTimeConverter.java index 176d299ce..cb54c3092 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/LocalTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/LocalTimeConverter.java @@ -36,7 +36,6 @@ public final class LocalTimeConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public LocalTimeConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/MonthDayConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/MonthDayConverter.java index b9b342aab..d829d5df3 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/MonthDayConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/MonthDayConverter.java @@ -36,7 +36,6 @@ public final class MonthDayConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public MonthDayConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/NumberConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/NumberConverter.java index 899274fb1..3f745f760 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/NumberConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/NumberConverter.java @@ -104,7 +104,6 @@ public abstract class NumberConverter extends AbstractConverter { * @param allowDecimals Indicates whether decimals are allowed */ public NumberConverter(final boolean allowDecimals) { - super(); this.allowDecimals = allowDecimals; } @@ -116,7 +115,6 @@ public NumberConverter(final boolean allowDecimals) { * @param defaultValue The default value to be returned */ public NumberConverter(final boolean allowDecimals, final Object defaultValue) { - super(); this.allowDecimals = allowDecimals; setDefaultValue(defaultValue); } diff --git a/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java index a85751410..969689055 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java @@ -42,7 +42,6 @@ public final class OffsetDateTimeConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public OffsetDateTimeConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/OffsetTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/OffsetTimeConverter.java index 0c2795058..5def1050f 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/OffsetTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/OffsetTimeConverter.java @@ -36,7 +36,6 @@ public final class OffsetTimeConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public OffsetTimeConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/PathConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/PathConverter.java index d454f29bd..cf6bf607e 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/PathConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/PathConverter.java @@ -35,7 +35,6 @@ public final class PathConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public PathConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java index a714a98b9..305146b26 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java @@ -36,7 +36,6 @@ public final class PeriodConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public PeriodConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/SqlDateConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/SqlDateConverter.java index 109e6a636..480e1f019 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/SqlDateConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/SqlDateConverter.java @@ -39,7 +39,6 @@ public final class SqlDateConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public SqlDateConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java index 4fdc9442a..03f755663 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java @@ -42,7 +42,6 @@ public final class SqlTimeConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public SqlTimeConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/SqlTimestampConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/SqlTimestampConverter.java index c4576d8cc..e0e0e43c7 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/SqlTimestampConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/SqlTimestampConverter.java @@ -42,7 +42,6 @@ public final class SqlTimestampConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public SqlTimestampConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/StringConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/StringConverter.java index 2d1ca40c0..0bb4e1ac2 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/StringConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/StringConverter.java @@ -47,7 +47,6 @@ public final class StringConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public StringConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/URIConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/URIConverter.java index 902ebe2ed..6b8377705 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/URIConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/URIConverter.java @@ -34,7 +34,6 @@ public final class URIConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public URIConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/URLConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/URLConverter.java index d4f269506..341683aed 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/URLConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/URLConverter.java @@ -34,7 +34,6 @@ public final class URLConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public URLConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/UUIDConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/UUIDConverter.java index c2f09e6d5..7e4a0c096 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/UUIDConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/UUIDConverter.java @@ -34,7 +34,6 @@ public final class UUIDConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public UUIDConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/YearConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/YearConverter.java index 89395baf0..b269bd8e7 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/YearConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/YearConverter.java @@ -36,7 +36,6 @@ public final class YearConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public YearConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/YearMonthConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/YearMonthConverter.java index a47e9f680..e81fdcb13 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/YearMonthConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/YearMonthConverter.java @@ -36,7 +36,6 @@ public final class YearMonthConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public YearMonthConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ZoneIdConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ZoneIdConverter.java index f5e5928e4..51b2df6a5 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/ZoneIdConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/ZoneIdConverter.java @@ -36,7 +36,6 @@ public final class ZoneIdConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public ZoneIdConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ZoneOffsetConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ZoneOffsetConverter.java index 00133ca9b..ac7051c62 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/ZoneOffsetConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/ZoneOffsetConverter.java @@ -36,7 +36,6 @@ public final class ZoneOffsetConverter extends AbstractConverter { * a {@code ConversionException} if an error occurs. */ public ZoneOffsetConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java index c1c9f1e53..25592f4f4 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java @@ -42,7 +42,6 @@ public final class ZonedDateTimeConverter extends DateTimeConverter { * a {@code ConversionException} if an error occurs. */ public ZonedDateTimeConverter() { - super(); } /** diff --git a/src/main/java/org/apache/commons/beanutils2/expression/DefaultResolver.java b/src/main/java/org/apache/commons/beanutils2/expression/DefaultResolver.java index 32e30d8dd..6f23ccbd8 100644 --- a/src/main/java/org/apache/commons/beanutils2/expression/DefaultResolver.java +++ b/src/main/java/org/apache/commons/beanutils2/expression/DefaultResolver.java @@ -239,12 +239,17 @@ public String next(final String expression) { return expression.substring(0, i + 1); } } else { - if (c == NESTED) { + switch (c) { + case NESTED: return expression.substring(0, i); - } else if (c == MAPPED_START) { + case MAPPED_START: mapped = true; - } else if (c == INDEXED_START) { + break; + case INDEXED_START: indexed = true; + break; + default: + break; } } } diff --git a/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java index b59b31c2d..8bb73f62e 100644 --- a/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsBean.java @@ -26,7 +26,6 @@ import java.util.Set; import org.apache.commons.beanutils2.BeanUtils; -import org.apache.commons.beanutils2.WeakFastHashMap; import org.apache.commons.beanutils2.locale.converters.BigDecimalLocaleConverter; import org.apache.commons.beanutils2.locale.converters.BigIntegerLocaleConverter; import org.apache.commons.beanutils2.locale.converters.ByteLocaleConverter; @@ -116,9 +115,7 @@ public static LocaleConvertUtilsBean getInstance() { * and then registers default locale converters. */ public LocaleConvertUtilsBean() { - mapConverters.setFast(false); deregister(); - mapConverters.setFast(true); } @@ -369,15 +366,10 @@ public void register(final LocaleConverter converter, final Class clazz, fina * Remove any registered {@link LocaleConverter}. */ public void deregister() { - final Map, LocaleConverter> defaultConverter = lookup(defaultLocale); - mapConverters.setFast(false); - mapConverters.clear(); mapConverters.put(defaultLocale, defaultConverter); - - mapConverters.setFast(true); } /** @@ -457,7 +449,6 @@ protected Map, LocaleConverter> lookup(final Locale locale) { protected Map, LocaleConverter> create(final Locale locale) { final DelegateFastHashMap converter = new DelegateFastHashMap(BeanUtils.createCache()); - converter.setFast(false); converter.put(BigDecimal.class, new BigDecimalLocaleConverter(locale, applyLocalized)); converter.put(BigInteger.class, new BigIntegerLocaleConverter(locale, applyLocalized)); @@ -490,8 +481,6 @@ protected Map, LocaleConverter> create(final Locale locale) { new SqlTimestampLocaleConverter(locale, "yyyy-MM-dd HH:mm:ss.S") ); - converter.setFast(true); - return converter; } @@ -560,10 +549,5 @@ public int size() { public Collection values() { return map.values(); } - public void setFast(final boolean fast) { - if (map instanceof WeakFastHashMap) { - ((WeakFastHashMap)map).setFast(fast); - } - } } } diff --git a/src/test/java/org/apache/commons/beanutils2/BeanUtilsTestCase.java b/src/test/java/org/apache/commons/beanutils2/BeanUtilsTestCase.java index 8513ea8fb..9688ed63d 100644 --- a/src/test/java/org/apache/commons/beanutils2/BeanUtilsTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/BeanUtilsTestCase.java @@ -838,8 +838,8 @@ public void testPopulateScalar() { */ public void testSetPropertyNullValues() throws Exception { - Object oldValue = null; - Object newValue = null; + Object oldValue; + Object newValue; // Scalar value into array oldValue = PropertyUtils.getSimpleProperty(bean, "stringArray"); diff --git a/src/test/java/org/apache/commons/beanutils2/ConvertUtilsTestCase.java b/src/test/java/org/apache/commons/beanutils2/ConvertUtilsTestCase.java index 4d107dd7f..fb6d9f9ab 100644 --- a/src/test/java/org/apache/commons/beanutils2/ConvertUtilsTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/ConvertUtilsTestCase.java @@ -86,7 +86,7 @@ public void tearDown() { */ public void testNegativeIntegerArray() { - Object value = null; + Object value; final int[] intArray = new int[0]; value = ConvertUtils.convert((String) null, intArray.getClass()); @@ -112,7 +112,7 @@ public void testNegativeIntegerArray() { */ public void testNegativeScalar() { - Object value = null; + Object value; value = ConvertUtils.convert("foo", Boolean.TYPE); assertTrue(value instanceof Boolean); @@ -188,7 +188,7 @@ public void testNegativeScalar() { */ public void testNegativeStringArray() { - Object value = null; + Object value; final String[] stringArray = new String[0]; value = ConvertUtils.convert((String) null, stringArray.getClass()); @@ -283,7 +283,7 @@ public void testPositiveArray() { */ public void testPositiveIntegerArray() { - Object value = null; + Object value; final int[] intArray = new int[0]; final int[] intArray1 = new int[] { 0 }; final int[] intArray2 = new int[] { 0, 10 }; @@ -318,7 +318,7 @@ public void testPositiveIntegerArray() { */ public void testPositiveScalar() { - Object value = null; + Object value; value = ConvertUtils.convert("true", Boolean.TYPE); assertTrue(value instanceof Boolean); @@ -446,7 +446,7 @@ public void testPositiveScalar() { assertTrue(value instanceof Short); assertEquals(((Short) value).shortValue(), (short) 123); - String input = null; + String input; input = "2002-03-17"; value = ConvertUtils.convert(input, Date.class); @@ -470,7 +470,7 @@ public void testPositiveScalar() { */ public void testPositiveStringArray() { - Object value = null; + Object value; final String[] stringArray = new String[0]; final String[] stringArray1 = new String[] { "abc" }; diff --git a/src/test/java/org/apache/commons/beanutils2/DynaBeanUtilsTestCase.java b/src/test/java/org/apache/commons/beanutils2/DynaBeanUtilsTestCase.java index f346618c8..9fe380f81 100644 --- a/src/test/java/org/apache/commons/beanutils2/DynaBeanUtilsTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/DynaBeanUtilsTestCase.java @@ -867,8 +867,8 @@ public void testPopulateScalar() { */ public void testSetPropertyNullValues() throws Exception { - Object oldValue = null; - Object newValue = null; + Object oldValue; + Object newValue; // Scalar value into array oldValue = PropertyUtils.getSimpleProperty(bean, "stringArray"); diff --git a/src/test/java/org/apache/commons/beanutils2/DynaRowSetTestCase.java b/src/test/java/org/apache/commons/beanutils2/DynaRowSetTestCase.java index 77608335a..b1e3a11c1 100644 --- a/src/test/java/org/apache/commons/beanutils2/DynaRowSetTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/DynaRowSetTestCase.java @@ -306,8 +306,8 @@ public void testInconsistentOracleDriver() throws Exception { final RowSetDynaClass inconsistentDynaClass = new RowSetDynaClass(resultSet); final DynaBean firstRow = inconsistentDynaClass.getRows().get(0); - Class expectedType = null; - DynaProperty property = null; + Class expectedType; + DynaProperty property; // Test Date property = firstRow.getDynaClass().getDynaProperty("dateproperty"); diff --git a/src/test/java/org/apache/commons/beanutils2/MethodUtilsTestCase.java b/src/test/java/org/apache/commons/beanutils2/MethodUtilsTestCase.java index bc690eee0..178c16166 100644 --- a/src/test/java/org/apache/commons/beanutils2/MethodUtilsTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/MethodUtilsTestCase.java @@ -269,7 +269,7 @@ public void testInvokeMethodPrimitiveDouble() throws Exception { public void testStaticInvokeMethod() throws Exception { - Object value = null; + Object value; int current = TestBean.currentCounter(); value = MethodUtils.invokeStaticMethod(TestBean.class, "currentCounter", new Object[0]); diff --git a/src/test/java/org/apache/commons/beanutils2/PropsFirstPropertyUtilsBean.java b/src/test/java/org/apache/commons/beanutils2/PropsFirstPropertyUtilsBean.java index 76284f0ae..3486ea151 100644 --- a/src/test/java/org/apache/commons/beanutils2/PropsFirstPropertyUtilsBean.java +++ b/src/test/java/org/apache/commons/beanutils2/PropsFirstPropertyUtilsBean.java @@ -29,7 +29,6 @@ public class PropsFirstPropertyUtilsBean extends PropertyUtilsBean { public PropsFirstPropertyUtilsBean() { - super(); } /** diff --git a/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTestCase.java b/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTestCase.java index 249385fc0..6417fb7ff 100644 --- a/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/converters/ArrayConverterTestCase.java @@ -18,6 +18,7 @@ import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Collections; import java.util.Locale; import junit.framework.TestCase; @@ -283,9 +284,7 @@ public void testStringArrayToNumber() { // Test Data final String[] array = new String[] {"10", " 11", "12 ", " 13 "}; final ArrayList list = new ArrayList<>(); - for (final String element : array) { - list.add(element); - } + Collections.addAll(list, array); // Expected results String msg = null; @@ -333,7 +332,7 @@ public void testTheMatrix() { // Test Date - create the Matrix!! // Following String uses two delimiter: // - comma (",") to separate individual numbers - // - semi-colon (";") to separate lists of numbers + // - semicolon (";") to separate lists of numbers final String matrixString = "11,12,13 ; 21,22,23 ; 31,32,33 ; 41,42,43"; final int[][] expected = new int[][] {new int[] {11, 12, 13}, new int[] {21, 22, 23}, @@ -350,7 +349,7 @@ public void testTheMatrix() { // Construct a "Matrix" Converter which converts arrays of integer arrays using // the first (int[]) Converter as the element Converter. - // N.B. Uses a semi-colon (i.e. ";") as the delimiter to separate the different sets of numbers. + // N.B. Uses a semicolon (i.e. ";") as the delimiter to separate the different sets of numbers. // Also the delimiter for the above array Converter needs to be added to this // array Converter's "allowed characters" final ArrayConverter matrixConverter = new ArrayConverter(int[][].class, arrayConverter); diff --git a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java index 40c2cbe2d..aa4bbfcea 100644 --- a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java +++ b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java @@ -311,8 +311,8 @@ public void testLocale() { * Test Converter with multiple patterns */ public void testMultiplePatterns() { - String testString = null; - Object expected = null; + String testString; + Object expected; // Create & Configure the Converter final String[] patterns = new String[] {"yyyy-MM-dd", "yyyy/MM/dd"}; diff --git a/src/test/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsTestCase.java b/src/test/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsTestCase.java index aba8f7d51..5d94f465f 100644 --- a/src/test/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsTestCase.java +++ b/src/test/java/org/apache/commons/beanutils2/locale/LocaleConvertUtilsTestCase.java @@ -95,7 +95,7 @@ public void fixmetestNegativeIntegerArray() { fail("Array conversions not implemented yet."); - Object value = null; + Object value; final int[] intArray = new int[0]; value = LocaleConvertUtils.convert((String) null, intArray.getClass()); @@ -231,7 +231,7 @@ public void fixmetestNegativeStringArray() { fail("Array conversions not implemented yet."); - Object value = null; + Object value; final String[] stringArray = new String[0]; value = LocaleConvertUtils.convert((String) null, stringArray.getClass()); @@ -329,7 +329,7 @@ public void fixmetestPositiveIntegerArray() { fail("Array conversions not implemented yet."); - Object value = null; + Object value; final int[] intArray = new int[0]; final int[] intArray1 = new int[]{0}; final int[] intArray2 = new int[]{0, 10}; @@ -362,7 +362,7 @@ public void fixmetestPositiveIntegerArray() { * Positive scalar conversion tests. */ public void testPositiveScalar() { - Object value = null; + Object value; /* fixme Boolean converters not implemented value = LocaleConvertUtils.convert("true", Boolean.TYPE); @@ -497,7 +497,7 @@ public void testPositiveScalar() { assertEquals(((Short) value).shortValue(), (short) 123); */ - String input = null; + String input; input = "2002-03-17"; value = LocaleConvertUtils.convert(input, Date.class); @@ -523,7 +523,7 @@ public void fixmetestPositiveStringArray() { fail("Array conversions not implemented yet."); - Object value = null; + Object value; final String[] stringArray = new String[0]; final String[] stringArray1 = new String[] {"abc"}; diff --git a/src/test/java/org/apache/commons/beanutils2/priv/PackageBean.java b/src/test/java/org/apache/commons/beanutils2/priv/PackageBean.java index ca3b3b8a0..f31d61095 100644 --- a/src/test/java/org/apache/commons/beanutils2/priv/PackageBean.java +++ b/src/test/java/org/apache/commons/beanutils2/priv/PackageBean.java @@ -37,8 +37,6 @@ class PackageBean { */ PackageBean() { - super(); - } diff --git a/src/test/java/org/apache/commons/beanutils2/priv/PrivateBean.java b/src/test/java/org/apache/commons/beanutils2/priv/PrivateBean.java index 8f5b6d61d..78c115b1e 100644 --- a/src/test/java/org/apache/commons/beanutils2/priv/PrivateBean.java +++ b/src/test/java/org/apache/commons/beanutils2/priv/PrivateBean.java @@ -38,8 +38,6 @@ class PrivateBean implements PrivateDirect { */ PrivateBean() { - super(); - } diff --git a/src/test/java/org/apache/commons/beanutils2/priv/PrivateBeanSubclass.java b/src/test/java/org/apache/commons/beanutils2/priv/PrivateBeanSubclass.java index e47bdb859..2568c97e8 100644 --- a/src/test/java/org/apache/commons/beanutils2/priv/PrivateBeanSubclass.java +++ b/src/test/java/org/apache/commons/beanutils2/priv/PrivateBeanSubclass.java @@ -32,8 +32,6 @@ class PrivateBeanSubclass extends PrivateBean { */ PrivateBeanSubclass() { - super(); - } diff --git a/src/test/java/org/apache/commons/beanutils2/priv/PublicSubBean.java b/src/test/java/org/apache/commons/beanutils2/priv/PublicSubBean.java index 25d25589d..a35ffcaf6 100644 --- a/src/test/java/org/apache/commons/beanutils2/priv/PublicSubBean.java +++ b/src/test/java/org/apache/commons/beanutils2/priv/PublicSubBean.java @@ -37,8 +37,6 @@ public class PublicSubBean extends PackageBean { */ public PublicSubBean() { - super(); - }