Weird JavaCore IType cache problem - eclipse-plugin

I'm developing a plugin that takes all enums in workspace that implements certain interface (IDomain) parses the code (Using AST) does some modification over the enum and marks it as processed with an annotation (#IDomainInfo).
For example, it takes someting like this:
public
enum SomeEnum implements IDomain {
// ...
}
And generates something like this:
public #IDomainInfo(domainId = 1)
enum SomeEnum implements IDomain {
// Some changes here...
}
The idea behind of the #IDomainInfo is that annotated enums have not to be processed anymore by the plugin.
Basically what I do to accomplish the task is to make a search with JavaSearch API to find all the enums implementing IDomain (easy task), and as result I get a list of IJavaElements (which are in fact instances of IType). Then I call a method that iterates through the resulting list and creates a new list of all the IType instances that are not annotated with #IDomainInfo and then process the resulting list: For each non annotated IType do some work, annotate the IType with the #IDomainInfo annotation (Using AST) and then save back the results to file (using IFile, so I can see the changes without refresh, and in fact, if I have the enum open in the editor I see it refreshed instantly :-)
All that works fine, but if I open an #IDomainInfo annotated enum (just for testing) then remove the #IDomainInfo, save the file (I'm sure) and then call the action that does all the job I've described before, when I get to the part that filters annotated IType from non annotated ones, code is something like this:
for (IType type : typeList) {
IAnnotation annotation = type.getAnnotation(“IDomainInfo”);
if (!annotation.exists()) {
// The annotation does not exist, so add the type to the
// list of elements to update and go on...
ret.add(type);
continue;
}
// Something else here...
}
Well, it results that for the file I've just saved the IType detects the annotation I've just removed as if it's still there. If I close and reopen eclipse all works normally.
Now, I've just checked and triple checked my code, so I'm sure that I'm not keeping a stale copy of the old IType unedited still with the annotation version (all my IType come from a fresh java search call every time I run the action).
So the question is, what might I be doing wrong? I mean, I've just read the JavaCore API many times to check If I might be using it wrong or if I have some conceptual flaw there but really I have no clue, it's like if eclipse would be caching the IType ignoring the changes I've just made in the editor :-/
If any one have an idea I would appreciate it a lot :-)

When or how is your plugin called ? Did you register a resource listener or is it a project builder or something else ? If it is called by a resource listener, your plugin may be reading the 'primary copy' for your IType, which has not been saved yet. Hence your changes are still in the Working Copy.

Related

"Convert property getter to initializer" - but they aren't the same thing, are they?

I'm using IntelliJ with a mixed Java/Kotlin project. In one of my Kotlin files, I have this property:
override val value: String
get() {
return webElement.getAttribute("value")
}
IntelliJ's light bulb offers to "Convert property getter to initializer", which changes the code to this:
override val value: String = webElement.getAttribute("value")
To me, it seems like this isn't a simple refactoring, but a significant code change. What I think is happening is:
In the first version, the value property is retrieved when I call value.
In the changed version, the value property is set immediately when the class instance is constructed, and then never changes for that class instance.
But maybe this is more like C# expression-bodied members, which use a lambda arrow => instead of braces and return but otherwise work exactly the same way.
So...which is it? When will the second version of the code initialize?
You are correct regarding these statements:
In the first version, the value property is retrieved when I call value.
In the changed version, the value property is set immediately when the class instance is constructed, and then never changes for that class instance.
IntelliJ's light bulb offers to "Convert property getter to initializer" because it is just an option available. Light bulb only highlights the actions you can do with a selected piece of code.
IntelliJ does not try to tell you that "property initializer" and "property getter" are equal. What it tells you is that you can convert one to the other if you wish to.
I agree that it is confusing, especially considering this quote from IntelliJ Idea documentation:
As soon as the IDE finds a way to alter your code, it displays a yellow bulb icon in the editor next to the current line. By clicking this icon, you can view intention actions available for this unit of code. Intention actions cover a wide range of situations from warnings to optimization suggestions. You can view the full list of intentions and customize them in the Settings/Preferences dialog ⌘,.
Having this in mind it could appear that your code is either can be optimized or has a warning.
The answer
When will the second version of the code initialize?
... immediately when the class instance is constructed.
You are correct.

How to move a function from one Kotlin class to another using IntelliJ?

I'm using IntelliJ IDEA to refactor some Kotlin code. I have two classes in the same file and I want to move a function from one class to another using Refactor -> Move (F6), but that doesn't work, and I get tooltip message that says: "Cannot perform refactoring. Move declaration is only supported for top-level declarations and nested classes".
Am I doing something wrong? Or that refactoring is simply not supported?
[edit1] I tried to do the same operation with Java classes and everything works perfectly; so why this is not allowed for Kotlin?
[edit2] I thought that the problem is only when to two classes are in the same file, but it turns out that is not possible to move a function between classes in separate files!
It's a well-known Kotlin-only problem.
in IDEA (both free and paid editions);
in Android Studio.
Official ticket
There is an easy, but slightly janky, work around.
You just need to wrap the function you want to move in a class:
class TopLevelClass {
fun functionToMove() {
//...
}
}
wrap it in a new class
class TopLevelClass {
class TemporaryMoveClass{ /** you can now move this entire new class */
fun functionToMove() {
//...
}
}
}
and after you do the refactor, delete the temporary wrapper class you created.
The janky part is that you need to replace all instances of functionToMove() with NewTopLevelClass.functionToMove() yourself.
One of the major benefits of doing it this way, rather than just cut and pasting it yourself, is that as soon as you wrap it in the TemporaryMoveClass it will tell you any parameters you need to introduce(Refactor>Extract>Parameter). And then you can do that inside the original TopLevelClass before you move it. (this preserves the types of any TopLevelClass properties you were using, and automatically introduces the new parameter(s) into the existing function calls)

