Showing posts with label scala. Show all posts
Showing posts with label scala. Show all posts

Tuesday, December 4, 2007

Improving code using more Scala features

A couple of days ago Eric Willigers gave me some nice feedback on how to improve the code of the quick fix created for the previous post.

Before modifying the document we are required to lock it and then release it. I did it like this:


...
doc.atomicLock
doc.replace(ifRange.getStart,ifRange.getLength,resultingStatement,null)
doc.atomicUnlock
...


However this is not safe since the replace method could throw a BadLocationException. One of the things we could do is to add a try/catch/finally block. However Eric pointed me to a much nicer way to do , by using two techniques called:



The Loan pattern is a way to ensure resource disposal when the control leaves some scope. For this propose closures are used.

The Pimp my library pattern, based on a Martin Odersky’s article, provides a way to extend existing class libraries with new operations without recompiling. New operations are added by defining a wrapper for specific classes. In this case we need to apply the Loan pattern to the BaseDocument class


class RichDocument(doc : BaseDocument) {
def withLock[T](f : BaseDocument => T ) = {
try {
doc.atomicLock()

f(doc)

} catch {
case ble : BadLocationException =>
Exceptions.printStackTrace(ble)
} finally {
doc.atomicUnlock()
}
}
}


This class defines our wrapper, the f is an anonymous function with code that will modify the document. Now we can add this functionality to the BaseDocument class by using the following declaration:


object DocumentImplicits {
implicit def toRichDocument(doc : BaseDocument) = new RichDocument(doc)
}



Putting using these definitions in the fix code leaves the code like this:


...
doc.withLock(_.replace(ifRange.getStart,
ifRange.getLength,
resultingStatement,
null))
...


The "_.replace ..." syntax is interesting way of specifying an anonymous function with one argument. For more information on this see Placeholder Syntax for Anonymous Functions in section 6.23 of the Scala Language Specification.


Another thing that could be done using this pattern is to eliminate direct uses of the AstUtilities class. By doing this we can add the following definitions:


object AstNodeImplicits {
implicit def toRichAstNode(node : Node) = new RichAstNode(node)
}

class RichAstNode(node : Node) {
def getRange() = AstUtilities.getRange(node)
}


Before using this implicits we need to import them:


import org.langexplr.nbhints.utils.DocumentImplicits.toRichDocument
import org.langexplr.nbhints.utils.AstNodeImplicits.toRichAstNode


The final code for the fix method is the following:


def implement() = {
val doc = info.getDocument.asInstanceOf[BaseDocument]
val conditionRange = condition.getRange()
val conditionText = getConditionText(doc,conditionRange)
val statementRange = statement.getRange()
val statementText = doc.getText(statementRange.getStart,
statementRange.getLength)
val ifRange = ifNode.getRange()

val resultingStatement = statementText + getModifierText + conditionText

doc.withLock(_.replace(ifRange.getStart,
ifRange.getLength,
resultingStatement,
null))
}


Code for this example can be found here (Now updated to the released Netbeans 6.0 ) .

Saturday, November 24, 2007

Creating Netbeans Ruby Hints with Scala, Part 2

In this part I'm going to show a quick fix for the Netbeans Ruby hint presented in part 1.

The quick fix class

The definition of the quick fix for the hint presented in part 1 is the following:


class IfToStatementModifierFix(
info : CompilationInfo,
condition : Node,
statement : Node,
ifNode : Node,
kind : StatementModifierFix) extends Fix {

def isSafe = true
def isInteractive = false

...

}


As shown in this fragment the class that represents the fix requires a reference to each tree element involved in the problem. In this case the important parts are the complete if statement, the condition and the then statement.

Also a kind argument(specific for this quick fix) is used to determine if a if or a unless statement modifier needs to be created. The definition for StatementModifierFix, looks like this:

abstract class StatementModifierFix
case class IfStatementModifierFix extends StatementModifierFix
case class UnlessStatementModifierFix extends StatementModifierFix


The quick fix implementation

The IfToStatementModifierFix class needs to implement the implement method in order to do its work, here's the code:


