All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spotify.mobius.extras.Connectables Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
/*
 * -\-\-
 * Mobius
 * --
 * Copyright (c) 2017-2018 Spotify AB
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * -/-/-
 */
package com.spotify.mobius.extras;

import com.spotify.mobius.Connectable;
import com.spotify.mobius.Connection;
import com.spotify.mobius.ConnectionLimitExceededException;
import com.spotify.mobius.functions.Consumer;
import com.spotify.mobius.functions.Function;
import javax.annotation.Nonnull;

/** Contains utility functions for working with {@link Connectables}. */
public final class Connectables {

  private Connectables() {
    // prevent instantiation
  }

  /**
   * Convert a {@code Connectable} to a {@code Connectable} by applying the supplied
   * function from J to I for each J received, before passing it on to a {@code Connection}
   * received from the underlying {@code Connectable}. This makes {@link Connectable} a contravariant
   * functor in functional programming terms.
   *
   * 

The returned {@link Connectable} doesn't enforce a connection limit, but of course the * connection limit of the wrapped {@link Connectable} applies. * *

This is useful for instance if you want your UI to use a subset or a transformed version of * the full model used in the {@code MobiusLoop}. As a simplified example, suppose that your model * consists of a {@code Long} timestamp that you want to format to a {@code String} before * rendering it in the UI. Your UI could then implement {@code Connectable}, and * you could create a {@code Function} that does the formatting. The {@link * com.spotify.mobius.MobiusLoop} would be outputting {@code Long} models that you need to convert * to Strings before they can be accepted by the UI. * *

   * public class Formatter {
   *    public static String format(Long timestamp) { ... }
   * }
   *
   * public class MyUi implements Connectable {
   *    // other things in the UI implementation
   *
   *   {@literal @}Override
   *    public Connection connect(Consumer output) {
   *       return new Connection() {
   *        {@literal @}Override
   *         public void accept(String value) {
   *           // bind the value to the right UI element
   *         }
   *
   *        {@literal @}Override
   *         public void dispose() {
   *           // dispose of any resources, if needed
   *         }
   *       }
   *    }
   * }
   *
   * // Then, to connect the UI to a MobiusLoop.Controller with a Long model:
   * MobiusLoop.Controller controller = ... ;
   * MyUi myUi = ... ;
   *
   * controller.connect(Connectables.contramap(Formatter::format, myUi));
   * 
* * @param mapper the mapping function to apply * @param connectable the underlying connectable * @param the type the underlying connectable accepts * @param the type the resulting connectable accepts * @param the output type; usually the event type */ @Nonnull public static Connectable contramap( final Function mapper, final Connectable connectable) { return new Connectable() { @Nonnull @Override public Connection connect(Consumer output) throws ConnectionLimitExceededException { final Connection delegateConnection = connectable.connect(output); return new Connection() { @Override public void accept(J value) { delegateConnection.accept(mapper.apply(value)); } @Override public void dispose() { delegateConnection.dispose(); } }; } }; } }