Intellij unused Injects are not detected

I am trying to clean my code and delete #Inject fields in my classes which are not used. I tried to follow this https://blog.jetbrains.com/idea/2009/04/global-unused-declaration-inspection/ in order to achieve this. Somebody seems to have succeeded in making it work in the comments of the link but it didn't work at all for me. As soon as I add #Inject over my fields, there is no warning, even if the injected variable is private and never used !
Is this not supported for some reason or is there somewhere else I need to change the settings ? What I did was to set private on the inspection of the fields. I even tried to add an entry point on the Inject annotation. Needless to say that nothing of this worked.

IntelliJ type error when using Geb static content DSL with parameters

Whenever I use a static-content defined closure that takes parameters, IntelliJ will complain that the types do not match (even if I specify the type inside the closure).
For example, if I use this static content block:
static content = {
myModule { $('myModule').module(CustomModule) }
namedModule { String name -> $(".$name").module(CustomModule) }
}
Both of the above items can be used successfully in my tests, but if I was to use 'namedModule' in one of my tests as follows:
page.namedModule("moduleName").moduleMethod("blah blah blah")
IntelliJ will highlight the parameter "moduleName" in yellow with the error:
'namedModule' cannot be applied to '(java.lang.String)'
We are trying to refactor our tests in a way that means you can navigate through the code easier (e.g. avoiding any Geb/Groovy 'magic' that IntelliJ can't resolve), and this is one of the last remaining issues preventing this from being possible.
This is a known limitation to Geb support in IntelliJ. IntelliJ always treats content definitions as properties of pages and modules even though they can be parametrised. Given that Geb support in IntelliJ is open sourced we could probably add support for this.
In the mean time, as a workaround you can use methods for parametrised content instead of content definitions and IntelliJ will be able to understand these and be able to refactor them:
void namedModule(String name) {
$(".$name").module(CustomModule)
}
There are some caveats, though:
you will loose ability to use content definition options; if you need to use these for a content definition then I suggest creating a parameterised "private" content definition (for example with a _ at the beginning of the name) that you will only ever access from within the page or module
RequiredPageContentNotPresent will not be thrown even if the returned content is empty; to work around it you will either need to add manual verification to each such method or use a strategy outlined in the first bullet point with using "private" content definitions

Play Framework 2.1.1: bindFromRequest() returns the correct data but ignores all data pertaining to relations

I have a form that is supposed to create an entity of type Load, but for some reason, doesn't seem to be actually passing or seeing any of the data related to associations of the entity (load.user, load.client, etc). This all used to work fine but stopped working at some point during a bunch of refactoring (that didn't change any of the fields in any of the models). Now all of the forms in my website have broken the same way and I have no clue where to even look to start fixing it.
From the view, I submit the form for a new Load, printing out the data everywhere I can along the way. Printing out the data being sent to the server before it's sent shows all the data is there like it should be. Printing out Form.form(Load.class).bindFromRequest() in the controller shows the form's data contains everything needed, for example, the value user.id=1 is in the data. However, there is also a validation error saying that the user is missing. How can this be?
Form(of=class models.Load, data={ a bunch of stuff, user.id=1, a bunch more stuff}, value=None, errors={=[ValidationError(,Logged in user is missing or invalid.,[])]})
The validation error is being generated by public String validate() in the Load class, which is simply checking if(user==null) and returning that string if it is. I should note that every form that submits multiple entities (for example, submitting a Dock and then also the Dock's Location) only saves the main entity (in this example, the Dock) and ignores all others (the Dock's Location is never saved, even though Dock cascades in the model to also save the Location). All of our form fields are labelled correctly, this code did used to work at some point before it mysteriously stopped working!
So why did all of my forms suddenly stop correctly dealing with anything but the main model for the form? It is as if they cannot even "see" the data contained in bindFromRequest(). If I print out a variable in the validation method of Load, such as this.status, it prints the correct thing. But if I try to print something like this.user.id or this.client.id I get a null pointer error. Where is the code in Play that actually interprets the data (user.id=1) and turns it into the User associated with the Load, and how could it be breaking?
Edit: Also, yes, I did try "play clean", it was the first thing I tried since usually it fixes weird errors like these! But this time, no dice.
Edit2: I'm including the html from the form, in case it is helpful.
<input type="text" id="user_id" name="user.id" value="1" class="idfield">
Edit3: The only change I made during the refactoring that might have influenced this is that I had to make some setter methods like Load.setBroker() because the ones that are supposedly generated by Play didn't work. For example, load.broker=aBroker would not have set the Load's Broker before, so I had to make a public void setBroker(Broker broker) method in Load. Does Play use the auto-generated setters to bind the data? Could overwriting them cause problems?
Whoops, I figured it out. It was the setters I had written. Some of them were set to private purely by mistake, and apparently this was preventing Play from setting the values when binding the data. Changed them all to public and the mystery error vanished.