def implement() = {
val doc = info.getDocument.asInstanceOf[BaseDocument]
val conditionRange = AstUtilities.getRange(condition)
val conditionText = getConditionText(doc,conditionRange)
val statementRange = AstUtilities.getRange(statement)
val statementText = doc.getText(statementRange.getStart,statementRange.getLength)
val ifRange = AstUtilities.getRange(ifNode)

val resultingStatement = statementText + getModifierText + conditionText
doc.atomicLock
doc.replace(ifRange.getStart,ifRange.getLength,resultingStatement,null)
doc.atomicUnlock
}


In order to generate the unless statement we need to change the operator from != to ==.
The getConditionText method does this job:


def getConditionText(doc : BaseDocument,range: OffsetRange) = {
val text = doc.getText(range.getStart,range.getLength)
kind match {
case IfStatementModifierFix() => text
case UnlessStatementModifierFix() => text.replace("!=","==")
}
}


The last thing we need to do is to connect the hint with this quick fix. In order to do this we need to modify the run method of the IfToStatementModifierHint class presented in part 1. We need to add a fix for each identified case.

The case when a method is used as a condition:


...
case ifStat@IfStatement(methodCall : CallNode,
NewlineNode(thenStat),
null) => {

val range = AstUtilities.getRange(node)

val fixes = new java.util.ArrayList
fixes.add(
new IfToStatementModifierFix(info,methodCall,thenStat,ifStat,IfStatementModifierFix))

val desc =
new Description(this,
recomendationText,
info.getFileObject,range,
fixes,
600)
result.add(desc)
}
...


The case when the condition is a not-equal comparison with nil:


case ifStat@IfStatement(condition@NotNode(eqExp@EqualComparison(x,_ : NilNode)),
NewlineNode(thenStat),
null) => {


val range = AstUtilities.getRange(node)

val fixes = new java.util.ArrayList
fixes.add(
new IfToStatementModifierFix(info,condition,thenStat,ifStat,IfStatementModifierFix))
fixes.add(
new IfToStatementModifierFix(info,eqExp,thenStat,ifStat,UnlessStatementModifierFix))

val desc =
new Description(this,
recomendationText,
info.getFileObject,range,
fixes,
600)
result.add(desc)
}


Note that for this case we also offer the quick fix for unless.

Example 1

Activating the hint on this code:



Now displays the following quick fix:



And changes the code as:



Example 2

Also for code like this:



The following options are presented:



By selecting option 2 the code is changed as:



Code for this experiment can be found here.

Monday, November 12, 2007

Creating Netbeans Ruby Hints with Scala, Part 1

In this post I'm going to show a little experiment of creating a Netbeans Ruby Hint using the Scala language. This is the first of a two-part series of posts on this topic.

Netbeans Ruby Hints and Quick Fixes is a very nice feature that will be part of Netbeans 6.0 Ruby integration. This feature can be used to identify potential problems or to promote best practices in Ruby programs. I first learned about it by reading Tor Norbye's blog.

I really like the idea of mixing languages to accomplish something. This is the main reason I chose Scala to do this experiment. Also I wanted to try to apply some ideas from previous posts. For a future experiment it will be very nice to write this code using JRuby.

For this post I'm using Netbeans 6.0 Beta 2. As described below the APIs for writing hints are still not officially public because they're still under development. Again, as with previous posts, this is only an experiment to see how different programming languages could be used solve special tasks.

Motivation

Several weeks ago I wrote a post on Ruby's yield statement, there I showed a function that calculates the Fibonacci sequence using a while statement in a pretty traditional way. Then someone posted a comment with a much compact version of it by using some nice Ruby features. That got me thinking how Netbeans Ruby Hints could also be used to help Ruby beginners(like me) to find out about alternative ways to do something.

The Hint

One of the things I like about Ruby is statement modifiers. That's one of the first things you notice while reading the Why's (Poignant) Guide to Ruby.

I think statement modifiers makes the code look nice if used carefully.

So the hint that will be implement it's going to identify opportunities to apply the if or unless statement modifiers. For example:


table = Hash.new
...
if table.empty? then
puts "No items"
end
...


Could also be written as:


table = Hash.new
...
puts "No items" if table.empty?
...


Creating the Netbeans module

The biggest challenge I had was to create a Netbeans module using only Scala. The main issue is not having all the nice GUI facilities available to create to module.

