com.vaadin.flow.function.SerializableFunction Java Examples

The following examples show how to use com.vaadin.flow.function.SerializableFunction. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ResultTest.java    From flow with Apache License 2.0 6 votes vote down vote up
@SuppressWarnings("serial")
@Test
public void map_error_mapperIsApplied() {
    Result<String> result = new SimpleResult<String>("foo", null) {

        @Override
        public <S> Result<S> flatMap(
                SerializableFunction<String, Result<S>> mapper) {
            return new SimpleResult<>(null, "bar");
        }
    };
    Result<String> mapResult = result.map(value -> {
        Assert.assertEquals("foo", value);
        return "somevalue";
    });
    Assert.assertTrue(mapResult instanceof SimpleResult);
    Assert.assertTrue(mapResult.isError());
    mapResult.ifError(msg -> Assert.assertEquals("bar", msg));
}
 
Example #2
Source File: ResultTest.java    From flow with Apache License 2.0 6 votes vote down vote up
@SuppressWarnings("serial")
@Test
public void map_norError_mapperIsApplied() {
    Result<String> result = new SimpleResult<String>("foo", null) {

        @Override
        public <S> Result<S> flatMap(
                SerializableFunction<String, Result<S>> mapper) {
            return mapper.apply("foo");
        }
    };
    Result<String> mapResult = result.map(value -> {
        Assert.assertEquals("foo", value);
        return "bar";
    });
    Assert.assertTrue(mapResult instanceof SimpleResult);
    Assert.assertFalse(mapResult.isError());
    mapResult.ifOk(v -> Assert.assertEquals("bar", v));
}
 
Example #3
Source File: ReflectionCache.java    From flow with Apache License 2.0 6 votes vote down vote up
@SuppressWarnings({ "unchecked", "rawtypes" })
private static <C, T> SerializableFunction<Class<? extends C>, T> wrapValueProvider(
        SerializableFunction<Class<C>, T> valueProvider) {
    return type -> {
        Map<Class<?>, CurrentInstance> instances = CurrentInstance
                .getInstances();
        try {
            CurrentInstance.clearAll();

            /*
             * Raw cast to deal with weird generics of valueProvider which
             * in turn is there to deal with the fact that javac in some
             * cases cannot infer type parameters for Foo::new as a
             * Function<Class<? extends C>, T> even when Foo has a
             * constructor that takes Class<? extends Something>.
             */
            return (T) ((Function) valueProvider).apply(type);
        } finally {
            CurrentInstance.restoreInstances(instances);
        }
    };
}
 
Example #4
Source File: AbstractListDataView.java    From flow with Apache License 2.0 5 votes vote down vote up
private void addItemOnTarget(
        T item, T target, String targetItemNotFoundErrorMessage,
        SerializableFunction<Integer, Integer> insertItemsIndexProvider) {
    final ListDataProvider<T> dataProvider = getDataProvider();
    final Collection<T> backendItems = dataProvider.getItems();

    if (!(backendItems instanceof List)) {
        throw new IllegalArgumentException(
                String.format(COLLECTION_TYPE_ERROR_MESSAGE_PATTERN,
                        backendItems.getClass().getSimpleName()));
    }

    if (equals(item, target)) {
        return;
    }

    if (!contains(target)) {
        throw new IllegalArgumentException(targetItemNotFoundErrorMessage);
    }

    final List<T> itemList = (List<T>) backendItems;
    /*
     * If the item is already present in the data provider, then it
     * firstly removed from a data provider and secondly re-added into
     * the proper position towards to target item.
     */
    removeItemIfPresent(item, dataProvider);
    itemList.add(insertItemsIndexProvider
            .apply(getItemIndex(target)), item);
    dataProvider.refreshAll();
}
 
Example #5
Source File: HierarchicalDataProvider.java    From flow with Apache License 2.0 5 votes vote down vote up
@Override
@SuppressWarnings("serial")
default <C> HierarchicalDataProvider<T, C> withConvertedFilter(
        SerializableFunction<C, F> filterConverter) {
    return new HierarchicalFilterDataProviderWrapper<T, C, F>(this) {
        @Override
        protected F getFilter(Query<T, C> query) {
            return FilterUtils.convertFilter(filterConverter, query);
        }
    };
}
 
