/**
* Copyright 2014 Opower, 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.opower.rest.client.generator.core;
import com.opower.rest.client.generator.plugins.providers.Builtin;
import com.opower.rest.client.generator.util.Types;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Providers;
/**
* Much of this class was extracted from ResteasyProviderFactory in Resteasy 2.3.4.Final
* @author chris.phillips
*/
public class ClientProviders implements Providers {
protected MediaTypeMap<SortedKey<MessageBodyReader>> messageBodyReaders = new MediaTypeMap<>();
protected MediaTypeMap<SortedKey<MessageBodyWriter>> messageBodyWriters = new MediaTypeMap<>();
public ClientProviders() {
// register the builtins
for(Object p : Builtin.providerInstances()) {
registerProviderInstance(p, true);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType) {
List<SortedKey<MessageBodyReader>> readers = messageBodyReaders.getPossible(mediaType, type);
for (SortedKey<MessageBodyReader> reader : readers) {
if (reader.obj.isReadable(type, genericType, annotations, mediaType)) {
return (MessageBodyReader<T>) reader.obj;
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType) {
List<SortedKey<MessageBodyWriter>> writers = messageBodyWriters.getPossible(mediaType, type);
for (SortedKey<MessageBodyWriter> writer : writers) {
if (writer.obj.isWriteable(type, genericType, annotations, mediaType)) {
return (MessageBodyWriter<T>) writer.obj;
}
}
return null;
}
@Override
public <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> type) {
return null;
}
@Override
public <T> ContextResolver<T> getContextResolver(Class<T> contextType, MediaType mediaType) {
return null;
}
/**
* Allow us to sort message body implementations that are more specific for their types
* i.e. MessageBodyWriter<Object> is less specific than MessageBodyWriter<String>.
* <p/>
* This helps out a lot when the desired media type is a wildcard and to weed out all the possible
* default mappings.
*/
protected static class SortedKey<T> implements Comparable<SortedKey<T>>, MediaTypeMap.Typed {
public Class readerClass;
public T obj;
public boolean isGeneric = false;
public boolean isBuiltin = false;
public Class template = null;
private SortedKey(Class intf, T reader, Class readerClass, boolean isBuiltin) {
this(intf, reader, readerClass);
this.isBuiltin = isBuiltin;
}
private SortedKey(Class intf, T reader, Class readerClass) {
this.readerClass = readerClass;
this.obj = reader;
// check the super class for the generic type 1st
template = Types.getTemplateParameterOfInterface(readerClass, intf);
isGeneric = template == null || Object.class.equals(template);
}
public int compareTo(SortedKey<T> tMessageBodyKey) {
// Sort more specific template parameter types before non-specific
// Sort user provider before builtins
if (this == tMessageBodyKey) {
return 0;
}
if (isGeneric != tMessageBodyKey.isGeneric) {
if (isGeneric) {
return 1;
} else {
return -1;
}
}
if (isBuiltin == tMessageBodyKey.isBuiltin) {
return 0;
}
if (isBuiltin) {
return 1;
} else {
return -1;
}
}
public Class getType() {
return template;
}
}
/**
* Register a @Provider class. Can be a MessageBodyReader/Writer or ExceptionMapper.
*
* @param provider
*/
public void registerProviderInstance(Object provider) {
registerProviderInstance(provider, false);
}
public void registerProviderInstance(Object provider, boolean builtin) {
if (provider instanceof MessageBodyReader) {
try {
addMessageBodyReader((MessageBodyReader)provider, provider.getClass(), builtin);
} catch (Exception e) {
throw new RuntimeException("Unable to instantiate MessageBodyReader", e);
}
}
if (provider instanceof MessageBodyWriter) {
try {
addMessageBodyWriter((MessageBodyWriter)provider, provider.getClass(), builtin);
} catch (Exception e) {
throw new RuntimeException("Unable to instantiate MessageBodyWriter", e);
}
}
}
/**
* Specify the provider class. This is there just in case the provider instance is a proxy. Proxies tend
* to lose generic type information
*
* @param provider
* @param providerClass
* @param isBuiltin
*/
public void addMessageBodyReader(MessageBodyReader provider, Class providerClass, boolean isBuiltin)
{
SortedKey<MessageBodyReader> key = new SortedKey<>(MessageBodyReader.class, provider, providerClass, isBuiltin);
Consumes consumeMime = provider.getClass().getAnnotation(Consumes.class);
if (consumeMime != null) {
for (String consume : consumeMime.value()) {
MediaType mime = MediaType.valueOf(consume);
messageBodyReaders.add(mime, key);
}
}
else {
messageBodyReaders.add(new MediaType("*", "*"), key);
}
}
/**
* Specify the provider class. This is there jsut in case the provider instance is a proxy. Proxies tend
* to lose generic type information
*
* @param provider
* @param providerClass
* @param isBuiltin
*/
public void addMessageBodyWriter(MessageBodyWriter provider, Class providerClass, boolean isBuiltin)
{
Produces consumeMime = provider.getClass().getAnnotation(Produces.class);
SortedKey<MessageBodyWriter> key = new SortedKey<>(MessageBodyWriter.class, provider, providerClass, isBuiltin);
if (consumeMime != null) {
for (String consume : consumeMime.value()) {
MediaType mime = MediaType.valueOf(consume);
messageBodyWriters.add(mime, key);
}
}
else {
messageBodyWriters.add(new MediaType("*", "*"), key);
}
}
}