All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wl4g.infra.common.yaml.map.CollectionFactory Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.wl4g.infra.common.yaml.map;

import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.annotation.Nullable;

import com.wl4g.infra.common.collection.multimap.LinkedMultiValueMap;
import com.wl4g.infra.common.collection.multimap.MultiValueMap;
import com.wl4g.infra.common.lang.Assert2;
import com.wl4g.infra.common.reflect.ReflectionUtils2;

/**
 * Factory for collections that is aware of common Java and Spring collection
 * types.
 * 
 * @since Based on modifiy of {@link com.wl4g.infra.common.yaml.map.springframework.core.CollectionFactory}
 */
final class CollectionFactory {

    private static final Set> approximableCollectionTypes = new HashSet<>();

    private static final Set> approximableMapTypes = new HashSet<>();

    static {
        // Standard collection interfaces
        approximableCollectionTypes.add(Collection.class);
        approximableCollectionTypes.add(List.class);
        approximableCollectionTypes.add(Set.class);
        approximableCollectionTypes.add(SortedSet.class);
        approximableCollectionTypes.add(NavigableSet.class);
        approximableMapTypes.add(Map.class);
        approximableMapTypes.add(SortedMap.class);
        approximableMapTypes.add(NavigableMap.class);

        // Common concrete collection classes
        approximableCollectionTypes.add(ArrayList.class);
        approximableCollectionTypes.add(LinkedList.class);
        approximableCollectionTypes.add(HashSet.class);
        approximableCollectionTypes.add(LinkedHashSet.class);
        approximableCollectionTypes.add(TreeSet.class);
        approximableCollectionTypes.add(EnumSet.class);
        approximableMapTypes.add(HashMap.class);
        approximableMapTypes.add(LinkedHashMap.class);
        approximableMapTypes.add(TreeMap.class);
        approximableMapTypes.add(EnumMap.class);
    }

    private CollectionFactory() {
    }

    /**
     * Determine whether the given collection type is an approximable
     * type, i.e. a type that {@link #createApproximateCollection} can
     * approximate.
     * 
     * @param collectionType
     *            the collection type to check
     * @return {@code true} if the type is approximable
     */
    public static boolean isApproximableCollectionType(@Nullable Class collectionType) {
        return (collectionType != null && approximableCollectionTypes.contains(collectionType));
    }

