Iso
Iso

Reputation: 3758

How to Read Java File Structure using Java?

I'm trying to read a java file and display in console the package, class and method name. something like this:

File: Test.java

package tspec.test;

public class Test {
   public void addTest () {}
   public void deleteTest () {}
}

Output:

package name: tspec.test
class name: Test
method name:
addTest
deleteTest

Thanks in advance :)

Upvotes: 4

Views: 5204

Answers (5)

Adam Paynter
Adam Paynter

Reputation: 46908

This can be accomplished using the Java Compiler API (introduced in Java 6). Unfortunately, this solution is limited to Sun's JDK. Therefore, you will have to have that JDK installed and you must include its tools.jar file in your class path.

public void displayInformation(File javaSourceFile) throws Exception {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    // The file manager locates your Java source file for the compiler. Null arguments indicate I am comfortable with its default behavior.
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

    // These will be parsed by the compiler
    Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(javaSourceFile);

    // Creates a new compilation task. This doesn't actually start the compilation process.
    // Null arguments indicate I am comfortable with its default behavior.
    CompilationTask task = compiler.getTask(null, null, null, null, null, fileObjects);

    // Cast to the Sun-specific CompilationTask.
    com.sun.tools.javac.api.JavacTaskImpl javacTask = (com.sun.tools.javac.api.JavacTaskImpl) task;

    // The Sun-specific JavacTaskImpl can parse the source file without compiling it, returning 
    // one CompilationUnitTree for each JavaFileObject given to the compiler.getTask call (only one in our case).
    Iterable<? extends CompilationUnitTree> trees = javacTask.parse();
    CompilationUnitTree tree = trees.iterator().next();

    // Create a class that implements the com.sun.source.tree.TreeVisitor interface.
    // The com.sun.source.util.TreeScanner is a good choice because it already implements most of the logic.
    // We just override the methods we're interested in.
    class MyTreeVisitor extends TreeScanner<Void, Void> {

        @Override
        public Void visitClass(ClassTree classTree, Void p) {
            System.out.println("class name: " + classTree.getSimpleName());
            System.out.println("method name:");
            return super.visitClass(classTree, p);
        }

        @Override
        public Void visitMethod(MethodTree methodTree, Void p) {
            System.out.println(methodTree.getName());
            return super.visitMethod(methodTree, p);
        }

    }

    tree.accept(new MyTreeVisitor(), null);
}

When I pass this method a File whose content is your sample, I receive this output:

class name: Test
method name:
addTest
deleteTest

Unfortunately, I haven't yet figured out where the package name is stored.

Upvotes: 6

Iain Samuel McLean Elder
Iain Samuel McLean Elder

Reputation: 20934

You don't have to do this by parsing the Java file yourself! Java already contains a way of getting information about its own classes, methods, and packages: it's called reflection.

Have a look at the java.lang.Class class. Each instance of this class represents a particular Java class, and contains methods to return the class name, the package it lives in, the methods it contains, and lots more information.

Also worth looking at is the java.lang.reflect package, since some of the methods of Class return types from this package. The package contains classes to represent things like methods, types, fields, and so on.

To obtain a Class instance of your Test class, you can use the following code:

Class<?> testclass = Class.forName("tspec.test.Test");

This returns a class of an unknown type, which is what the question mark inside the angle brackets means if you're not familiar with generics. Why the type of the class instance is unknown is because you specify the class name with a string, which is parsed at runtime. At compile-time, Java cannot be sure that the string passed to forName even represent a valid class at all.

However, testclass as defined above will be fine for getting the class's name, methods, and containing package.

Upvotes: 0

rlb.usa
rlb.usa

Reputation: 15043

It's purpose is to introspect Java code and report back about it's contents. With Reflection you can do things like :

 Class.forName(className).getDeclaredMethods();
  • Java also has the Java Mirror API with similiar functionality, but is not as commonly used.

Both of these solutions require no 3rd party libraries or tools.

Upvotes: 3

Kenji
Kenji

Reputation:

We use PMD Java code analyzer to solve similar problem. It is useful.

http://pmd.sourceforge.net/

Upvotes: 0

zs2020
zs2020

Reputation: 54514

The only difficult is the java code may not be well formatted. like the function declaration can be spread on multiple lines.

The ultimate solution is to create an automata to tokenize the source code first and then apply some compiler technique to grab what you want from the parsed data.

Upvotes: 0

Related Questions