I have a class like below.
public class Login {
private Keyword browser;
private String page;
}
Keyword is a class in different package. I want to get the fully qualified name of the class Keyword while parsing the Login class using javaparser.
You cannot do that using JavaParser because JavaParser does not resolve symbols. To resolve the name Keyword to a class you need to do several things:
* implements proper scope rules (you need to look for internal classes, then for other classes inside the same file, then to consider imports...)
* it depends on the classpath used to compile: changing it the same class could be resolved differently
To do that a symbol resolver should be written: it is not trivial but doable. If you are interested in the subject you can read a post I just wrote How to build a symbol solver for Java, in Clojure. The post contains also a link to the code, freely available on GitHub.
Source: I am a JavaParser contributor
You can use the JavaParser Symbol Solver:
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-symbol-solver-core</artifactId>
<version>3.14.5</version>
</dependency>
Then parse the code using the following configuration:
CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(),
new JavaParserTypeSolver(sourceRoot));
final ParserConfiguration config = new ParserConfiguration()
.setStoreTokens(true)
.setSymbolResolver(new JavaSymbolSolver(combinedTypeSolver));
SourceRoot root = new SourceRoot(Paths.get("/path/to/project/"));
root.parse("", config, (Path localPath, Path absolutePath, ParseResult<CompilationUnit> result) -> {
// Do something with the CompilationUnit
return Result.DONT_SAVE;
});
We can now get the fully qualified identifier of any ReferenceType using:
ResolvedType type = referenceType.resolve();
String qualifiedName = type.getQualifiedName();
Nobody so far appears to have read the question, but if you're parsing the source code, either it is in the current package or it is imported by an import statement.
I would have expected a Java parser writer or user to know that.
I wrote a method that can get the fully qualified name on basis of a ClassOrInterfaceDeclaration object (latest version of JavaParser):
private static String getFullyQualifiedName(ClassOrInterfaceDeclaration c2) {
String name = "";
ClassOrInterfaceDeclaration parentClass = c2.getParentNode().isPresent() ? getClass(c2.getParentNode().get()): null;
if(parentClass!=null) {
name+=getFullyQualifiedName(parentClass)+".";
} else {
CompilationUnit u = getCompilationUnit(c2);
if(u!=null && u.getPackageDeclaration().isPresent()) {
name+=u.getPackageDeclaration().get().getNameAsString()+".";
}
}
return name+c2.getNameAsString();
}
private static ClassOrInterfaceDeclaration getClass(Node n1) {
while (!(n1 instanceof ClassOrInterfaceDeclaration)) {
if(n1.getParentNode().isPresent()) {
n1 = n1.getParentNode().get();
} else return null;
}
return (ClassOrInterfaceDeclaration)n1;
}
private static CompilationUnit getCompilationUnit(Node n1) {
while (!(n1 instanceof CompilationUnit)) {
if(n1.getParentNode().isPresent()) {
n1 = n1.getParentNode().get();
} else return null;
}
return (CompilationUnit)n1;
}
A much simpler version can be used if you obtain the ClassOrInterfaceType of the class:
private static String getFullyQualifiedName(ClassOrInterfaceType e) {
String name = "";
if(e.getScope().isPresent())
name+=getFullyQualifiedName(e.getScope().get())+".";
return name+e.getNameAsString();
}
I hope this is of help to anyone!
If you are using the visitors, it seems that you only get the start and end indexes inside the source file for having the type name. That will not get you the fully qualified name.
So you should implement the visit(ImportDeclaration, A) method also in your custom visitor. This method then must store the import declarations, so you can later - when the method visit(FieldDeclaration, A) gets called - refer to the imported packages and assemble the fully qualified name.
JavaParser does not resolve imports (this isn't usually considered its job, anyway). You have to manually step through the import statements and search if the Keyword belongs to them. Keep in mind that Java implicitly does a import java.lang.*, otherwise you won't resolve types such as String.
Spring Roo has a JavaParserUtils class containing a method getJavaType(CompilationUnitServices compilationUnitServices, ClassOrInterfaceDeclaration cid). The class is not designed for usage outside of Roo, but you can use as a template for solving your problem.
this might be a better solution for your problem,
using instance of.
use of "Instance of" in java
Related
We're in the process of trying to identify everywhere that a specific type of object is used only to get a specific property from it, and pass that property into the method instead.
I'm thinking IntelliJ IDEA's "Structural Search" might be a good tool for this, but I'm not sure how to formulate the search template.
A concrete example:
public class MyClass {
public Long getId() {...}
public void setSomethingElse(int se) {...}
}
public class SomeOtherClasses {
public void shouldBeMatched(MyClass mc) {
doSomething();
mc.getId();
doSomethingElse();
}
public void shouldNotBeMatched(MyClass mc) {
doSomething();
mc.getId();
mc.setSomethingElse(14);
doSomethingElse();
}
public void alsoShouldNotBeMatched(MyClass mc) {
shouldBeMatched(mc);
}
}
In the above example, if I'm looking for methods that only use getId, then I should find shouldBeMatched, but not be bothered with shoudNotBeMatched and alsoShouldNotBeMatched, because they do something with the mc object other than call getId().
I'm thinking IntelliJ IDEA's "Structural Search" might be a good tool for this
And it is indeed. The documentation can be tough though.
Let's check Search templates, filters, and script constraints page. It goes as follows.
Let's say, you have a variable that matches a method, a toString()
method. Then this variable is actually a PsiMethod node. Retrieving
variable.parent will produce a PsiClass node, and so forth.
variable.text then will give you the entire text of the method. If you
just need the name of the method, you can use variable.name.
It seems that the task can be done by choosing the right template and writing a corresponding Groovy script.
The template is called methods of the class and can be found under Existing templates. They provide __context__variable to be used with a script.
We have to be sure matched methods have parameters. It is simple enough, just put a count filter on a $Parameter$ variable.
Then we need to extract the name of a parameter of desired type and see if it is called in the body of the method. The following script will do.
def parameters = __context__.getParameterList().getParameters();
def parameter = parameters.find { p -> p.getType().getName().equals('MyClass') };
if (parameter == null) return false;
String parameterName = parameter.getName();
String methodText = __context__.getText();
String occurrence = "${parameterName}.";
String methodCall = "${parameterName}.getId()";
return methodText.count(occurrence) > 0 && methodText.count(occurrence) == methodText.count(methodCall);
Put it in the $Method$ variable filter and verify the results.
I want to make a Message enumeration with each message being on enum type to avoid errors with typos in the message keys. I also want to use parameters (like #{0}) to be able to insert names and more information.
To make things a lot easier, I would like to add the method get, that has a dynamic number of (string typed) arguments - one for each parameter that I want to replace. The exact number of arguments should be set at compile-time and is defined by a field of that enum value.
Consider this enumeration:
public enum Message {
// Written by hand, ignore typos or other errors which make it not compile.
NO_PERMISSION("no_permission", 0),
YOU_DIED("you_died", 1),
PLAYER_LEFT("player_left", 2);
private String key;
private int argAmount;
Message(String key, int argAmount) {
this.key = key;
this.argAmount = argAmount;
}
public String replace(String... args) {
String message = get();
for (int i = 0; i < args.length; i++) {
message.replace("#{" + i + "}", args[i]);
}
return message;
}
public String get() {
return myConfigFileWrapper.getMessage(key);
}
}
When I want to retrieve a message, I use Message.YOU_DIED.replace(myInformation). However, I would have to lookup how many arguments the YOU_DIED message takes, and if there are multiple, I would need to take a look at the config file to see which index belongs to which parameter type.
To clarify this, here is an example:
The PLAYER_LEFT message is broadcasted to all players and tells them that player x has left with a score of y. In my .lang file, one would find player_left= The player #{0} left with the score #{1}!. In the source code, I will then need to use Message.PLAYER_LEFT.replace(name, score). When my enum is extended now, I probably have more than 100 messages.
This means I simply can not remember if the message was The player #{0} left with the score #{1}! or The player #{1} just left!.
My goal is that the compiler automatically throws an error when the get method is not given the exact amount of arguments it needs. This also means that my IDE autocompletion feature will tell me how many arguments to pass.
As you can see, at the moment I am using varargs to inject the variable information into the message. Why I want to take this a step further should be clear by now. I know that this is a kind of luxury feature, but I am only learning and have no one that expects some sort of result at some time.
One approach would be a Message class with tons of subclasses overriding the original get method with a set number of arguments: get(String name, String score). However, this would make a horrible mess with billions of subclasses - one for each message. I didn't even try to create this sort of Message class(es). Also, using this way would require a lot effort to 'create' all the messages and then later to add new ones.
Next, I looked over the reflection API to make this work, but as soon as I figured that reflection wouldn't work for dynamic compile-time methods, I went on. And as far as I know, actually creating new dynamic methods (and that is what I try to do, basically) is not possible especially as one couldn't use them via normal calls because the method wouldn't exist at compile-time.
The only application doing this thing I know so far is Lombok. Lombok uses annotations which are replaced with byte code at compile-time. I took a look into the source code, but just the core itself is pretty big and has cross dependencies everywhere which make it hard to really understand what is going on.
What is the best and easiest way to generate these methods with a dynamic argument number set at compile-time? And how does that said way work?
Code snippets and links to pages with further information are greatly appreciated.
You could limit the amount of subclasses by creating one general subclass for each distinct number of parameters:
public class Message {
public static final Message0Args NO_PERMISSION = new Message0Args("no_permission");
public static final Message1Arg YOU_DIED = new Message1Arg("you_died");
public static final Message2Args PLAYER_LEFT = new Message2Args("player_left");
private String key;
private int argAmount;
protected Message(String key, int argAmount) {
this.key = key;
this.argAmount = argAmount;
}
// Same replace() method, but make it protected
}
With the subclasses being e.g.:
public class Message2Args extends Message {
public Message2Args(String key) {
super(key, 2);
}
public String replace(String first, String second) {
return super.replace(first, second);
}
}
Note that Message is no longer an enum, but for all practical purposes it works the same way (with some added flexibility such as subclassing), since enum is just syntactic sugar for a class whose only instances are contained in its own public static final fields.
The trouble is that even if you know the number of arguments, you still don't know what they should be. Is it Message.PLAYER_LEFT.replace(name, score) or Message.PLAYER_LEFT.replace(score, name)? Or is it maybe Message.PLAYER_LEFT.replace(name, lastLocation)?
To avoid it, you can go one step further and do something like this:
public abstract class Message<T> {
public static final Message<Void> YOU_DIED = new Message<Void>("You died.") {
#Override
public String create(Void arguments) {
return this.replace();
}
};
public static final Message<Player> PLAYER_LEFT = new Message<Player>("Player %s left with score %d") {
#Override
public String create(Player arguments) {
return this.replace( arguments.getName(), arguments.getScore());
}
};
private Message(String template) {
this.template = template;
}
private final String template;
protected String replace( Object ... arguments) {
return String.format( template, arguments );
}
public abstract String create(T arguments);
}
Admittedly this is quite verbose, but there are a few things going for it:
All messages are typesafe.
You can (indeed you have to) use higher level objects, which hopefully carry more meaning. While it's difficult to figure out what should go in the two String parameters of Message.PLAYER_LEFT, if the only argument is an object of type Player, the answer is quite obvious.
Further to the above, what if halfway through you want to change the message to display the player's nickname or level too? All you need to modify is the actual message, the callers don't have to know about it.
The big downside of it though is that if you've got complex messages (for example Message.PLAYER_HIT, which should take two Player type parameters), you've got to write wrapper classes for the parameters (in our examples one that encapsulates both players). This can be quite tedious.
Personally, I would approach the problem this way, since I'm a strong-type guy
public interface Message
{
public static final Message instance = loadInstance();
String you_died(Player player);
String player_left(Player player, int score);
// etc. hundreds of them
}
// usage
String x = Message.instance.player_left(player, 10);
// one subclass per language
public class Message_jp implements Message
{
public String you_died(Player player){ return player.lastName + "君,你地死啦死啦"; }
// or whatever way you like to create a String
// etc.
}
At runtime, you need to load the proper subclass of Message.
static Message loadInstance()
{
String lang = conf.get("language"); // e.g. "jp"
Class clazz = Class.forName("Message_"+lang); // Message_jp.class
return clazz.newInstance();
}
This approach embeds all messages in class files, which should be fine.
After many many hours of reading and experimenting, I now finally got my own Annotation Processor and Source Code Generator.
Thanks to #biziclop, #bayou.io and #Aasmund Eldhuset for 3 very different as well as great answers to this question explaining smart approaches. This answer is accepted because it is the approach the OP (me) finally used. If you do not want to put as much work into your project as I did, consider looking at them also.
I followed the guide #Radiodef posted in his comment and everything worked great until I got to the point where he explained how to integrate the annotation processor with maven. After some difficulties in the beginning using maven and following that guide, it actually turned out, Apache Maven was and is the best Dependency and Build Management Tool to use for this type of Annotation Processing. So if you also read that guide and use maven, I recommend you skip part 2.
But, now, it’s not about which problems occurred but what one has to do to make it work:
The maven dependencies required: org.apache.velocity:velocity:1.7:jar.
The project setup changes a bit as the actual project with the source will be enclosed in a root container project. This is not necessary but it allows for a cleaner project structure and way more readable POMs.
There are 4 POMs:
RootProject
ActualProject
Annotations
AnnotationProcessors
As said, the RootProject doesn’t contain any source code nor any files but the other projects in general and therefor its pom is simple:
<modules>
<module>ActualProject</module>
<module>Annotations</module>
<module>AnnotationProcessors</module>
</modules>
<!— Global dependencies can be configured here as well —>
The ActualProject obviously depends on both the Annotations artifact as well as the AnnotationProcessors artifact. And because the AnnotationProcessors artifact depends on the Annotation project, we get the following order for the maven reactor:
Annotations
AnnotationProcessors
ActualProject
We also need to configure which projects to perform annotation processors on and which not to. The annotation processor itself shall not be executed during its own compilation, therefore add the compiler argument -proc:none:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<compilerArgs>
<arg>-proc:none</arg>
</compilerArgs>
</configuration>
</plugin>
For the actual project, we will also disable annotation processing the same way during normal compilation and use the maven-processor-plugin together with the build-helper-maven-plugin:
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>2.2.4</version>
<executions>
<!-- Run annotation processors on src/main/java sources -->
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>target/generated-sources</outputDirectory>
<processors>
<processor>my.annotations.processors.MessageListProcessor</processor>
</processors>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.9.1</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>target/generated-sources</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
The Annotation artifact contains most importantly the annotation with the value field which is of type String and an interface the annotated class has to implement as well.
There are two methods the enum has to implement, which are obviously String getKey() and String[] getParams(). Following this the enum from the question (Messages) is extended like this:
#MessageList("my.config.file.wrapper.type")
public enum Messages implements MessageInfo {
NO_PERMISSION("no_permission"),
YOU_DIED("you_died", "score"),
PLAYER_LEFT("player_left", "player_name", "server_name");
private String key;
private String[] params;
Messages(String key, String… params) {
this.key = key;
this.params = params;
#Override
public String getKey() { return key; }
#Override
public String[] getParams() { return params; }
}
Next, to our AnnotationProcessor. Of course, we implement AbstractProcessor and therefore #Override the process method. The class also registers itself with the annotation #SupportedAnnotationTypes("my.annotation.type"). First, we perform some checks against the annotated class. Note that the elements annotated with the annotation are handed in in a set which means there will be a foreach loop. However, it is expected to only find one #MessageList annotation in one project - ever. This is obviously a potential risk, especially when this is used with an unspecific project. Here, it doesn’t matter as we know how to use the Annotation properly.
(One could extend this processor to collect the messages from multiple enums, but it isn’t needed at all.)
for (Element e : roundEnv.getElementsAnnotatedWith(MessageList.class)) {
if (!(e.getKind() == ElementKind.ENUM)) {
raiseErrorAt(e, "Can only annotate enum types");
continue;
} ... }
Next, we would have to check if the annotated class actually implements the interface. Just one little problem: The annotated class isn’t compiled yet. The class object of the MessageInfo interface is obtained quite easily:
Class<MessageInfo> messageInfoClass = (Class<MessageInfo>) Class.forName("my.annotations.MessageInfo");
Yes, this is indeed an unchecked cast, but we use a constant string value so this won’t result in a ClassCastException. Anyway, let’s compile the annotated class. That means, the annotated class musn’t import any other classes which potentially aren’t compiled yet. It shouldn’t because it only serves as a rich resource and could technically also be a .properties file. Again, also a potential risk, and again, we don’t care because we don’t import anything else.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// The convertToPath method just returns "src/main/java/<pathWithSlashes>.java"
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(
new File("ActualProject/" + convertToPath(element.getQualifiedName().toString())));
// The boolean here defines whether the last separator char should be cut off.
// We need to expand the class path so we might as well leave it there.
String classpath = getCurrentClasspath(false) +
new File("Annotations/target/Annotations-version.jar").getAbsolutePath();
File outputDir = new File("ActualProject/target/classes/");
Iterable<String> arguments = Arrays.asList("-proc:none",
"-d", outputDir.getAbsolutePath(),
"-classpath", classpath);
boolean success = compiler.getTask(null, fileManager, null, arguments, null, compilationUnits).call();
fileManager.close();
Finally, the last thing done is to check the value of success and return is it’s false.
Here is the getCurrentClassPath method:
private String getCurrentClasspath(boolean trim) {
StringBuilder builder = new StringBuilder();
for (URL url : ((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs()) {
builder.append(new File(url.getPath()));
builder.append(System.getProperty("path.separator"));
}
String classpath = builder.toString();
return trim ? classpath.substring(0, classpath.length() - 1) : classpath;
}
Now, with the annotated class compiled, we can load it:
URL classesURL = new URL("file://" + outputDir.getAbsolutePath() + "/");
// The current class loader serves as the parent class loader for the custom one.
// Obviously, it won’t find the compiled class.
URLClassLoader customCL = URLClassLoader.newInstance(new URL[]{classesURL}, classLoader);
Class<?> annotatedClass = customCL.loadClass(element.getQualifiedName().toString());
So, here is the check whether the annotated enum implements the interface:
if (!Arrays.asList(annotatedClass.getInterfaces()).contains(messageInfoClass)) {
raiseErrorAt(element, "Can only annotate subclasses of MessageInfo");
continue;
}
Now, read the values the pass to the source code generator:
MessageList annotation = element.getAnnotation(MessageList.class);
String locals = annotation.value();
// To get the package name, I used a while loop with an empty body. Does its job just fine.
Element enclosingElement = element;
while (!((enclosingElement = enclosingElement.getEnclosingElement()) instanceof PackageElement)) ;
String packageName = ((PackageElement) enclosingElement).getQualifiedName().toString();
ArrayList<Message> messages = new ArrayList<>();
for (Field field : annotatedClass.getDeclaredFields()) {
if (!field.isEnumConstant()) continue;
// Enum constants are static:
Object value = field.get(null);
MessageInfo messageInfo = messageInfoClass.cast(value);
messages.add(new Message(field.getName(), messageInfo.getKey(), messageInfo.getParams()));
}
The Message class used here is just a data class with private final fields and respective getter methods. It is found in the annotation artifact, but I am not sure about where to put it.
And that’s it! The Velocity Engine and the Context can now be instanitated and passed the values. The last piece of the puzzle is the Template for the source.
First of all, I created 3 variables but special characters, because I failed terribly at intergrating velocity’s escape tool into my project…
#set ($doubleq = '"')
#set ($opencb = "{")
#set ($closecb = "}“)
package $package;
The the class body is almost just a foreach loop:
/**
* This class was generated by the Annotation Processor for the project ActualProject.
*/
public abstract class Message {
#foreach ($message in $messages)
#set ($args = "")
#set ($replaces = "")
#foreach ($param in $message.params)
#set ($args = "${args}String $param, ")
#set ($replaces = "${replaces}.replace($doubleq$opencb$param$closecb$doubleq, $param)")
#end
#set ($endIndex = $args.length() - 2)
#if ($endIndex < 0)
#set ($endIndex = 0)
#end
#set ($args = $args.substring(0, $endIndex))
public static final String ${message.name}($args) {
return locals.getMessage("$message.key")$replaces;
}
#end
private static final $locals locals = ${locals}.getInstance();
}
That giant set of Velocity directives may seem a bit weird at first glance, but it is really simple. There are no blank lines because they would actually be generated making the generated file quite messy. So what is done? We iterate over all messages. For each message do:
Define two variables of type String, args and replaces
For each parameter the message takes do:
Append the string „String , „ to the args variable.
Append the string „.replace(„{}“, )“ to the params variable.
Remove the last comma and space from the args variable. (When there are no parameters for a message, endIndex has a negative value. If that’s the case, set endIndex to 0.)
Generate the actual method with the name of the enum constant and the argument string generated in 2 and 3.
The method returns the message which is retrieved via the class that handles the different languages with the placeholders replaced.
At the end of the file, we define the instance of the Locals class. My first plan was to use an interface, but that didn’t work out too well, so I just require the class to be a singleton. Third time, this is an other potential risk, third time ignored for the same reason.
Oh, and the raiseErrorAt(Element, String) method you maybe stumbled across is just a wrapper for the imo very long call of processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, element);
I hope this helped. The full project is public here. For the commit referenced to in this post, see here.
If there are any questions or improvements, feel free to comment.
UPDATE: After getting an unexpected-in-a-good-way answer, I've added some context to the bottom of this question, stating exactly how I'll be using these string-function-calls.
I need to translate a string such as
my.package.ClassName#functionName(1, "a string value", true)
into a reflective call to that function. Getting the package, class, and function name is not a problem. I have started rolling my own solution for parsing the parameter list, and determining the type of each and returning an appropriate object.
(I'm limiting the universe of types to the eight primitives, plus string. null would be considered a string, and commas and double-quotes must be strictly escaped with some simple marker, such as __DBL_QT__, to avoid complications with unescaping and splitting on the comma.)
I am not asking how to do this via string-parsing, as I understand how. It's just a lot of work and I'm hoping there's a solution already out there. Unfortunately it's such generic terminology, I'm getting nowhere with searching.
I understand asking for an external existing library is off topic for SO. I'm just hoping to get some feedback before it's shutdown, or even a suggestion on better search terms. Or perhaps, there is a completely different approach that might be suggested...
Thank you.
Context:
Each function call is found within a function's JavaDoc block, and represents a piece of example code--either its source code or its System.out output--which will be displayed in that spot.
The parameters are for customizing its display, such as
indentation,
eliminating irrelevant parts (like the license-block), and
for JavaDoc-linking the most important functions.
This customization is mostly for the source-code presentation, but may also be applied to its output.
(The first parameter is always an Appendable, which will do the actual outputting.)
The user needs to be be able to call any function, which in many cases will be a private-static function located directly below the JavaDoc-ed function itself.
The application I'm writing will read in the source-code file (the one containing the JavaDoc blocks, in which these string-function-calls exist), and create a duplicate of the *.java file, which will subsequently processed by javadoc.
So for every piece of example code, there will be likely two, and possibly more of these string-function-calls. There may be more, because I may want to show different slices of the same example, in different contexts--perhaps the whole example in the overall class JavaDoc block, and snippets from it in the relevant functions in that class.
I have already written the process that parses the source code (the source code containing the JavaDoc blocks, which is separate from the one that reads the example-code), and re-outputs its source-code blindly with insert example-code here and insert example-code-output here markers.
I'm now at the point where I have this string-function-call in an InsertExampleCode object, in a string-field. Now I need to do as described at the top of this question. Figure out which function they want to invoke, and do so.
Change the # to a dot (.), write a class definition around it so that you have a valid Java source file, include tools.jar in your classpath and invoke com.sun.tools.javac.Main.
Create your own instance of a ClassLoader to load the compiled class, and run it (make it implement a useful interface, such as java.util.concurrent.Callable so that you can get the result of the invocation easily)
That should do the trick.
The class I created for this, called com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature, is a significant piece of Codelet, used to translate the "customizer" portion of each taglet, which is a function that customizes the taglet's output.
(Installation instructions. The only jars that must be in your classpath are codelet and xbnjava.)
Example string signatures, in taglets:
{#.codelet.and.out com.github.aliteralmind.codelet.examples.adder.AdderDemo%eliminateCommentBlocksAndPackageDecl()}
The customizer portion is everything following the percent sign (%). This customizer contains only the function name and empty parameters. This implies that the function must exist in one of a few, strictly-specified, set of classes.
{#.codelet.and.out com.github.aliteralmind.codelet.examples.adder.AdderDemo%lineRange(1, false, "Adder adder", 2, false, "println(adder.getSum())", "^ ")}
This specifies parameters as well, which are, by design, "simple"--either non-null strings, or a primitive type.
{#.codelet.and.out com.github.aliteralmind.codelet.examples.adder.AdderDemo%com.github.aliteralmind.codelet.examples.LineRangeWithLinksCompact#adderDemo_lineSnippetWithLinks()}
Specifies the explicit package and class in which the function exists.
Because of the nature of these taglets and how the string-signatures are implemented, I decided to stick with direct string parsing instead of dynamic compilation.
Two example uses of SimpleMethodSignature:
In this first example, the full signature (the package, class, and function name, including all its parameters) are specified in the string.
import com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature;
import com.github.xbn.lang.reflect.InvokeMethodWithRtx;
import java.lang.reflect.Method;
public class SimpleMethodSigNoDefaults {
public static final void main(String[] ignored) {
String strSig = "com.github.aliteralmind.codelet.examples.simplesig." +
"SimpleMethodSigNoDefaults#getStringForBoolInt(false, 3)";
SimpleMethodSignature simpleSig = null;
try {
simpleSig = SimpleMethodSignature.newFromStringAndDefaults(
String.class, strSig, null, null,
null); //debug (on=System.out, off=null)
} catch(ClassNotFoundException cnfx) {
throw new RuntimeException(cnfx);
}
Method m = null;
try {
m = simpleSig.getMethod();
} catch(NoSuchMethodException nsmx) {
throw new RuntimeException(nsmx);
}
m.setAccessible(true);
Object returnValue = new InvokeMethodWithRtx(m).sstatic().
parameters(simpleSig.getParamValueObjectList().toArray()).invokeGetReturnValue();
System.out.println(returnValue);
}
public static final String getStringForBoolInt(Boolean b, Integer i) {
return "b=" + b + ", i=" + i;
}
}
Output:
b=false, i=3
This second example demonstrates a string signature in which the (package and) class name are not specified. The potential classes, one in which the function must exist, are provided directly.
import com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature;
import com.github.xbn.lang.reflect.InvokeMethodWithRtx;
import java.lang.reflect.Method;
public class SimpleMethodSigWithClassDefaults {
public static final void main(String[] ignored) {
String strSig = "getStringForBoolInt(false, 3)";
SimpleMethodSignature simpleSig = null;
try {
simpleSig = SimpleMethodSignature.newFromStringAndDefaults(
String.class, strSig, null,
new Class[]{Object.class, SimpleMethodSigWithClassDefaults.class, SimpleMethodSignature.class},
null); //debug (on=System.out, off=null)
} catch(ClassNotFoundException cnfx) {
throw new RuntimeException(cnfx);
}
Method m = null;
try {
m = simpleSig.getMethod();
} catch(NoSuchMethodException nsmx) {
throw new RuntimeException(nsmx);
}
m.setAccessible(true);
Object returnValue = new InvokeMethodWithRtx(m).sstatic().
parameters(simpleSig.getParamValueObjectList().toArray()).invokeGetReturnValue();
System.out.println(returnValue);
}
public static final String getStringForBoolInt(Boolean b, Integer i) {
return "b=" + b + ", i=" + i;
}
}
Output:
b=false, i=3
This question already has answers here:
How to get the name of the calling class in Java?
(13 answers)
Closed 6 years ago.
I want to get the caller class of the method, i.e.
class foo{
bar();
}
In the method bar, I need to get the class name foo, and I found this method:
Class clazz = sun.reflect.Reflection.getCallerClass(1);
However, even though getCallerClass is public, when I try to call it Eclipse says:
Access restriction: The method getCallerClass() from the type
Reflection is not accessible due to restriction on required library
C:\Program Files\Java\jre7\lib\rt.jar
Are there any other choices?
You can generate a stack trace and use the informations in the StackTraceElements.
For example an utility class can return you the calling class name :
public class KDebug {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
return ste.getClassName();
}
}
return null;
}
}
If you call KDebug.getCallerClassName() from bar(), you'll get "foo".
Now supposing you want to know the class of the method calling bar (which is more interesting and maybe what you really wanted). You could use this method :
public static String getCallerCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String callerClassName = null;
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
if (callerClassName==null) {
callerClassName = ste.getClassName();
} else if (!callerClassName.equals(ste.getClassName())) {
return ste.getClassName();
}
}
}
return null;
}
Is that for debugging ? If not, there may be a better solution to your problem.
StackTrace
This Highly depends on what you are looking for... But this should get the class and method that called this method within this object directly.
index 0 = Thread
index 1 = this
index 2 = direct caller, can be self.
index 3 ... n = classes and methods that called each other to get to the index 2 and below.
For Class/Method/File name:
Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();
For Class:
Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())
FYI: Class.forName() throws a ClassNotFoundException which is NOT runtime. Youll need try catch.
Also, if you are looking to ignore the calls within the class itself, you have to add some looping with logic to check for that particular thing.
Something like... (I have not tested this piece of code so beware)
StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
if(!stes[i].getClassName().equals(this.getClass().getName()))
return stes[i].getClassName();
StackWalker
StackWalker StackFrame
Note that this is not an extensive guide but an example of the possibility.
Prints the Class of each StackFrame (by grabbing the Class reference)
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.forEach(frame -> System.out.println(frame.getDeclaringClass()));
Does the same thing but first collects the stream into a List.
Just for demonstration purposes.
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.walk(stream -> stream.collect(Collectors.toList()))
.forEach(frame -> System.out.println(frame.getDeclaringClass()));
To get caller/called class name use below code, it works fine for me.
String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();
SecurityManager has a protected method getClassContext
By creating a utility class which extends SecurityManager, you can access this.
public class CallingClass extends SecurityManager {
public static final CallingClass INSTANCE = new CallingClass();
public Class[] getCallingClasses() {
return getClassContext();
}
}
Use CallingClass.INSTANCE.getCallingClasses() to retrieve the calling classes.
There is also a small library (disclaimer: mine) WhoCalled which exposes this information. It uses Reflection.getCallerClass when available, else falls back to SecurityManager.
I know this is an old question but I believed the asker wanted the class, not the class name. I wrote a little method that will get the actual class. It is sort of cheaty and may not always work, but sometimes when you need the actual class, you will have to use this method...
/**
* Get the caller class.
* #param level The level of the caller class.
* For example: If you are calling this class inside a method and you want to get the caller class of that method,
* you would use level 2. If you want the caller of that class, you would use level 3.
*
* Usually level 2 is the one you want.
* #return The caller class.
* #throws ClassNotFoundException We failed to find the caller class.
*/
public static Class getCallerClass(int level) throws ClassNotFoundException {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String rawFQN = stElements[level+1].toString().split("\\(")[0];
return Class.forName(rawFQN.substring(0, rawFQN.lastIndexOf('.')));
}
This is the most efficient way to get just the callers class. Other approaches take an entire stack dump and only give you the class name.
However, this class in under sun.* which is really for internal use. This means that it may not work on other Java platforms or even other Java versions. You have to decide whether this is a problem or not.
The error message the OP is encountering is just an Eclipse feature. If you are willing to tie your code to a specific maker (and even version) of the JVM, you can effectively use method sun.reflect.Reflection.getCallerClass(). You can then compile the code outside of Eclipse or configure it not to consider this diagnostic an error.
The worse Eclipse configuration is to disable all occurrences of the error by:
Project Properties / Java Compiler / Errors/Warnings / Enable project specific settings set to checked / Deprecated and restrited API / Forbidden reference (access rules) set to Warning or Ignore.
The better Eclipse configuration is to disable a specific occurrence of the error by:
Project Properties / Java Build Path / Libraries / JRE System Library expand / Access rules: select / Edit... / Add... / Resolution: set to Discouraged or Accessible / Rule Pattern set to sun/reflect/Reflection.
Find below a simple example illustrating how to get class and method names.
public static void main(String args[])
{
callMe();
}
void callMe()
{
try
{
throw new Exception("Who called me?");
}
catch( Exception e )
{
System.out.println( "I was called by " +
e.getStackTrace()[1].getClassName() +
"." +
e.getStackTrace()[1].getMethodName() +
"()!" );
}
}
e has getClassName(), getFileName(), getLineNumber() and getMethodName()...
Since I currently have the same problem here is what I do:
I prefer com.sun.Reflection instead of stackTrace since a stack trace is only producing the name not the class (including the classloader) itself.
The method is deprecated but still around in Java 8 SDK.
// Method descriptor #124 (I)Ljava/lang/Class; (deprecated)
// Signature: (I)Ljava/lang/Class<*>;
#java.lang.Deprecated
public static native java.lang.Class getCallerClass(int arg0);
The method without int argument is not deprecated
// Method descriptor #122 ()Ljava/lang/Class;
// Signature: ()Ljava/lang/Class<*>;
#sun.reflect.CallerSensitive
public static native java.lang.Class getCallerClass();
Since I have to be platform independent bla bla including Security Restrictions, I just create a flexible method:
Check if com.sun.Reflection is available (security exceptions disable this mechanism)
If 1 is yes then get the method with int or no int argument.
If 2 is yes call it.
If 3. was never reached, I use the stack trace to return the name. I use a special result object that contains either the class or the string and this object tells exactly what it is and why.
[Summary]
I use stacktrace for backup and to bypass eclipse compiler warnings I use reflections. Works very good. Keeps the code clean, works like a charm and also states the problems involved correctly.
I use this for quite a long time and today I searched a related question so
i am using the following method to get the caller for a specific class from the stacktrace:
package test.log;
public class CallerClassTest {
public static void main(final String[] args) {
final Caller caller = new Caller(new Callee());
caller.execute();
}
private static class Caller {
private final Callee c;
public Caller(final Callee c) {
this.c = c;
}
void execute() {
c.call();
}
}
static class Callee {
void call() {
System.out.println(getCallerClassName(this.getClass()));
}
}
/**
* Searches the current threads stacktrace for the class that called the given class. Returns {#code null} if the
* calling class could not be found.
*
* #param clazz
* the class that has been called
*
* #return the caller that called the class or {#code null}
*/
public static String getCallerClassName(final Class<?> clazz) {
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
final String className = clazz.getName();
boolean classFound = false;
for (int i = 1; i < stackTrace.length; i++) {
final StackTraceElement element = stackTrace[i];
final String callerClassName = element.getClassName();
// check if class name is the requested class
if (callerClassName.equals(className)) classFound = true;
else if (classFound) return callerClassName;
}
return null;
}
}
I have a package which contains an interface and several classes,some classes in this package implement that interface ,In one class that does not implement that interface,I want to write a method that returns an object of all classes which implement that interface,I dont know the name of classes which implement that interface,how can I write this method?
Generally such functionality is missing in java reflection API. but your can probably implement it yourself pretty easily.
Shortly you can use systemp property java.class.path to get all classpath elements. Split it using property path.separator. Then iterate over the resulted array, read each jar file using JAR API, instantiate each Class and check if your interface isAssignableFrom(theClass).
Here is the code snippet that looks for all BSF engines available in your classpath. I wrote it for my blog post I am working on this days. This code has limitation: it works with jar files only. I believe it is enough to explain the idea.
private static Map<String, Boolean> getEngines() throws Exception {
Map<String, Boolean> result = new HashMap<String, Boolean>();
String[] pathElements = System.getProperty("java.class.path").split(System.getProperty("path.separator"));
for (String pathElement : pathElements) {
File resource = new File(pathElement);
if (!resource.isFile()) {
continue;
}
JarFile jar = new JarFile(resource);
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements();) {
JarEntry entry = e.nextElement();
if(entry.isDirectory()) {
continue;
}
if(!entry.getName().endsWith("Engine.class")) {
continue;
}
String className = entry.getName().replaceFirst("\\.class$", "").replace('/', '.');
try {
if(BSFEngine.class.getName().equals(className)) {
continue;
}
Class<?> clazz = Class.forName(className);
if(BSFEngine.class.isAssignableFrom(clazz) && !clazz.equals(BSFEngine.class)) {
result.put(className, true);
}
} catch (NoClassDefFoundError ex) {
// ignore...
result.put(className, false);
}
}
}
return result;
}
The method returns Map where engine class name is used as a key and boolean value indicates whether the engine is available. Engine is unavailable if it requires additional classes in classpath.
There is no straightforward solution for this problem but I suggest you to use reflections library.
Once you have it, you can just do:
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends SomeClassOrInterface>> subTypes =
reflections.getSubTypesOf(SomeClassOrInterface.class);
If I understand your question correctly, you need a way to check, whether particular class from the list implements some specific interface. You may use Class.getInterfaces() method for that.
If you have troubles with listing all classes from particular package, you may take a look, for example, at this question and at further links there.