Example #6
Source File: SimpleResult.java    From flow with Apache License 2.0 5 votes vote down vote up
@Override
public <X extends Throwable> R getOrThrow(
        SerializableFunction<String, ? extends X> exceptionSupplier)
                throws X {
    Objects.requireNonNull(exceptionSupplier,
            "Exception supplier cannot be null");
    if (isError()) {
        throw exceptionSupplier.apply(message);
    } else {
        return value;
    }
}
 
Example #7
Source File: SimpleResult.java    From flow with Apache License 2.0 5 votes vote down vote up
@Override
@SuppressWarnings("unchecked")
public <S> Result<S> flatMap(SerializableFunction<R, Result<S>> mapper) {
    Objects.requireNonNull(mapper, "mapper cannot be null");

    if (isError()) {
        // Safe cast; valueless
        return (Result<S>) this;
    } else {
        return mapper.apply(value);
    }
}
 
Example #8
Source File: ValidationResultWrap.java    From flow with Apache License 2.0 5 votes vote down vote up
@Override
public <S> Result<S> flatMap(SerializableFunction<R, Result<S>> mapper) {
    Result<S> result = wrappedResult.flatMap(mapper);
    if (!(result instanceof ValidationResultWrap)) {
        return new ValidationResultWrap<S>(result, resultList);
    }

    List<ValidationResult> currentResults = new ArrayList<>(resultList);
    ValidationResultWrap<S> resultWrap = (ValidationResultWrap<S>) result;
    currentResults.addAll(resultWrap.getValidationResults());

    return new ValidationResultWrap<>(resultWrap.getWrappedResult(),
            currentResults);
}
 
Example #9
Source File: MultiselectComboBox.java    From multiselect-combo-box-flow with Apache License 2.0 4 votes vote down vote up
/**
 * {@inheritDoc}
 * <p>
 * MultiselectComboBox triggers filtering queries based on the strings users
 * type into the field. For this reason you need to provide the second
 * parameter, a function which converts the filter-string typed by the user
 * into filter-type used by your data provider. If your data provider
 * already supports String as the filter-type, it can be used without a
 * converter function via {@link #setDataProvider(DataProvider)}.
 * <p>
 * Using this method provides the same result as using a data provider
 * wrapped with
 * {@link DataProvider#withConvertedFilter(SerializableFunction)}.
 * <p>
 * Changing the multiselect combo box's data provider resets its current
 * value to {@code null}.
 */
@Override
public <C> void setDataProvider(DataProvider<T, C> dataProvider,
        SerializableFunction<String, C> filterConverter) {

    Objects.requireNonNull(dataProvider,
            "The data provider can not be null");
    Objects.requireNonNull(filterConverter,
            "filterConverter cannot be null");

    if (userProvidedFilter == UserProvidedFilter.UNDECIDED) {
        userProvidedFilter = UserProvidedFilter.YES;
    }

    if (dataCommunicator == null) {
        dataCommunicator = new MultiselectComboBoxDataCommunicator<>(dataGenerator,
                arrayUpdater, data -> getElement()
                        .callJsFunction("$connector.updateData", data),
                getElement().getNode());
    }

    scheduleRender();
    setValue(null);

    SerializableFunction<String, C> convertOrNull = filterText -> {
        if (filterText == null) {
            return null;
        }

        return filterConverter.apply(filterText);
    };

    SerializableConsumer<C> providerFilterSlot = dataCommunicator
            .setDataProvider(dataProvider, convertOrNull.apply(null));

    filterSlot = filter -> {
        if (!Objects.equals(filter, lastFilter)) {
            providerFilterSlot.accept(convertOrNull.apply(filter));
            lastFilter = filter;
        }
    };

    boolean shouldForceServerSideFiltering = userProvidedFilter == UserProvidedFilter.YES;

    dataProvider.addDataProviderListener(e -> {
        if (e instanceof DataChangeEvent.DataRefreshEvent) {
            dataCommunicator.refresh(
                    ((DataChangeEvent.DataRefreshEvent<T>) e).getItem());
        } else {
            refreshAllData(shouldForceServerSideFiltering);
        }
    });
    refreshAllData(shouldForceServerSideFiltering);

    userProvidedFilter = UserProvidedFilter.UNDECIDED;
}
 
