package fr.openwide.core.wicket.more.rendering.service;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.wicket.Application;
import org.apache.wicket.Localizer;
import org.apache.wicket.Session;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.util.convert.IConverter;
import org.bindgen.BindingRoot;
import org.javatuples.Triplet;
import com.google.common.collect.Maps;
import fr.openwide.core.commons.util.context.IExecutionContext;
import fr.openwide.core.commons.util.context.IExecutionContext.ITearDownHandle;
import fr.openwide.core.commons.util.fieldpath.FieldPath;
import fr.openwide.core.jpa.more.rendering.service.IRendererService;
import fr.openwide.core.wicket.more.notification.service.IWicketContextProvider;
import fr.openwide.core.wicket.more.rendering.EnumRenderer;
import fr.openwide.core.wicket.more.rendering.Renderer;
public class RendererServiceImpl
implements IRendererService, IRendererRegistry {
private Map<Triplet<Class<?>, FieldPath, Class<?>>, Renderer<?>> cache = Maps.newHashMap();
private Map<Triplet<Class<?>, FieldPath, Class<?>>, Renderer<?>> renderers = Maps.newHashMap();
private IWicketContextProvider wicketContextProvider;
public RendererServiceImpl(IWicketContextProvider wicketContextProvider) {
super();
this.wicketContextProvider = wicketContextProvider;
}
@Override
@Deprecated
public <T> T runWithContext(Callable<T> callable) throws Exception {
return context().run(callable);
}
@Override
public IExecutionContext context() {
return wicketContextProvider.context();
}
@Override
public IExecutionContext context(Locale locale) {
return wicketContextProvider.context(locale);
}
@Override
@SuppressWarnings("unchecked")
public final <T> Renderer<? super T> findRenderer(Class<?> rootType, FieldPath path, Class<T> valueType) {
Triplet<Class<?>, FieldPath, Class<?>> parameters = Triplet.<Class<?>, FieldPath, Class<?>>with(rootType, path, valueType);
Renderer<? super T> renderer = (Renderer<? super T>) cache.get(parameters);
if (renderer != null) {
return renderer;
} else {
renderer = findRendererNoCache(rootType, path, valueType);
cache.put(parameters, renderer);
return renderer;
}
}
@SuppressWarnings("unchecked")
private <T> Renderer<? super T> findRendererNoCache(Class<?> rootType, FieldPath path, Class<T> valueType) {
Renderer<? super T> renderer = (Renderer<? super T>) renderers.get(Triplet.with(rootType, path, valueType));
if (renderer != null) {
return renderer;
}
renderer = (Renderer<? super T>) renderers.get(Triplet.with(rootType, path, null));
if (renderer != null) {
return renderer;
}
renderer = (Renderer<? super T>) renderers.get(Triplet.with(rootType, null, valueType));
if (renderer != null) {
return renderer;
}
renderer = (Renderer<? super T>) renderers.get(Triplet.with(null, null, valueType));
if (renderer != null) {
return renderer;
}
return findDefaultRenderer(valueType);
}
private <T> Renderer<? super T> findDefaultRenderer(final Class<T> valueType) {
IConverter<T> converter;
try (ITearDownHandle handle = context().open()) {
converter = Application.get().getConverterLocator().getConverter(valueType);
}
return new BackgroundWicketThreadContextRenderer<T>(converter).nullsAsNull();
}
/**
* Wrapper which makes sure that a renderer will have access to all the elements provided by Wicket
* (especially the Localizer) when it's executed.
*/
private class BackgroundWicketThreadContextRenderer<T> extends Renderer<T> {
private static final long serialVersionUID = 3035924297487452645L;
private final IConverter<? super T> delegate;
public BackgroundWicketThreadContextRenderer(IConverter<? super T> delegate) {
super();
this.delegate = delegate;
}
@Override
public String render(final T value, final Locale locale) {
try (ITearDownHandle handle = context().open()) {
return delegate.convertToString(value, locale);
}
}
}
@Override
public final <T> Renderer<? super T> findRenderer(Class<?> rootType, Class<T> valueType) {
return findRenderer(rootType, null, valueType);
}
@Override
public final <T> Renderer<? super T> findRenderer(Class<T> valueType) {
return findRenderer(null, null, valueType);
}
@Override
public final <R, T> void registerRenderer(Class<R> rootType, FieldPath path, Class<T> valueType, Renderer<? super T> renderer) {
cache.clear();
renderers.put(
Triplet.<Class<?>, FieldPath, Class<?>> with(
rootType,
path,
valueType
),
new BackgroundWicketThreadContextRenderer<T>(renderer).nullsAsNull()
);
}
@Override
public final <R, T> void registerRenderer(Class<R> rootType, BindingRoot<R, T> binding, Class<T> valueType,
Renderer<? super T> renderer) {
registerRenderer(rootType, FieldPath.fromBinding(binding), valueType, renderer);
}
@Override
public final <T> void registerRenderer(Class<?> rootType, Class<T> valueType, Renderer<? super T> renderer) {
registerRenderer(rootType, (FieldPath) null, valueType, renderer);
}
@Override
public final <R, T> void registerRenderer(Class<R> rootType, BindingRoot<R, T> binding, Renderer<? super T> renderer) {
registerRenderer(rootType, FieldPath.fromBinding(binding), null, renderer);
}
@Override
public final <R, T> void registerRenderer(Class<R> rootType, FieldPath path, Renderer<? super T> renderer) {
registerRenderer(rootType, path, null, renderer);
}
@Override
public final <T> void registerRenderer(Class<T> valueType, Renderer<? super T> renderer) {
registerRenderer(null, (FieldPath) null, valueType, renderer);
}
@Override
public final String localize(final String resourceKey) {
return localize(resourceKey, null);
}
@Override
public String localize(String resourceKey, Object namedParameters, Object... positionalParameters) {
return localize(resourceKey, null, namedParameters, positionalParameters);
}
@Override
public String localize(Enum<?> enumValue, String prefix, String suffix) {
return localize(enumValue, prefix, suffix, null);
}
@Override
public <T> String localize(Class<T> clazz, T value) {
return localize(clazz, value, null);
}
@Override
public final String localize(final String resourceKey, final Locale locale) {
try (ITearDownHandle handle = context().open()) {
return Localizer.get().getString(resourceKey, null, null, locale, null, (String) null);
}
}
@Override
public String localize(final String resourceKey, final Locale locale, final Object namedParameters, final Object... positionalParameters) {
try (ITearDownHandle handle = context().open()) {
IModel<?> modelParameter;
if (namedParameters instanceof IModel) {
modelParameter = (IModel<?>) namedParameters;
} else {
modelParameter = new AbstractReadOnlyModel<Object>() {
private static final long serialVersionUID = 1L;
@Override
public Object getObject() {
return namedParameters;
}
};
}
String result = new StringResourceModel(resourceKey)
.setModel(modelParameter).setParameters(positionalParameters)
.getObject();
return result;
}
}
@Override
public final String localize(final Enum<?> enumValue, final String prefix, final String suffix, final Locale locale) {
try (ITearDownHandle handle = context().open()) {
return EnumRenderer.with(prefix, suffix)
.render(enumValue, locale != null ? locale : Session.get().getLocale());
}
}
@Override
public final <T> String localize(final Class<T> clazz, final T value, final Locale locale) {
try (ITearDownHandle handle = context().open()) {
return Application.get().getConverterLocator().getConverter(clazz)
.convertToString(value, locale != null ? locale : Session.get().getLocale());
}
}
}