/**
  * Convert a Map to a Map with keys and values of the desired types. If it is a Map and it is of
  * the same Map, Key and Value types it is returned as is, otherwise a new Map is created with all
  * keys and values converted to the desired type. If it is a String representation of a Map it is
  * converted to a Map.
  *
  * @param <K> The key type.
  * @param <V> The value type.
  * @param <M> The Map type.
  * @param obj The Object to be converted.
  * @param mapType The desired Map type.
  * @param keyType The desired key type.
  * @param valueType The desired value type.
  * @param emptyIfNull Whether or not an empty Map should be returned if the Map could not be
  *     converted.
  * @return The converted Collection.
  */
 public <K, V, M extends Map<K, V>> M convertToMap(
     Object obj, Class<M> mapType, Class<K> keyType, Class<V> valueType, boolean emptyIfNull) {
   if (obj != null) {
     if (obj instanceof Map) {
       // If it's already a collection
       Map map = (Map) obj;
       Pair<Class<?>, Class<?>> cachedType = getCachedKeyValueTypes(map);
       // Look up the cached types if available, if they can be downcast to the
       // requested types do so and update cache
       if (cachedType != null
           && keyType.isAssignableFrom(cachedType.getKey())
           && valueType.isAssignableFrom(cachedType.getValue())) {
         setCacheKeyValueTypes(map, keyType, valueType);
         return (M) map;
       } else {
         return _convertContents(map, mapType, keyType, valueType);
       }
     } else {
       try {
         // If it's a string representation of key-value pairs
         M m = MapUtils.of(mapType);
         String string = convert(obj, String.class);
         if (string.startsWith("{") && string.endsWith("}")) {
           string = string.substring(1, string.length() - 1);
         }
         String[] entries = TextUtils.splitOnSpacesAndCommas(string);
         for (String str : entries) {
           String[] split = str.split("=");
           if (split.length == 2) {
             try {
               K k = convert(split[0], keyType);
               V v = convert(split[1], valueType);
               m.put(k, v);
             } catch (ConversionException ex) {
             }
           }
         }
         if (!m.isEmpty()) {
           return m;
         }
       } catch (ConversionException ex) {
       }
     }
   }
   if (emptyIfNull) {
     return MapUtils.of(mapType);
   }
   return null;
 }
 private <V, M extends Map<Object, V>> M _convertValues(
     Map<?, ?> map, Class<M> mapClass, Class<V> valueClass) {
   M newMap = (M) MapUtils.of(mapClass);
   for (Entry entry : map.entrySet()) {
     try {
       V v = convert(entry.getValue(), valueClass);
       newMap.put(entry.getKey(), v);
     } catch (ConversionException ex) {
     }
   }
   return newMap;
 }
 private <K, V, M extends Map<K, V>> M _convertContents(
     Map<?, ?> map, Class<M> mapClass, Class<K> keyClass, Class<V> valueClass) {
   M newMap = (M) MapUtils.of(mapClass);
   for (Entry entry : map.entrySet()) {
     try {
       K k = convert(entry.getKey(), keyClass);
       V v = convert(entry.getValue(), valueClass);
       newMap.put(k, v);
     } catch (ConversionException ex) {
     }
   }
   setCacheKeyValueTypes(map, keyClass, valueClass);
   return newMap;
 }