Luckily there's a series of articles in Geertjan's Weblog called NetBeans Modules for Dummies Part 1,2,3,4. These articles provided a lot of information on how to create an NBM file using Ant tasks .

I also had to create a some Netbeans modules using Java to dissect the resulting NBM file and to provide some of the missing arguments. The resulting Ant file is can be found here.

Another challenge that I had was that some of the classes that I needed to access were only available to friend packages, this mainly because I was trying to access APIs that are still changing. Luckly, Tor Norbye from the Netbeans Ruby development list, provided a solution for this issue which could be found here. Again this is a consequence of using API's that are still not public.

The last issue that I had was to add the Scala Runtime Jar file as part of the package. The How do module dependencies/classloading work? Netbeans Faq entry was very helpful to solve this.

The code

By reading the code of existing hints, I learned that, the way to define hints (which is still under development) is very nice and very simple. Also there's useful documentation in the Writing New Ruby Hints entry from the Netbeans wiki.

Here's the code:

class IfToStatementModifierHint extends AstRule {

def appliesTo(info : CompilationInfo) = true

def getKinds() : Set = {
val nodeKinds = new HashSet()
nodeKinds.add(NodeTypes.IFNODE)
nodeKinds
}

def run(info :CompilationInfo,
node : Node,
path : AstPath ,
caretOffset : int,
result : java.util.List) : unit = {

node match {
case IfStatement(callNode : CallNode,
NewlineNode(thenStat),
null) => {

val range = AstUtilities.getRange(node)

val desc =
new Description(this,
recomendationText,
info.getFileObject,range,
Collections.emptyList,
600)
result.add(desc)
}
case IfStatement(NotNode(EqualComparison(x,_ : NilNode)),
NewlineNode(thenStat),
null) => {


val range = AstUtilities.getRange(node)

val desc =
new Description(this,
recomendationText,
info.getFileObject,range,
Collections.emptyList,
600)
result.add(desc)
}
case _ =>
{
}
}
}

def getId() = "TestHint1"

def getDisplayName() = "If statement as statement modifier"

def getDescription() = "Identifies certain ..."

def getDefaultEnabled() = true

def getDefaultSeverity() = HintSeverity.WARNING

def getCustomizer(node: Preferences) : JComponent = null

def showInTasklist() = true

def recomendationText = "Consider changing..."

}


The most interesting methods of this class are getHints and run.

The getHints method defines the kinds of AST nodes this method applies to. The run method identifies which kinds of if statement apply to this hint. To do this a couple of extractor objects defined in previous posts are used. Two special cases are identified:

  • One if statement with a method call as condition, one statement in the then section and no statements in the else section.

  • One if statement with an equal comparison to nil as condition, one statement in the then section and no statements in the else section.


This Hint is shown as a WARNING because it's the how severity that seems to apply. Although it will be very nice to have a RECOMMENDATION severity which seems to be more appropriate for this hint.

I really like this way of defining hints because you have to write exactly the code related to your problem.

Here's and example in action:




Code for this experiment can be found here.

For the next post I'll try to implement a quick fix for this hint.

Saturday, November 10, 2007

Exploring JRuby Syntax Trees With Scala, Part 2

In this post I'm going to show a couple of examples of using Scala to extract elements of Ruby programs.

Because of its functional influence Scala provides nice features to manipulate tree structures. As I mentioned in previous posts, I think one of the most interesting features provided by Scala is pattern matching. Among other pattern matching constructs, Scala provides one that I particularly like : Extractors.

The Matching Object With Patterns paper by Burak Emir , Martin Odersky , and John Williams, describes several techniques for exploring complex object data structures. One of this techniques is the Visitor design pattern which is one of the most used techniques that is used to navigate a syntax tree structure. I think the pattern matching approach can be combined with the visitors to solve different tasks.

In this post I'm interested in showing pattern matching using Scala extractors on the JRuby AST. The JRuby AST seems to provide nice visitor mechanism which will be explored in future posts.