    /**
     * Create the most approximate collection for the given collection.
     * 

* Warning: Since the parameterized type {@code E} is not * bound to the type of elements contained in the supplied * {@code collection}, type safety cannot be guaranteed if the supplied * {@code collection} is an {@link EnumSet}. In such scenarios, the caller * is responsible for ensuring that the element type for the supplied * {@code collection} is an enum type matching type {@code E}. As an * alternative, the caller may wish to treat the return value as a raw * collection or collection of {@link Object}. * * @param collection * the original collection object, potentially {@code null} * @param capacity * the initial capacity * @return a new, empty collection instance * @see #isApproximableCollectionType * @see java.util.LinkedList * @see java.util.ArrayList * @see java.util.EnumSet * @see java.util.TreeSet * @see java.util.LinkedHashSet */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static Collection createApproximateCollection(@Nullable Object collection, int capacity) { if (collection instanceof LinkedList) { return new LinkedList<>(); } else if (collection instanceof List) { return new ArrayList<>(capacity); } else if (collection instanceof EnumSet) { // Cast is necessary for compilation in Eclipse 4.4.1. Collection enumSet = (Collection) EnumSet.copyOf((EnumSet) collection); enumSet.clear(); return enumSet; } else if (collection instanceof SortedSet) { return new TreeSet<>(((SortedSet) collection).comparator()); } else { return new LinkedHashSet<>(capacity); } } /** * Create the most appropriate collection for the given collection type. *

* Delegates to {@link #createCollection(Class, Class, int)} with a * {@code null} element type. * * @param collectionType * the desired type of the target collection (never {@code null}) * @param capacity * the initial capacity * @return a new collection instance * @throws IllegalArgumentException * if the supplied {@code collectionType} is {@code null} or of * type {@link EnumSet} */ public static Collection createCollection(Class collectionType, int capacity) { return createCollection(collectionType, null, capacity); } /** * Create the most appropriate collection for the given collection type. *

* Warning: Since the parameterized type {@code E} is not * bound to the supplied {@code elementType}, type safety cannot be * guaranteed if the desired {@code collectionType} is {@link EnumSet}. In * such scenarios, the caller is responsible for ensuring that the supplied * {@code elementType} is an enum type matching type {@code E}. As an * alternative, the caller may wish to treat the return value as a raw * collection or collection of {@link Object}. * * @param collectionType * the desired type of the target collection (never {@code null}) * @param elementType * the collection's element type, or {@code null} if unknown * (note: only relevant for {@link EnumSet} creation) * @param capacity * the initial capacity * @return a new collection instance * @throws IllegalArgumentException * if the supplied {@code collectionType} is {@code null}; or if * the desired {@code collectionType} is {@link EnumSet} and the * supplied {@code elementType} is not a subtype of {@link Enum} * @since 4.1.3 * @see java.util.LinkedHashSet * @see java.util.ArrayList * @see java.util.TreeSet * @see java.util.EnumSet */ @SuppressWarnings({ "unchecked" }) public static Collection createCollection(Class collectionType, @Nullable Class elementType, int capacity) { Assert2.notNull(collectionType, "Collection type must not be null"); if (LinkedHashSet.class == collectionType || HashSet.class == collectionType || Set.class == collectionType || Collection.class == collectionType) { return new LinkedHashSet<>(capacity); } else if (ArrayList.class == collectionType || List.class == collectionType) { return new ArrayList<>(capacity); } else if (LinkedList.class == collectionType) { return new LinkedList<>(); } else if (SortedSet.class == collectionType || NavigableSet.class == collectionType) { return new TreeSet<>(); } else if (EnumSet.class.isAssignableFrom(collectionType)) { Assert2.notNull(elementType, "Cannot create EnumSet for unknown element type"); // Cast is necessary for compilation in Eclipse 4.4.1. return (Collection) EnumSet.noneOf(asEnumType(elementType)); } else { if (collectionType.isInterface() || !Collection.class.isAssignableFrom(collectionType)) { throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName()); } try { return (Collection) ReflectionUtils2.accessibleConstructor(collectionType).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException("Could not instantiate Collection type: " + collectionType.getName(), ex); } } } /** * Determine whether the given map type is an approximable type, * i.e. a type that {@link #createApproximateMap} can approximate. * * @param mapType * the map type to check * @return {@code true} if the type is approximable */ public static boolean isApproximableMapType(@Nullable Class mapType) { return (mapType != null && approximableMapTypes.contains(mapType)); } /** * Create the most approximate map for the given map. *

* Warning: Since the parameterized type {@code K} is not * bound to the type of keys contained in the supplied {@code map}, type * safety cannot be guaranteed if the supplied {@code map} is an * {@link EnumMap}. In such scenarios, the caller is responsible for * ensuring that the key type in the supplied {@code map} is an enum type * matching type {@code K}. As an alternative, the caller may wish to treat * the return value as a raw map or map keyed by {@link Object}. * * @param map * the original map object, potentially {@code null} * @param capacity * the initial capacity * @return a new, empty map instance * @see #isApproximableMapType * @see java.util.EnumMap * @see java.util.TreeMap * @see java.util.LinkedHashMap */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static Map createApproximateMap(@Nullable Object map, int capacity) { if (map instanceof EnumMap) { EnumMap enumMap = new EnumMap((EnumMap) map); enumMap.clear(); return enumMap; } else if (map instanceof SortedMap) { return new TreeMap<>(((SortedMap) map).comparator()); } else { return new LinkedHashMap<>(capacity); } } /** * Create the most appropriate map for the given map type. *

* Delegates to {@link #createMap(Class, Class, int)} with a {@code null} * key type. * * @param mapType * the desired type of the target map * @param capacity * the initial capacity * @return a new map instance * @throws IllegalArgumentException * if the supplied {@code mapType} is {@code null} or of type * {@link EnumMap} */ public static Map createMap(Class mapType, int capacity) { return createMap(mapType, null, capacity); } /** * Create the most appropriate map for the given map type. *

* Warning: Since the parameterized type {@code K} is not * bound to the supplied {@code keyType}, type safety cannot be guaranteed * if the desired {@code mapType} is {@link EnumMap}. In such scenarios, the * caller is responsible for ensuring that the {@code keyType} is an enum * type matching type {@code K}. As an alternative, the caller may wish to * treat the return value as a raw map or map keyed by {@link Object}. * Similarly, type safety cannot be enforced if the desired {@code mapType} * is {@link MultiValueMap}. * * @param mapType * the desired type of the target map (never {@code null}) * @param keyType * the map's key type, or {@code null} if unknown (note: only * relevant for {@link EnumMap} creation) * @param capacity * the initial capacity * @return a new map instance * @throws IllegalArgumentException * if the supplied {@code mapType} is {@code null}; or if the * desired {@code mapType} is {@link EnumMap} and the supplied * {@code keyType} is not a subtype of {@link Enum} * @since 4.1.3 * @see java.util.LinkedHashMap * @see java.util.TreeMap * @see org.springframework.util.LinkedMultiValueMap * @see java.util.EnumMap */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static Map createMap(Class mapType, @Nullable Class keyType, int capacity) { Assert2.notNull(mapType, "Map type must not be null"); if (mapType.isInterface()) { if (Map.class == mapType) { return new LinkedHashMap<>(capacity); } else if (SortedMap.class == mapType || NavigableMap.class == mapType) { return new TreeMap<>(); } else if (MultiValueMap.class == mapType) { return new LinkedMultiValueMap(); } else { throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName()); } } else if (EnumMap.class == mapType) { Assert2.notNull(keyType, "Cannot create EnumMap for unknown key type"); return new EnumMap(asEnumType(keyType)); } else { if (!Map.class.isAssignableFrom(mapType)) { throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName()); } try { return (Map) ReflectionUtils2.accessibleConstructor(mapType).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException("Could not instantiate Map type: " + mapType.getName(), ex); } } } /** * Create a variant of {@link java.util.Properties} that automatically * adapts non-String values to String representations in * {@link Properties#getProperty}. *

* In addition, the returned {@code Properties} instance sorts properties * alphanumerically based on their keys. * * @return a new {@code Properties} instance * @since 4.3.4 * @see #createSortedProperties(boolean) * @see #createSortedProperties(Properties, boolean) */ @SuppressWarnings("serial") public static Properties createStringAdaptingProperties() { return new SortedProperties(false) { @Override @Nullable public String getProperty(String key) { Object value = get(key); return (value != null ? value.toString() : null); } }; } /** * Create a variant of {@link java.util.Properties} that sorts properties * alphanumerically based on their keys. *

* This can be useful when storing the {@link Properties} instance in a * properties file, since it allows such files to be generated in a * repeatable manner with consistent ordering of properties. Comments in * generated properties files can also be optionally omitted. * * @param omitComments * {@code true} if comments should be omitted when storing * properties in a file * @return a new {@code Properties} instance * @since 5.2 * @see #createStringAdaptingProperties() * @see #createSortedProperties(Properties, boolean) */ public static Properties createSortedProperties(boolean omitComments) { return new SortedProperties(omitComments); } /** * Create a variant of {@link java.util.Properties} that sorts properties * alphanumerically based on their keys. *

* This can be useful when storing the {@code Properties} instance in a * properties file, since it allows such files to be generated in a * repeatable manner with consistent ordering of properties. Comments in * generated properties files can also be optionally omitted. *

* The returned {@code Properties} instance will be populated with * properties from the supplied {@code properties} object, but default * properties from the supplied {@code properties} object will not be * copied. * * @param properties * the {@code Properties} object from which to copy the initial * properties * @param omitComments * {@code true} if comments should be omitted when storing * properties in a file * @return a new {@code Properties} instance * @since 5.2 * @see #createStringAdaptingProperties() * @see #createSortedProperties(boolean) */ public static Properties createSortedProperties(Properties properties, boolean omitComments) { return new SortedProperties(properties, omitComments); } /** * Cast the given type to a subtype of {@link Enum}. * * @param enumType * the enum type, never {@code null} * @return the given type as subtype of {@link Enum} * @throws IllegalArgumentException * if the given type is not a subtype of {@link Enum} */ @SuppressWarnings("rawtypes") private static Class asEnumType(Class enumType) { Assert2.notNull(enumType, "Enum type must not be null"); if (!Enum.class.isAssignableFrom(enumType)) { throw new IllegalArgumentException("Supplied type is not an enum: " + enumType.getName()); } return enumType.asSubclass(Enum.class); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy