/**
* Copyright (C) 2012 Ness Computing, Inc.
*
* 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.nesscomputing.jackson;
import static com.nesscomputing.jackson.JacksonSerializerBinder.keyFor;
import java.lang.annotation.Annotation;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.util.Types;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import com.nesscomputing.callback.Callback;
class SerializerBinderBuilderImpl<T> implements SerializerBinderBuilder<T> {
private static final TypeReference<byte[]> BYTEA_TYPE = new TypeReference<byte[]>() {};
private static final TypeReference<String> STRING_TYPE = new TypeReference<String>() {};
private final Binder binder;
private final TypeReference<T> type;
private Callback<Throwable> action = new Callback<Throwable>() {
@Override
public void call(Throwable item) throws Exception {
throw Throwables.propagate(item);
}
};
SerializerBinderBuilderImpl(Binder binder, TypeReference<T> type) {
this.binder = binder;
this.type = type;
}
// CONFIGURATION
@Override
public SerializerBinderBuilder<T> onError(Callback<Throwable> action) {
this.action = action;
return this;
}
// END CONFIGURATION. BEGIN BINDING
@Override
public void bind() {
binder.install(new BindingModule());
}
private class BindingModule extends AbstractModule
{
@Override
protected void configure()
{
install(new SerializerModule());
install(new DeserializerModule());
}
SerializerBinderBuilderImpl<?> getOuter()
{
return SerializerBinderBuilderImpl.this;
}
@Override
public int hashCode()
{
return new HashCodeBuilder().append(type.getType()).toHashCode();
}
@Override
public boolean equals(Object obj)
{
if (! (obj instanceof SerializerBinderBuilderImpl<?>.BindingModule)) {
return false;
}
SerializerBinderBuilderImpl<?>.BindingModule other = (SerializerBinderBuilderImpl<?>.BindingModule) obj;
return new EqualsBuilder().append(type.getType(), other.getOuter().type.getType()).isEquals();
}
}
private class SerializerModule extends AbstractModule
{
@Override
protected void configure() {
bindSerializers(JsonSerializerFunction.class, SmileSerializerFunction.class);
}
@SuppressWarnings("unchecked")
private void bindSerializers(Class<? extends Annotation> jsonSerializerAnnotation, Class<? extends Annotation> smileSerializerAnnotation)
{
// T -> String
SerializerProvider<T, String> stringProvider =
new StringSerializerProvider<T>(action);
// T -> byte[]
SerializerProvider<T, byte[]> bytesProviderSmile = new SmileSerializerProvider<T>(action);
SerializerProvider<T, byte[]> bytesProviderJson = new JsonBytesSerializerProvider<T>(action);
// @Json T -> String
bind (keyFor(type, STRING_TYPE, jsonSerializerAnnotation))
.toProvider(stringProvider).in(Scopes.SINGLETON);
// @Json ? super T -> String
bind ((Key<Function<? super T, String>>)
Key.get(Types.newParameterizedType(
Function.class,
Types.supertypeOf(type.getType()),
String.class), jsonSerializerAnnotation))
.toProvider(stringProvider).in(Scopes.SINGLETON);
// @Json T -> byte[]
bind (keyFor(type, BYTEA_TYPE, jsonSerializerAnnotation))
.toProvider(bytesProviderJson).in(Scopes.SINGLETON);
// @Json ? super T -> byte[]
bind ((Key<Function<? super T, byte[]>>)
Key.get(Types.newParameterizedType(
Function.class,
Types.supertypeOf(type.getType()),
byte[].class), jsonSerializerAnnotation))
.toProvider(bytesProviderJson).in(Scopes.SINGLETON);
// @Smile T -> byte[]
bind (keyFor(type, BYTEA_TYPE, smileSerializerAnnotation))
.toProvider(bytesProviderSmile).in(Scopes.SINGLETON);
// @Smile ? super T -> byte[]
bind ((Key<Function<? super T, byte[]>>)
Key.get(Types.newParameterizedType(
Function.class,
Types.supertypeOf(type.getType()),
byte[].class), smileSerializerAnnotation))
.toProvider(bytesProviderSmile).in(Scopes.SINGLETON);
}
}
private class DeserializerModule extends AbstractModule
{
@Override
protected void configure() {
bindDeserializers(JsonDeserializerFunction.class, SmileDeserializerFunction.class, true);
}
@SuppressWarnings("unchecked")
private void bindDeserializers(Class<? extends Annotation> jsonDeserializerAnnotation, Class<? extends Annotation> smileDeserializerAnnotation, boolean bindAuto)
{
// String -> T
SerializerProvider<String, T> stringProvider = new StringDeserializerProvider<T>(type, action);
// byte[] -> T
SerializerProvider<byte[], T> bytesProviderSmile = new SmileDeserializerProvider<T>(type, action);
SerializerProvider<byte[], T> bytesProviderJson = new JsonBytesDeserializerProvider<T>(type, action);
// @Json String -> T
bind (keyFor(STRING_TYPE, type, jsonDeserializerAnnotation))
.toProvider(stringProvider).in(Scopes.SINGLETON);
// @Json String -> ? extends T
bind ((Key<Function<String, ? extends T>>)
Key.get(Types.newParameterizedType(
Function.class,
String.class,
Types.subtypeOf(type.getType()), jsonDeserializerAnnotation)))
.toProvider(stringProvider).in(Scopes.SINGLETON);
// @Json byte[] -> T
bind (keyFor(BYTEA_TYPE, type, jsonDeserializerAnnotation))
.toProvider(bytesProviderJson).in(Scopes.SINGLETON);
// @Json byte[] -> ? extends T
bind ((Key<Function<byte[], ? extends T>>)
Key.get(Types.newParameterizedType(
Function.class,
byte[].class,
Types.subtypeOf(type.getType())), jsonDeserializerAnnotation))
.toProvider(bytesProviderJson).in(Scopes.SINGLETON);
// @Smile byte[] -> T
bind (keyFor(BYTEA_TYPE, type, smileDeserializerAnnotation))
.toProvider(bytesProviderSmile).in(Scopes.SINGLETON);
// @Smile byte[] -> ? extends T
bind ((Key<Function<byte[], ? extends T>>)
Key.get(Types.newParameterizedType(
Function.class,
byte[].class,
Types.subtypeOf(type.getType())), smileDeserializerAnnotation))
.toProvider(bytesProviderSmile).in(Scopes.SINGLETON);
if (!bindAuto)
{
return;
}
Provider<Function<byte[], T>> bytesProviderAuto = new AutodetectDeserializerProvider<T>(bytesProviderJson, bytesProviderSmile);
// @Autodetect byte[] -> T
bind (keyFor(BYTEA_TYPE, type, JsonAutodetectDeserializer.class))
.toProvider(bytesProviderAuto).in(Scopes.SINGLETON);
// @Autodetect byte[] -> ? extends T
bind ((Key<Function<byte[], ? extends T>>)
Key.get(Types.newParameterizedType(
Function.class,
byte[].class,
Types.subtypeOf(type.getType())), JsonAutodetectDeserializer.class))
.toProvider(bytesProviderAuto).in(Scopes.SINGLETON);
}
}
// END BINDING.
/** Handles error handling logic */
abstract static class SerializerProvider<In, Out> implements Provider<Function<In, Out>> {
private final Callback<Throwable> action;
SerializerProvider(Callback<Throwable> action) {
this.action = action;
}
protected abstract Out serialize(In input) throws Exception;
@Override
public Function<In, Out> get() {
return new Function<In, Out>() {
@Override
public Out apply(In input) {
try {
try {
return serialize(input);
} catch (Exception e) {
action.call(e);
return null;
}
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
};
}
}
static class StringSerializerProvider<T> extends SerializerProvider<T, String> {
StringSerializerProvider(Callback<Throwable> action) {
super(action);
}
private ObjectMapper mapper;
@Inject
void setObjectMapper(@JsonMapper ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected String serialize(T input) throws Exception {
return mapper.writeValueAsString(input);
}
}
static class SmileSerializerProvider<T> extends SerializerProvider<T, byte[]> {
SmileSerializerProvider(Callback<Throwable> action) {
super(action);
}
private ObjectMapper mapper;
@Inject
void setObjectMapper(@SmileMapper ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected byte[] serialize(T input) throws Exception {
return mapper.writeValueAsBytes(input);
}
}
static class JsonBytesSerializerProvider<T> extends SerializerProvider<T, byte[]> {
JsonBytesSerializerProvider(Callback<Throwable> action) {
super(action);
}
private ObjectMapper mapper;
@Inject
void setObjectMapper(@JsonMapper ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected byte[] serialize(T input) throws Exception {
return mapper.writeValueAsBytes(input);
}
}
static class StringDeserializerProvider<T> extends SerializerProvider<String, T> {
private final TypeReference<T> type;
StringDeserializerProvider(TypeReference<T> type, Callback<Throwable> action) {
super(action);
this.type = type;
}
private ObjectMapper mapper;
@Inject
void setObjectMapper(@JsonMapper ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected T serialize(String input) throws Exception {
return mapper.readValue(input, type);
}
}
static class SmileDeserializerProvider<T> extends SerializerProvider<byte[], T> {
private final TypeReference<T> type;
SmileDeserializerProvider(TypeReference<T> type, Callback<Throwable> action) {
super(action);
this.type = type;
}
private ObjectMapper mapper;
@Inject
void setObjectMapper(@SmileMapper ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected T serialize(byte[] input) throws Exception {
return mapper.readValue(input, type);
}
}
static class JsonBytesDeserializerProvider<T> extends SerializerProvider<byte[], T> {
private final TypeReference<T> type;
JsonBytesDeserializerProvider(TypeReference<T> type, Callback<Throwable> action) {
super(action);
this.type = type;
}
private ObjectMapper mapper;
@Inject
void setObjectMapper(@JsonMapper ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected T serialize(byte[] input) throws Exception {
return mapper.readValue(input, type);
}
}
static class AutodetectDeserializerProvider<T> implements Provider<Function<byte[], T>>, Function<byte[], T> {
private final Function<byte[], T> bytesProviderJson;
private final Function<byte[], T> bytesProviderSmile;
AutodetectDeserializerProvider(
SerializerProvider<byte[], T> bytesProviderJson,
SerializerProvider<byte[], T> bytesProviderSmile)
{
this.bytesProviderJson = bytesProviderJson.get();
this.bytesProviderSmile = bytesProviderSmile.get();
}
@Override
public Function<byte[], T> get()
{
return this;
}
@Override
public T apply(byte[] input)
{
if (input.length > 0 && input[0] == ':')
{
return bytesProviderSmile.apply(input);
}
return bytesProviderJson.apply(input);
}
}
}