Scala extractor objects allow existing Scala or Java Platform classes to participate in pattern matching just as case classes. Extractor objects can be used to expose only interesting elements for a certain problem and also makes the definition of the class independent of the way it is used in a match construct. A nice description of this feature for Scala is provided in the Matching Object With Patterns paper. A similar feature is implemented in F# as active patterns.This concept was originated by work on Views by Philip Walder .

I'm going to show a couple of examples of using this feature with the JRuby AST.

Example 1:

Say that you want to identify the key value that is given to Hash assignment: (here highlighted in red)


x["a"] = 1


We need to define extractors for related objects. In order to do this I'm going to use the tool from the previous post to see which classes we need to represent.



By reading this code we noticed that we need at least extractors for

  • BlockNode

  • NewlineNode

  • AttrAssignNode

  • LocalVarNode

  • ArrayNode

  • StrNode

  • FixnumNode



Basic extractors for the child nodes of these elements could be defined as follows:


object NewlineNode {
def unapply(astNode : Node) =
if (astNode.isInstanceOf[NewlineNode]) {
Some(astNode.asInstanceOf[NewlineNode].getNextNode)
} else {
None
}
}

object LocalVarNode {
def unapply(astNode : Node) = {
if (astNode.isInstanceOf[LocalVarNode]) {
Some (astNode.asInstanceOf[LocalVarNode].getName)
} else {
None
}
}
}

object StrNode {
def unapply(astNode : Node) = {
if (astNode.isInstanceOf[StrNode]) {
Some (astNode.asInstanceOf[StrNode].getValue)
} else {
None
}
}
}

object BlockNode {
def unapply(astNode : Node) = {
if (astNode.isInstanceOf[BlockNode]) {
Some (List.fromIterator(
new JavaIteratorWrapper[Node](
astNode.asInstanceOf[BlockNode].childNodes.iterator())))
} else {
None
}
}
}

object FixnumNode {
def unapply(astNode : Node) = {
if (astNode.isInstanceOf[FixnumNode]) {
Some (astNode.asInstanceOf[FixnumNode].getValue)
} else {
None
}
}
}


object AttrAssignNode {
def unapply(astNode : Node) = {
if (astNode.isInstanceOf[AttrAssignNode]) {
val attAsgn = astNode.asInstanceOf[AttrAssignNode]
Some (attAsgn.getReceiverNode,attAsgn.getArgsNode)
} else {
None
}
}
}

object ArrayNode {
def unapply(astNode : Node) =
if (astNode.isInstanceOf[ArrayNode]) {
val theArrayNode = astNode.asInstanceOf[ArrayNode]
Some(List.fromIterator(new JavaIteratorWrapper[Node](theArrayNode.childNodes.iterator)))
} else {
None
}
}


By defining these extractors we can combine them with existing Scala pattern matching constructs to create the required pattern:


val rubyCode2 = "x = Hash.new\n"+"x[\"a\"] = 1"
val rootNode2 = r.parse(rubyCode2,"dummy.rb",r.getCurrentContext.getCurrentScope,0).asInstanceOf[RootNode]

rootNode2.getBodyNode match {
case BlockNode(
List(_,
NewlineNode(
AttrAssignNode(
LocalVarNode("x"),
ArrayNode(
List(key,
FixnumNode(1))))))) =>
System.out.println("Matches for: "+ key.toString())
case _ => System.out.println("No Match!")
}


Example 2:

For the next example say that we want to extract the identifier which is compared to some number in the condition of an if statement. For example:


if the_variable == 1 then
...
else
...
end


The tree representation of a code similar to this looks like:



The following extractors will be used to identify this pattern:


object IfStatement {
def unapply(astNode : Node) =
if (astNode.isInstanceOf[IfNode]) {
val theIfNode = astNode.asInstanceOf[IfNode]
Some (theIfNode.getCondition,theIfNode.getThenBody,theIfNode.getElseBody)
} else {
None
}
}


object EqualComparison {
def unapply(astNode : Node) =
if (astNode.isInstanceOf[CallNode]) {
val theCallNode = astNode.asInstanceOf[CallNode]
if (theCallNode.getName.equals("==") &&
theCallNode.getArgsNode.isInstanceOf[ArrayNode] &&
theCallNode.getArgsNode.asInstanceOf[ArrayNode].size == 1) {
Some (theCallNode.getReceiverNode,
theCallNode.getArgsNode.asInstanceOf[ArrayNode].get(0))
} else {
None
}
} else {
None
}
}


