generics - dynamically using types - java

I have a class Column, which describes the column of a SQL-like table:
public interface Column<S extends Schema<S>, T> {
default String encode(final T value) {
return value.toString();
}
}
A column holds a certain type (integer, string, ...) and has a utility function to convert an instance of that type to a string (which is useful for logging purposes).
Next, I have a class Schema which describes the schema of a table:
public interface Schema<S extends Schema<S>> {
List<Column<S, ?>> getColumns();
}
A schema holds a list of columns.
Let's create a concrete schema with just one column:
public static class MySchema implements Schema<MySchema> {
public static final Column<MySchema, Integer> ID = new Column<MySchema, Integer>(){};
#Override
public List<Column<MySchema, ?>> getColumns() {
return Collections.singletonList(ID);
}
}
Next, I have a class MyData which contains data corresponding with a schema:
public static class MyData<S extends Schema<S>> {
public <T> T get(final Column<S, T> column) {
return (T) new Integer(164); // actual implementation left out
}
}
Encoding the value of a column manually is pretty easy:
final MySchema s = new MySchema();
final MyData<MySchema> d = new MyData<>();
System.out.println("encoded identifier: " + MySchema.ID.encode(d.get(MySchema.ID)));
Now, let's try this dynamically:
for (final Column<MySchema, ?> column : s.getColumns()) {
System.out.println("encoded identifier: " + column.encode(d.get(column)));
}
This doesn't work, since d.get(column) is inferred as capture<?>, which is not what Column.encode() accepts.
How can I solve this? I understand what is going wrong here (Column.encode(T value) only accepts a T, which we don't have right now), but I can't find a solution which does not loose the type-garanty we enforce by only accepting a T.
As a fiddle, here is complete code:
public class Test {
public interface Column<S extends Schema<S>, T> {
default String encode(final T value) {
return value.toString();
}
}
public interface Schema<S extends Schema<S>> {
List<Column<S, ?>> getColumns();
}
public static class MyData<S extends Schema<S>> {
public <T> T get(final Column<S, T> column) {
return (T) new Integer(164); // actual implementation left out
}
}
public static class MySchema implements Schema<MySchema> {
public static final Column<MySchema, Integer> ID = new Column<MySchema, Integer>(){};
#Override
public List<Column<MySchema, ?>> getColumns() {
return Collections.singletonList(ID);
}
}
public static void main(final String a[]) {
final MySchema s = new MySchema();
final MyData<MySchema> d = new MyData<>();
System.out.println("encoded identifier: " + MySchema.ID.encode(d.get(MySchema.ID)));
for (final Column<MySchema, ?> column : s.getColumns()) {
System.out.println("encoded identifier: " + column.encode(d.get(column)));
}
}
}

You can create a helper function with a generic parameter T which allows you to have a Column<T> instead of a Column<?>:
public static void main(final String a[]) {
final MySchema s = new MySchema();
final MyData<MySchema> d = new MyData<>();
for (final Column<MySchema, ?> column : s.getColumns())
encode(column, d);
}
private static <T> void encode(Column<T> column, MyData<MySchema> d) {
System.out.println("encoded identifier: " + column.encode(d.get(column)));
}
Another possibility is to use the same trick and provide a convenience method MyData.getEncoded:
public static class MyData<S extends Schema<S>> {
public <T> T get(final Column<S, T> column) {...}
public <T> String getEncoded(final Column<S, T> column) {
return column.encode(get(column));
}
}

Related

Dynamodb attribute converter provider for enhanced type extending Hashmap

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.
}

Creating a Generic method to load dataSet in cucumber using Poiji library