Example #10
Source File: AbstractTestHasValueAndValidation.java    From flow with Apache License 2.0 4 votes vote down vote up
public AbstractTestHasValueAndValidation(T defaultValue,
        SerializableFunction<String, T> propertyToValue,
        SerializableFunction<T, String> valueToProperty) {
    super("value", defaultValue, String.class, propertyToValue,
            valueToProperty);
}
 
Example #11
Source File: TestTextField.java    From flow with Apache License 2.0 4 votes vote down vote up
public TestTextField() {
    super("", SerializableFunction.identity(),
            SerializableFunction.identity());
}
 
Example #12
Source File: AbstractHierarchicalDataProvider.java    From flow with Apache License 2.0 4 votes vote down vote up
@Override
public <C> HierarchicalDataProvider<T, C> withConvertedFilter(
        SerializableFunction<C, F> filterConverter) {
    return HierarchicalDataProvider.super.withConvertedFilter(
            filterConverter);
}
 
Example #13
Source File: HierarchicalFilterUtils.java    From flow with Apache License 2.0 4 votes vote down vote up
@Override
public <U> HierarchicalDataProvider<T, U> withConvertedFilter(
        SerializableFunction<U, F> filterConverter) {
    return HierarchicalDataProvider.super.withConvertedFilter(
            filterConverter);
}
 
Example #14
Source File: HierarchicalFilterUtils.java    From flow with Apache License 2.0 4 votes vote down vote up
@Override
public <U> HierarchicalDataProvider<T, U> withConvertedFilter(
        SerializableFunction<U, Q> filterConverter) {
    return HierarchicalConfigurableFilterDataProvider.super.withConvertedFilter(
            filterConverter);
}
 
Example #15
Source File: ValidationResultWrap.java    From flow with Apache License 2.0 4 votes vote down vote up
@Override
public <X extends Throwable> R getOrThrow(
        SerializableFunction<String, ? extends X> exceptionProvider)
        throws X {
    return wrappedResult.getOrThrow(exceptionProvider);
}
 
Example #16
Source File: NodeFeatureRegistry.java    From flow with Apache License 2.0 4 votes vote down vote up
private <T extends NodeFeature> NodeFeatureData(
        SerializableFunction<StateNode, T> factory, int id) {
    this.factory = factory;
    this.id = id;
    priority = nextNodePriority++;
}
 
Example #17
Source File: NodeFeatureRegistry.java    From flow with Apache License 2.0 4 votes vote down vote up
private static <T extends NodeFeature> void registerFeature(Class<T> type,
        SerializableFunction<StateNode, T> factory, int id) {
    NodeFeatureData featureData = new NodeFeatureData(factory, id);
    nodeFeatures.put(type, featureData);
    idToFeature.put(featureData.id, type);
}
 
Example #18
Source File: Binder.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Maps the binding to another data type using the mapping functions and
 * a possible exception as the error message.
 * <p>
 * The mapping functions are used to convert between a presentation
 * type, which must match the current target data type of the binding,
 * and a model type, which can be any data type and becomes the new
 * target type of the binding. When invoking
 * {@link #bind(ValueProvider, Setter)}, the target type of the binding
 * must match the getter/setter types.
 * <p>
 * For instance, a {@code TextField} can be bound to an integer-typed
 * property using appropriate functions such as:
 * <code>withConverter(Integer::valueOf, String::valueOf);</code>
 *
 * @param <NEWTARGET>
 *            the type to convert to
 * @param toModel
 *            the function which can convert from the old target type to
 *            the new target type
 * @param toPresentation
 *            the function which can convert from the new target type to
 *            the old target type
 * @return a new binding with the appropriate type
 * @throws IllegalStateException
 *             if {@code bind} has already been called
 */
default <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
        SerializableFunction<TARGET, NEWTARGET> toModel,
        SerializableFunction<NEWTARGET, TARGET> toPresentation) {
    return withConverter(Converter.from(toModel, toPresentation,
            Throwable::getMessage));
}
 
