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 :)
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.
Reflection and Introspection Java API's.
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.
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.
We use PMD Java code analyzer to solve similar problem.
It is useful.
http://pmd.sourceforge.net/
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.
Related
I'm trying to use the Java TreePathScanner API to determine the list of class files that will be generated from a given compilation. For example, the following source code:
public class InnerClass {
private final InnerInnerClass clazz = new InnerInnerClass();
private class InnerInnerClass {
}
}
will generate the following files:
InnerClass.class
InnerClass$1.class
InnerClass$InnerInnerClass.class
However, in my TreePathScanner subclass, visitClass is only called twice, for the InnerClass class, and the InnerInnerClass classes, but not the anonymously named class created from the new class statement. Changing the source to the following works as expected:
public class InnerClass {
private final InnerInnerClass clazz = new InnerInnerClass() { };
private class InnerInnerClass {
}
}
My tool's full source code is available here for reference, specifically ArtifactScanner.java.
Either this is a bug or a flaw in the API as there doesn't seem to be any other way to get all of the binary names that will be generated from a given compilation unit's source code. Am I missing something?
One of the JDK developers explained on this bug report that the observed behavior really is not a bug, and that the additional class files are generated as a result of the Java compiler backend which rewrites more complex language constructs into simpler ones before generating class files.
The TreePathScanner API therefore does produce the correct output in this case, and the short of it is that TreePathScanner is the wrong solution to use for my use case (determining the list of class files that will be produced) and that com.sun.source.util.TaskListener, TaskEvent, and TaskEvent.Kind, and JavaFileManager.inferBinaryName should be used instead.
I can't find a single example of how #:classCode is used (to inject a native member directly into the file). This fails with Unexpected untyped at compile time:
#:classCode
untyped __java__("public void paintComponent(Graphics g) {doStuff();}");
I can't simply use standard Haxe code to have a paintComponent() method because of the way Haxe eats #:overload methods (and forces use of #:overload in the first place).
There are some examples of it in the Haxe standard library, for instance:
#:classCode('override public string Message { get { return this.toString(); } }\n\n')
#:nativeGen #:keep #:native("haxe.lang.HaxeException")
private class HaxeException extends Exception
Looks like the class code metadata has a string argument containing the code to be inserted and should be used on a class declaration.
Because a single Java file can compile into any number of class files, is there some way from the compiler API to find out which class files were generated? It's outputting to a directory that may have other files in it, so I can't just look at that.
I figured out something that appears to work. The *FileManager has callbacks to get the locations for things, including things for output. You can wrap it using the ForwardingJavaFileManager, override, and store the values from the calls.
final List<String> classFiles = new ArrayList<>();
StandardJavaFileManager inner = compiler.getStandardFileManager(null, null, null);
JavaFileManager fileManager = new ForwardingJavaFileManager(inner) {
#Override
public JavaFileObject getJavaFileForOutput(Location location, String className,
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
JavaFileObject o = super.getJavaFileForOutput(location, className, kind, sibling);
classFiles.add(o.getName());
return o;
}
};
javax.tools package shows several ways to manage compilation units.
See JavaFileObject or JavaFileManager
I don't think we can find the non public classes created by Java Compiler API using any of the given references. You would need to parse (or apply reg expression ) on the input java files to identify the available class names. Once you get the name of the classes, you should be able to load them using custom class loaders.
Satheesh
I've got a library that allows clients to provide a list of text files, each of which contains groovy code for a class that extends java class Z. For instance file 'A.groovy' contains
package com.mypkg;
public class A extends Z {
#Override
public void someMethod() {
// do something A-ish
}
}
etc.
The library compiles each of these and (in this case) would return to the clients an instance of type Z.
My issue comes when a client needs something like this:
package com.mypkg;
public class B extends A { // extends A!
#Override
public void someMethod() {
// do something B-ish instead of A-ish
}
}
where B extends A, and class A was parsed before class B.
The issue is that the GroovyClassLoader can't seem to find class A, even though it just parsed A. Here's the code that compiles the scripts and creates the instances:
for (String fileName : listOfScriptFiles) {
InputStream in = getInputStreamFromFile(fileName);
CompilerConfiguration compConfig = new CompilerConfiguration();
GroovyClassLoader classLoader = new GroovyClassLoader(Thread.currentThread()
.getContextClassLoader(), compConfig);
Z service = null;
Class clazz = classLoader.parseClass(in);
service = (Z) clazz.newInstance();
return service;
}
Is there a way to 'register' class A with the runtime so that when Groovy tries to compile class B it will not complain that class A doesn't exist?
UPDATE
I was actually able to solve this by instantiating the GroovyClassLoader outside the loop that iterates through the client's code list, so the classloader that parses A is the same that parses B.
The question still stands, though, because I could envision a case where in one part of someone's code they parse A, and then in a completely different part, where the same classloader is unavailable, they parse B.
In my experience with the Groovy classloader (which is similar in behavior with Ant and beanshell's classloader in this respect) , you have to decide up front whether you are going to use the default system classloader, in which case you would build the classpath into the command that launches the Groovy script, OR on the other hand, you specify ONLY the groovy jar on the command line classpath and then you dynamically add classes at the beginning of your Groovy script on the custom classloader.
You aren't providing much information in your question, but my guess is that you put class "A" on the classpath before you launched the script and then your trying to load class "B" dynamically. That wouldn't work as far as I know.
NOTE: I myself have been trying to figure out how to do this kind of thing. It seems it would be possible but I still haven't figured it out.
There are two classes A and B in the same package.
how to get name of the functions of class A called by class B.
Restriction is that the code of A and B cannot be modified.
You cannot use Java reflection to do static code analysis tasks like this. The reflection APIs don't provide the information that is needed.
Off the top of my head, I can think of two approaches that will work:
If you only have the ".class" files, then you can use a bytecode library such as BCEL to load the classes and traverse them to identify all of the method calls.
If you have source code, you could also use some existing Java parser library to create ASTs for your code and analyse them.
(The first approach is probably simpler if all you want is a list of class and method names.)
Reflection can be very useful, but very complicated if you don't understand it.
If you have the name of the class, and want to print the methods:
Class c = Class.forName(the_class_name);
for(Method m : c.getMethods()) {
System.out.println(m.toString());
}
If you want the name of a class given any Object:
String className = the_object.getClass().getName();
A combination of the two could look like this:
for(Method m : the_object.getClass().getMethods())
System.out.println(m.toString());
I think what you are asking for is the names of all the methods from A that B calls.
That can't really be done with reflection, mostly because Java doesn't provide any method for doing this.
The API, as always, provides more information. If you look through there, you might come up with a simple work around.
"Class of all the functions called by class A or class B" is confusing. But, If You want to get the class and function name of caller to a method detectsName described in your class A , then following code will be useful to you.
public class A {
public void detectsName() {
Throwable t = new Throwable();
StackTraceElement traceLine = t.getStackTrace()[1];
// t.printStackTrace();
String className = traceLine.getClassName();
String methodName = traceLine.getMethodName();
int lineNumber = traceLine.getLineNumber();
System.out.println(className);
System.out.println(methodName);
System.out.println(lineNumber);
}
}
If you call this method from any other class say - B, it will be detected.
public class B {
public static void main(String[] args) {
A a = new A();
a.detectsName();
}
}
Dependency Finder can do queries for this. Its approach is to generate meta data in XML and then use regexp based comparison. You may be specifically looking for feature called 'closure' with inbound reference..