/*
   * (non-Javadoc)
   * @see org.springframework.data.convert.TypeInformationMapper#createAliasFor(org.springframework.data.util.TypeInformation)
   */
  public Object createAliasFor(TypeInformation<?> type) {

    CacheValue<Object> key = typeMap.get(type);

    if (key != null) {
      return key.getValue();
    }

    PersistentEntity<?, ?> entity = mappingContext.getPersistentEntity(type);

    if (entity == null) {
      return null;
    }

    Object alias = entity.getTypeAlias();
    safelyAddToCache(type.getRawTypeInformation(), alias);

    return alias;
  }
  /*
   * (non-Javadoc)
   * @see org.springframework.data.convert.TypeInformationMapper#resolveTypeFrom(java.lang.Object)
   */
  public ClassTypeInformation<?> resolveTypeFrom(Object alias) {

    if (alias == null) {
      return null;
    }

    for (Entry<ClassTypeInformation<?>, CacheValue<Object>> entry : typeMap.entrySet()) {

      CacheValue<Object> cachedAlias = entry.getValue();

      if (cachedAlias.hasValue(alias)) {
        return entry.getKey();
      }
    }

    for (PersistentEntity<?, ?> entity : mappingContext.getPersistentEntities()) {
      if (alias.equals(entity.getTypeAlias())) {
        return entity.getTypeInformation().getRawTypeInformation();
      }
    }

    return null;
  }
  /**
   * Adds the given alias to the cache in a {@literal null}-safe manner.
   *
   * @param key must not be {@literal null}.
   * @param alias can be {@literal null}.
   */
  private void safelyAddToCache(ClassTypeInformation<?> key, Object alias) {

    CacheValue<Object> aliasToBeCached = CacheValue.ofNullable(alias);

    if (alias == null && !typeMap.containsKey(key)) {
      typeMap.put(key, aliasToBeCached);
      return;
    }

    CacheValue<Object> alreadyCachedAlias = typeMap.get(key);

    // Reject second alias for same type

    if (alreadyCachedAlias != null
        && alreadyCachedAlias.isPresent()
        && !alreadyCachedAlias.hasValue(alias)) {
      throw new IllegalArgumentException(
          String.format(
              "Trying to register alias '%s', but found already registered alias '%s' for type %s!",
              alias, alreadyCachedAlias, key));
    }

    // Reject second type for same alias

    if (typeMap.containsValue(aliasToBeCached)) {

      for (Entry<ClassTypeInformation<?>, CacheValue<Object>> entry : typeMap.entrySet()) {

        CacheValue<Object> value = entry.getValue();

        if (!value.isPresent()) {
          continue;
        }

        if (value.hasValue(alias) && !entry.getKey().equals(key)) {
          throw new IllegalArgumentException(
              String.format(
                  "Detected existing type mapping of %s to alias '%s' but attempted to bind the same alias to %s!",
                  key, alias, entry.getKey()));
        }
      }
    }

    typeMap.put(key, aliasToBeCached);
  }