Example #19
Source File: MultiselectComboBox.java    From multiselect-combo-box-flow with Apache License 2.0 3 votes vote down vote up
/**
 * Sets a CallbackDataProvider using the given fetch items callback and a
 * size callback.
 * <p>
 * This method is a shorthand for making a {@link CallbackDataProvider} that
 * handles a partial Query object.
 * <p>
 * Changing the multiselect combo box's data provider resets its current
 * value to {@code null}.
 *
 * @param fetchItems
 *            a callback for fetching items
 * @param sizeCallback
 *            a callback for getting the count of items
 *
 * @see CallbackDataProvider
 * @see #setDataProvider(DataProvider)
 */
public void setDataProvider(FetchItemsCallback<T> fetchItems,
        SerializableFunction<String, Integer> sizeCallback) {
    userProvidedFilter = UserProvidedFilter.YES;
    setDataProvider(new CallbackDataProvider<>(
            q -> fetchItems.fetchItems(q.getFilter().orElse(""),
                    q.getOffset(), q.getLimit()),
            q -> sizeCallback.apply(q.getFilter().orElse(""))));
}
 
Example #20
Source File: TaskUpdateImports.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Create an instance of the updater given all configurable parameters.
 *
 * @param finder
 *            a reusable class finder
 * @param frontendDepScanner
 *            a reusable frontend dependencies scanner
 * @param fallBackScannerProvider
 *            fallback scanner provider, not {@code null}
 * @param npmFolder
 *            folder with the `package.json` file
 * @param generatedPath
 *            folder where flow generated files will be placed.
 * @param frontendDirectory
 *            a directory with project's frontend files
 * @param tokenFile
 *            the token (flow-build-info.json) path, may be {@code null}
 * @param disablePnpm
 *            if {@code true} then npm is used instead of pnpm, otherwise
 *            pnpm is used
 */
TaskUpdateImports(ClassFinder finder,
        FrontendDependenciesScanner frontendDepScanner,
        SerializableFunction<ClassFinder, FrontendDependenciesScanner> fallBackScannerProvider,
        File npmFolder, File generatedPath, File frontendDirectory,
        File tokenFile, boolean disablePnpm) {
    this(finder, frontendDepScanner, fallBackScannerProvider, npmFolder,
            generatedPath, frontendDirectory, tokenFile, null, disablePnpm);
}
 
Example #21
Source File: TaskUpdateImports.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Create an instance of the updater given all configurable parameters.
 *
 * @param finder
 *            a reusable class finder
 * @param frontendDepScanner
 *            a reusable frontend dependencies scanner
 * @param fallBackScannerProvider
 *            fallback scanner provider, not {@code null}
 * @param npmFolder
 *            folder with the `package.json` file
 * @param generatedPath
 *            folder where flow generated files will be placed.
 * @param frontendDirectory
 *            a directory with project's frontend files
 * @param tokenFile
 *            the token (flow-build-info.json) path, may be {@code null}
 * @param tokenFileData
 *            object to fill with token file data, may be {@code null}
 * @param disablePnpm
 *            if {@code true} then npm is used instead of pnpm, otherwise
 *            pnpm is used
 */
TaskUpdateImports(ClassFinder finder,
        FrontendDependenciesScanner frontendDepScanner,
        SerializableFunction<ClassFinder, FrontendDependenciesScanner> fallBackScannerProvider,
        File npmFolder, File generatedPath, File frontendDirectory,
        File tokenFile, JsonObject tokenFileData, boolean disablePnpm) {
    super(finder, frontendDepScanner, npmFolder, generatedPath, null);
    this.frontendDirectory = frontendDirectory;
    fallbackScanner = fallBackScannerProvider.apply(finder);
    this.tokenFile = tokenFile;
    this.tokenFileData = tokenFileData;
    this.disablePnpm = disablePnpm;
}
 
Example #22
Source File: DataProvider.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Wraps this data provider to create a data provider that uses a different
 * filter type. This can be used for adapting this data provider to a filter
 * type provided by a Component such as ComboBox.
 * <p>
 * For example receiving a String from ComboBox and making a Predicate based
 * on it:
 *
 * <pre>
 * DataProvider&lt;Person, Predicate&lt;Person&gt;&gt; dataProvider;
 * // ComboBox uses String as the filter type
 * DataProvider&lt;Person, String&gt; wrappedProvider = dataProvider
 *         .withConvertedFilter(filterText -&gt; {
 *             Predicate&lt;Person&gt; predicate = person -&gt; person.getName()
 *                     .startsWith(filterText);
 *             return predicate;
 *         });
 * comboBox.setDataProvider(wrappedProvider);
 * </pre>
 *
 * @param filterConverter
 *            callback that converts the filter in the query of the wrapped
 *            data provider into a filter supported by this data provider.
 *            Will only be called if the query contains a filter. Not
 *            <code>null</code>
 *
 * @param <C>
 *            the filter type that the wrapped data provider accepts;
 *            typically provided by a Component
 *
 * @return wrapped data provider, not <code>null</code>
 */
