package org.glassfish.jersey.jackson.internal;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.EndpointConfigBase;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.ObjectWriterInjector;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.ObjectWriterModifier;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JsonEndpointConfig;
import org.glassfish.jersey.message.filtering.spi.ObjectProvider;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.ser.BeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
@Singleton
public final class FilteringJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {
@Inject
private Provider<ObjectProvider<FilterProvider>> provider;
@Override
protected JsonEndpointConfig _configForWriting(final ObjectMapper mapper, final Annotation[] annotations,
final Class<?> defaultView) {
final AnnotationIntrospector customIntrospector = mapper.getSerializationConfig().getAnnotationIntrospector();
final ObjectMapper filteringMapper = mapper.setAnnotationIntrospector(AnnotationIntrospector.pair(customIntrospector,
new JacksonAnnotationIntrospector() {
@Override
public Object findFilterId(final Annotated a) {
final Object filterId = super.findFilterId(a);
if (filterId != null) {
return filterId;
}
if (a instanceof AnnotatedMethod) {
final Method method = ((AnnotatedMethod) a).getAnnotated();
if (ReflectionHelper.isGetter(method)) {
return ReflectionHelper.getPropertyName(method);
}
}
if (a instanceof AnnotatedField || a instanceof AnnotatedClass) {
return a.getName();
}
return null;
}
}));
return super._configForWriting(filteringMapper, annotations, defaultView);
}
@Override
public void writeTo(final Object value,
final Class<?> type,
final Type genericType,
final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders,
final OutputStream entityStream) throws IOException {
final FilterProvider filterProvider = provider.get().getFilteringObject(genericType, true, annotations);
if (filterProvider != null) {
ObjectWriterInjector.set(new FilteringObjectWriterModifier(filterProvider, ObjectWriterInjector.getAndClear()));
}
super.writeTo(value, type, genericType, annotations, mediaType, httpHeaders, entityStream);
}
private static final class FilteringObjectWriterModifier extends ObjectWriterModifier {
private final ObjectWriterModifier original;
private final FilterProvider filterProvider;
private FilteringObjectWriterModifier(final FilterProvider filterProvider, final ObjectWriterModifier original) {
this.original = original;
this.filterProvider = filterProvider;
}
@Override
public ObjectWriter modify(final EndpointConfigBase<?> endpoint,
final MultivaluedMap<String, Object> responseHeaders,
final Object valueToWrite,
final ObjectWriter w,
final JsonGenerator g) throws IOException {
final ObjectWriter writer = original == null ? w : original.modify(endpoint, responseHeaders, valueToWrite, w, g);
final FilterProvider customFilterProvider = writer.getConfig().getFilterProvider();
return customFilterProvider == null
? writer.with(filterProvider)
: writer.with(new FilterProvider() {
@Override
public BeanPropertyFilter findFilter(final Object filterId) {
return customFilterProvider.findFilter(filterId);
}
@Override
public PropertyFilter findPropertyFilter(final Object filterId, final Object valueToFilter) {
final PropertyFilter filter = customFilterProvider.findPropertyFilter(filterId, valueToFilter);
if (filter != null) {
return filter;
}
return filterProvider.findPropertyFilter(filterId, valueToFilter);
}
});
}
}
}