Note that in this case I'm using a shortcut, instead of defining an extractor for CallNode and using it with ArrayNode a single extractor is used to identify the binary equal comparison.

By using this extractors we can define the pattern as follows:


val rubyCode3 = "if the_variable == 30 then\n puts 2\nelse\n puts 3\nend"
val rootNode3 = r.parse(rubyCode3,"dummy.rb",r.getCurrentContext.getCurrentScope,0).asInstanceOf[RootNode]
val nodeToMatch = rootNode3.getBodyNode.asInstanceOf[NewlineNode].getNextNode
nodeToMatch match {
case IfStatement(EqualComparison(leftSide,_ : FixnumNode),
_,
_) =>
System.out.println("Matches for: "+ leftSide.toString())
case _ => System.out.println("No Match!")
}



For future post I'm going to be using these extractors to create more examples in other contexts.

Wednesday, November 7, 2007

Exploring JRuby Syntax Trees With Scala, Part 1

In this post I'm going to show how to use Scala to have access to JRuby syntax trees.

Abstract syntax trees(AST) provide a representation of the source code that can be manipulated by another program. These trees are used by compilers and IDEs to do their work.

The JRuby distribution includes a parser and a nice tree representation for Ruby source code. This AST is used by the JRuby implementation to do its work, and also recently I noticed that the Netbeans Ruby support uses this representation for some tasks.

Calling JRuby Parser is pretty straightforward, just do the following:


package org.langexplr.jrubyasttests;
import org.jruby._
import org.jruby.ast._


object ScalaTestApp {
def main(args : Array[String]) : Unit = {
val rubyCode = "puts \"Hello\""

val r = Ruby.getDefaultInstance
val rootNode = r.parse(rubyCode,"dummy.rb",r.getCurrentContext.getCurrentScope,0).asInstanceOf[RootNode]
...
}
}


The rootNode variable contains the root of the syntax tree for the code stored in rubyCode.

In order to explore the syntax tree I wrote a small program that shows a Swing JTreecomponent with the result of parsing a snippet. Creating this program was easy because Node, the base class of all the elements in JRuby AST, provides a childNodes method. The model for the AST is the following:


package org.langexpr.jrubyasttest;

import javax.swing.tree.TreeModel
import javax.swing.event.TreeModelListener
import javax.swing.tree.TreePath
import org.jruby._
import org.jruby.ast._

class JRubyTreeModel() extends TreeModel {
var rootNode : Node = null;
def this(rubyCode : String) = {
this()
val r = Ruby.getDefaultInstance
this.rootNode = r.parse(rubyCode,"dummy.rb",r.getCurrentContext.getCurrentScope,0)
}

def getRoot : Object = rootNode

def isLeaf(node: Object ) =
node.asInstanceOf[Node].childNodes.size == 0

def getChildCount( parent : Object) : int =
parent.asInstanceOf[Node].childNodes.size

def getChild(parent : Object , index : int) : Object = {
parent.asInstanceOf[Node].childNodes.get(index)
}
def getIndexOfChild( parent : Object, child : Object) : int = {
parent.asInstanceOf[Node].childNodes.indexOf(child)
}
def valueForPathChanged( path : TreePath, newValue : Object) : unit ={

}
def addTreeModelListener( l : TreeModelListener) : unit = {

}
def removeTreeModelListener( l : TreeModelListener) : unit = {

}
}


With this model now we can create the main program:


package org.langexpr.jrubyasttest;


import javax.swing._
import java.awt.event._
import java.awt.BorderLayout
import java.awt.Dimension

