package fr.openwide.core.wicket.more.link.descriptor.mapper;
import java.util.Iterator;
import org.apache.wicket.model.IModel;
import org.javatuples.Pair;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import fr.openwide.core.commons.util.functional.SerializableFunction;
import fr.openwide.core.wicket.more.link.descriptor.generator.IImageResourceLinkGenerator;
import fr.openwide.core.wicket.more.link.descriptor.generator.ILinkGenerator;
import fr.openwide.core.wicket.more.link.descriptor.generator.IPageLinkGenerator;
import fr.openwide.core.wicket.more.link.util.LinkDescriptors;
import fr.openwide.core.wicket.more.model.SafeCastModel;
/**
* A {@link IOneParameterLinkDescriptorMapper} that will delegate to other link descriptor mappers.
* <p>This may be useful when you have a model of a general, abstract type, and want to use a different link depending
* on the actual type of the model object.
* <p>This mappers' delegates are called "the chain". Upon mapping, each element of the chain will be given a
* {@link SafeCastModel casted} model, and will return a {@link ILinkGenerator} (which may or may not be
* {@link ILinkGenerator#isAccessible() accessible}). Those link generators will be
* {@link ILinkGenerator#chain(ILinkGenerator) chained}, so that the resulting link descriptor will delegate to the
* first valid link generator returned by the mapper chain.
*/
public class ChainedLinkGeneratorMapper<L extends ILinkGenerator, T>
extends AbstractOneParameterLinkDescriptorMapper<L, T> {
private static final long serialVersionUID = 1568363162862717474L;
/**
* Start building a {@link ChainedLinkGeneratorMapper} for mappers that return
* {@link IPageLinkGenerator page link generators}.
*/
public static <T> Builder<IPageLinkGenerator, T> forPage() {
return new Builder<>(
new SerializableFunction<Pair<IPageLinkGenerator, IPageLinkGenerator>, IPageLinkGenerator>() {
private static final long serialVersionUID = 1L;
@Override
public IPageLinkGenerator apply(Pair<IPageLinkGenerator, IPageLinkGenerator> input) {
return input.getValue0().chain(input.getValue1());
}
}
);
}
/**
* Start building a {@link ChainedLinkGeneratorMapper} for mappers that return
* {@link ILinkGenerator resource link generators}.
*/
public static <T> Builder<ILinkGenerator, T> forResource() {
return new Builder<>(
new SerializableFunction<Pair<ILinkGenerator, ILinkGenerator>, ILinkGenerator>() {
private static final long serialVersionUID = 1L;
@Override
public ILinkGenerator apply(Pair<ILinkGenerator, ILinkGenerator> input) {
return input.getValue0().chain(input.getValue1());
}
}
);
}
/**
* Start building a {@link ChainedLinkGeneratorMapper} for mappers that return
* {@link IImageResourceLinkGenerator image resource link generators}.
*/
public static <T> Builder<IImageResourceLinkGenerator, T> forImage() {
return new Builder<>(
new SerializableFunction<Pair<IImageResourceLinkGenerator, IImageResourceLinkGenerator>, IImageResourceLinkGenerator>() {
private static final long serialVersionUID = 1L;
@Override
public IImageResourceLinkGenerator apply(Pair<IImageResourceLinkGenerator, IImageResourceLinkGenerator> input) {
return input.getValue0().chain(input.getValue1());
}
}
);
}
public static class Builder<L extends ILinkGenerator, T> {
private final ImmutableList.Builder<ILinkDescriptorMapper<? extends L, IModel<T>>> chain = ImmutableList.builder();
private final Function<? super Pair<L, L>, ? extends L> generatorChainFunction;
private Builder(Function<? super Pair<L, L>, ? extends L> generatorChainFunction) {
this.generatorChainFunction = generatorChainFunction;
}
public <L2 extends L, T2 extends T> Builder<L, T> add(ILinkDescriptorMapper<L2, IModel<T>> delegate) {
chain.add(delegate);
return this;
}
public <L2 extends L, T2 extends T> Builder<L, T> add(ILinkDescriptorMapper<L2, IModel<T2>> delegate, Class<T2> castClass) {
chain.add(new SafeCastedMapper<L2, T, T2>(delegate, castClass));
return this;
}
public ChainedLinkGeneratorMapper<L, T> build() {
return new ChainedLinkGeneratorMapper<>(chain.build(), generatorChainFunction);
}
}
private static class SafeCastedMapper<L, T, T2 extends T> implements ILinkDescriptorMapper<L, IModel<T>> {
private static final long serialVersionUID = 1L;
private final ILinkDescriptorMapper<L, IModel<T2>> delegate;
private final Class<T2> clazz;
public SafeCastedMapper(ILinkDescriptorMapper<L, IModel<T2>> delegate, Class<T2> clazz) {
super();
this.delegate = delegate;
this.clazz = clazz;
}
@Override
public L map(IModel<T> model1) {
return delegate.map(SafeCastModel.of(clazz, model1));
}
@Override
public void detach() {
delegate.detach();
}
}
private final Iterable<ILinkDescriptorMapper<? extends L, IModel<T>>> chain;
private final Function<? super Pair<L, L>, ? extends L> generatorChainFunction;
public ChainedLinkGeneratorMapper(Iterable<ILinkDescriptorMapper<? extends L, IModel<T>>> chain,
Function<? super Pair<L, L>, ? extends L> generatorChainFunction) {
this.chain = chain;
this.generatorChainFunction = generatorChainFunction;
}
@SuppressWarnings("unchecked")
@Override
public L map(IModel<T> model) {
return recurseChain(model, (L) LinkDescriptors.invalid(), chain.iterator());
}
private L recurseChain(IModel<T> model, L first, Iterator<ILinkDescriptorMapper<? extends L, IModel<T>>> it) {
if (it.hasNext()) {
return recurseChain(
model,
generatorChainFunction.apply(Pair.with(first, (L)it.next().map(model))),
it
);
} else {
return first;
}
}
}