/**
  * 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;
 }
 /**
  * Initialize this PropertyHolder, loading and verifying it's properties. A PropertyHolder that
  * has not been initialized or whose initialization failed will throw an exception when accessed.
  * This method should be called only once, typically by the PropertyHolderRegistry that handles
  * PropertyHolders of this type before registration.
  *
  * @throws InitializationException if the PropertyHolder could not be initialized.
  */
 public final void initialize() throws InitializationException {
   if (!isInitialized()) {
     try {
       verifyProperties();
       _initialize();
       initialized = true;
     } catch (PropertyValidationException ex) {
       throw new InitializationException(ex);
     }
   } else {
     throw new IllegalStateException(
         "Called initialize() on "
             + TextUtils.getTypeDescription(this)
             + " while already initialized.");
   }
 }
  /**
   * Convert an Object to a Collection with elements of the desired type. If it is a Collection and
   * it is of the same collection and element type it is returned as is, otherwise a new Collection
   * is created with all elements converted to the desired type. If it is a String representation of
   * a Collection it is converted to a Collection.
   *
   * @param <E> The element type.
   * @param <C> The Collection type.
   * @param obj The Object to be converted.
   * @param collectionType The desired Collection type.
   * @param elementType The desired element type.
   * @param emptyIfNull Whether or not an empty Collection should be returned if the Collection
   *     could not be converted.
   * @return The converted Collection.
   */
  public <E, C extends Collection<E>> C convertToCollection(
      Object obj, Class<C> collectionType, Class<E> elementType, boolean emptyIfNull) {
    // Grab defaults for types if interfaces or non-datareference collections are provided
    Class<? extends C> constructionCollectionType;
    Class<? extends E> constructionElementType;
    if (collectionType.equals(List.class)) {
      if (DataReferencable.class.isAssignableFrom(elementType)) {
        constructionCollectionType = (Class) DataReferenceList.class;
        constructionElementType = (Class) DataReference.class;
      } else {
        constructionCollectionType = (Class) ArrayList.class;
      }
    } else if (collectionType.equals(Set.class)) {
      if (DataReferencable.class.isAssignableFrom(elementType)) {
        constructionElementType = (Class) DataReference.class;
        throw new IllegalArgumentException(
            "Set class is not supported for DataReferencable elements");
      }
      constructionCollectionType = (Class) HashSet.class;
    } else {
      constructionCollectionType = collectionType;
    }

    if (obj != null) {
      if (obj instanceof Collection) {
        // If it's already a collection
        Collection collection = (Collection) obj;
        Class cachedElementType = getCachedElementType(collection);
        // If we have cached it's element type return if same
        if (cachedElementType != null
            && elementType.isAssignableFrom(cachedElementType)
            && collection.getClass().equals(constructionCollectionType)) {
          setCachedElementType(collection, elementType);
          return (C) collection;
        } else {
          return _convertContents(collection, constructionCollectionType, elementType);
        }
      } else {
        try {
          // If it's a single value convertable to E
          E e = convert(obj, elementType);
          C c = CollectionUtils.instanceOf(constructionCollectionType);
          c.add(e);
          return c;
        } catch (ConversionException ex) {
        }
        try {
          // If it's a string representation of one or many E's
          String string = convert(obj, String.class);
          String[] split = TextUtils.splitOnSpacesAndCommas(string);
          C c = CollectionUtils.instanceOf(constructionCollectionType);
          for (String str : split) {
            try {
              E e = convert(obj, elementType);
              c.add(e);
            } catch (ConversionException ex) {

            }
          }
          if (!c.isEmpty()) {
            return c;
          }
        } catch (ConversionException ex) {

        }
      }
    }

    // Fall through to here if a collection could not be converted for any reason
    if (emptyIfNull) {
      return CollectionUtils.instanceOf(constructionCollectionType);
    } else {
      return null;
    }
  }