Monday, August 29, 2011

Contributing a Quick Assist based on text Changes in Eclipse

Moving from a refactoring to a quick assist

I'm back with a quick update. I said that the previous post was going to be the last one. Where I gave a step by step guide of how to build a refactoring for eclipse and gave my comments on the subject. But my original intend was to contribute a Quick assist instead of a refactoring.

I will focus on the creation of the quick assist and specially how to build it using Changes.

As a side note, or should I say, a sad note. I'm about to present an implementation of a quick assist that has an equivalent in the JDT. When a field that can be final doesn't have the final modifier the JDT presents the quick assist that reads 'Change modifier to final'. I just want to clarify that I didn't base my code on that quick assist. It will be fun to compare though :).
As usual here's the code.

Contributing a quick assist for the JDT

To start with a quick assist, the first thing to find out is which is the extension point is needed to insert the new assist. This is it: org.eclipse.jdt.ui.quickAssistProcessors
here's how I contributed the quick assist:

The important thing to know right now is that the id is a unique indentifier for your contribution and class is the fully qualified name of the class that will implement the new functionality. By the way the class must implement the interface IQuickAssistProcessor.

Implementing the Quick Assist

When creating a refactoring, the way to build the transformations is by the use of Changes which were easy to create by the use of a AstRewrite instance. But building a quick assist is quite different. I'd say that the quick assist API expects you to handle things at a lower level of abstraction than the refactorings API. The main differences I found between implementing a refactoring and contributing quick assist are:
  • In the quick assist the full parsed AST is already there for you. Which is good
  • In the refactoring you as the implementor are responsible of returning a potential change to the Ast while in the quick assist you must write the code to apply the change to the document. Here's where the abstraction goes a little south in comparison with the refactorings API. Because in the refactoring case the idea is to change a Data Structure, whereas in the quick assist the changes are performed in a plain text document
Let's move on and take a look to ToFinalQuickAssistProcessor which is the class that implements the IQuickAssistProcessor

ToFinalQuickAssistProcessor



ToFinalQuickAssistProcessor has to implement two methods: hasAssists and getAssists. hasAssists evaluates if there are assists for a given context, and getAssists returns all the completion proposals for a given context as well. The detection code must be the same in both methods. The difference is that hasAssists must stop searching after finding the first assist and returns. It's and existence operation. getAssists must collect all the proposals whose detection code succeeds. getAssists returns an array of completion proposals which are implementations of the interface IJavaCompletionProposal. The implementers of this interface are responsible for applying the changes to the document when chosen by the user or when the quick assist is previewed.

Before getting started looking at the implementation please take into account the fact that the code is based on a refactoring. I reused as much code as possible but I didn't reuse the refactoring itself. I took the time to refactor my code and created two classes one called MakeFieldFinalDetector that contains the detection code that was present in the method checkInitialConditions and other class called FinalModifierAdder that takes care of transforming the AST which basically contains the code that belonged to the CreateChange method. I will not explain these two classes because is pretty much the same code explained in the previous post and because is not the main focus of this post.

As customary I'm going to dissect the methods implementation one by one in a top-down fashion.

hasAssists



The first thing to do here is verifying that the assist applies for the current current context. We must obtain the nodes covered by the context by invoking the method getCoveringNode. Taking advantage of the fact that a full parsed AST is available is easy to determine if the covering node is a SimpleName. Then we need to obtain the corresponding binding. With the binding and the AST then we can invoke hasToFinalQuickAssist which is going to perform the specifics of the detection.


hasToFinalQuickAssist


With the binding at hand we must check if it corresponds to a Field and then we can use the method MakeFieldFinalDetector.detect to find out if the field is assigned at declaration time and only there. If those  conditions are met then the method returns true, false otherwise.

getAssists


