Java Parser iterate over methods of a class - java

I would like to know if exists such thing like java parser (like parser xml). I mean from a String like this
String javaToParse = "public class x{void foo(){...} void baar(){...}}"
I could for example get the body (in string format) from foo or the body of all methods etc..
I have some like that
public class Some {
#PostConstruct
public void init1() {
//some to do
}
#PostConstruct
public void init2() {
//some to do2
}
#PostConstruct
public void init3() {
//some to do3
}
//..more..
}
Here there are one or more #PostConstruct
This class is autogenerated and I cannot modified it manually.
I would like to iterate all #PostConstruct methods and put all his bodies into only one #Postcontruct method and export to file and get this:
public class Some {
#PostConstruct
public void init() {
//some to do
//some to do2
//some to do3
}
}
I see that it's possible to do this getting that file as String and operate manually with fors and search manually but maybe there are librearies to do it.
EDIT:
Resolved with JavaParser
If somebody has similar problem here my solution:
public static void createUniquePostContruct(File InputFile,File outputFile) throws FileNotFoundException {
//get class to modified
FileInputStream in = new FileInputStream(ficheroEntradaJava);
// parse the file
CompilationUnit cu = JavaParser.parse(in);
//store methods with #PostContruct
List<MethodDeclaration> methodsPost = new ArrayList<>();
//iterate over all class' methods
cu.accept(new VoidVisitorAdapter<Void>() {
#Override
public void visit(MethodDeclaration method, Void arg) {
//if this method has #PostConstruct
if (method.getAnnotationByName("PostConstruct").isPresent()) {
methodsPost .add(method);
}
}
}, null);
//delete all methods with #PostConstruct
methodsPost.forEach((method) -> method.remove());
//add a unique #PostConstruct method using body of anothers #PostConstruct methods
MethodDeclaration uniqueMethodPostConstruct = new MethodDeclaration(EnumSet.of(Modifier.PUBLIC), new VoidType(), "init");
uniqueMethodPostConstruct.addAnnotation("PostConstruct");
BlockStmt bodyUniqueMethodPostConstruct= new BlockStmt();
metodosPost.forEach(method-> {
method.getBody().get().getStatements().forEach(statement -> {
bodyUniqueMethodPostConstruct.addStatement(statement);
});
});
metodoUnicoPostConstruct.setBody(bodyUniqueMethodPostConstruct);
//get main class and put our method
Optional<ClassOrInterfaceDeclaration> clazz = cu.getClassByName(ficheroEntradaJava.getName().split("\\.")[0]);
clazz.get().addMember(uniqueMethodPostConstruct);
System.out.println(cu.toString());
//write to file
try (PrintWriter out = new PrintWriter(outputFile)) {
out.println(cu.toString());
} catch (FileNotFoundException ex) {
Logger.getLogger(ParserMetodos.class.getName()).log(Level.SEVERE, null, ex);
}
}

Related

Use java operator :: dynamically

