ConcurrentCollectionDetector.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.jdt.internal.corext.fix.helper;
import java.util.Set;
import org.eclipse.jdt.core.dom.ITypeBinding;
/**
* Detects concurrent collection types that require special handling during
* loop-to-stream conversions.
*
* <p>Concurrent collections like {@link java.util.concurrent.CopyOnWriteArrayList}
* and {@link java.util.concurrent.ConcurrentHashMap} have different iteration
* semantics than standard collections:</p>
*
* <ul>
* <li><b>CopyOnWrite collections:</b> Iterators provide a snapshot of the collection
* at the time the iterator was created. Modifications during iteration do not throw
* {@code ConcurrentModificationException} but are not visible to the iterator.</li>
*
* <li><b>Concurrent collections:</b> Weakly consistent iterators that may reflect
* modifications made after iterator creation, but never throw {@code ConcurrentModificationException}.</li>
*
* <li><b>iterator.remove() not supported:</b> Many concurrent collections do not support
* {@code iterator.remove()}, and attempting to call it will throw
* {@code UnsupportedOperationException}.</li>
* </ul>
*
* <p><b>Safety Rules for Concurrent Collections:</b></p>
* <ul>
* <li>Never generate {@code iterator.remove()} for concurrent collections</li>
* <li>Be aware that modifications may not be visible during iteration</li>
* <li>Consider the threading context (field vs local variable)</li>
* </ul>
*
* @see <a href="https://github.com/carstenartur/sandbox/issues/670">Issue #670 - Point 2.4</a>
* @since 1.0.0
*/
public final class ConcurrentCollectionDetector {
/**
* Fully qualified names of concurrent collection types.
* These collections have special iteration semantics and do not support iterator.remove().
*/
private static final Set<String> CONCURRENT_COLLECTION_TYPES = Set.of(
"java.util.concurrent.CopyOnWriteArrayList", //$NON-NLS-1$
"java.util.concurrent.CopyOnWriteArraySet", //$NON-NLS-1$
"java.util.concurrent.ConcurrentHashMap", //$NON-NLS-1$
"java.util.concurrent.ConcurrentSkipListMap", //$NON-NLS-1$
"java.util.concurrent.ConcurrentSkipListSet", //$NON-NLS-1$
"java.util.concurrent.ConcurrentLinkedQueue", //$NON-NLS-1$
"java.util.concurrent.ConcurrentLinkedDeque", //$NON-NLS-1$
"java.util.concurrent.LinkedBlockingQueue", //$NON-NLS-1$
"java.util.concurrent.LinkedBlockingDeque", //$NON-NLS-1$
"java.util.concurrent.ArrayBlockingQueue", //$NON-NLS-1$
"java.util.concurrent.PriorityBlockingQueue", //$NON-NLS-1$
"java.util.concurrent.DelayQueue", //$NON-NLS-1$
"java.util.concurrent.SynchronousQueue" //$NON-NLS-1$
);
private ConcurrentCollectionDetector() {
// utility class
}
/**
* Checks if the given type is a concurrent collection.
*
* @param typeBinding the type to check (may be null)
* @return {@code true} if the type is a concurrent collection
*/
public static boolean isConcurrentCollection(ITypeBinding typeBinding) {
if (typeBinding == null) {
return false;
}
// Check the erasure (raw type) to handle generics
String qualifiedName = typeBinding.getErasure().getQualifiedName();
return CONCURRENT_COLLECTION_TYPES.contains(qualifiedName);
}
/**
* Checks if the given qualified type name is a concurrent collection.
*
* @param qualifiedTypeName the fully qualified type name (may be null)
* @return {@code true} if the type name matches a concurrent collection
*/
public static boolean isConcurrentCollection(String qualifiedTypeName) {
if (qualifiedTypeName == null) {
return false;
}
return CONCURRENT_COLLECTION_TYPES.contains(qualifiedTypeName);
}
}