As I mentioned before this method shares the detecting part in common with hasAssists.
Once the validation code has passed is time to create the proposal.
Here's where I got stuck for a while. Because I knew refactorings but now I had to do everything in a new place I knew nothing about. So what did I do?, I looked at the codebase an checked how it was done by other assists. For sure someone else had done what I was intending: reuse a refactoring's code to build a quick assist.
After a few hours of browsing and going back and forth there it was, what I needed. I felt I little guilty because the thing did everything for me.  I just had to instantiate it and that was all. I run into CUCorrectionProposal. I created the class ToFinalQuickAssistCompletionProposal which inherits from CUCorrectionProposal.
To instantiate ToFinalQuickAssistCompletionProposal We need the context and the fragment that will be modified. To obtain the fragment there are a few steps we need to take in order to get to it from the binding instance:
  • Obtain the SourceField instance using the getJavaElemtent method of the binding
  • We invoke the SourceField's method findNode to obtain the corresponding FieldDeclaration instance
  • We use the utility method getDeclarationFragmentByName to isolate the fragment we are interest in

ToFinalQuickAssistCompletionProposal



Here's where I used the not so public class CUCorrectionProposal. Basically what I do is use the context and the fragment to obtain the change instance and get the parameters needed by CUCorrectionProposal constructor parameters. Here's the summary of the purpose of CUCorrectionProposal class from the java file:


And this is the description of the parameters of the constructor being invoked:


Let's concentrate on how to obtain the change argument. Here's where I reuse the class FinalModifierAdder. To do its job this class needs the following arguments on its constructor:
  • ast: We obtain it from the context calling context.getASTRoot().getAST()
  • compilationUnit: This one is also available from the context by calling context.getCompilationUnit()
  • fragment: This is the fragment that will be placed on another declaration if needed

getDeclarationFragmentByName


Becuase a FieldDeclaration can contain more then one fragment. We need to obtain the specific fragment that corresponds to a name. As you can see we are just iterating and looking for the fragment by name.

This is how I contributed a quick assists based on my refactoring code. I hope this can be useful in your eclipse projects.

Saturday, May 7, 2011

The experience of creating an Eclipse refactoring (part 4)

From Code to AST and Back


This is the last post on creating a refactoring for eclipse. In this post I'll cover the process of creating the changes  needed to transform the code once all the conditions have been validated.
The first post introduced my Refactoring named ToFinal which adds the final modifier to the private fields that are only assigned at initialization time. I gave a quick overview of the refactoring API provided by the LTK. In the second installment  I covered the topic of contributing a new refactoring using a command. And the last time around I covered the process of validating that the selected field can be made final using the checkInintialConditions method. Now I'm going to finish the series showing the way to transform the ast and taking the changes made to the actual source code.
For this post I used a newer version of the code. You can get the source code here.
Following the same standard I've used previously I'll start showing the code in a top-down fashion. I'll start explaining the createChange method and then I'll dig deeper.

createChange


As you can tell from the code, once it has been determined that the transformation can be performed there are two scenarios to take into account. The first one occurs when the field is declared alone. In the eclipse ast jargon that's a field declaration with a single fragment. The second case is a field declaration with multiple fragments. The method responsible of detecting that is isThereOnlyOneDeclarationInTheLine. In the first case the only thing needed to do is adding the final modifier to the declaration. The second case is a little more complex because the field declaration needs to be split and then the final modifier needs to be added to the new declaration. Before moving on, I want to describe the problem the guys behind the IDEs have to deal with when transforming/modifying source code.

As you have seen in my previous posts we've done all the processing using a convenient AST. We are ready to transform the code. And of course we are going to do is transform the AST. Here comes the problem. We are transforming the AST not the original source code. We need to translate the changes to the AST into changes to the text file. One solution is "printing" the whole AST back into the text file. But IDE users like you and me don't want the IDE to modify the whole file when what is needed is a simple change. Like in this case we need to add just the final modifier to a declaration, how hard can that be?. For starters the AST needs to remember to which parts of the file the nodes correspond to. And a mechanism for tracking changes in the AST to convert them to changes in the text file is also required. It's a good thing eclipse provides all that for us. We only need to learn how to use the API for that purpose. Here's where the ASTRewrite class comes handy. ASTRewrite does exactly what I said before, keeps track of the changes made to the AST, and, here comes the catch, without modifying the AST. It's like a list of potential changes.

