Late night PSI fun

Some PSI question, apologies if these are basic questions that are blindingly obvious. I couldn't find any docs after a cursory search:

1) Given a PsiClass that extends Class X, how do I make it not extend said class? I want something like psiClass.setSuper(null)

2) What's the best way to iterate through all the code constructs within a java class? Let's say I want to, for example, find all usages of OtherClass.doStuff() within a particular PsiClass

3) Even better than the above, can I call structured S&R programatically?

8 comments
Comment actions Permalink


1) Given a PsiClass that extends Class X, how do I make it not extend said class? I want something like psiClass.setSuper(null)

I think psiClass.getExtendsClause().remove() should do it.

+
2) What's the best way to iterate through all the code constructs within a java class? Let's say I want to, for example, find all usages of OtherClass.doStuff() within a particular PsiClass
+

Create a subclass of PsiRecursiveElementVisitor, overriding the appropriate "visit" methods. In the case you indicate, you'd want to override visitMethodCall(), and have it test that the method name was "doStuff", and that the method resolved to a static method of class "OtherClass". Call psiClass.accept(visitor), doing whatever depraved thing you wish to the all the calls you find. There's approximately six hundred variations of this pattern to be found in the InspectionGadgets source, if you wish to look at some examples.

--Dave Griffith

0
Comment actions Permalink

Excellent, I'm well on the way to much debauchery.

The next hurdle is creating asserts. PsiElementFactory doesn't want to play along, so I'm forced to do something like this:

(PsiAssertStatement)factory.createStatementFromText("assert true", methodCall.getParent())

then faff about with the contents of the assert statement. Should I feel ashamed?

Finally, let's say I have a PsiBinaryExpression, what's the best way to invert it?

Finally, why am I asking questions here instead of digging through the intentions source code?

0
Comment actions Permalink

+The next hurdle is creating asserts. PsiElementFactory doesn't want to play along, so I'm forced to do something like this:

(PsiAssertStatement)factory.createStatementFromText("assert true", methodCall.getParent())

then faff about with the contents of the assert statement. +

I've never had any problem just creating the statement from the text, without later modifications. What issues are you seeing (other than leaving the semicolon off your example)? After you create the statement, you addBefore/addAfter/replace to put it somewhere. Then it's polite to use the CodeStyleManager to reformat the resulting statement to the user's specs. There's about 100 examples of this sort of thing in InspectionGadgets, and another 60 or so in IntentionPowerPack.

Finally, let's say I have a PsiBinaryExpression, what's the best way to invert it?

There's a "Flip Comparison" intention that you should be able to adapt for your needs.

--Dave Griffith

0
Comment actions Permalink

Excellent, I thought there might have been a stigma with just passing in text fragments, glad there isn't!

Next up, how do I resolve a PsiMethodCallExpression back to the actual PsiMethod?

For example, in a JUnit test:

assertEquals(blah, blah);

This code block is a PsiMethodCallExpression, from this, want to get at the PsiMethod for assertEquals in junit.framework.Assert, which is what this actual method points to.

resolveMethod() returns null, as does getMethodExpression().resolve(), sadly.

This time I did actually look at the ipp sourcecode, and couldn't find anything that does just that, from cursory glance. A bunch of stuff does use resolveMethod and seems to assume it works.

0
Comment actions Permalink

resolveMethod() returns null, as does getMethodExpression().resolve()

This should work (returning the appropriate PsiMethod), unless JUnit isn't in the classpath. Can't think of any reason why it wouldn't otherwise. Sorry.

--Dave Griffith

0
Comment actions Permalink

Figured it out!

The problem was that earlier on, I modify the import list, so method calls to the superclass no longer resolved. Delaying the import munging until the end keeps methods resolved, and my plugin happy.

Thanks for all your help! The end result is an intention/inspection to automatically convert JUnit testcases to TestNG ones, uploaded hopefully later today!

0
Comment actions Permalink

Excellent. If you're releasing the code, I'll undoubtedly reuse it for JUnit 3.0 -> JUnit 4.0 conversion utilities, once 4.0 goes GA.

Out of curiousity, what's the attraction of TestNG? I can (theoretically) see the value of dependent test cases and expected exception cases are cool, but the downsides of dealing with XML suite configuration and not having IDEA's JUnit runner would seem to outweigh those.

--Dave Griffith

0
Comment actions Permalink

The fact that it's maintained! Also, if you come up with a decent new feature idea, chances are it'll go in, and you have a sane forum to debate it in. Good luck getting that sort of support for JUnit 4.0. As for the runner, the plugin will slowly gain most of the features. It's already good enough now for me to be able to use it regularly.

Finally, the xml file is optional, you can run tests within IDEA the same way as you would JUnit tests (package, project, single class, single method). In build.xml, you can likewise specify the classfileset to use, without a testng.xml.

As for advantages of testng itself, there are plenty of articles about that. In terms of usage I find it a lot more pragmatic than junit. For example, it's possible to keep state and do per-class/suite init calls, instead of having a full setUp/tearDown cycle for every single test. The dependency stuff is also quite nice, so you don't end up running 500 tests when the one sanity check test you run up front fails.

0

Please sign in to leave a comment.