I have a class that loaded data from scenario steps
my first class is LoadUserStepDfn
public class LoadUserStepDfn extends LoadDataStepDfn<User> {
public LoadUserStepDfn(ReadingUserUsingPoiji readingUserUsingPoiji) {
super.readingExcelUsingPoiji = readingUserUsingPoiji;
}
#Given("^Data is loaded from \"([^\"]*)\"$")
public void data_is_loaded_from (String filePath) throws Throwable {
super.data_is_loaded_from(filePath);
}
and it call class named LoadDataStepDfn
public class LoadDataStepDfn<T> {
public List<T> data;
protected ReadingExcelUsingPoiji readingExcelUsingPoiji;
public void data_is_loaded_from (String filePath) throws Throwable {
data = readingExcelUsingPoiji.TransformExcelToClass(filePath);
}
and here is my class that reads excel and store it to java class
public abstract class ReadingExcelUsingPoiji<T> {
public List<T> TransformExcelToClass(String filePath){
PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().addListDelimiter(";").build();
List<T> data = Poiji.fromExcel(new File(filePath), getMyType(), options);
return data;
}
public abstract Class<T> getMyType();
}
the problem that I want to use one class I don't want it to be abstract and use another one wiche is this class
public class ReadingUserUsingPoiji extends ReadingExcelUsingPoiji<User> {
public Class<User> getMyType(){
return User.class;
}
I am trying to understand here, so you dont want #override, but rather 1 method that returns you the type of class to transform to??
Why can't it be that simple... You have a method that determines what class you should use to transform to...
I dont understand why you are using generics...your logic doesnt seem to really care for it? Especially if you have 1 ReadingExcelUsingPoiji class..it really shouldnt care.
public class ReadingExcelUsingPoiji<T> {
public List<T> transformExcelToClass(String filePath, Class<T> classToTransformTo) {
PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().addListDelimiter(";").build();
List<T> data = Poiji.fromExcel(new File(filePath), classToTransformTo, options);
return data;
}
public static void main(String [] args) {
ReadingExcelUsingPoiji genericConverter = new ReadingExcelUsingPoiji();
List<User> listOfUsers = genericConverter.transformExcelToClass("yourFilePath", User.class);
List<Car> listOfCars = genericConverter.transformExcelToClass("yourFilePath", Car.class);
}
}
public class LoadUserStepDfn extends LoadDataStepDfn<User> {
#Given("^Data is loaded from \"([^\"]*)\"$")
public void data_is_loaded_from (String filePath) throws Throwable {
super.data_is_loaded_from(filePath , User.class);
}
}
public class LoadDataStepDfn<T> {
public List<T> data;
protected ReadingExcelUsingPoiji readingExcelUsingPoiji;
protected void data_is_loaded_from(String filePath, Class<T> classToTransformTo) throws Throwable {
data = readingExcelUsingPoiji.transformExcelToClass(filePath, classToTransformTo);
}
}

Generic object factory

I got several objects that are created based on DataObject class.
So I'd like to make generic factory to construct them.
I'v tried something like this:
interface FactoryObject<T> {
T create(DataObject data);
}
public class Factory {
List<FactoryObject> fromDataObjectArray(DataObject[] data, Class<? extends FactoryObject> cls) {
return Arrays.stream(Optional.ofNullable(data).orElse(new DataObject[0]))
.map(d -> cls.create()).collect(Collectors.toList());
}
}
Finally, I'd like to call
List<MyClass> myClasses = Factory.fromDataObjectArray(data, MyClass.class);
But method create() cannot be resolved, how can I achieve what I need?
If I understand you code, you want to create a list of instances; you could do something like that:
<T> List<T> fromDataObjectArray(DataObject[] data, FactoryObject<T> fac) {
return Arrays.stream(Optional.ofNullable(data).orElse(new DataObject[0]))
.map(d -> fac.create(d)).collect(Collectors.toList());
}
UPDATE:
If I understand your comment below, you want a composite factory that will determine from a DataObject what is the actual factory you want to use to create your instance.
You could do something like this:
public class CompositeFactory<T> implements FactoryObject<T> {
private final Function<DataObject,FactoryObject<? extends T>>[] funcs;
public CompositeFactory(
Function<DataObject,FactoryObject<? extends T>>... funcs) {
this.funcs = funcs;
}
#Override
public T create(DataObject data) {
for (Function<DataObject,FactoryObject<? extends T>> func: funcs) {
FactoryObject<? extends T> fac = func.apply(data);
if (fac != null) {
return fac.create(data);
}
}
return null; // or throw an exception
}
}
Another way to do that is conditional factories:
public class ConditionalFactory<T> implements FactoryObject<T> {
private final Predicate<DataObject> cond;
private final FactoryObject<? extends T> ifFac;
private final FactoryObject<? extends T> elseFac;
public ConditionalFactory(Predicate<DataObject> cond,
FactoryObject<? extends T> ifFac,
FactoryObject<? extends T> elseFac) {
this.cond = cond;
this.ifFac = ifFac;
this.elseFac = elseFac;
}
#Override
public T create(DataObject data) {
return (cond.test(data) ? ifFac : elseFac).create(data);
}
}
UPDATE 2:
Exemple: let's say you have the following classes:
class MyClass1 extends MyClass {
public MyClass1(DataObject data) {
}
}
class MyClass2 extends MyClass {
public MyClass2(DataObject data) {
}
}
...
and the corresponding factories:
FactoryObject<MyClass1> fac1 = (data) -> new MyClass1(data);
FactoryObject<MyClass2> fac2 = (data) -> new MyClass2(data);
FactoryObject<MyClass3> fac3 = (data) -> new MyClass3(data);
...
and let's say you can determine the actual class from the value of DataObject.getType():
You could do:
FactoryObject<MyClass> fact = new CompositeFactory<MyClass>(
(data)-> data.getType().equals("value1") ? fac1 : null,
(data)-> data.getType().equals("value2") ? fac2 : null,
(data)-> data.getType().equals("value3") ? fac3 : null,
...
);
you could also do:
FactoryObject<MyClass> fac = new CompositeFactory<MyClass>(
(data)->{
switch (data.getType()) {
case "value1":
return fac1;
case "value2":
return fac2;
...
default:
return null;
}
});
or:
FactoryObject<MyClass> fac = new ConditionalFactory<MyClass>(
(data)->data.getType().equals("value1"), fac1,
new ConditionalFactory<MyClass>(
(data)->data.getType().equals("value2"), fac2,
fac3));

Gson check enum value during deserialization

Assuming the following enum in a Java class:
enum AccessMode {
READ_WRITE,
READ_ONLY,
WRITE_ONLY
};
JSON deserialization works fine with Gson as long as the JSON contains a valid value for the enum field, e.g.:
"access": "READ_WRITE"
Unfortunately, fromJson() does seem to detect invalid enum values in the JSON, such as:
"access": "READ_XXX"
How can I add enum value checking when deserializing a JSON file using Gson?
As of version 2.8.2, Gson does not support such a use case.
I believe it's worthy to be submitted as a suggestion to the Gson development team as a special GsonBuilder configuration method.
The most you can do now is writing a custom enum type adapter that almost duplicates com.google.gson.internal.bind.EnumTypeAdapter functionality but adds the name check.
final class StrictEnumTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory allStrictEnumTypeAdapterFactory = new StrictEnumTypeAdapterFactory(enumClass -> true);
private final Predicate<? super Class<? extends Enum<?>>> isStrictEnumClass;
private StrictEnumTypeAdapterFactory(final Predicate<? super Class<? extends Enum<?>>> isStrictEnumClass) {
this.isStrictEnumClass = isStrictEnumClass;
}
static TypeAdapterFactory get(final Predicate<? super Class<? extends Enum<?>>> isStrictEnumClass) {
return new StrictEnumTypeAdapterFactory(isStrictEnumClass);
}
static TypeAdapterFactory get() {
return allStrictEnumTypeAdapterFactory;
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final Class<? super T> rawType = typeToken.getRawType();
// Skip non-enums
if ( !Enum.class.isAssignableFrom(rawType) ) {
return null;
}
// Check if the enum is supported by the "strict" policy
#SuppressWarnings("unchecked")
final Class<? extends Enum<?>> enumRawType = (Class<? extends Enum<?>>) rawType;
if ( !isStrictEnumClass.test(enumRawType) ) {
return null;
}
// Trivial rawtypes/unchecked casts
#SuppressWarnings({ "rawtypes", "unchecked" })
final TypeAdapter<? extends Enum<?>> strictEnumTypeAdapter = StrictEnumTypeAdapter.get((Class) enumRawType);
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) strictEnumTypeAdapter;
return castTypeAdapter;
}
private static final class StrictEnumTypeAdapter<E extends Enum<E>>
extends TypeAdapter<E> {
private final Class<E> enumClass;
private final Map<String, E> nameToEnumConstant;
private final Map<E, String> enumConstantToName;
private StrictEnumTypeAdapter(final Class<E> enumClass, final Map<String, E> nameToEnumConstant, final Map<E, String> enumConstantToName) {
this.enumClass = enumClass;
this.nameToEnumConstant = nameToEnumConstant;
this.enumConstantToName = enumConstantToName;
}
private static <E extends Enum<E>> TypeAdapter<E> get(final Class<E> enumClass) {
final Map<String, E> nameToEnumConstant = new HashMap<>();
final Map<E, String> enumConstantToName = new HashMap<>();
final Map<String, E> enumNameToEnumConstant = Stream.of(enumClass.getEnumConstants())
.collect(Collectors.toMap(Enum::name, Function.identity()));
Stream.of(enumClass.getFields())
// It can be either a simple enum constant, or an enum constant that overrides
.filter(field -> enumClass.isAssignableFrom(field.getType()))
.forEach(field -> {
final E enumConstant = enumNameToEnumConstant.get(field.getName());
// For compatibility with the original type adapter, we have to respect the #SeriaizedName annotation
final SerializedName serializedName = field.getAnnotation(SerializedName.class);
if ( serializedName == null ) {
nameToEnumConstant.put(field.getName(), enumConstant);
enumConstantToName.put(enumConstant, field.getName());
} else {
nameToEnumConstant.put(serializedName.value(), enumConstant);
enumConstantToName.put(enumConstant, serializedName.value());
for ( final String alternate : serializedName.alternate() ) {
nameToEnumConstant.put(alternate, enumConstant);
}
}
});
return new StrictEnumTypeAdapter<>(enumClass, nameToEnumConstant, enumConstantToName)
.nullSafe(); // A convenient method to handle nulls
}
#Override
public void write(final JsonWriter out, final E value)
throws IOException {
out.value(enumConstantToName.get(value));
}
#Override
public E read(final JsonReader in)
throws IOException {
final String key = in.nextString();
// This is what the original type adapter probably misses
if ( !nameToEnumConstant.containsKey(key) ) {
throw new JsonParseException(enumClass + " does not have an enum named " + key + " at " + in);
}
return nameToEnumConstant.get(key);
}
}
}
Simple test:
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(StrictEnumTypeAdapterFactory.get())
.create();
public static void main(final String... args)
throws IOException {
try ( final JsonReader jsonReader = Resources.getPackageResourceJsonReader(Q49572505.class, "good.json") ) {
System.out.println(gson.<Status>fromJson(jsonReader, Status.class).access);
}
try ( final JsonReader jsonReader = Resources.getPackageResourceJsonReader(Q49572505.class, "bad.json") ) {
try {
gson.<Status>fromJson(jsonReader, Status.class);
throw new AssertionError();
} catch ( final JsonParseException ex ) {
System.out.println(ex.getMessage());
}
}
}
Output:
READ_WRITE
class q49572505.AccessMode does not have an enum named READ_XXX at JsonReader at line 2 column 22 path $.access
You could look # Moshi. I have found it a suitable and straightforward replacement for GSON and it already supports this behavior.
#Lyubomyr_Shaydarlv's solution works, but if you don't want to duplicate GSON's internal code, you can use it as a delegate in a custom TypeAdapterFactory. Run the adapter, and if it returns null, you know the value was invalid. This has the advantage that it inherits and changes to default enum converter.
class StrictEnumTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (!rawType.isEnum()) {
return null;
}
return newStrictEnumAdapter(gson.getDelegateAdapter(this, type));
}
private <T> TypeAdapter<T> newStrictEnumAdapter(
final TypeAdapter<T> delegateAdapter) {
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) throws IOException {
delegateAdapter.write(out, value);
}
#Override
public T read(JsonReader in) throws IOException {
// Peek at the next value and save it for the error message
// if you don't need the offending value's actual name
String enumValue = in.nextString();
JsonReader delegateReader = new JsonReader(new StringReader('"' + enumValue + '"'));
T value = delegateAdapter.read(delegateReader);
delegateReader.close();
if (value == null) throw new IllegalStateException("Invalid enum value - " + enumValue);
return value;
}
};
}
}

How generify class with T and List<T>

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());
}
}

Categories