I am trying to generify my class structure.
I will show my real structure to be more specific.
I am writing application with offline mode support, so I decided to implement my ETag cache mechanism in using Robospice and GreenDao ORM.
I need to cache only GET requests.
Firstly my requests should extend base request(not mine), in my case RetrofitSpiceRequest<T, V>
T is type of return data
V is service type, in my case I am using Retrofit.
The problem is that return type is not List of T types by default and I need to create subclass that extends array of T objects and that use it as return type.
Something like this
public class City {
....
....
....
public static class List extends ArrayList<City> {
.....
.....
}
}
And use City.List as return type.
But I have my DAO declared as following
public class CityDao extends AbstractDao<City, Long> {
}
In each request (GET) I need to have specific DAO as a member in order to cache data if it differs from the server data. Or load data from the local database if there is no connection.
The problem here is that request generified by T type which is mostly list, City.List in my case, of some objects, but my dao is generified by, for example E type which is City in my case.
I want to create method like this
public AbastractDao<T,Long> getRequestDao() {
}
But as far as my Request returns City.List, I have no idea how to generify this class, I feel that it is possible, but now no ideas.
In case of non generic dao method, I have to duplicate code like this
#Override
public void insertReceivedData(City.List received) {
mCityDao.insertOrReplaceInTx(received);
}
#Override
public City.List getCachedData() {
if (mFilterMap != null && mFilterMap.size() > 0) {
return (City.List) mCityDao.loadAll();
} else {
WhereCondition[] whereConditions = QueryUtils.convertPropertyMapToConditionalArray(mFilterMap);
return (City.List) mCityDao.queryBuilder().where(whereConditions[0], Arrays.copyOfRange(whereConditions, 1, whereConditions.length)).list();
}
}
In each request
Please share your ideas.
Thanks.
I end up with following solution. It is not as good as I wanted, but it works and better than duplicating code.
My base request class.
public abstract class BaseGetRequest<L extends List<T>, T, V> extends RetrofitSpiceRequest<L, V> implements FilterableRequest {
// Context
protected Context mContext;
// Filter used in request and in queries
protected Map<Property, String> mFilterMap;
// Session provided Singletone
protected DaoSessionProvider mSessionProvider;
public BaseGetRequest(Class<L> clazz, Class<V> retrofitedInterfaceClass, Context context, Map<Property, String> filterMap) {
super(clazz, retrofitedInterfaceClass);
mContext = context;
mFilterMap = filterMap;
mSessionProvider = ((DaoSessionProvider) mContext.getApplicationContext());
// TODO determine required retry count
setRetryPolicy(new RetryPolicy() {
#Override
public int getRetryCount() {
return 0;
}
#Override
public void retry(SpiceException e) {
}
#Override
public long getDelayBeforeRetry() {
return 0;
}
});
}
protected WhereCondition[] getWhereConditions() {
return QueryUtils.convertPropertyMapToConditionalArray(mFilterMap);
}
public BaseGetRequestV2(Class<L> clazz, Class<V> retrofitedInterfaceClass, Context context) {
this(clazz, retrofitedInterfaceClass, context, null);
}
public abstract AbstractDao<T, Long> getDao();
public abstract L createDataList(List<T> list);
public L getCachedData() {
if (mFilterMap != null && mFilterMap.size() > 0) {
WhereCondition[] whereConditions = getWhereConditions();
return createDataList(getDao().queryBuilder().where(whereConditions[0], Arrays.copyOfRange(whereConditions, 1, whereConditions.length)).list());
} else {
return createDataList(getDao().loadAll());
}
}
public abstract L getData();
#Override
public Map<Property, String> getFilterMap() {
return mFilterMap;
}
public Map<String, String> getStringMap() {
return QueryUtils.convertPropertyMapToString(mFilterMap);
}
#Override
public L loadDataFromNetwork() throws Exception {
L receivedData = null;
try {
receivedData = getData();
WhereCondition[] conditions = getWhereConditions();
getDao().queryBuilder().where(conditions[0],Arrays.copyOfRange(conditions, 1, conditions.length)).buildDelete().executeDeleteWithoutDetachingEntities();
getDao().insertOrReplaceInTx(receivedData);
} catch (Exception ex) {
receivedData = getCachedData();
}
return receivedData;
}
}
And I can extend this class like so:
public class NewsRequest extends BaseGetRequest<NewsArticle.List, NewsArticle, API> {
public static final String TARGET_URL = "/news";
NewsArticleDao mNewsArticleDao;
public NewsRequest(Context context) {
this(context, null);
}
public NewsRequest(Context context, Map<Property, String> filterMap) {
super(NewsArticle.List.class, API.class, context, filterMap);
mNewsArticleDao = mSessionProvider.getDaoSession().getNewsArticleDao();
}
#Override
public AbstractDao<NewsArticle, Long> getDao() {
return mNewsArticleDao;
}
#Override
public NewsArticle.List createDataList(List<NewsArticle> list) {
return new NewsArticle.List(list);
}
#Override
public NewsArticle.List getData() {
return getService().getNews(getStringMap());
}
}
Related
I want to pass a class as an argument so List changes dynamically, I've tried lots of things but none of them have worked out, I don't even know if it's actually possible in Java.
Any advice?
public class QueryMapper {
protected Object targetClass;
public QueryMapper(Object targetClass) {
this.targetClass = targetClass;
}
public Object getTargetClass() {
// targetClass;
return targetClass.getClass();
}
public void setTargetClass(Object targetClass) {
this.targetClass = targetClass;
}
public List<targetClass> mapper(Query q) {
NativeQueryImpl nativeQuery = (NativeQueryImpl) q;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String, targetClass>> result = nativeQuery.getResultList();
return q.getResultList();
}
}
I have a type which is extending HashMap<String, String>. As per the documentation here, it is possible to add a custom converter for the type. But it seems not working. The contents of the hashMap doesn't get converted, output looks like below;
"summary": {
"en": null
},
Any idea how to convert Label and its fields along with it's hashmap's contents?
Parent
#DynamoDbBean(converterProviders = {
CustomAttributeConverterProvider.class,
DefaultAttributeConverterProvider.class})
public class Summary extends BaseEntry {
private #Valid Label summary = null;
}
Child
#DynamoDbBean(converterProviders = {
CustomAttributeConverterProvider.class,
DefaultAttributeConverterProvider.class})
public class Label extends HashMap<String, String> {
private #Valid String en = null;
}
HashMapAttributeConverter
public class HashMapAttributeConverter implements AttributeConverter<Map<String, String>> {
private static AttributeConverter<Map<String, String>> mapConverter;
/** Default constructor. */
public HashMapAttributeConverter() {
mapConverter =
MapAttributeConverter.builder(EnhancedType.mapOf(String.class, String.class))
.mapConstructor(HashMap::new)
.keyConverter(StringStringConverter.create())
.valueConverter(StringAttributeConverter.create())
.build();
}
#Override
public AttributeValue transformFrom(Map<String, String> input) {
return mapConverter.transformFrom(input);
}
#Override
public Map<String, String> transformTo(AttributeValue input) {
return mapConverter.transformTo(input);
}
#Override
public EnhancedType<Map<String, String>> type() {
return mapConverter.type();
}
#Override
public AttributeValueType attributeValueType() {
return mapConverter.attributeValueType();
}
}
CustomAttributeConverterProvider
public class CustomAttributeConverterProvider implements AttributeConverterProvider {
private final List<AttributeConverter<?>> customConverters =
Arrays.asList(new HashMapAttributeConverter());
private final Map<EnhancedType<?>, AttributeConverter<?>> customConvertersMap;
private final AttributeConverterProvider defaultProvider =
DefaultAttributeConverterProvider.create();
public CustomAttributeConverterProvider() {
customConvertersMap =
customConverters.stream().collect(Collectors.toMap(AttributeConverter::type, c -> c));
}
#Override
public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
return (AttributeConverter<T>)
customConvertersMap.computeIfAbsent(enhancedType, defaultProvider::converterFor);
}
}
#Override
public <T> AttributeConverter<T> converterFor(EnhancedType<T> type) {
// in this method you have to return only your converter based on type
// otherwise null should be returned. It will fix your issue.
}
I'm trying to improve my Android persistence layer to be used across multiple applications.
What i have done so far is to setup a base repository abstract class and also a base repository interface, the complete code can be check here: https://github.com/grmaciel/android-repository-ormlite
Interface:
public interface IRepository<T, Id> {
public void save(T entity) throws SQLException;
public void saveBatch(List<T> entities) throws Exception;
public List<T> queryAll() throws SQLException;
public T findById(Id id) throws SQLException;
public void delete(T entity) throws SQLException;
}
Now all my repositories extends my base repository, like this:
public class DependencyRepository extends BaseRepository<Dependency>
implements IDependenceyRepository {
public DependencyRepository(Context context) {
super(context);
}
}
What i'm trying to achieve now is to create a repository factory that would allow people to not have to instantiate their repositories all over the place with the new Instance()
What i did was to create a Singleton factory that has to be initialized with a container that has the class relations, like this:
public abstract class BaseRepositoryContainer {
private final Context context;
public BaseRepositoryContainer(Context context) {
this.context = context;
}
public abstract <T extends IRepository> Map<Class<T>, Class<T>> getRepositoriesMap();
public Context getContext() {
return context;
}
}
The factory:
public class RepositoryFactory {
private Map<Object, Object> repositories = new HashMap<>();
private final String LOG_TAG = RepositoryFactory.class.getSimpleName();
private Context context;
private static RepositoryFactory instance;
private BaseRepositoryContainer container;
private RepositoryFactory() {}
public void init(BaseRepositoryContainer container) {
this.container = container;
this.context = container.getContext();
this.configureRepositories(container.getRepositoriesMap());
}
private <T extends IRepository> void configureRepositories(Map<Class<T>, Class<T>> repositoriesMap) {
for (Entry<Class<T>, Class<T>> entry : repositoriesMap.entrySet()) {
this.registerRepository(entry.getKey(), entry.getValue());
}
}
private <T extends IRepository> void registerRepository(Class<T> repInterface, Class<T> realRepository) {
repositories.put(repInterface, this.createRepository(realRepository));
}
public <T extends IRepository> T getRepository(Class<T> repInterface) {
if (container == null) {
throw new UnsupportedOperationException("You should call init method providing a container.");
}
return (T) repositories.get(repInterface);
}
private <T extends IRepository> T createRepository(Class<T> repoClass) {
try {
T instance = repoClass.getConstructor(Context.class).newInstance(context);
Log.d(LOG_TAG, "Repository " + repoClass.getSimpleName() + " created");
return instance;
} catch (InstantiationException e) {
Log.d(LOG_TAG, e.toString());
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.toString());
} catch (InvocationTargetException e) {
Log.d(LOG_TAG, e.toString());
} catch (NoSuchMethodException e) {
Log.d(LOG_TAG, e.toString());
}
return null;
}
public static RepositoryFactory getInstance() {
if (instance == null) {
instance = new RepositoryFactory();
}
return instance;
}
}
And then it could be called like this:
// when the application is first run
RepositoryFactory.getInstance().init(new RepositoryContainer(this));
// retrieving the repository
IDependenceyRepository repository = RepositoryFactory.getInstance()
.getRepository(IDependenceyRepository.class);
So i was wondering if is this a good approach to help to implement towards an abstraction? I don't like much the idea of to have the calling the init method of the factory without obligating people to it, the only way to know is if you dont call will throw an exception which i dont like.
Does anyone can point me the right direction? A way to improve this design? I dont want to discover later on my project that i have created lots of strong dependencies and have a hard time to change something.
Any advices would be appreciated.
What i did to improve my code was instead of reinventing the wheel i started using a dependency injection library (Dagger 2 - http://google.github.io/dagger/).
You can define modules that return your desidered repository across an application or activities depending on your needs.
I am developing a small code generator using JDK 6's Annotation Processing API and am stuck trying to get the actual generic type of a field in the class. To be clearer, let's say I have a class like this:
#MyAnnotation
public class User {
private String id;
private String username;
private String password;
private Set<Role> roles = new HashSet<Role>();
private UserProfile profile;
}
and here is my annotation processor class:
#SupportedAnnotationTypes({ "xxx.MyAnnotation" })
#SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MongoDocumentAnnotationProcessor extends AbstractProcessor {
private Types typeUtils = null;
private Elements elementUtils = null;
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
typeUtils = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
debug("Running " + getClass().getSimpleName());
if (roundEnv.processingOver() || annotations.size() == 0) {
return false;
}
for (Element element : roundEnv.getRootElements()) {
if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) {
for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) {
String fieldName = variableElement.getSimpleName().toString();
Element innerElement = typeUtils.asElement(variableElement.asType());
String fieldClass = "";
if (innerElement == null) { // Primitive type
PrimitiveType primitiveType = (PrimitiveType) variableElement.asType();
fieldClass = typeUtils.boxedClass(primitiveType).getQualifiedName().toString();
} else {
if (innerElement instanceof TypeElement) {
TypeElement typeElement = (TypeElement) innerElement;
fieldClass = typeElement.getQualifiedName().toString();
TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection");
if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) {
TypeVariable typeMirror = (TypeVariable)((DeclaredType)typeElement.asType()).getTypeArguments().get(0);
TypeParameterElement typeParameterElement = (TypeParameterElement) typeUtils.asElement(typeMirror);
// I am stuck here. I don't know how to get the
// full qualified class name of the generic type of
// property 'roles' when the code processes the User
// class as above. What I want to retrieve is the
// 'my.package.Role' value
}
}
}
}
}
}
return false;
}
private boolean isAnnotated(Element element) {
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
if (annotationMirrors == null || annotationMirrors.size() == 0) return false;
for (AnnotationMirror annotationMirror : annotationMirrors) {
String qualifiedName = ((TypeElement)annotationMirror.getAnnotationType().asElement()).getQualifiedName().toString();
if ("xxx.MyAnnotation".equals(qualifiedName)) return true;
}
return false;
}
}
Any hint would be really appreciated!
Copy-paste of my original answer:
This seems to be a common question so, for those arriving from Google: there is hope.
The Dagger DI project is licensed under the Apache 2.0 License and contains some utility methods for working with types in an annotation processor.
In particular, the Util class can be viewed in full on GitHub (Util.java) and defines a method public static String typeToString(TypeMirror type). It uses a TypeVisitor and some recursive calls to build up a string representation of a type. Here is a snippet for reference:
public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator)
{
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
TypeElement typeElement = (TypeElement) declaredType.asElement();
rawTypeToString(result, typeElement, innerClassSeparator);
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result.append("<");
for (int i = 0; i < typeArguments.size(); i++)
{
if (i != 0)
{
result.append(", ");
}
// NOTE: Recursively resolve the types
typeToString(typeArguments.get(i), result, innerClassSeparator);
}
result.append(">");
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v) { ... }
#Override
public Void visitArray(ArrayType arrayType, Void v) { ... }
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
result.append(typeVariable.asElement().getSimpleName());
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v) { ... }
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v) { ... }
}, null);
}
I am busy with my own project which generates class extensions. The Dagger method works for complex situations, including generic inner classes. I have the following results:
My test class with field to extend:
public class AnnotationTest
{
...
public static class A
{
#MyAnnotation
private Set<B<Integer>> _bs;
}
public static class B<T>
{
private T _value;
}
}
Calling the Dagger method on the Element the processor provides for the _bs field:
accessor.type = DaggerUtils.typeToString(element.asType());
The generated source (custom, of course). Note the awesome nested generic types.
public java.util.Set<AnnotationTest.B<java.lang.Integer>> AnnotationTest.A.getBsGenerated()
{
return this._bs;
}
EDIT: adapting the concept to extract a TypeMirror of the first generic argument, null otherwise:
public static TypeMirror getGenericType(final TypeMirror type)
{
final TypeMirror[] result = { null };
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result[0] = typeArguments.get(0);
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v)
{
return null;
}
#Override
public Void visitArray(ArrayType arrayType, Void v)
{
return null;
}
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v)
{
return null;
}
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v)
{
throw new UnsupportedOperationException();
}
}, null);
return result[0];
}
Looks like there are a couple of problems. One, the isAssignable() isnt working as expected. Second, in the above code you are trying to get the generic parameters of the Set type (T), rather than the variable declaration (Role).
Nevertheless, the following code should demonstrate what you need:
#SupportedAnnotationTypes({ "xxx.MyAnnotation" })
#SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MongoDocumentAnnotationProcessor extends AbstractProcessor {
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver() || annotations.size() == 0) {
return false;
}
for (Element element : roundEnv.getRootElements()) {
if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) {
System.out.println("Running " + getClass().getSimpleName());
for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) {
if(variableElement.asType() instanceof DeclaredType){
DeclaredType declaredType = (DeclaredType) variableElement.asType();
for (TypeMirror typeMirror : declaredType.getTypeArguments()) {
System.out.println(typeMirror.toString());
}
}
}
}
}
return true; //processed
}
private boolean isAnnotatedWithMongoDocument(Element element) {
return element.getAnnotation(MyAnnotation.class) != null;
}
}
This code should output:
xxx.Role
All the other answers, while having lots of good points. Don't really show you the problem you have and it's solution.
The problem in your code is here
TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection");
if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) {
...
Your type is not extending java.util.Collection but rather java.util.Collection<*>. Let's rewrite the above block to reflect this:
WildcardType WILDCARD_TYPE_NULL = this.typeUtils.getWildcardType(null, null);
final TypeElement collectionTypeElement = this.elementUtils.getTypeElement(Collection.class.getName());
TypeMirror[] typex = {WILDCARD_TYPE_NULL};
DeclaredType collectionType=this.typeUtils.getDeclaredType(collectionTypeElement, typex);
if (typeUtils.isAssignable(typeElement.asType(), collectionType)){
...
That should make it work
Using Java 11 you can cast your TypeMirror to Type.ClassType
This code
// classToIntrospect is a TypeMirror of java.util.List<it.firegloves.sragen.Dog>
(ClassType)classToIntrospect
will be evaluated in
Event dispatcher interface
public interface EventDispatcher {
<T> EventListener<T> addEventListener(EventListener<T> l);
<T> void removeEventListener(EventListener<T> l);
}
Implementation
public class DefaultEventDispatcher implements EventDispatcher {
#SuppressWarnings("unchecked")
private Map<Class, Set<EventListener>> listeners = new HashMap<Class, Set<EventListener>>();
public void addSupportedEvent(Class eventType) {
listeners.put(eventType, new HashSet<EventListener>());
}
#Override
public <T> EventListener<T> addEventListener(EventListener<T> l) {
Set<EventListener> lsts = listeners.get(T); // ****** error: cannot resolve T
if (lsts == null) throw new RuntimeException("Unsupported event type");
if (!lsts.add(l)) throw new RuntimeException("Listener already added");
return l;
}
#Override
public <T> void removeEventListener(EventListener<T> l) {
Set<EventListener> lsts = listeners.get(T); // ************* same error
if (lsts == null) throw new RuntimeException("Unsupported event type");
if (!lsts.remove(l)) throw new RuntimeException("Listener is not here");
}
}
Usage
EventListener<ShapeAddEvent> l = addEventListener(new EventListener<ShapeAddEvent>() {
#Override
public void onEvent(ShapeAddEvent event) {
// TODO Auto-generated method stub
}
});
removeEventListener(l);
I've marked two errors with a comment above (in the implementation). Is there any way to get runtime access to this information?
No, you can't refer 'T' at runtime.
http://java.sun.com/docs/books/tutorial/java/generics/erasure.html
update
But something like this would achieve similar effect
abstract class EventListener<T> {
private Class<T> type;
EventListener(Class<T> type) {
this.type = type;
}
Class<T> getType() {
return type;
}
abstract void onEvent(T t);
}
And to create listener
EventListener<String> e = new EventListener<String>(String.class) {
public void onEvent(String event) {
}
};
e.getType();
You can't do it in the approach you are trying, due to erasure.
However, with a little change in the design I believe you can achieve what you need. Consider adding the following method to EventListener interface:
public Class<T> getEventClass();
Every EventListener implementation has to state the class of events it works with (I assume that T stands for an event type). Now you can invoke this method in your addEventListener method, and determine the type at runtime.