Home > Software design >  Run function from text file in Java
Run function from text file in Java

Time:07-16

I want to implement a program that reads a text file and executes every line on it.

For example if I have this txt file:

Commands.txt
{
  t.getP().getName()
  t.getP().getAge()
  t.getP().getLocation()
}

And the following Java program:

public class Test {
    private Person p;

    public Test(){
        p = new Person("Jhon", "19", "New York", "Mathematician");
    }

    public Person getP() {
        return p;
    }

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException{
        Test t = new Test();

        // Read txt file with methods
        String line1 = "t.getP().getName()";
        String line2 = "t.getP().getAge()";
        String line3 = "t.getP().getLocation()";

        // Execute methods
        Method m = t.getClass().getMethod(line1);
        m.invoke(t);

    }
}

I am getting the following error:

Exception in thread "main" java.lang.NoSuchMethodException: Test.t.getP().getName()()
    at java.lang.Class.getMethod(Class.java:1786)
    at Test.main(Test.java:24)

I understand that the program does not find any method, but I do not how to fix it.

Thanks in advance!

CodePudding user response:

When you use Java Reflection to invoke methods, you need to pass the name of the function without parentheses. If you have methods that require parameters, you pass those parameters to the reflection method, not the method you are invoking.

Assuming those methods exist in your Person class, removing the parentheses from the method names should work. Also, as @UnholySheep mentioned in the comments, you need to break down your lines in commands.txt so that each line contains the right "command" you need to invoke. For example:

Method m = Class.forName("Person").getMethod("getName");

Your parser should retrieve these parameters is some similar fashion. If you pay attention to the error you are getting, you will notice two sets of parentheses at the end. This is because the API is assuming the method name itself contains the characters ( and ).

UPDATE: I don't know who downvoted this answer, so let me be clearer. You need to break down your commands. You also need to obtain object p from your Test class and then invoke the methods of the Person class.

Method getPerson = t.getClass().getMethod("getP");
Person p = (Person)getPerson.invoke(t);
Method getName = p.getClass().getMethod("getName");
String name = (String)getName.invoke(p);

Each of the String arguments could be in separate lines OR in the same line delimited by some character. For example

getP|getName
getP|get
getP|getAge
getP|getLocation

You can even loop through the lines in the file, tokenize each string and pass the arguments referencing the array of method names.

try (BufferedReader br = new BufferedReader(new FileReader(new File("Commands.txt")))) {
    String line;
    while ((line = br.readLine()) != null) {
       String tokens = line.split("|");
       Method getPerson = t.getClass().getMethod(tokens[0]);
       Person p = (Person)getPerson.invoke(t);
       Method getName = p.getClass().getMethod(tokens[1]);
       String name = (String)getName.invoke(p);
    }
}

In summary:

  1. The class Test doesn't have a method called t.getP().getName().
  2. The parentheses are not part of the method name.

UPDATE #2: I wrote the following to demonstrate how to use reflection to do what I mentioned above AND to get the return types from the methods:

public class Person {
    private final String name;
    private final int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {return name;}
    public int getAge() {return age;}
    
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Person p = new Person("Hector", 54);
        Method getName = p.getClass().getMethod("getName");
        Method getAge = p.getClass().getMethod("getAge");
        System.out.println(getName.invoke(p));
        System.out.println(getAge.invoke(p));
        
        System.out.println("getName() returns "   getName.getReturnType().getName());
        System.out.println("getAge() returns "   getAge.getReturnType().getName());
    }
}

The above code outputs:

Hector
54
getName() returns java.lang.String
getAge() returns int

CodePudding user response:

In comments, you elaborated that your goal is to do things in order to determine whether or not you want to append .toString().

I have a text file with tons of lines, that comes from another program, which is a XML translator to Java kind of, but it is not perfect. So, I want to check that all the methods calls at the end return a String. I have been doing this by hand. At the end the goal is, if the method does not return a String, we just append a .toString() at the end. I want to do this programmaticaly.

With that problem statement, here's an entirely different approach:

  • check nothing
  • skip all of the reflection dynamic code evaluation from textfile stuff
  • simply append .toString() everywhere

Some of the lines will need a .toString() appended, based on what you alluded to in comments, namely that it's an "XML translator to Java kind of, but it is not perfect".

Some of the lines will not need it, but it is fine to tack an additional .toString(). For example, this is completely valid Java:

"aardvark".toString().toString().toString();
  • Related