default <C> DataProvider<T, C> withConvertedFilter(
        SerializableFunction<C, F> filterConverter) {
    Objects.requireNonNull(filterConverter,
            "Filter converter can't be null");
    return new DataProviderWrapper<T, C, F>(this) {
        @Override
        protected F getFilter(Query<T, C> query) {
            return FilterUtils.convertFilter(filterConverter, query);
        }
    };
}
 
Example #23
Source File: ReflectionCache.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Creates a new reflection cache with the given value provider. The value
 * provider will be used to produce a new cached value whenever there is a
 * cache miss. It will be run in a context where no {@link CurrentInstance}
 * is available to prevent accidentally caching values that are computed
 * differently depending on external circumstances.
 *
 * @param valueProvider
 *            a function that computes the cached value for a class, not
 *            <code>null</code>
 */
public ReflectionCache(SerializableFunction<Class<C>, T> valueProvider) {
    if (valueProvider == null) {
        throw new IllegalArgumentException("value provider cannot be null");
    }
    this.valueProvider = wrapValueProvider(valueProvider);

    addClearAllAction(clearAction);
}
 
Example #24
Source File: HierarchicalCommunicationController.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Constructs communication controller with support for hierarchical data
 * structure.
 * 
 * @param parentKey
 *            parent key or null if root
 * @param keyMapper
 *            Object to String key mapper
 * @param mapper
 *            Mapper for hierarchical data
 * @param dataGenerator
 *            A data generator for items
 * @param startUpdate
 *            Function for creating a new {@link Update} for client
 * @param fetchItems
 *            Function for fetching items for target parent and specified
 *            range
 */
public HierarchicalCommunicationController(String parentKey,
        DataKeyMapper<T> keyMapper, HierarchyMapper<T, ?> mapper,
        DataGenerator<T> dataGenerator,
        SerializableFunction<Integer, HierarchicalUpdate> startUpdate,
        SerializableBiFunction<String, Range, Stream<T>> fetchItems) {
    this.parentKey = parentKey;
    this.keyMapper = keyMapper;
    this.mapper = mapper;
    this.dataGenerator = dataGenerator;
    this.startUpdate = startUpdate;
    this.fetchItems = fetchItems;
}
 
Example #25
Source File: AbstractSinglePropertyField.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Creates a new field that uses a property value with the given stateless
 * converters for producing a model value.
 * <p>
 * The property type must be one of the types that can be written as an
 * element property value: String, Integer, Double or Boolean.
 *
 * @param propertyName
 *            the name of the element property to use
 * @param defaultValue
 *            the default value to use if the property isn't defined
 * @param elementPropertyType
 *            the type of the element property
 * @param presentationToModel
 *            a function that converts a property value to a model value
 * @param modelToPresentation
 *            a function that converts a model value to a property value
 * @param <P>
 *            the property type
 */
public <P> AbstractSinglePropertyField(String propertyName, T defaultValue,
        Class<P> elementPropertyType,
        SerializableFunction<P, T> presentationToModel,
        SerializableFunction<T, P> modelToPresentation) {
    this(propertyName, defaultValue, elementPropertyType,
            (ignore, value) -> presentationToModel.apply(value),
            (ignore, value) -> modelToPresentation.apply(value));
}
 
Example #26
Source File: Converter.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Constructs a converter from two functions. Any {@code Exception}
 * instances thrown from the {@code toModel} function are converted into
 * error-bearing {@code Result} objects using the given {@code onError}
 * function.
 *
 * @param <P>
 *            the presentation type
 * @param <M>
 *            the model type
 * @param toModel
 *            the function to convert to model
 * @param toPresentation
 *            the function to convert to presentation
 * @param onError
 *            the function to provide error messages
 * @return the new converter
 *
 * @see Result
 * @see Function
 */