I have the following implementation:
private SomeWritter someWritter(String someArgument) {
SomeWritter.Builder builder = SomeWritter.builder(someArguments);
builder = builder.addColumn("colName1", TargetClass::getArg1)
builder = builder.addColumn("colName2", TargetClass::getArg2)
return builder.build();
}
private Builder<T> addColumn(String colName, ToDoubleFunction<T> getter){
//implementation goes here
}
my issue is that I need to iterate over the addColumns call, something among these lines:
private void SomeWritter(String someArgument) {
SomeWritter.Builder builder = SomeWritter.builder(someArguments);
for (Field field : getFilteredFieldsFromClass(TargetClass.class)) {
builder = builder.addColumn(field.getName(), [SOMEHOW GET THE REF TO GETTER HERE])
}
return builder.build();
}
in order to get the refference to the getter, I tryed to do
TargetClass.class.getMethod("getArg1", ...);
this works, but I have a Method, not a ToDoubleFunction.
I need to somehow get that ToDoDoubleFunction, programatically, I want to do the same that the TargetClass:: does, dinamically, not harcoded. any ideas ?
import java.lang.reflect.Field;
public class Main {
static class Example{
double arg1;
int arg2;
}
interface Foo<T>{
double toDouble(T example);
}
public static void addColumn(Foo<Example> foo){
//do nothing
}
public static void main(String[] args) {
final var example = new Example();
for(Field field: Example.class.getDeclaredFields()){
addColumn(example1 -> {
try {
return (double) field.get(example1);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
}
}
That code iterates over all fields of the Example class and uses the reflection inside a lambda.
Side Note. Intellij can replace method references with lambda when you click alt+enter when cursor is on them (Windows).

specify file type in method parameter

I was wondering if it was possible to require a certain type of file in class constructor?
For example:
private class name(File location.txt)
I want to have a different constructor for each type of file format that I am going to support. I could write a method that checks the file and sends it to the appropriate method, but was wondering if it was a possibility to skip that logic?
I hope this mini factory sample would work for you:
public class Test {
private FileReaderFactory fileReaderFactory = new FileReaderFactory();
public static void main(String[] args) {
String fileTxt = "test1.txt";
String filePdf = "test2.pdf";
Test test = new Test();
FileTypeI filereaderTxt = test.fileReaderFactory.createFromExtension(fileTxt);
FileTypeI filereaderPdf = test.fileReaderFactory.createFromExtension(filePdf);
filereaderTxt.readFile();
filereaderPdf.readFile();
}
public interface FileTypeI {
void readFile();
}
class TextFileReader implements FileTypeI {
#Override
public void readFile() {
//Code to read Text File
}
}
class PDFFileReader implements FileTypeI {
#Override
public void readFile() {
//Code to read PDF File
}
}
class FileReaderFactory {
public FileTypeI createFromExtension (String filename) {
FileTypeI returningValue = null;
if (filename != null && filename.endsWith(".txt")) {
returningValue = new TextFileReader();
}
else if (filename != null && filename.endsWith(".pdf")) {
returningValue = new PDFFileReader();
}
return returningValue;
}
}
}

Java Reflection: Find method usage in custom AbstractProcessor

I'm newbie in reflection. Is there any way to detect where is an specific method invoked? For example:
public class MyClass {
public static void method(){
//DO SOMETHING
}
}
public class Test {
public test(){
MyClass.method();
}
}
public class MyProcessor extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Method method = MyClass.class.getDeclaredMethod("method");
Class classWhereMethodIsInvoked = obtainClassWhereMethodIsInvoked(method);
}
public Class obtainClassWhereMethodIsInvoked(Method method) {
//here I want to search one class that invoke that method, in this case Test.class
}
}
is something like this possible or I am going crazy?
As mentioned in the comments, Apache BCEL is suitable for your problem. Such libraries are often particularly used for determining compile-time information such as method usage and control flow analysis from the generated bytecode, and such information are difficult, if not impossible, to retrieve using reflection. If you use the BCEL solution, you probably no longer require a custom annotation processor.
But since you already seem to be using a custom annotation processor, the whole point of it is to be able to process annotations in the source files. So one way is to define a custom annotation that marks a method being called, and have the custom processor read these annotations to know which classes call which methods:
#CallerClass("MyClass.method")
public class Test {
public test() {
MyClass.method();
}
}
In the above (trivial) example, a custom CallerClass annotation marks that a class calls the method specified in the annotation's element inside parentheses. The annotation processor can read this annotation and construct the caller information.
Yes it doable if you really want it. You can use the classLoader to search through the class path and scan for the method name through all the class files. Below is a very simplistic example to show that it is doable. In the example below I find usage of the "println" method being used in this class. Essentially you can just broaden the scope from one file in my example to all the class files.
public class SearchClasses {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws FileNotFoundException {
// InputStream is = SearchClasses.class.getClassLoader().getResourceAsStream("resources.SearchClasses.class");
InputStream is = new FileInputStream(new File("build/classes/resources/SearchClasses.class"));
boolean found = false;
Scanner scanner = new Scanner(is);
while (scanner.hasNext()) {
if (scanner.nextLine().contains("println")) {
System.out.print("println found");
found = true;
break;
}
}
if (!found) {
System.out.print("println NOT found");
}
}
public static void testMethod() {
System.out.println("testing");
}
}
In my IDE I had to use the FileInputStream to access the class file I was searching in.... but if you are searching through jar files then you can use the classLoader instead. You would need mechanism to search through all of the class path... this is not impossible but I left it our for brevity.
EDIT: Here is an attempt to get it working completely.. searches all files in class path for your method.
public class SearchClasses {
/**
* #param args the command line arguments
* #throws java.io.FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException, IOException {
printAllFileWithMethod("println");
}
public static void printAllFileWithMethod(String methodName) throws FileNotFoundException, IOException {
Enumeration<URL> roots = SearchClasses.class.getClassLoader().getResources("");
List<File> allClassFiles = new ArrayList<>();
while (roots.hasMoreElements()) {
File root = new File(roots.nextElement().getPath());
allClassFiles.addAll(getFilesInDirectoryWithSuffix(root, "class"));
}
for (File classFile : allClassFiles) {
InputStream is = new FileInputStream(classFile);
boolean found = false;
Scanner scanner = new Scanner(is);
while (scanner.hasNext()) {
if (scanner.nextLine().contains(methodName)) {
System.out.print(methodName + " found in " + classFile.getName() + "\n");
found = true;
break;
}
}
}
}
public static void testMethod() {
System.out.println("testing");
}
static List<File> getFilesInDirectoryWithSuffix(File dir, String suffix) {
List<File> foundFiles = new ArrayList<>();
if (!dir.isDirectory()) {
return foundFiles;
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
foundFiles.addAll(getFilesInDirectoryWithSuffix(file, suffix));
} else {
String name = file.getName();
if (name.endsWith(suffix)) {
foundFiles.add(file);
}
}
}
return foundFiles;
}
}
You could define your own mechanism. Use a Map to store the caller of each method :
public static Map<Method, List<String>> callStack = new HashMap<Method, List<String>>();
public static void registerCaller(Method m)
{
List<String> callers = callStack.get(m);
if (callers == null)
{
callers = new ArrayList<String>();
callStack.put(m, callers);
}
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
callers.add(stackTraceElements[3].getClassName());
}
The target class :
class MyClass
{
public static void method()
{
registerCaller(new Object(){}.getClass().getEnclosingMethod());
// DO SOMETHING
}
}
Some caller classes :
package the.package.of;
class Test
{
public void test()
{
MyClass.method();
}
}
class Foo
{
public void bar()
{
MyClass.method();
}
}
And finally, the test :
new Test().test();
new Foo().bar();
Method method = MyClass.class.getDeclaredMethod("method");
for (String clazz : callStack.get(method))
{
System.out.println(clazz);
}
Prints :
the.package.of.Test
the.package.of.Foo
Well, if you use Eclipse as an IDE, you can find the complete call hierarchy via "Open Call Hierarchy" function. This will find all usages of your method in any open Eclipse projects.
However, if you want to find out during runtime programmatically, then you need to integrate some library, that can statically analyze the bytecode of your classpath for use of your method.
You can obtain stack trace right inside the test method:
public class Test {
public void test() {
System.out.println(getCallerClass());
}
public static String getCallerClass() {
for (StackTraceElement e: Thread.currentThread().getStackTrace()) {
if (!"java.lang.Thread".equals(e.getClassName()) && !e.getClassName().equals(Test.class.getName()))
return e.getClassName();
}
return null;
}
}

Java - Execute a class method with a specify annotation

I have a android application, but it is not relevant.
I have a class called "Front controller" which will receive some message
through it's constructor. The message, for brievity, could be an integer.
I want somewhere else to create a new controller which will execute
a method based on the integer defined above
public class OtherController {
#MessageId("100")
public void doSomething(){
//execute this code
}
#MessageId("101")
public void doSomethingElse(){
//code
}
}
The front controller could be something like this:
public class FrontController {
private int id;
public FrontController(int id){
this.id=id;
executeProperControllerMethodBasedOnId();
}
public void executeProperControllerMethodBasedOnId(){
//code here
}
public int getId(){
return id;
}
}
So, if the Front Controller will receive the integer 100, it
will execute the method annotated with #MessageId(100). The
front controller don't know exactly the class where this method
is.
The problem which I found is that I need to register somehow
each controller class. I Spring I had #Component or #Controller
for autoloading. After each controllers are register, I need to
call the properly annotated method.
How to achieve this task? In Spring MVC, I had this system
implemented, used to match the HTTP routes. How could I implement
this in a plain java project?
Any suggestions?
Thanks to Google Reflections (hope you can integrate this in your android project.)
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections-maven</artifactId>
<version>0.9.8</version>
</dependency>
For optimisation I've added the requirement to also annotate the class with MessageType annotation and the classes should be in the same package (org.conffusion in my example):
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MessageType {
}
The OtherController looks like:
#MessageType
public class OtherController {
#MessageId(id=101)
public void method1()
{
System.out.println("executing method1");
}
#MessageId(id=102)
public void method2()
{
System.out.println("executing method2");
}
}
The implementation will look like:
public void executeProperControllerMethodBasedOnId() {
Set<Class<?>> classes = new org.reflections.Reflections("org.conffusion")
.getTypesAnnotatedWith(MessageType.class);
System.out.println("found classes " + classes.size());
for (Class<?> c : classes) {
for (Method m : c.getMethods()) {
try {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object o = c.newInstance();
if (mid.id() == id)
m.invoke(o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Maybe you can optimise and build a static hashmap containing already scanned message ids.
You need to implement some of the work by yourself using reflection, I would recommend to prepare message handlers on initial phase in regards to performance. Also you possibly want to think about Singleton/Per Request controllers. Some of the ways to implement the solution:
interface MessageProcessor {
void execute() throws Exception;
}
/* Holds single instance and method to invoke */
class SingletonProcessor implements MessageProcessor {
private final Object instance;
private final Method method;
SingletonProcessor(Object instance, Method method) {
this.instance = instance;
this.method = method;
}
public void execute() throws Exception {
method.invoke(instance);
}
}
/* Create instance and invoke the method on execute */
class PerRequestProcessor implements MessageProcessor {
private final Class clazz;
private final Method method;
PerRequestProcessor(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
public void execute() throws Exception {
Object instance = clazz.newInstance();
method.invoke(instance);
}
}
/* Dummy controllers */
class PerRequestController {
#MessageId(1)
public void handleMessage1(){System.out.println(this + " - Message1");}
}
class SingletonController {
#MessageId(2)
public void handleMessage2(){System.out.println(this + " - Message2");}
}
class FrontController {
private static final Map<Integer, MessageProcessor> processors = new HashMap<Integer, MessageProcessor>();
static {
try {
// register your controllers
// also you can scan for annotated controllers as suggested by Conffusion
registerPerRequestController(PerRequestController.class);
registerSingletonController(SingletonController.class);
} catch (Exception e) {
throw new ExceptionInInitializerError();
}
}
private static void registerPerRequestController(Class aClass) {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
processors.put(mid.value(), new PerRequestProcessor(aClass, m));
}
}
}
private static void registerSingletonController(Class aClass) throws Exception {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object instance = aClass.newInstance();
processors.put(mid.value(), new SingletonProcessor(instance, m));
}
}
}
/* To process the message you just need to look up processor and execute */
public void processMessage(int id) throws Exception {
if (processors.containsKey(id)) {
processors.get(id).execute();
} else {
System.err.print("Processor not found for message " + id);
}
}
}

