Sunday, July 29, 2007

Structural Types in Scala 2.6.0-RC1

An interesting feature that comes with Scala 2.6.0-RC1 is Structural Types.

This feature allows you to specify a type by specifying characteristics of the desired type. For example the following function take objects that have a setText method:


def setElementText(element : {def setText(text : String)},
text : String) = {

element.setText(text.trim()
.replaceAll("\n","")
.replaceAll("\t"," "))
}


This method can be called with any object that have a setText method. For example:


val display = new Display()
val shell = new Shell(display)

val c = new Composite(shell,SWT.NONE)
val layout = new GridLayout
layout.marginTop = 10
layout.marginLeft = 10
layout.numColumns = 2
c.setLayout(layout)
val label = new Label(c,SWT.NONE)
val textControl = new Text(c,SWT.NONE)

setElementText(label," Hello\tWorld ")
setElementText(shell,"The title ")
setElementText(textControl,"Text goes here\n")


Here setElementText is called with instances of Shell, Text and Label which don't share a common super class that have a setText method.

Tuesday, July 24, 2007

Creating Java refactorings with Scala and Eclipse LTK - Part 2

This is the second of a two-part series of posts on creating a simple Java refactoring using the Scala programming language and the Eclipse Language Toolkit.

As mentioned in the first part, the refactoring is implemented by inheriting from the Refactoring class. This is an abstract class, in order to implement it we need to override the following methods:


  • abstract RefactoringStatus checkFinalConditions(IProgressMonitor pm)

  • abstract RefactoringStatus checkInitialConditions(IProgressMonitor pm)

  • abstract Change createChange(IProgressMonitor pm)

  • abstract String getName()



According to Unleashing the Power of Refactoring the checkInitialConditions method is used to verify that the refactoring can be performed. The checkFinalConditions is used to "perform long running checks before change generation...", but also can contain most of the work involved in the change generation. The createChange method is used to create the Change object that represents the actual change that will be performed in the code.


class InvertIfStatementRefactoring extends Refactoring {
var propagateNegation : boolean = false
var compilationUnit : ICompilationUnit = null
var selection : ITextSelection = null
var textChange : TextFileChange = null

override def checkInitialConditions( pm : IProgressMonitor) : RefactoringStatus = {
val status = new RefactoringStatus
if (compilationUnit == null || selection == null) {
status.merge(
RefactoringStatus.createFatalErrorStatus(
"Expression not identified yet!"));
}
return status
}
override def checkFinalConditions(pm : IProgressMonitor) : RefactoringStatus = {
val status = new RefactoringStatus
val requestor =
new ASTRequestor() {
override def acceptAST(source : ICompilationUnit,
ast : CompilationUnit) =
performRewrite(source,ast,status)
}

val parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(false);

try {
parser.createASTs(
Array[ICompilationUnit](this.compilationUnit),
new Array[String](0),
requestor,
pm)
} catch {
case ex : RuntimeException => {
status.merge(
RefactoringStatus.createFatalErrorStatus(
ex.getMessage()));
}
}
return status

}

override def createChange(pm : IProgressMonitor) : Change =
return textChange


override def getName() = "Invert if statement"

...
}


For this experiment most of the work is done from checkFinalConditionsMethod, in this method the code is parsed and the resulting AST is manipulated to generate a TextChange object that will be returned by the createChange method. The performRewrite and rewriteIfStatement methods do this by identifying the elements of the IF statement that will be manipulated.

   
def performRewrite(source : ICompilationUnit,
ast : CompilationUnit,
status : RefactoringStatus) : Unit = {

val rewrite = ASTRewrite.create(ast.getAST())
val theAst = ast.getAST()
val n = NodeFinder.perform(ast,
selection.getOffset(),
selection.getLength())

n match {
case ifStatNode@IfStatement(_,_,_) =>
rewriteIfStatement(
rewrite,
theAst,
ifStatNode.asInstanceOf[IfStatement])
case _ =>
throw new RuntimeException("Expecting IfStatement found: "+n.getClass().toString()+n.toString())
}

textChange = new TextFileChange(source.getElementName(),
source.getResource().asInstanceOf[IFile])
textChange.setTextType("java")
textChange.setEdit(rewrite.rewriteAST())
}