static <P, M> Converter<P, M> from(SerializableFunction<P, M> toModel,
        SerializableFunction<M, P> toPresentation,
        SerializableFunction<Exception, String> onError) {

    return from(val -> Result.of(() -> toModel.apply(val), onError),
            toPresentation);
}
 
Example #27
Source File: Result.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Returns a Result representing the result of invoking the given supplier.
 * If the supplier returns a value, returns a {@code Result.ok} of the
 * value; if an exception is thrown, returns the message in a
 * {@code Result.error}.
 *
 * @param <R>
 *            the result value type
 * @param supplier
 *            the supplier to run
 * @param onError
 *            the function to provide the error message
 * @return the result of invoking the supplier
 */
static <R> Result<R> of(SerializableSupplier<R> supplier,
                        SerializableFunction<Exception, String> onError) {
    Objects.requireNonNull(supplier, "supplier cannot be null");
    Objects.requireNonNull(onError, "onError cannot be null");

    try {
        return ok(supplier.get());
    } catch (Exception e) {
        return error(onError.apply(e));
    }
}
 
Example #28
Source File: IconRenderer.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Creates a new renderer instance using the provided {@code iconGenerator}
 * and {@code itemLabelGenerator}.
 *
 * @param iconGenerator
 *            the icon component generator
 * @param itemLabelGenerator
 *            the item label generator
 */
public IconRenderer(
        SerializableFunction<ITEM, ? extends Component> iconGenerator,
        ItemLabelGenerator<ITEM> itemLabelGenerator) {
    this.iconGenerator = iconGenerator;
    this.itemLabelGenerator = itemLabelGenerator;
}
 
Example #29
Source File: Binder.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Maps the binding to another data type using the mapping functions and
 * the given error error message if a value cannot be converted to the
 * new target type.
 * <p>
 * The mapping functions are used to convert between a presentation
 * type, which must match the current target data type of the binding,
 * and a model type, which can be any data type and becomes the new
 * target type of the binding. When invoking
 * {@link #bind(ValueProvider, Setter)}, the target type of the binding
 * must match the getter/setter types.
 * <p>
 * For instance, a {@code TextField} can be bound to an integer-typed
 * property using appropriate functions such as:
 * <code>withConverter(Integer::valueOf, String::valueOf);</code>
 *
 * @param <NEWTARGET>
 *            the type to convert to
 * @param toModel
 *            the function which can convert from the old target type to
 *            the new target type
 * @param toPresentation
 *            the function which can convert from the new target type to
 *            the old target type
 * @param errorMessage
 *            the error message to use if conversion using
 *            <code>toModel</code> fails
 * @return a new binding with the appropriate type
 * @throws IllegalStateException
 *             if {@code bind} has already been called
 */
default <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
        SerializableFunction<TARGET, NEWTARGET> toModel,
        SerializableFunction<NEWTARGET, TARGET> toPresentation,
        String errorMessage) {
    return withConverter(Converter.from(toModel, toPresentation,
            exception -> errorMessage));
}
 
Example #30
Source File: ComponentRenderer.java    From flow with Apache License 2.0 3 votes vote down vote up
/**
 * Creates a new ComponentRenderer that uses the componentFunction to
 * generate new {@link Component} instances, and a componentUpdateFunction
 * to update existing {@link Component} instances.
 * <p>
 * The componentUpdateFunction can return a different component than the one
 * previously created. In those cases, the new instance is used, and the old
 * is discarded.
 * <p>
 * Some components may support several rendered components at once, so
 * different component instances should be created for each different item
 * for those components.
 *
 * @param componentFunction
 *            a function that can generate new component instances
 * @param componentUpdateFunction
 *            a function that can update the existing component instance for
 *            the item, or generate a new component based on the item
 *            update. When the function is <code>null</code>, the
 *            componentFunction is always used instead
 */
public ComponentRenderer(
        SerializableFunction<SOURCE, COMPONENT> componentFunction,
        SerializableBiFunction<Component, SOURCE, Component> componentUpdateFunction) {
    this.componentFunction = componentFunction;
    this.componentUpdateFunction = componentUpdateFunction;
}