Iterate Fields of Fields continuously with reflection - java

Please avoid giving answers in Kotlin only and higher than Android 21.
I'm trying to build an API parser that makes use of class hierarchy logic to represent the API hierarchy itself. With this structure I am able to parse the API in an uncomplicated fashion and I was able to achieve this already, but I'd like to improve it further.
I'll begin explaining what I already have implemented.
This is an example URL that my app will receive via GET, parse and dispatch internally:
http://www.example.com/news/article/1105
In the app the base domain is irrelevant, but what comes after is the API structure.
In this case we have a mixture of commands and variables:
news (command)
article (command)
1105 (variable)
To establish what is a command and what is a variable I built the following class structures:
public class API {
public static final News extends AbstractNews {}
}
public class AbstractNews {
public static final Article extends AbstractArticle {}
}
public class Article {
public static void GET(String articleId) {
// ...
}
}
And I iterate through each class after splitting the URL while matching each command to each class (or subclass) starting from the API class. Until I reach the end of the split URL any matches that fail are stored in a separate list as variables.
The process is as follows for the example provided above:
Split URL each forward slash (ignoring the base domain)
/news/article/1105
List<String> stringList = [
news,
article,
1105
];
Iterate each item in the split list and match agains the API structured classes (the following is just a sample example, it is not 100% of what I currently have implemtend):
List<String> variableList = new ArrayList<>();
Class lastClass = API.class;
for (String stringItem : stringList) {
if ((lastClass = classHasSubClass(lastClass, stringItem)) != null) {
continue;
}
variableList.add(stringItem);
}
Once the end of the list is reached I check if the last class contains the request method (in this case GET) and invoke along with the variable list.
Like I said before this is working perfectly fine, but it leaves every class directly exposed and as a result they can be accessed directly and incorrectly by anyone else working on the project, so I am trying to make the hierarchy more contained.
I want to keep the ability to access the methods via hierarchy as well, so the following can still be possible:
API.News.Article.GET(42334);
While at the same time I don't want it to be possible to do the following as well:
AbstractArticle.GET(42334);
I have tried making each subclass into a class instance field instead
public class API {
// this one is static on purpose to avoid having to instantiate
// the API class before accessing its fields
public static final AbstractNews News = new AbstractNews();
}
public class AbstractNews {
public final AbstractArticle Article = new AbstractArticle();
}
public class Article {
public void GET(String articleId) {
// ...
}
}
This works well for the two points I wanted to achieve before, however I am not able to find a way to iterate the class fields in a way that allows me to invoke the final methods correctly.
For the previous logic all I needed to iterate was the following:
private static Class classHasSubClass(Class<?> currentClass, String fieldName) {
Class[] classes;
classes = currentClass.getClasses();
for (final Class classItem : classes) {
if (classItem.getSimpleName().toLowerCase().equals(fieldName)) {
return classItem;
}
}
return null;
}
But for the second logic attempt with fields I was not able to invoke the final method correctly, probably because the resulting logic was in fact trying to do the following:
AbstractArticle.GET(42334);
Instead of
API.News.Article.GET(42334);
I suspect it is because the first parameter of the invoke method can no longer be null like I was doing before and has to be the correct equivalent of API.News.Article.GET(42334);
Is there a way to make this work or is there a better/different way of doing this?

