Showing posts with label eclipse. Show all posts
Showing posts with label eclipse. Show all posts

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.