Can an inherited method call an overriding method instead of the original?

Imagine the following situation, where an inherited method that calls a method of the superclass must call a method of the child class instead:
// super.java
public class Processor {
public void process(String path) {
File file = new File(path);
// some code
// ...
processFile(file);
}
protected void processFile(File file) {
// some code
// ...
reportAction(file.name());
}
protected void reportAction(String path) {
System.out.println("processing: " + path);
}
}
// child.java
public class BatchProcessor extends Processor {
public void process(String path) {
File folder = new File(path);
File[] contents = folder.listFiles();
int i;
// some code
// ...
for (i = 0; i < contents.length; i++) super.processFile(file);
}
protected void reportAction(String path) {
System.out.println("batch processing: " + path);
}
}
Obviously, the code presented above doesn't work as it should. The class BatchProcessor prints "processing: <file>" instead of "batch processing: <file>" as it calls the method from the superclass instead of the new one. Is there any way to overcome this obstacle?
Thanks in Advance! :D
Try this :
Processor processor = new Processor();
processor.process("filePath"); // will print "processing: <file>"
// and
Processor batchProcessor = new BatchProcessor();
batchProcessor.process("filePath"); // will print "batch processing: <file>"
this is how polymorphic methods work. I guess you are just not calling processor on subclass instance ?
edit
Please run the following code for a quick proof for yourself:
class Parent {
void test() {
subTest();
}
void subTest() {
System.out.println("subTest parent");
}
}
class Child extends Parent {
void subTest() {
System.out.println("subTest Child");
}
public static void main(String... args) {
new Child().test(); // prints "subTest Child"
}
}
Here is what happens when you call superClass processFile method on your subClass instance:
your this reference across this call will refer to your subClass instance, always resulting in polymorphic call of subClass's methods if they are overriden.
You can remove reportAction() from processFile() and call it separately if likely to change:
// super.java
public class Processor {
public void process(String path) {
File file = new File(path);
// some code
// ...
processFile(file);
reportAction(file.name());
}
protected void processFile(File file) {
// some code
// ...
}
protected void reportAction(String path) {
System.out.println("processing: " + path);
}
}
// child.java
public class BatchProcessor extends Processor {
public void process(String path) {
File folder = new File(path);
File[] contents = folder.listFiles();
int i;
// some code
// ...
for (i = 0; i < contents.length; i++)
{
super.processFile(file);
reportAction(file.name());
}
}
protected void reportAction(String path) {
System.out.println("batch processing: " + path);
}
}

Categories