def rewriteIfStatement(rewrite : ASTRewrite,
theAst : AST,
ifStatement : IfStatement ) : Unit = {

val newIfStatement = theAst.newIfStatement

val resultIfCondition =
if (this.propagateNegation) {
propagateNegationInCondition(ifStatement.getExpression,rewrite,theAst) }
else {
val pExpr = theAst.newParenthesizedExpression
pExpr.setExpression(
rewrite.createCopyTarget(
ifStatement.getExpression()).asInstanceOf[Expression]
)

val negatedExpression = theAst.newPrefixExpression()
negatedExpression.setOperand(pExpr)
negatedExpression.setOperator(PrefixExpression.Operator.NOT)
negatedExpression
}

newIfStatement.setExpression( resultIfCondition )

if (ifStatement.getElseStatement != null) {
newIfStatement.setThenStatement(
rewrite.createMoveTarget(
ifStatement.getElseStatement()).asInstanceOf[Statement])
} else {
newIfStatement.setThenStatement(
theAst.newBlock)
}

newIfStatement.setElseStatement(
rewrite.createMoveTarget(
ifStatement.getThenStatement()).asInstanceOf[Statement])
rewrite.replace(ifStatement,newIfStatement,null)
}


The changes in the AST are recorded using the ASTRewrite class. More details on how to manipulate JDT AST trees can be found in AST Syntax Tree.

The NodeFinder class is used although its use is not encouraged because of its visibility. This class identifies the selected node under the tree of the compilation unit. In future posts I'll try to replace the use of this class.

If the propagate negation check box is not checked the only thing that we need to do with the condition expression is to wrap it with parenthesis (ParenthesizedExpression) and with a NOT expression(PrefixExpression with the NOT operator).

In order to propagate negation several scenarios must be considered. For example if the original expression is (x == 1) the desired resulting expression must be (x != 1) , also if the condition is (x == 1) && (y == 4) then it can be transformed to (x != 1) || (y != 4). Inspired by the arithmetic simplification examples presented in Matching Objects With Patterns paper, this was implemented using Scala extractors . The following definitions of extractors were defined:


object NotExpression {
def unapply(e : ASTNode) =
if (e.isInstanceOf[PrefixExpression] &&
e.asInstanceOf[PrefixExpression].getOperator == PrefixExpression.Operator.NOT) {
Some(e.asInstanceOf[PrefixExpression].getOperand)
}
else {
None
}
}

object AndExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.CONDITIONAL_AND)
}

object OrExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.CONDITIONAL_OR)
}

object EqualsExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.EQUALS)
}

object NotEqualsExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.NOT_EQUALS)
}

object LessExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.LESS)
}

object GreaterExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.GREATER)
}

object LessEqualsExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.LESS_EQUALS)
}

object GreaterEqualsExpression {
def unapply(e : ASTNode) =
Utilities.identifyInfixExpression(e,InfixExpression.Operator.GREATER_EQUALS)
}

object Parenthesis {
def unapply(node : ASTNode) =
if (node.isInstanceOf[ParenthesizedExpression]) {
Some(node.asInstanceOf[ParenthesizedExpression].getExpression)
}
else {
None
}
}

object IfStatement {
def unapply(node : ASTNode) =
if (node.isInstanceOf[IfStatement]) {
val ifStatement = node.asInstanceOf[IfStatement]
Some(ifStatement.getExpression,
ifStatement.getThenStatement,
ifStatement.getElseStatement)
} else {
None
}
}


Given this definition we can implement the propagateNegationInCodition method by checking every case that we want to transform:


def propagateNegationInCondition(condition : Expression, rewrite : ASTRewrite,ast : AST) : Expression = {
condition match {
case EqualsExpression(x,y) =>
Utilities.createInfixExpression(
rewrite.createMoveTarget(x).asInstanceOf[Expression],
rewrite.createMoveTarget(y).asInstanceOf[Expression],
InfixExpression.Operator.NOT_EQUALS,
ast)

case NotEqualsExpression(x,y) =>
Utilities.createInfixExpression(
rewrite.createMoveTarget(x).asInstanceOf[Expression],
rewrite.createMoveTarget(y).asInstanceOf[Expression],
InfixExpression.Operator.EQUALS,
ast)

case OrExpression(x,y) =>
Utilities.createInfixExpression(
propagateNegationInCondition(x,rewrite,ast),
propagateNegationInCondition(y,rewrite,ast),
InfixExpression.Operator.CONDITIONAL_AND,
ast)

case AndExpression(x,y) =>
Utilities.createInfixExpression(
propagateNegationInCondition(x,rewrite,ast),
propagateNegationInCondition(y,rewrite,ast),
InfixExpression.Operator.CONDITIONAL_OR,
ast)

case NotExpression(negatedExpression) =>
rewrite.createMoveTarget(negatedExpression).asInstanceOf[Expression]

case x if x.isInstanceOf[InfixExpression] => {
val parenthesis = ast.newParenthesizedExpression
parenthesis.setExpression(
rewrite.createMoveTarget(condition).asInstanceOf[Expression])
val notExpression = ast.newPrefixExpression
notExpression.setOperand(parenthesis)
notExpression.setOperator(PrefixExpression.Operator.NOT)
notExpression
}
case _ => {
val notExpression = ast.newPrefixExpression
notExpression.setOperand(
rewrite.createMoveTarget(condition).asInstanceOf[Expression])
notExpression.setOperator(PrefixExpression.Operator.NOT)
notExpression
}
}
}


Given the following code

if(goo(3) && !(x < 3)) {
System.out.print(2);
} else {
System.out.println(4);
}


By executing the refactoring without propagating the negation:


By executing the refactoring propagating the negation:




Code for this experiment can be found here.

Monday, July 23, 2007

Creating Java refactorings with Scala and Eclipse LTK - Part 1

This is the first of a two-part series of posts on creating a simple Java refactoring using the Scala programming language and the Eclipse Language Toolkit.

The first part is about adding the required elements for the refactoring to appear in the Eclipse UI, the second part (which I think is the most interesting) is about manipulating the Java AST to actually perform the refactoring.

The purpose of this post is to show that Scala can be used to work with existing/complex Java APIs . The purpose of this post is not to how to create Eclipse refactorings, there's already nice documentation that does that.

The refactoring that will be implemented is called "Invert IF statement blocks", the purpose of this refactoring is to swap the THEN and ELSE sections of an IF statement preserving its behavior.

For example applying this refactoring to the following code:


if (x == 20) {
System.out.println("x is 20!");
} else {
System.out.println("x is NOT 20!");
}


Will result in the following code:


if (!(x == 20)) {
System.out.println("x is NOT 20!");
} else {
System.out.println("x is 20!");
}


Also, as an option, the refactoring is going to propagate the negation of the IF's condition expression. For example for code above the resulting statement will be:


if (x != 20) {
System.out.println("x is NOT 20!");
} else {
System.out.println("x is 20!");
}


In order to add the refactoring to Eclipse we're going to use the Eclipse Language Toolkit which makes it easy to access existing Eclipse infrastructure elements such as refactoring wizards, diff window, etc. A very nice explanation on how to create a refactoring using the Language Toolkit is found in Unleashing the Power of Refactoring, the code in this post is based on this article .

The first step for creating the refactoring, was to create an Eclipse Plugin . The first challenge was that using File -> New Project -> Plugin project will create a Java project will the required elements to create the plugin. But what we need is to create a Scala project with the same elements.

After trying several unsuccessful attempts, I found a couple of very useful posts from Neil’s point-free blog the first An OSGi Bundle… built in Scala and also Eclipse PDE does Scala . With this information I was able to create an Eclipse Plugin project with the Scala nature.

Based on Unleashing the Power of Refactoring, the minimal code required for a refactoring includes:


  • An action that is used to add the refactoring the UI

  • A refactoring wizard page for the refactoring arguments

  • A class that inherits from Refactoring that represents the refactoring and contains the AST manipulation code.