From the createChange you can see that astRewrite is passed as a parameter to addFinalModifierToDeclaration and
splitMultipleDeclaration. Those methods are the ones in charge of adding changes to the astRewrite. Once the transformation is ready there's one last step left. Create a Change using astRewrite that has all the recorded modifications. That's the purpose of the createChangeFromAstRewrite method. Let's drill down into the methods mentioned above.

isThereOnlyOneDeclarationInTheLine


This method exists just for making the condition easier to understand, obviously, if there's only one element in the list of fragments it implies that this particular declaration only is declaring one field, duhh.

addFinalModifierToDeclaration


For reutilization sake I created the template class FieldDeclarationChanger which takes care of modifying one declaration. In this case the template method adds the final modifier to the declaration. Please note that the new nodes are created by the AST.

splitMultipleDeclaration


After siting with a cup of coffee looking at this code I realized that splitMultipleDeclaration is a terrible name for what this method does. Let me explain. When a declaration has multiple fragments I can't add the final modifier to all the declared fields. So I need to create a new declaration for the field being refactored, obviously add the final modifier and finally I need to remove it from the previous place it was being declared. Maybe createNewFinalField is more descriptive then splitMultipleDeclaration. In order to perform the task at hand I created a new field declaration using the createNewFieldDeclaration and again using the template class FieldDeclarationChanger but this time I used it to remove fragment of the field being refactored from the list of fragments contained in the declaration.

createNewFieldDeclaration



In createNewFieldDeclaration I create a new field declaration with a single fragment, what the method does is quite simple: Copy the parts that I need from the original declaration to define the new one.
I want you to put special attention to the last two statements in the method. That's how new nodes are added using the astRewrite. The method getListRewrite returns a ListRewrite instance that you can use to move around,insert,replace and remove child nodes of a give parent node. In this case I'm using it to insert the new field declaration after the existing one. I know what you are thinking now. Why the author didn't do this to add the final modifier in addFinalModifierToDeclaration ?. Good question. I could have done that. I just thought it was simpler the way I did it. And I needed an excuse to reuse my beloved template FieldDeclarationChanger :).

FieldDeclarationChanger


Existing nodes of the AST cannot be modified, in order to edit a node, a copy must be obtained, edited , and then record the modification by replacing the original with new one using the astRewrite. There are more fined grained ways of editing nodes but I found this method quite simple. The method applyEdition clones the node to be edited, invokes the abstract method editFieldDeclaration which is the one that will perform the edition on the cloned node and finally then whole declaration is replaced by the astRewrite instance. I've debated with my self if the FieldDeclarationChanger template is an overkill solution or not. At the end of the day I'm just trying to reuse 3 lines of code and wrote 20 to do that. But let's assume that is needed ...

createChangeFromAstRewrite


So far we have been working with the AST, all the editions are ready and it's time to create a Change, a CompilationUnitChange is what we need. Which is a text based Change, And requires a TextEdit. To translate the changes to the AST recorded in the astRewrite to changes to the original text the method to use is rewriteAST. Then it's just a matter of set the edit to the the change. And that's it.

I hope these posts helps you get started with the JDT/LTK api. See you later.

Update

I didn't feel comfortable with the FieldDeclarationChanger class. Because it was replacing a whole declaration when I was just trying to modify just parts of it, as I mentioned above. So I did my homework and looked for ways to make the modifications more granular.
Below I'll show you new versions of the methods addFinalModifierToDeclaration and splitMultipleDeclaration. They do the same but with less changes to the file.

addFinalModifierToDeclaration


splitMultipleDeclaration



