001package arez.persist.runtime;
002
003import arez.Arez;
004import arez.SafeProcedure;
005import java.util.Map;
006import java.util.Objects;
007import javax.annotation.Nonnull;
008import javax.annotation.Nullable;
009
010/**
011 * Interface implemented to provide the state storage and retrieval services.
012 */
013public interface StorageService
014{
015  /**
016   * Dispose the service.
017   * The service may release any resources no longer required after dispose has been
018   * called. It is expected that no other methods will be invoked after this method
019   * has been invoked.
020   */
021  void dispose();
022
023  /**
024   * Method invoked when state has changed.
025   * The storage service is expected to invoke the {@code commitTriggerAction.call()}
026   * method when service wants to commit changes to the service. This is likely to be
027   * invoked many times in quick succession and it is up to the storage service to decide
028   * how to batch up the changes and thus when to schedule the commit.
029   *
030   * @param commitTriggerAction the action to invoke when a commit should be triggered.
031   */
032  void scheduleCommit( @Nonnull SafeProcedure commitTriggerAction );
033
034  /**
035   * Invoked by the store to save the state to the storage service.
036   * The map contains scopes, mapped to component types, mapped to component ids, map
037   * to instance data for that component.
038   *
039   * @param state the state to store.
040   */
041  void commit( @Nonnull final Map<Scope, Map<String, Map<String, Entry>>> state );
042
043  /**
044   * Restore state from the service into the specified state parameter.
045   *
046   * @param state the map in which to store data retrieved from the service.
047   */
048  void restore( @Nonnull final Map<Scope, Map<String, Map<String, Entry>>> state );
049
050  /**
051   * Encode state in a way that makes storage to the backend easier.
052   * This is invoked when the state change occurs.
053   *
054   * @param state     the component state.
055   * @param converter the converter for the type.
056   * @return the encoded form for this service.
057   */
058  @Nonnull
059  Object encodeState( @Nonnull Map<String, Object> state, @Nonnull final TypeConverter converter );
060
061  /**
062   * Decode state from backend storage form.
063   * This occurs on access to state.
064   *
065   * @param encoded   the encoded component state.
066   * @param converter the converter for the type.
067   * @return the decoded form of component state.
068   */
069  @Nonnull
070  Map<String, Object> decodeState( @Nonnull Object encoded, @Nonnull final TypeConverter converter );
071
072  /**
073   * An entry containing the state for a particular component.
074   */
075  final class Entry
076  {
077    @Nullable
078    private Map<String, Object> _data;
079    @Nonnull
080    private final Object _encoded;
081
082    public Entry( @Nullable final Map<String, Object> data, @Nonnull final Object encoded )
083    {
084      _data = data;
085      _encoded = Objects.requireNonNull( encoded );
086    }
087
088    /**
089     * Return the decoded data.
090     *
091     * @return the decoded data.
092     */
093    @Nullable
094    public Map<String, Object> getData()
095    {
096      return _data;
097    }
098
099    void setData( @Nullable final Map<String, Object> data )
100    {
101      _data = data;
102    }
103
104    /**
105     * Return the encoded data.
106     *
107     * @return the encoded data.
108     */
109    @Nonnull
110    public Object getEncoded()
111    {
112      return _encoded;
113    }
114
115    @Override
116    public String toString()
117    {
118      if ( Arez.areNamesEnabled() )
119      {
120        return String.valueOf( _data );
121      }
122      else
123      {
124        return super.toString();
125      }
126    }
127  }
128}