The action used for this post is associated with the menubar ( for future posts, I think the most appropriate location of this refactoring is on the Refactor menu of Eclipse). In order to add this to the UI the following information was added to the plugin.xml file:


<extension
point="org.eclipse.ui.actionSets">
<actionSet
id="ScalaTestRefactoring.actionSet1"
label="label">
<action
id="ScalaTestRefactoring.action1"
label="label">
</action>
</actionSet>
</extension>
<extension
point="org.eclipse.ui.actionSets">
<actionSet
description="Experimental actions"
id="org.eclipse.refactoring.actionSet"
label="Langexplr Section"
visible="true">
<menu
id="ScalaTestRefactoring.menu1"
label="Langexplr entries 2"
path="edit">
<separator
name="ScalaTestRefactoring.separator1">
</separator>
</menu>
<action
class="langexplr.eclipse.InvertIfStatementRefactoringAction"
definitionId="ScalaTestRefactoring.command1"
enablesFor="*"
id="ScalaTestRefactoring.action1"
label="Invert if statement(s)..."
menubarPath="ScalaTestRefactoring.menu1/ScalaTestRefactoring.separator1"
style="push">
</action>
</actionSet>
</extension>


Which says that the option "Invert if statement(s)..." will be added to a menu called "Langexplr entries 2" and that the action is implemented in the class langexplr.eclipse.InvertIfStatementRefactoringAction. More information about adding options to the Eclipse UI can be found in Contributing Actions to the Eclipse Workbench.

The code for the langexplr.eclipse.InvertIfStatementRefactoringAction class is the following:


class InvertIfStatementRefactoringAction extends IWorkbenchWindowActionDelegate {
var window : IWorkbenchWindow = null
var selection : ITextSelection = null
var compilationUnit : ICompilationUnit = null

override def dispose : unit = {
return
}
override def init( window : IWorkbenchWindow) = {
this.window = window;
}
override def run( action : IAction) = {
if (selection != null && window != null && compilationUnit != null) {
val refactoring = new InvertIfStatementRefactoring
refactoring.selection = selection
refactoring.compilationUnit = compilationUnit
val wizardOperation : RefactoringWizardOpenOperation =
new RefactoringWizardOpenOperation(
new InvertIfStatementRefactoringWizard(refactoring))
wizardOperation.run(window.getShell(), "Invert if statement");
}
}
override def selectionChanged( action : IAction, selection : ISelection) = {
this.selection = null;

if(selection.isInstanceOf[ITextSelection]) {
val s = selection.asInstanceOf[ITextSelection];
val cu = compilationUnitForCurrentEditor();
if (cu != null) {
compilationUnit = cu;
val selectionText = s.getText()
if (selectionText.indexOf("if") != -1) {
action.setEnabled(true)
this.selection = s
} else {
action.setEnabled(false)
}
}
} else {
action.setEnabled(false);
}
}
...
}


What this code actually does, is that it activates the action when the selected text contains the "if" string (which I know is a pretty weak criteria) more verifications are added later. Also when executed the action will run the wizard.

The refactoring wizard is only used to ask the user if the negation of the resulting IF statements needs to be propagated. In order to do this a wizard page most be created to ask that question. Here's the code for this page:


class InvertIfStatementRefactoringInputPage(name : String)
extends UserInputWizardPage(name) {
override def createControl(c : Composite) = {
val refactoringControl = new Composite(c,SWT.NONE)

this.setControl(refactoringControl)

val layout = new GridLayout
layout.marginTop = 10
layout.marginLeft = 10
layout.numColumns = 2

refactoringControl.setLayout(layout)

val iLabel = new Label(refactoringControl,SWT.NONE)
iLabel.setText("Propagate negation")
val cbButton = new Button(refactoringControl,SWT.CHECK)

cbButton.addSelectionListener(
new SelectionAdapter() {
override def widgetSelected(e : SelectionEvent) = {
val r = getRefactoring().asInstanceOf[InvertIfStatementRefactoring]
r.propagateNegation = cbButton.getSelection()
}
}
)
setPageComplete(true);
}
}


Given this we can create the class that represents the wizard:


class InvertIfStatementRefactoringWizard(r : Refactoring)
extends RefactoringWizard(r,
RefactoringWizard.DIALOG_BASED_USER_INTERFACE |
RefactoringWizard.PREVIEW_EXPAND_FIRST_NODE) {

override def addUserInputPages() = {
setDefaultPageTitle("Invert IF statement")
addPage(new InvertIfStatementRefactoringInputPage("InputPage"))
}
}


The resulting dialog looks like this:



In the second part, the implementation of the actual refactoring will be presented.

Code for this experiment can be found here.

Tuesday, July 3, 2007

Creating FxCop rules with F#

In this post I'm going to show a little example of an FxCop rule created with F#. My goal is to show that F# features can be used to define code rules.

The purpose of the post is not to show how to create FxCop rules, a nice explanation can be found in this useful blog entry: FxCop This (also useful links are provided).

The rule that I'm going to show is used to detect assignments from a field to itself. For example:


public class AClass {

int aValue;
public void AMethod(int aValeu){
this.aValue = aValue;
...
}
...
}


In this example, an error typing the name of the first argument of AMethod leads to an incorrect assignment of field aValue to itself. The C# compiler gives you a warning on this, however the code is generated because it is a valid program.

In order to create an FxCop rule that detects this, we have to identify the IL code sequence that represents the assignment. By using ILDASM we can see this:


.method public hidebysig instance void AMethod(int32 aValeu) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldfld int32 AClass::aValue
IL_0008: stfld int32 AClass::aValue
...
} // end of method AClass::AMethod



So what we need to find is the simple pattern of a LDFLD opcode followed by a STFLD to the same field. The code for this rule looks like this:


type SameFieldAssignment = class
inherit BaseIntrospectionRule
new() = {inherit BaseIntrospectionRule("SameFieldAssignment","SameFieldAssignment",Assembly.GetExecutingAssembly());}

override x.Check(m : Member) =
match m with
| (:? Method as meth) ->
let instrs = instructions_to_list meth.Instructions
in
x.find_assignment instrs
| _ -> x.Problems

member x.find_assignment (instructions : Instruction list) =
match instructions with
| (load::store::r) when
(load.OpCode = OpCode.Ldfld &&
store.OpCode = OpCode.Stfld &&
load.Value = store.Value)
-> x.Problems.Add(new Problem(x.GetNamedResolution("SameFieldAssignment",[||]),store.SourceContext))
x.find_assignment r
| (_::r) -> x.find_assignment r
| _ -> x.Problems
end


The find_assignment method is used to search for the desired pattern. The IL code sequence is converted to a F# list (via the instructions_to_list utility function), then a list pattern is the used to identify the desired pattern.

We can improve this code by using active patterns. For example we can define the following patterns:


let (|Ldfld|_|) (i : Instruction) =
if (i.OpCode = OpCode.Ldfld) then
Some ((i.Value :?> Field) , i.SourceContext)
else
None

let (|Stfld|_|) (i : Instruction) =
if (i.OpCode = OpCode.Stfld) then
Some((i.Value :?> Field),i.SourceContext)
else
None

let (|MethodContent|_|) (m : Member) =
match m with
| (:? Method as meth) ->
Some (meth.FullName,
instructions_to_list meth.Instructions)
| _ -> None


Now we can write:


type SameFieldAssignmentAp = class
inherit BaseIntrospectionRule
new() = {inherit BaseIntrospectionRule("SameFieldAssignmentAp","SameFieldAssignmentAp",Assembly.GetExecutingAssembly());}

override x.Check(m : Member) =
match m with
| MethodContent(_,instrs) -> x.find_assignment instrs
| _ -> x.Problems

member x.find_assignment (instructions : Instruction list) =
match instructions with
| (Ldfld(lField,_)::Stfld(sField,source)::r) when (lField = sField)
-> let p = new Problem(x.GetNamedResolution("SameFieldAssignmentAp",[|lField.FullName|]),source) in
x.Problems.Add(p)
x.find_assignment r
| (_::r) -> x.find_assignment r
| _ -> x.Problems
end


Code for this experiment can be found here.