From bf803c725be7394384e1d6bc5c0732195e7819c0 Mon Sep 17 00:00:00 2001
From: Paul Gregoire <mondain@gmail.com>
Date: Wed, 3 Jan 2024 08:27:27 -0800
Subject: [PATCH] Fix issue with conversion of ImmutableCollections type

---
 .../org/red5/io/utils/ConversionUtils.java    | 65 +++++++++++--------
 .../red5/io/utils/ConversionUtilsTest.java    |  5 +-
 2 files changed, 38 insertions(+), 32 deletions(-)

diff --git a/io/src/main/java/org/red5/io/utils/ConversionUtils.java b/io/src/main/java/org/red5/io/utils/ConversionUtils.java
index 6970568d..babaa2e2 100644
--- a/io/src/main/java/org/red5/io/utils/ConversionUtils.java
+++ b/io/src/main/java/org/red5/io/utils/ConversionUtils.java
@@ -18,6 +18,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.commons.beanutils.BeanMap;
 import org.apache.commons.beanutils.BeanUtils;
@@ -107,13 +108,14 @@ public class ConversionUtils {
             // Don't convert NaN values
             return source;
         }
-        if (target.isInstance(source)) {
-            return source;
-        }
-        if (target.isAssignableFrom(source.getClass())) {
+        final Class<?> sourceClass = source.getClass();
+        log.info("Source: {} target: {}", sourceClass, target);
+        if (target.isInstance(source) || target.isAssignableFrom(sourceClass)) {
+            log.info("Source: {} is already an instance of: {}", source, target);
             return source;
         }
         if (target.isArray()) {
+            log.info("Source: {} to target array: {}", source, target);
             return convertToArray(source, target);
         }
         if (target.equals(String.class)) {
@@ -128,20 +130,37 @@ public class ConversionUtils {
         if (target.equals(Map.class)) {
             return convertBeanToMap(source);
         }
-        if (target.equals(List.class) || target.equals(Collection.class)) {
-            if (source.getClass().equals(LinkedHashMap.class)) {
-                return convertMapToList((LinkedHashMap<?, ?>) source);
-            } else if (source.getClass().isArray()) {
-                return convertArrayToList((Object[]) source);
+        if (sourceClass.equals(LinkedHashMap.class)) {
+            return convertMapToList((LinkedHashMap<?, ?>) source);
+        } else if (sourceClass.isArray()) {
+            if (List.class.isAssignableFrom(target)) {
+                log.info("Source: {} to target list: {}", source, target);
+                return Arrays.stream((Object[]) source).collect(Collectors.toCollection(ArrayList::new));
+            } else if (Set.class.isAssignableFrom(target)) {
+                log.info("Source: {} to target set: {}", source, target);
+                // special handling for sets when the source is a list
+                if (source instanceof List) {
+                    return ((List<?>) source).stream().collect(Collectors.toCollection(HashSet::new));
+                }
+                return Arrays.stream((Object[]) source).collect(Collectors.toCollection(HashSet::new));
             }
         }
-        if (target.equals(Set.class) && source.getClass().isArray()) {
-            return convertArrayToSet((Object[]) source);
-        }
-        if (target.equals(Set.class) && source instanceof List) {
-            return new HashSet((List) source);
+        if (Map.class.isAssignableFrom(sourceClass)) {
+            return convertMapToBean((Map) source, target);
         }
-        if (source instanceof Map) {
+        // handle immutable collections
+        final String sourceClassName = sourceClass.getName();
+        if (sourceClassName.equals("java.util.ImmutableCollections$ListN")) {
+            if (Set.class.isAssignableFrom(target)) {
+                return ((List<?>) source).stream().collect(Collectors.toCollection(HashSet::new));
+            }
+            return ((List<?>) source).stream().collect(Collectors.toCollection(ArrayList::new));
+        } else if (sourceClassName.equals("java.util.ImmutableCollections$SetN")) {
+            if (Set.class.isAssignableFrom(target)) {
+                return ((Set<?>) source).stream().collect(Collectors.toCollection(HashSet::new));
+            }
+            return ((Set<?>) source).stream().collect(Collectors.toCollection(ArrayList::new));
+        } else if (sourceClassName.equals("java.util.ImmutableCollections$MapN")) {
             return convertMapToBean((Map) source, target);
         }
         throw new ConversionException(String.format("Unable to preform conversion from %s to %s", source, target));
@@ -185,9 +204,7 @@ public class ConversionUtils {
     }
 
     public static List<Object> convertMapToList(Map<?, ?> map) {
-        List<Object> list = new ArrayList<Object>(map.size());
-        list.addAll(map.values());
-        return list;
+        return List.of(map.values());
     }
 
     /**
@@ -333,11 +350,7 @@ public class ConversionUtils {
      *             on failure
      */
     public static List<?> convertArrayToList(Object[] source) throws ConversionException {
-        List<Object> list = new ArrayList<Object>(source.length);
-        for (Object element : source) {
-            list.add(element);
-        }
-        return list;
+        return Arrays.stream(source).collect(Collectors.toCollection(ArrayList::new));
     }
 
     /**
@@ -387,11 +400,7 @@ public class ConversionUtils {
      * @return Set
      */
     public static Set<?> convertArrayToSet(Object[] source) {
-        Set<Object> set = new HashSet<Object>();
-        for (Object element : source) {
-            set.add(element);
-        }
-        return set;
+        return Arrays.stream(source).collect(Collectors.toCollection(HashSet::new));
     }
 
     /**
diff --git a/server/src/test/java/org/red5/io/utils/ConversionUtilsTest.java b/server/src/test/java/org/red5/io/utils/ConversionUtilsTest.java
index 7c3674dd..77ebd317 100644
--- a/server/src/test/java/org/red5/io/utils/ConversionUtilsTest.java
+++ b/server/src/test/java/org/red5/io/utils/ConversionUtilsTest.java
@@ -122,10 +122,7 @@ public class ConversionUtilsTest {
 
     @Test
     public void testConvertArrayListToSet() {
-        List<String> source = new ArrayList<String>(3);
-        source.add("a");
-        source.add("b");
-        source.add("c");
+        List<String> source = List.of("a", "b", "c");
         Object result = ConversionUtils.convert(source, Set.class);
         if (!(result instanceof Set<?>)) {
             fail("Should be a set");
-- 
GitLab