object JRubyAstTest {
val treeVisualizer = new JTree
val input = new JTextArea


def createFrame = {
val theFrame = new JFrame("JRuby AST test")
theFrame.setSize(new Dimension(640,450))
val content = theFrame.getContentPane
content.setLayout(new BorderLayout)
content.add(treeVisualizer,BorderLayout.CENTER)


val bottomPanel = new JPanel
bottomPanel.setLayout(new BorderLayout)
content.add(bottomPanel,BorderLayout.SOUTH)

bottomPanel.add(new JScrollPane(input),BorderLayout.CENTER)
input.setText("x=1\ny = 2\n")

treeVisualizer.setModel(new JRubyTreeModel(input.getText))

val button = new JButton("Parse!")
button.addActionListener( new ActionListener() {
def actionPerformed(ae : ActionEvent) =
{
treeVisualizer.setModel(new JRubyTreeModel(input.getText))
}
})

bottomPanel.add(button,BorderLayout.SOUTH)

theFrame
}
def main(args : Array[String]) : Unit = {
val f = createFrame
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true)
}
}


Now we can visually explore the AST using this little program.

The following code:

def foo(x)
return x*x
end


Is visualized like this:



Examples in this post could also be implemented easily using JRuby but in the second part I'm going to a couple of Scala features that I specially like to manipulate this data structure.

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.

Wednesday, May 30, 2007

Using Scala Extractors with the XSD Schema Infoset Model

In this post I'm going to use Scala extractor objects with the Eclipse XML Schema Infoset Model to identify common ways of defining XML Schemas.

Hopefully I'm going to show that complex patterns on common Java objects can be identified using Scala extractors.

The Eclipse Schema Infoset Model is a complex EMF model the represents the W3C XML Schema. The Analyzing XML schemas with the Schema Infoset Model and Analyze Schemas with the XML Schema Infoset Model articles provide a nice explanation on how to work with this model.

For this post I wanted to create Scala patterns that identify common design patterns in Xml Schemas. There are four common patterns for XML Schemas: Russian Doll, Salami Slice, Venetian Blind and Garden of Eden.

The article Introducing Design Patterns in XML Schemas provide a nice explanation on each of this patterns. Also the article talks about a nice feature of NetBeans Enterprise Pack that allows the user to move a schema from one design pattern to another. More information on these design patterns can be found on the article Global vs Local from the xFront site.

The W3C XML Schema model is huge, but for this post I'm going to consider only a small subset.

The first step is the definition of the extractor objects that will be used to have access to certain properties of the Xml Schema Infoset model.


package langexplr.scalaextractorexperiments;

import org.eclipse.emf.ecore.resource._
import org.eclipse.emf.ecore.resource.impl._
import org.eclipse.xsd._
import org.eclipse.xsd.impl._
import org.eclipse.xsd.util._
import org.eclipse.emf.common.util.URI

object XSDSchemaParts {
def unapply(schema : XSDSchema) =
Some ((schema.getTargetNamespace(),
List.fromIterator(
new JavaIteratorWrapper[XSDTypeDefinition](
schema.getTypeDefinitions().iterator())),
List.fromIterator(
new JavaIteratorWrapper[XSDElementDeclaration](
schema.getElementDeclarations().iterator()))))

}

object XSDElementParts {
def unapply(elementDeclaration : XSDElementDeclaration) =
Some((elementDeclaration.getName(),elementDeclaration.getTypeDefinition()))


}

object XSDComplexType {
def unapply(typeDefinition : XSDTypeDefinition) =
if (typeDefinition.isInstanceOf[XSDComplexTypeDefinition]) {
val complexType = typeDefinition.asInstanceOf[XSDComplexTypeDefinition];
Some((complexType.getName(),complexType.getContent()))
} else {
None
}

}

object XSDSimpleType {
def unapply(typeDefinition : XSDTypeDefinition) = {
if (typeDefinition.isInstanceOf[XSDSimpleTypeDefinition]) {
Some(typeDefinition.asInstanceOf[XSDSimpleTypeDefinition])
} else {
None
}
}
}

object XSDParticleContent {
def unapply(p : XSDParticle) = Some(p.getContent())
}

