How can I retrieve the fully qualified name of a superinterface of a ClassTree without using symbolType() nor visiting the import node ?

151 views
Skip to first unread message

Michel Pawlak

unread,
Jun 18, 2015, 11:43:53 AM6/18/15
to sona...@googlegroups.com
Hi,

I have the following code :

import a.b.c.AnInterface;

public class AClass implements AnInterface {
        // something to check here that is not important in the context of this post
}

I need to write a custom java check (IssuableSubscriptionVisitorthat only applies to classes directly implementing a.b.c.AnInterface and do not want my check to depend on the jar containing a.b.c.AnInterface (see alternative 1 below). Therefore I want to do a simple String comparison of the interface's fullyQualifiedName. However I cannot find a way to simply retrieve the fully qualified name of AClass's interfaces (see alternative 2).

Here is a code snippet :

public class MyCheck extends IssuableSubscriptionVisitor {
//...
@Override
public void visitNode(final Tree tree) {
//...
if (tree.is(Kind.CLASS) {
ClassTree classTree = (ClassTree) tree;
boolean implementsSelectedInterface = false;
for (TypeTree superInterface : classTree.superInterfaces()) {
// Alternative 1. the following line requires that the interface code is on the classpath, if it isn't the type is unknown
if (superInterface.symbolType().isSubtypeOf("a.b.c.AnInterface")) {
implementsSelectedInterface = true;
}
// Alternative 2. the toString() method returns the simple name of the interface, without the package... and then I don't know how to retrieve the package name
if ("AnInterface".equals(superInterface.toString())) {
implementsSelectedInterface = true;
}
}
if (importsSelectedInterface && implementsSelectedInterface) { // Alternative 3. works but is ugly : also visit the import node
//...
}
}
}
//...
}

The 3d alternative is what I've done so far. Prior to visiting the Kind.CLASS node, I visit the Kind.IMPORT node, check that the ImportTree contains an a.b.c.AnInterface entry, and if it is the case I set the importsSelectedInterface to true. It works but it is really ugly and looks non optimal.

Is there a way to do it in a cleaner way ?

Thanks in advance,

Michel

Michael Gumowski

unread,
Jun 23, 2015, 5:12:00 AM6/23/15
to Michel Pawlak, sona...@googlegroups.com
Hello Michel,

Regarding your solution, if you don't want your check to depend of the jar containing the interface, it means that semantic analysis will be considerably degraded... Which is especially annoying in that case. As you won't be able to use semantic analysis, you'll indeed have to check everything manually! I hope that you realize that the issue you are trying to solve is equivalent to re-write parts of the semantic analysis at some point. Can't you provide a simple jar containing only the interfaces used by your modules?

A few remarks regarding your implementation however:
  • Your 1st alternative will be available if bytecode is available. Consequently, you could have simplified it by checking directly the type of the "classTree" (see code below). In that case, skip the rest, as you are 100% sure that the class implements the interface and you don't need to check the other interfaces nor the imports.
ClassTree classTree = (ClassTree) tree;
if (classTree.symbol().type().isSubtypeOf("a.b.c.AnInterface")) {
  // do somthing
}
  • If you trigger 2nd alternative, then you may face ambiguities and you definitely have to check the imports... But it will be a pain.
    • The class may implement an interface having the same name.
    • The class may be within the same package of the interface.
    • The imports may contain star imports.
    • You won't be able to perform cross file analysis.
  • To avoid using "toString()", you probably want to reconstruct the name by calculating it from the identifier of the interface. The simplest way is to define a method reconstructing the name of the type by looping over identifierTree or  MemberSelectTree (don't forget star imports).
Note that with this manual approach, you may be able to detect situations 1,2, and 3 from code below with some limitations, but 4th case will probably be impossible to verify... When providing bytecode would allow it directly.

class MyClass1 implements AnInterface {}

class MyClass2 implements a.b.c.AnInterface {}

class MyClass3 implements AnInterface, Closeable {}

class MyClass4 extends MyClass1 {}

I hope this helps,

Regards,


Michael GUMOWSKI | SonarSource
Software Developer @ Language Team
http://sonarsource.com

--
You received this message because you are subscribed to the Google Groups "SonarQube" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sonarqube+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/sonarqube/7ea5dfd5-fea6-4e56-8b5f-a6109ac9804c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michel Pawlak

unread,
Jun 23, 2015, 4:03:57 PM6/23/15
to sona...@googlegroups.com
Hi Michael,

Thanks for your reply and help. I've finally decided to use the first alternative, simplified as you suggested.

Cheers,

Michel
Reply all
Reply to author
Forward
0 new messages