I discovered that I was on the right path with the instance fields, but was missing part of the necessary information to invoke the method correctly at the end.
When iterating the fields I was only using the Class of each field, which was working perfectly fine before with the static class references since those weren't instances, but now it requires the instance of the field in order to work correctly.
In the end the iterating method used in place of classHasSubClass that got this to work is as follows:
private static Object getFieldClass(Class<?> currentClass, Object currentObject, final String fieldName) {
Field[] fieldList;
fieldList = currentClass.getDeclaredFields();
for (final Field field : fieldList) {
if (field.getName().toLowerCase().equals(fieldName)) {
try {
return field.get(currentObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
break;
}
}
}
return null;
}
With this I always keep an instance object reference to the final field that I want to invoke to pass as the 1st parameter (someMethod.invoke(objectInstance);) instead of null.

Related

Java class method with Class and Field as parameters

How can i create a method that accepts Class and Field as parameters? Like this:
List<SomeClassEntity> list = ...;
// Service to make useful things around a list of objects
UsefulThingsService<SomeClassEntity> usefulThingsService = new UsefulThingsService<>();
// Maybe invoke like this. Did't work
usefulThingsService.makeUsefulThings(list, SomeClassEntity.class, SomeClassEntity::getFieldOne);
// or like this. Will cause delayed runtime erros
usefulThingsService.makeUsefulThings(list, SomeClassEntity.class, "fieldTwo");
public class SomeClassEntity {
Integer fieldOne = 10;
Double fieldThree = 0.123;
public Integer getFieldOne() {
return fieldOne;
}
public void setFieldOne(Integer fieldOne) {
this.fieldOne = fieldOne;
}
public Double getFieldThree() {
return fieldThree;
}
public void setFieldThree(Double fieldThree) {
this.fieldThree = fieldThree;
}
}
public class UsefulThingsService<T> {
public void makeUsefulThings(Class<T> someClassBClass, String fieldName) {
// there is some code
}
}
Want to have correct references on compile stage, not at runtime.
Update:
I need code that would look more convenient than this:
Field fieldOne = null;
try {
fieldOne = SomeClassEntity.class.getDeclaredField("fieldOne");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
usefulThingsService.makeUsefulThings(SomeClassEntity.class, fieldOne);
I apologize for the next clarification.
Update 2:
- The service compares the list with the previous list, reveals only the changed fields of objects (list items) and updates these fields in the objects in the original list.
- Currently i use annotation on entity's field that is actually ID of the entity and that ID is used to detect identically entities (old and new) when i need to update field of entity in source list.
- Service detect annotated field and use it for next update process.
- I want to refuse to use annotations and provide an Field directly in constructor of service. Or use something other that could establish a relationship between class and field on compilation stage.
Assuming that you want field access because you want to get and set the value, you’d need two functions:
public class UsefulThingsService<T> {
public <V> void makeUsefulThings(List<T> list, Function<T,V> get, BiConsumer<T,V> set) {
for(T object: list) {
V v = get.apply(object);
// there is some code
set.accept(object, v);
}
}
}
and
usefulThingsService.makeUsefulThings(
list, SomeClassEntity::getFieldOne, SomeClassEntity::setFieldOne);
usefulThingsService.makeUsefulThings(
list, SomeClassEntity::getFieldThree, SomeClassEntity::setFieldThree);
There are, however, some things open. E.g., how is this service supposed to do something useful with the field resp. property, without even knowing its actual type. In your example, both are subtypes of Number, so you could declare <V extends Number>, so the method knows how to extract numerical values, however, constructing an appropriate result object would require specifying another function argument.

How to get a list of all implemented classes in Strategy Pattern?

I want to design a system that allows the user to select from a list of file types to save the file as. I have a class named Word, and an interface named SaveFileType. Every filetype implements SaveFileType which has a saveFile() method. The idea is that when the 'programmer' wants to add a new filetype, none of the code in the application has to be changed.
This is the UML diagram I have made:
The problem that I am facing is the Word class doesn't have the list of all available file types, that I need to display to the user.
Some sample code below:
Word class:
public class Word {
SaveFileAs saveFileAs;
Document currentDocument;
public Word(Document currentDocument) {
this.currentDocument = currentDocument;
}
public void saveFile() {
// Print all available filetypes
// No actual file-saving logic is needed.
}
}
Word97 Class:
public class Word97 implements SaveFileAs {
#Override
public void saveFile(Document currentDocument) {
// Do some Java wizardry here.
System.out.println("Document named '" + currentDocument.getTitle() + "' has been saved as filetype 'Word97' " );
}
}
Main class:
public class Main {
public static void main(String[] args) {
Document notes = new Document("Notes", "This is a note.");
Word wordProgram = new Word(notes);
// saveFile should print out a list of all possible filetypes.
wordProgram.saveFile();
}
}
Strategy is for changing implementation at runtime, you cannot get all implementations. It would be the task of another class. Also you need somehow a method like setStrategy(Strategy) in your Word class, that's why you've chosen the pattern right?
For get all implementations, you could make use of ServiceLoader. I would add an enum in the picture.
So the example codes look like:
method in Word class:
void setSaveFileStrategy(AvailableStrategy strategy){
this.saveFileAs = strategy.strategy();
}
The enum:
enum AvailableStrategy{
Word97( Word97.class),
//.... once new strategy was introduced, you need add an entry here.
WordXml( WordXml.class);
private Class<saveFileAs> strategyClass;
AvailableStrategies(Class<saveFileAs> strategyClass) {
this.strategyClass = strategyClass;
}
saveFileAs strategy() throws IllegalAccessException, InstantiationException {
return strategyClass.newInstance() ;
}
}
I think you know how to get all enum instances (available strategies).
Note that codes were not compiled and tested, just for showing the idea. Exception handlings were ignored.
It would be bad if the Word class knew about all the types. It's the job of another class, even if word uses it. One solution would be to have a new class that maps a string extension to the strategy. And can enumerate those strategies:
public final class DocumentTypeMap implements Iterable<SaveFileAs> {
private final Map<String, SaveFileAs> docTypes = new HashMap<>;
public void register(String extension, SaveFileAs saveFileAs) {
docTypes.put(extension, saveFileAs);
}
public Iterator<SaveFileAs> iterator() {
return docTypes.values().iterator();
}
}
Usage:
DocumentTypeMap map = new DocumentTypeMap();
map.register(".doc", new Word97()); //etc.
Word word = new Word(map); //inject the dependency of a pre-configured map into the word class.
Then when the Word class needs the correct strategy during saving, it can use a method on DocumentTypeMap (not provided here) to get the correct one. I'm thinking that might be by extension.
If you want to be able to add a document type without changing any code, it means that the document type list has to be defined outside your code, in file like a property file and your code has to read the property file to know all available types.
Then you need to add in this property file which class implements how to save a specific document type and you implement a factory which instantiate a class given its name, and a class which associate the right instance according to the chosen type.
For the properties files, you can have entries like:
ext_1=.doc
ext_2=.xml
ext_3=.rtf
class_1=Word97
class_2=WordXML
class_3=RTF ...
A such file is easy to parse to know the types list and which class has to be used to save a document.
To know how to instantiate a class from its name, see the class Class and the method newInstance.
This is an "old way", maybe with injection is there a most up to date solution.
In your UML model, I would add the class which reads the properties file, the class which instantiates a class from its name, and the class which associate the right instance to Word. To model the properties file, maybe an instance objet may be use since a properties file is an instance of ResourceBundle.

ByteBuddy: How to implement field access interceptor?

I'am trying to make a OGM to translate object to Vertex for the OrientDB. Currently i'am using GCLib but i read that ByteBuddy could implements two critical things that if work, it will improve the OGM speed.
Could ByteBuddy implement field access control? I read the doc but it's not clear or I do not understand it.
Dinamically add default empty constructor.
The current problem is this: We do not know the class definition that will be passed as a parameter. The idea is to redefine the class and implement the empty constructor if it not have one, add a field named __BB__Dirty to set the object as dirty if an assign operation was detected and force the implementation of an interface to talk with the object.
Example:
A generic class:
public class Example {
int i = 0;
String stringField;
public Example(Strinf s) {
stringField = s;
}
public void addToI(){
i++;
}
}
Now we have an interface like this:
public interface DirtyCheck {
public boolean isDirty();
}
So, I want to force the Example class to implement the interface, the method isDirty(), a field to work on and a default contructor so the class should be translated to:
public class Example implements DirtyCheck {
int i = 0;
String stringField;
boolean __BB__dirty = false;
public Example() {
}
public Example(Strinf s) {
stringField = s;
}
public void addToI(){
i++;
}
public boolean isDirty() {
return this.__BB__dirty;
}
}
and the some magically assigner so if any field (except __BB__dirty) is modified, the __BB__dirty field is set to True;
I have tried the first part of this but I fail :(
...
ByteBuddyAgent.install();
Example ex = new ByteBuddy()
.redefine(Example.class)
.defineField("__BB__Dirty", boolean.class, Visibility.PUBLIC)
.make()
.load(Example.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
.getLoaded().newInstance();
....
ex.addToI(); // <--- this should set __BB__dirty to true since it
// assign a value to i.
But i get this error:
Exception in thread "main" java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$1.apply(ClassReloadingStrategy.java:297)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:173)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4350)
at Test.TestBB.<init>(TestBB.java:33)
at Test.TestBB.main(TestBB.java:23)
I'am stuck in the very first stage to solve the problem with BB.
Thanks
The Java virtual machine does not support changing the layout of classes that are already loaded when redefining a class. This is not a limitation of Byte Buddy but the VM implementation.
In order to do what you want, you should look at the AgentBuilder API which allows you to modify classes before they are loaded. Creating an agent does however require you to add it explicitly as an agent on startup (opposed to adding the library to the class path.
You can implement the interface by calling:
.implement(DirtyCheck.class).intercept(FieldAccessor.of("__dirty__");
You can also add a default constructor by simply defining one:
.defineConstructor(Visibility.PUBLIC).intercept(SuperMethodCall.INSTANCE)
The latter definition requires the super class to define a default constructor.

Generalized method to get similar object attributes

I have an object which has a few arrays as fields. It's class roughly looks like this:
public class Helper {
InsuranceInvoices[] insuranceInvoices;
InsuranceCollectiveInvoices[] insuranceCollectiveInvoices
BankInvoices[] bankInvoices;
BankCollectiveInvoices[] bankCollectiveInvoices;
}
All of the invoice types have a mutual marker interface Invoices.
I need to get all of the invoices to invoke another method on them.
Helper helperObject = new Helper();
// ...
for (InsuranceInvoices invoice : helperObject.getInsuranceInvoices()) {
Integer customerId = invoice.getCustomerId();
// ...
}
for (BankInvoices invoice : helperObject.getBankInvoices()) {
Integer customerId = invoice.getCustomerId();
// ...
}
// repeat with all array fields
The problem is that all invoices only have the marker interface in common. The method getCustomerID() is not defined by a mutual interface or class. This is a behaviour I cannot change due to a given specification.
The code repetition inside the for-each-loop is something that bugs me. I have to do the exact same thing on all invoice objects in the four different arrays. Hence four for-each-loops that unecessary bloat the code.
Is there a way that I can write a general (private) method? One idea was:
private void generalMethod(Invoice[] invoiceArray){
// ...
}
But this would require four instanceof checks because the class Invoice doesn't know the method getCusomterId(). Therefore I would gain nothing; the method would still contain repetitions.
I'm thankful for every possible solution to generalize this problem!
Possible solutions to generalize the problem (ordered from best to worst):
Using wrapper class
public class InvoiceWrapper {
private String customerID;
public String getCustomerID() {
return customerID;
}
public InvoiceWrapper(BankInvoices invoice) {
this.customerID = invoice.getCustomerID();
}
public InvoiceWrapper(InsuranceInvoices invoice) {
this.customerID = invoice.getCustomerID();
}
// other constructors
}
Upd If I understood correctly, you need to do something with IDs in all arrays. To use InvoiceWrapper, you also need to implement iterator in Helper class, that will walk through arrays and return a wrapper for each entry. So, you will have code that works with 4 arrays anyway.
Using instance of casts
public class CustomerIdHelper {
public static String getID(Invoice invoice) {
if (invoice instanceof InsuranceInvoices) {
return ((InsuranceInvoices) invoices).getCustomerID();
} else if ...
}
}
Calling methods by name via Reflection
public class CustomerIdHelper {
public static String getID(Invoice invoice) {
Method method = invoice.getClass().getDeclaredMethod("getCustomerId");
return (String) method.invoke(invoice);
}
}
It's not pretty, but you could use reflection to look up the getCustomerId Method and then invoke() it, cf. Class.getDeclaredMethod().
private void generalMethod(Invoice[] invoiceArray){
try {
for (Invoice invoice : invoiceArray) {
Method getCustomerId = invoice.getClass().getDeclaredMethod("getCustomerId");
getCustomerId.invoke(invoice);
}
} catch (Exception e) {
// ...
}
}
Do note that this is untested.
If you are not allowed to change the classes you are handling by adding a custom interface to them. The best thing you can do is wrap them with a custom class that does have the desired properties.
This way you will have one class with all 'not so nice' code that converts the classes you can not touch to nice classes that match a proper and useful design.
For instance you could have a class WrappedInsuranceInvoice that extends WrappedInsurace and contains a member field InsuranceInvoice. If you don't need to keep the original class you would be off even better by copying the data. This way you could for instance lose the arrays and use lists instead.

Java - Class type from inside static initialization block

Is it possible to get the class type from inside the static initialization block?
This is a simplified version of what I currently have::
class Person extends SuperClass {
String firstName;
static{
// This function is on the "SuperClass":
// I'd for this function to be able to get "Person.class" without me
// having to explicitly type it in but "this.class" does not work in
// a static context.
doSomeReflectionStuff(Person.class); // IN "SuperClass"
}
}
This is closer to what I am doing, which is to initialize a data structure that holds information about the object and its annotations, etc... Perhaps I am using the wrong pattern?
public abstract SuperClass{
static void doSomeReflectionStuff( Class<?> classType, List<FieldData> fieldDataList ){
Field[] fields = classType.getDeclaredFields();
for( Field field : fields ){
// Initialize fieldDataList
}
}
}
public abstract class Person {
#SomeAnnotation
String firstName;
// Holds information on each of the fields, I used a Map<String, FieldData>
// in my actual implementation to map strings to the field information, but that
// seemed a little wordy for this example
static List<FieldData> fieldDataList = new List<FieldData>();
static{
// Again, it seems dangerous to have to type in the "Person.class"
// (or Address.class, PhoneNumber.class, etc...) every time.
// Ideally, I'd liken to eliminate all this code from the Sub class
// since now I have to copy and paste it into each Sub class.
doSomeReflectionStuff(Person.class, fieldDataList);
}
}
Edit
I picked the accepted answer based on what applied best to my problem, however it seems to me that all three of the current answers have their merits.
No, it's not possible without grabbing the stacktrace (which is imo nastier than your initial approach and for which I would in any way prefer Thread#getStackTrace() above new Exception()).
Rather do that job in a non-static initializer (or the default constructor) of the abstract class where you check the initialized status.
public abstract class SuperClass {
{
if (!isInitialized(getClass())) {
initialize(getClass());
}
}
}
The called methods in turn can be safely static.
yes, I use this often to initialize a static Log variable :
e.g. :
public class Project implements Serializable, Cloneable, Comparable<Project> {
private static final Logger LOG = LoggerFactory.getLogger(Project.class);
...
To get a class at runtime, you could do something along the lines of
public class Test {
public static void main(String[] args) {
try{
throw new Exception();
}
catch(Exception e){
StackTraceElement[] sTrace = e.getStackTrace();
// sTrace[0] will be always there
String className = sTrace[0].getClassName();
System.out.println(className);
}
}
}
Not pretty but will do the job (ripped from http://www.artima.com/forums/flat.jsp?forum=1&thread=155230).
This means you still make a call from the subclass (so is in the stack trace), but you don't need to include the XXX.class as an argument.

Categories

Resources