object XSDSimpleSequenceModelGroup {
def unapply(complexTypeContent : XSDComplexTypeContent) = {
complexTypeContent match {
case XSDParticleContent(mg : XSDModelGroup)
if (mg.getCompositor().getName == "sequence") =>
Some(
List.fromIterator(
new JavaIteratorWrapper[XSDParticle](
mg.getContents.iterator())))
case _ => None
}
}



The XSDSchemaParts, XSDElementParts, XSDComplexType, XSDSimpleType, and XSDParticleContent extractor objects provide access to some properties of a model object. For example the XSDSchemaParts returns a tuple with the target namespace, the complex type definitions and the element definitions.

Also the XSDSimpleSequenceModelGroup provide an easy way to identify a common pattern that is the use of a XSD sequence as the type main element.

A class will be created for each design pattern. The following trait is the base for all of them:


trait XsdDesignPattern {
def name : String
def identify(schema:XSDSchema) : boolean
}



Now we can define each pattern:

Russian Doll

This design pattern says that the structure of the XML Schema is similar to the document structure. Only one public element is defined and all other elements are defined inside of it.

For example:


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://langexplr.blogspot.com/DocsRussianDoll"
xmlns:p="http://langexplr.blogspot.com/DocsRussianDoll"
xmlns="http://langexplr.blogspot.com/DocsRussianDoll"
elementFormDefault="qualified">
<xs:element name="page">
<xs:complexType>
<xs:sequence>
<xs:element name="header">
<xs:complexType>
<xs:sequence>
<xs:element name="content" type="xs:string" />
</xs:sequence>
<xs:attribute name="margin"
type="xs:integer" />
</xs:complexType>

</xs:element>
<xs:element name="body">
<xs:complexType>
<xs:sequence>
<xs:element name="paragraph"
type="xs:string" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="footer">
<xs:complexType>
<xs:sequence>
<xs:element name="content" type="xs:string" />
</xs:sequence>
<xs:attribute name="margin"
type="xs:integer" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>


The Scala code to identify this design pattern looks like this:


class RussianDoll extends XsdDesignPattern {
def name = "Russian Doll"
def identify(schema : XSDSchema) =
schema match {
case XSDSchemaParts(
namespace,
List(),
List(XSDElementParts(
name,
XSDComplexType(
null,
XSDSimpleSequenceModelGroup(elements))))) => {
true
}
case _ => false
}
}




Salami Slice

This design pattern says that all elements must be declared at the top level with the type declaration inside of them.

For example:


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://langexplr.blogspot.com/DocsSalamiSlice"
xmlns:tns="http://langexplr.blogspot.com/DocsSalamiSlice"
xmlns="http://langexplr.blogspot.com/DocsSalamiSlice"
elementFormDefault="qualified">

<xs:element name="content" type="xs:string" />
<xs:element name="paragraph" type="xs:string" />

<xs:element name="header">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:content" />
</xs:sequence>
<xs:attribute name="margin" type="xs:integer" />
</xs:complexType>
</xs:element>

<xs:element name="footer">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:content" />
</xs:sequence>
<xs:attribute name="margin" type="xs:integer" />
</xs:complexType>
</xs:element>

<xs:element name="body">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:paragraph" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name="page">
<xs:complexType>
<xs:sequence>
<xs:element ref="tns:header" />
<xs:element ref="tns:body" />
<xs:element ref="tns:footer" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>



The Scala code to identify this design pattern looks like this:


class SalamiSlice extends XsdDesignPattern {
def name = "Salami Slice"
def identify(schema : XSDSchema) =
schema match {
case XSDSchemaParts(
namespace,
List(),
elements) => {
elementsWithReferences(elements)
}
case _ => false
}
// Utility methods

def forAllInnerElements(l : List[XSDElementDeclaration],
pred : XSDElementDeclaration => boolean) =
l.forall{
case XSDElementParts(
_,
XSDComplexType(null,XSDSimpleSequenceModelGroup(particles))) =>
particles.forall({
case XSDParticleContent(e:XSDElementDeclaration) => pred(e)
case _ => false })
case XSDElementParts(_,XSDComplexType(null,null)) => true
case XSDElementParts(_,XSDSimpleType(_)) => true
case _ => false
}

def elementsWithReferences(x : List[XSDElementDeclaration]) =
forAllInnerElements(
x,
(e:XSDElementDeclaration) => e.isElementDeclarationReference)

}




Venetian Blind

This design pattern says that there one global element and all other elements use types declared at the top level.

For example:


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://langexplr.blogspot.com/DocsVenetianBlind"
xmlns:tns="http://langexplr.blogspot.com/DocsVenetianBlind"
xmlns="http://langexplr.blogspot.com/DocsVenetianBlind"
elementFormDefault="qualified">

<xs:complexType name="sectionType">
<xs:sequence>
<xs:element name="content" type="xs:string" />
</xs:sequence>
<xs:attribute name="margin" type="xs:integer" />
</xs:complexType>

<xs:complexType name="bodyType">
<xs:sequence>
<xs:element name="paragraph" type="xs:string" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>

</xs:complexType>

<xs:element name="page">
<xs:complexType>
<xs:sequence>
<xs:element name="header" type="tns:sectionType" />
<xs:element name="body" type="tns:bodyType" />
<xs:element name="footer" type="tns:sectionType" />
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>




The Scala code for this pattern looks like this:


class VenetianBlind extends XsdDesignPattern {
def name = "Venetian Blind"
def identify(schema : XSDSchema) =
schema match {
case XSDSchemaParts(
namespace,
types,
List(XSDElementParts(
_,
XSDComplexType(
_,
XSDSimpleSequenceModelGroup(elements))))) =>
elements.forall((e:XSDParticle) =>
elementWithTypeReferences(e,types))
case _ => false
}
def elementWithTypeReferences(e : XSDParticle, types : List[XSDTypeDefinition]) =
e match {
case XSDParticleContent(e:XSDElementDeclaration) =>
e.getTypeDefinition.getContainer.isInstanceOf[XSDSchema] &&
!(types.find ((t:XSDTypeDefinition) => t == e.getTypeDefinition)).isEmpty
case _ => false
}

}



Garden of Eden

This design pattern says that all the elements and types must be declared global.


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://langexplr.blogspot.com/DocsGardenOfEden"
xmlns:tns="http://langexplr.blogspot.com/DocsGardenOfEden"
xmlns="http://langexplr.blogspot.com/DocsGardenOfEden"
elementFormDefault="qualified">

<xs:complexType name="sectionType">
<xs:sequence>
<xs:element ref="tns:content" />
</xs:sequence>
<xs:attribute name="margin" type="xs:integer" />
</xs:complexType>

<xs:complexType name="bodyType">
<xs:sequence>
<xs:element ref="tns:paragraph" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>

<xs:element name="content" type="xs:string" />

<xs:element name="paragraph" type="xs:string"/>

<xs:element name="header" type="tns:sectionType" />

<xs:element name="body" type="tns:bodyType" />

<xs:element name="footer" type="tns:sectionType" />

<xs:complexType name="pageType">
<xs:sequence>
<xs:element ref="tns:header" />
<xs:element ref="tns:body" />
<xs:element ref="tns:footer" />
</xs:sequence>
</xs:complexType>

<xs:element name="page" type="tns:pageType" />
</xs:schema>



The Scala code for this pattern looks like this:


class GardenOfEden extends XsdDesignPattern {
def name = "Garden Of Eden"
def identify(schema : XSDSchema) =
schema match {
case XSDSchemaParts(
namespace,
types,
elements) =>
elements.forall((e : XSDElementDeclaration) =>
elementWithTypeReferences(e,types))
case _ => false
}

def elementWithTypeReferences(e : XSDElementDeclaration, types : List[XSDTypeDefinition]) =
e.getTypeDefinition.getContainer.isInstanceOf[XSDSchema] &&
((types.find ((t:XSDTypeDefinition) => t == e.getTypeDefinition)) match {
case Some(XSDComplexType(_,XSDSimpleSequenceModelGroup(particles))) =>
particles.forall({
case XSDParticleContent(e:XSDElementDeclaration) =>
e.isElementDeclarationReference
case _ => false })
case Some(XSDComplexType(_,null)) => true
case Some(XSDSimpleType(_)) => true
case None =>
e.getTypeDefinition.getTargetNamespace == "http://www.w3.org/2001/XMLSchema"
case _ => false
})

}





Finally we need a class to test all the patterns:


object XsdDesignPatterns {
def patterns:List[XsdDesignPattern] = List(new RussianDoll,
new SalamiSlice,
new VenetianBlind,
new GardenOfEden)
def identify(schema : XSDSchema) =
patterns.filter((p:XsdDesignPattern) => p identify schema).map((p:XsdDesignPattern) => p.name)
}




The code for this experiment can be found here.