001package arez.component;
002
003import arez.Arez;
004import arez.Locator;
005import grim.annotations.OmitType;
006import java.util.HashMap;
007import java.util.Map;
008import java.util.function.Function;
009import javax.annotation.Nonnull;
010import javax.annotation.Nullable;
011import static org.realityforge.braincheck.Guards.*;
012
013/**
014 * An Locator implementation where you can register a function-per type to be resolved.
015 */
016@OmitType( unless = "arez.enable_references" )
017public final class TypeBasedLocator
018  implements Locator
019{
020  /**
021   * Factory methods for looking entities up by type.
022   */
023  @Nonnull
024  private final Map<Class<?>, Function<Object, ?>> _findByIdFunctions = new HashMap<>();
025
026  /**
027   * Register a function that will find entities of specified type by id.
028   * This must not be invoked if another function has already been registered for type.
029   *
030   * @param <T>              the type of the entity.
031   * @param type             the type of the entity.
032   * @param findByIdFunction the function that looks up the entity by id.
033   */
034  public <T> void registerLookup( @Nonnull final Class<T> type, @Nonnull final Function<Object, T> findByIdFunction )
035  {
036    if ( Arez.shouldCheckApiInvariants() )
037    {
038      apiInvariant( () -> !_findByIdFunctions.containsKey( type ),
039                    () -> "Arez-0188: Attempting to register lookup function for type " + type +
040                          " when a function for type already exists." );
041    }
042    _findByIdFunctions.put( type, findByIdFunction );
043  }
044
045  @Nullable
046  @Override
047  @SuppressWarnings( "unchecked" )
048  public <T> T findById( @Nonnull final Class<T> type, @Nonnull final Object id )
049  {
050    final Function<Object, ?> function = _findByIdFunctions.get( type );
051    if ( null != function )
052    {
053      return (T) function.apply( id );
054    }
055    else
056    {
057      return null;
058    }
059  }
060}