With this changes the class FieldDeclarationChanger can be let go :(

Wednesday, February 23, 2011

The experience of creating an Eclipse refactoring (part 3)

In my previous post I took a tour through the configuration needed to get a new Refactoring going. I promised that I would start showing the code that processed the refactoring. I intend to do exactly that now. I honestly wanted to cover the whole refactoring in this post but the condition checking code is big enough for one post. So I decided to leave the transformation code aside in this post. I really hope that this series can be useful for someone trying to build tools for the JDT. Not from an expert's point of view, but from the perspective of someone that is learning the eclipse framework and has some experience on processing source code. I want to share my thoughts and the things I found tricky. If someone runs into one of those issues he can have a reference.
I will assume you're somewhat familiar to the AST concept and JDT's AST in particular. If you are not, I recommend you the following articles

Here's a link to the source code of the project used for this post.

From Text to AST


If you read my previous installment you'll recall that I got to a point where we could get the refactoring wizard running without knowing much about what happens next. Let's build from there by taking a look at the whole ToFinalCmdHandler class. And I'll comment on the methods on a top-down fashion from the execute method. Then we'll do the same for the remaining classes.

ToFinalCmdHandler


execute
When the handler is executed it tries to update the fields fCompilationUnit and fField using the current editor and the current selection. The method responsible for that operation is updateDataFromSelection. Once the data is ready, the refactoring wizard is started by the method startWizard.

updateDataFromSelection
The first thing updated is the field fCompilationUnit using the method CompilationUnitForCurrentEditor. The next step is updating the selected field using the current selection. The current selection can be an instance of the following two interfaces:

ITextSelection: The current selection is an instance of an ITextSelection when there's text selected in the current Editor.

IStructuredSelection: The current selection is an IStructuredSelection when almost anything else is selected, like a node in the package explorer or an error in the problems view. I'm interested in the case when there's a field selected in the outline view.

Two different approaches are used to obtain the parsed field. When the selection is structured. The code is already parsed and the corresponding node from the AST can be obtained directly from the selection. When the selection is text we use the method codeResolve from the class SelectionConverter. The method codeResolve obtains the AST node that is located in the coordinates of the text selection. I have to warn you here. SelectionConverter is part of the JDT internal API. So there's no warranty that it will be there in future versions of JDT or if it will ever be public. But is handy nonetheless. The only thing left here is to validate if the obtained node (if any) is in fact a field. Then it is stored in fField. Let's take a closer look to CompilationUnitForCurrentEditor


CompilationUnitForCurrentEditor
Sorry about the violation to the coding standard. I found out my mistake after I published the code. The first thing you notice (after noticing the c#'ish name) when you see this method is the comment. I really don't like the idea of parsing the current editor. When I explored the code of the existing refactorings I found that the compilation unit was being passed as a parameter to them by a third party. But I have to admit that I didn't try to dig deeper to find out how to integrate with that mechanism. However I needed a compilation unit and this mechanism works. What this method does is simple but needs several steps to get the content of the current Editor as an IResource instance. By the way the getAdapter method is a perfect sample of an implementation of the Adapter pattern.


startWizard
There isn't much about this method. It receives the wizard and launches it using a RefactoringWizardOpenOperation. So we had already covered the RefactoringWizard in the previous post. Let's move to the Refactoring itself.

ToFinalRefactoring

Ok so we get to the interesting part.  In order to implement the refactoring my class must inherit from the Refactoring abstract class.  The official javadoc explains very well the Refactoring class:
Abstract super class for all refactorings. Refactorings are used to perform behavior-preserving workspace transformations. A refactoring offers two different kind of methods:

1. methods to check conditions to determine if the refactoring can be carried out in general and if transformation will be behavior-preserving.
2. a method to create a Change object that represents the actual work space modifications.

The life cycle of a refactoring is as follows:

1. the refactoring gets created
2. the refactoring is initialized with the elements to be refactored. It is up to a concrete refactoring implementation to provide corresponding API.
3. checkInitialConditions(IProgressMonitor) is called. The method can be called more than once.
4. additional arguments are provided to perform the refactoring (for example the new name of a element in the case of a rename refactoring). It is up to a concrete implementation to provide corresponding API.
5. checkFinalConditions(IProgressMonitor) is called. The method can be called more than once. The method must not be called if checkInitialConditions(IProgressMonitor) returns a refactoring status of severity RefactoringStatus.FATAL.
6. createChange(IProgressMonitor) is called. The method must only be called once after each call to checkFinalConditions(IProgressMonitor) and should not be called if one of the condition checking methods returns a refactoring status of severity RefactoringStatus.FATAL.
7. steps 4 to 6 can be executed repeatedly (for example when the user goes back from the preview page).

A refactoring can not assume that all resources are saved before any methods are called on it. Therefore a refactoring must be able to deal with unsaved resources.

The class should be subclassed by clients wishing to implement new refactorings.

If someone asks me how the refactorings for the JDT were designed I'd say they used a Behavior Driven Approach because the Refactoring class is clearly modeled in function of the User Interface workflow. And looks like the RefactoringDescriptor is a tradeoff of this design decision. When a refactoring needs to be run without user interaction the steps needed to make it work don't appear as clean as when it needs user interaction.

Now back to business. Let's see how I implemented the abstract methods to get my refactoring going. Let's start with checkInitialConditions.

checkInitialConditions
Half of the job is done by this method. What this method does basically is to determine if the refactoring can be applied on the selected field. These are the things that are validated:
  • The selection is actually a field
  • The class that defines the field is not an annotation
  • The field is private
  • The field is not already final
  • The field is initialized at declaration time and is not assigned anywhere else
The first 3 conditions are checked by the method doesFieldMatchesInitialConditions. If all of them are met then I proceed to validate the fifth condition that takes some work to find out.  In order to do that we need to need the help of a visitor and a fully parsed AST in order to create a class whose responsibility is to look for assignments to a variable all over the place (Inside the class methods that is). Now is time to take a look into the AssignmentsFinder class.


checkFinalConditions
After the method checkInitialConditions is invoked an input screen is shown to the user. Like the window that shows when moving code into a new method and the method name must be introduced. Once the user presses OK on that screen the next method invoked is checkFinalConditions. Now that there's more information available further validations can be performed. In this case there's no user input, rendering the method useless.


doesFieldMatchesInitialConditions
It's easy to find out all the things this method is looking for just by querying the field's properties and using the Flags class.

AssignmentsFinder


The responsibility of AssignmentsFinder is to traverse the code of the current class, and after doing so it must be able to respond to the question "Is the field initialized at declaration time and only is assigned there?"
If you take a look at the code you'll find that the code is rather simple. It overrides the visit method for the nodes in the AST that correspond with assignments:

ClassExample
VariableDeclarationFragment int variable=1;
PostfixExpression variable++
PrefixExpression ++variable
Assignmentvariable = value; variable += value

When visiting the VariableDeclarationFragment instances the intention is to find the declaration of the selected field. Once found the next step is to verify that it has an initialization expression. As a side note on the code I'm checking if the initialization value is a literal. And at this point I honestly don't remember why I set that constraint. The rest of the visited nodes are forms of assignments that need to be inspected in order to validate that the selected field is not being assigned.

The first thing that comes to mind when implementing this code is how to know that the being visited is actually the node that corresponds to the field. That can be accomplished by the use of bindings. Every time the parser finds a reference to an entity the same binding is assigned to that reference. Making it easy to know exactly what a symbol is 'bound' to.

After the visitor is finished traversing it is ready to answer the question I mentioned above. The method used for that purpose is canVariableBeFinal. If the method returns true the refactoring can proceed otherwise an error must be reported and the refactoring should not continue. As you can see in the checkInitialConditions method.

From AST back to Text

Next time I'll cover the code that transforms the AST and adds the final modifier to the field. Stay tuned. I expect to have the next post much sooner than what it took to get this one out of the door...