lundi 23 juillet 2018

Creating the types of elements passed into an array that takes a generic parameter using reflection

What I'm doing

I'm taking reflection to the extreme and making my code reusable. To do so I am parsing CSV files in which the data comes in as Strings. I am converting these Strings to Objects of varying types at runtime dynamically by use of reflection, factory pattern and a properties file. So far I've been able to create code that I think is able to construct Objects out of Strings for array types using the parameter of the array.

The Problem

Is that I do not know how to approach generic array parameters. Since the only data I have is a String, I cannot obtain the type from the data directly, rather I am building the type based on what the constructor needs. The type annotation for the constructor (I created) only shows the generic parameter of Object. I want the type conversion to be driven by the constructor but I want to be able to create Object's various subtypes out of Strings. Somewhere there needs to be some sort of data as to the specific type of each element but I can't think of a good place to put this without strictly writing it as like a second header on my CSV file. I want to avoid that because I'd rather that the Class implementation determine how the data gets used rather than the data file itself. (extreme reusability).

Code Example of process

Example Data from foo.txt:

//header
name,number,email,listOfFriends
//example data where underscores seperate elements of an array (old CSV format)
Bob,42,Billy@example.com,Sally_Sue_Robert

Example Class constructor:

//example class implementation of IObject (layer of abstraction used for 
//decoupling).
public Class Person Implements IObject{

//how the fields are matched in reflection. The data can have less than all 
//the fields required in each file. If I left out the email for example and 
//left an empty space, the field would be constructed as null.
@Annotation(value = {"name","number","email","listOfFriends"},
//how the types are specified. The data comes in as Strings and using this as 
//a guide, constructs Objects according to their type. The problem is 
//ArrayList.class
        type = {String.class,Integer.class,String.class,ArrayList.class})

    public Person (String name, Integer number, String Email, ArrayList<Object> listOfFriends);
}

The Goal and how the arguments come In

I want listOfFriends to be able to take in Generic Objects, but when the data Strings are parsed, it turns them into whatever the element type should be. So if listOfFriends has "Sally_Sue_Fred" as the argument string in the CSV. It gets parsed like this.

listOfFriends argument String:

"Sally_Sue_Fred"

goes through parsing as its marked as an Array type by my code comes out in a String[] as:

"Sally","Sue","Fred"

the reflection finds the parameter type using the @Annotation above in this case it finds Object. I want these to be made into Strings not Objects. if the String contained a number, like:

"Sally_Sue_Fred_5"

I would want something like:

"Sally","Sue","Fred",5

where Sally Sue and Fred are Strings and 5 is an Integer.

The Codeblock that Currently takes in the Array String of "Sally_Sue_Fred" and turns it into an array of 3 Objects

//where 'type' is the argument to a method that takes in a type and a String 
//and parses the string to that 'type'. It is obtained via the @Annotation 
Class parameterofArray = (Class) 
    ((ParameterizedType)type.getClass().getGenericSuperclass())
        .getActualTypeArguments()[0];

Notes

I also want this to work with nested Collections including arrays although my current implementation of String Parsing might not be suitable. I could add "[]" to the String potentially.

What I've done/am thinking

I don't know where to tell my code that "this element should be a String, and this one should be an int" but I need ideas. I could try doing something to the data but I'd prefer not to. I've searched around and found plenty of results on how to Obtain these types in this context. But I haven't seen anything on How to dynamically create them this way. I honestly can't think of anything other than adding the type to the String elements. This kind of defeats the whole "Class specifying how the data is used" thing. So I'd like to somehow do this with the Class thats actually holding the data (in this example its the Person Class). Yes I know this is extreme decoupling. I'm trying to learn how to make reusable code, this is how I'm doing it, by going off the deep end. So far its working well.





Aucun commentaire:

Enregistrer un commentaire