05 June 2007

Translating DNA to protein with the Google Web Toolkit: My notebook

.

Google Web Toolkit (GWT) is an open source Java software development framework that makes writing AJAX applications like Google Maps and Gmail easy for developers who don't speak browser quirks as a second language.

After the google dev day 2007 I've been playing with the Google Web Toolkit so here is my notebook. In this example I'll show how I used the GWT to create a classical program to translate a DNA sequence into a protein sequence with an option to choose between several genetic codes.

First download the GWT:

pierre@linux:~> cd tmp/GWT/toolkit/
pierre@linux:~/tmp/GWT/toolkit> wget "http://google-web-toolkit.googlecode.com/files/gwt-linux-1.3.3.tar.gz"
pierre@linux:~/tmp/GWT/toolkit> tar xfz gwt-linux-1.3.3.tar.gz


The GWT comes with an executable called 'projectCreator' generating a default project for eclipse. I also had to declare the variable LD_LIBRARY_PATH

export LD_LIBRARY_PATH=/home/pierre/tmp/GWT/toolkit/gwt-linux-1.3.3/mozilla-1.7.12 to make those things work.

pierre@linux:~/tmp/GWT> mkdir test01
pierre@linux:~/tmp/GWT/test01> ../toolkit/gwt-linux-1.3.3/projectCreator -eclipse Test01
Created directory /home/pierre/tmp/GWT/test01/test
Created file /home/pierre/tmp/GWT/test01/.project
Created file /home/pierre/tmp/GWT/test01/.classpath


Another executable creates the default files.
pierre@linux:~/tmp/GWT/test01> ../toolkit/gwt-linux-1.3.3/applicationCreator -eclipse Test01 org.lindenb.gwt.client.Main
Created directory /home/pierre/tmp/GWT/test01/src
Created directory /home/pierre/tmp/GWT/test01/src/org/lindenb/gwt
Created directory /home/pierre/tmp/GWT/test01/src/org/lindenb/gwt/client
Created directory /home/pierre/tmp/GWT/test01/src/org/lindenb/gwt/public
Created file /home/pierre/tmp/GWT/test01/src/org/lindenb/gwt/Main.gwt.xml
Created file /home/pierre/tmp/GWT/test01/src/org/lindenb/gwt/public/Main.html
Created file /home/pierre/tmp/GWT/test01/src/org/lindenb/gwt/client/Main.java
Created file /home/pierre/tmp/GWT/test01/Main.launch
Created file /home/pierre/tmp/GWT/test01/Main-shell
Created file /home/pierre/tmp/GWT/test01/Main-compile


To open your project in Eclipse, launch Eclipse and click the File -> Import menu. Choose "Existing Projects into Workspace" in the first screen of the wizard, and enter the directory in which you genetrated the .project file in the next screen of the wizard.

The file "Main.java" is localized in the package:org.lindenb.gwt.client
The source is available at http://lindenb.integragen.org/gwt/Main.java


First we created an abstract class describing a genetic code:

static abstract private class GeneticCode
{
public abstract String getName();
public abstract char translate(char a,char b,char c);
}



Then we wrote the standard genetic code by extending the previous class

static private class UniversalGeneticCode extends GeneticCode
{
public String getName()
{
return "Universal Genetic Code";
}
public char translate(char c1,char c2,char c3)
{
//trivial ......
}
}


and I wrote a new class fro the Mitochondrial Code


static private class MitochondrialGeneticCode extends UniversalGeneticCode
{
public String getName() {
return "Mitochondrial";
}

public char translate(char c1, char c2, char c3)
{
//trivial too...
}
}



Those codes will be stored in an array.

private GeneticCode geneticCodes[]=new GeneticCode[]{
new UniversalGeneticCode(),
new MitochondrialGeneticCode()

};


Programming the GWT is just as easy as programming with AWT or SWING. For this project we need a
ListBox to choose the genetic code and two TextArea: one for the DNA and the other for the protein.

private TextArea translated;
private TextArea userInput;
private ListBox choiceCode;


The function translateInput will be the workhorse of our class: In this function we get the index of our genetic-code list, we get the content of the TextArea containing the DNA, we translate the sequence using the genetic code and we put the result in the protein-TextArea


private void translateInput()
{
int idx= this.choiceCode.getSelectedIndex();
if(idx==-1) return;
GeneticCode code= this.geneticCodes[idx];
String dna= this.userInput.getText().replaceAll("[ \t\n\r]",&q
uot;").toLowerCase().replace('u', 't');
StringBuffer protein= new StringBuffer(dna.length()/3+1);
for(int i=0;i+2< dna.length();i+=3)
{
protein.append(code.translate(dna.charAt(i), dna.charAt(i+1), dn
a.charAt(i+2)));
if(protein.length()%40==0) protein.append("\n");
}
translated.setText(protein.toString());
}



When we create the list for the genetic codes we add a listener which will call translateInput() when the selection will be changed.

this.choiceCode = new ListBox();


for(int i=0;i< this.geneticCodes.length;++i)
{
this.choiceCode.addItem(this.geneticCodes[i].getName(), String.v
alueOf(i));
}
this.choiceCode.setSelectedIndex(0);
this.choiceCode.setVisibleItemCount(this.geneticCodes.length);
this.choiceCode.addClickListener(new ClickListener()
{
public void onClick(Widget sender) {
translateInput();
}
});


and when we create the TextArea for the DNA, we add a KeyboardListener calling translateInput() everytime a key is pressed.



this.userInput = new TextArea();
this.userInput.addKeyboardListener(new KeyboardListener()
{
public void onKeyDown(Widget sender, char keyCode, int modifiers
) {
translateInput();
}
public void onKeyPress(Widget sender, char keyCode, int modifier
s) {
translateInput();
}
public void onKeyUp(Widget sender, char keyCode, int modifiers)
{
translateInput();
}
});


In the html file there is a <div> element with an attribute id="main-id", this is where the script will insert the code.


RootPanel.get("main-id").add(tab);


The javascript pages are then generated using the script ./Main-compile which was previously generated.

That's it !


It worked fine without any line of javascript !!.

Updated 2010-08-12: source code

package org.lindenb.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class Main implements EntryPoint
{
static abstract private class GeneticCode
{
public abstract String getName();
public abstract char translate(char a,char b,char c);
}

static private class UniversalGeneticCode extends GeneticCode
{
public String getName()
{
return "Universal Genetic Code";
}
public char translate(char c1,char c2,char c3)
{

switch(c1)
{
case 'a':switch(c2)
{
case 'a':
switch(c3)
{
case 'a':return 'K';
case 't':return 'N';
case 'g':return 'K';
case 'c':return 'N';
default: return '?';
}

case 't':
switch(c3)
{
case 'a':return 'I';
case 't':return 'I';
case 'g':return 'M';
case 'c':return 'I';
default: return '?';
}

case 'g':
switch(c3)
{
case 'a':return 'R';
case 't':return 'S';
case 'g':return 'R';
case 'c':return 'S';
default: return '?';
}

case 'c':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'T';
default: return '?';
}
default: return '?';
}
case 't':switch(c2)
{
case 'a':
switch(c3)
{
case 'a':return '*';
case 't':return 'Y';
case 'g':return '*';
case 'c':return 'Y';
default: return '?';
}

case 't':
switch(c3)
{
case 'a':return 'L';
case 't':return 'F';
case 'g':return 'L';
case 'c':return 'F';
default: return '?';
}

case 'g':
switch(c3)
{
case 'a':return '*';
case 't':return 'C';
case 'g':return 'W';
case 'c':return 'C';
default: return '?';
}

case 'c':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'S';
default: return '?';
}

default: return '?';
}
case 'g':switch(c2)
{
case 'a':
switch(c3)
{
case 'a':return 'E';
case 't':return 'D';
case 'g':return 'E';
case 'c':return 'D';
default: return '?';
}

case 't':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'V';
default: return '?';
}

case 'g':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'G';
default: return '?';
}
case 'c':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'A';
default: return '?';
}
default: return '?';
}
case 'c':switch(c2)
{
case 'a':
switch(c3)
{
case 'a':return 'Q';
case 't':return 'H';
case 'g':return 'Q';
case 'c':return 'H';
default: return '?';
}
case 't':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'L';
default: return '?';
}

case 'g':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'R';
default: return '?';
}
case 'c':
switch(c3)
{
case 'a':
case 't':
case 'g':
case 'c':return 'P';
default: return '?';
}
default: return '?';
}
default: return '?';
}
}

}

/**
*
Differences from the Standard Code:
Code 3 Standard
AUA Met M Ile I
CUU Thr T Leu L
CUC Thr T Leu L
CUA Thr T Leu L
CUG Thr T Leu L
UGA Trp W Ter *

CGA absent Arg R
CGC absent Arg R
* @author pierre
*
*/
static private class MitochondrialGeneticCode extends UniversalGeneticCode
{
public String getName() {
return "Mitochondrial";
}

public char translate(char c1, char c2, char c3)
{
if(c1=='a' && c2=='t' && c3=='a') return 'M';
else if(c1=='c')
{
if(c2=='t')
{
switch(c3)
{
case 't': case 'c': case 'a' :case 'g': return 'T';
default: return '?';
}
}
else if(c2=='g')
{
if(c3=='a' || c3=='c') return '?';
}
}
else if(c1=='t' && c2=='g' && c3=='a') return 'W';
return super.translate(c1, c2, c3);
}
}

private GeneticCode geneticCodes[]=new GeneticCode[]{
new UniversalGeneticCode(),
new MitochondrialGeneticCode()

};

private TextArea translated;
private TextArea userInput;
private ListBox choiceCode;

public Main()
{

}



/**
* This is the entry point method.
*/
public void onModuleLoad()
{
TabPanel tab = new TabPanel();
tab.setWidth("100%");
tab.setHeight("100%");
VerticalPanel vbox = new VerticalPanel();
vbox.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE);
vbox.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);


vbox.add(new Label("My First Test with the Google Web Toolkit"));

HorizontalPanel hbox= new HorizontalPanel();
hbox.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
vbox.add(hbox);

Image me = new Image("http://www.urbigene.com/plindenbaum.jpg");
me.setTitle(me.getUrl());
hbox.add(new HTML("<span style=\"font-size:24pt;\"><a href=\'http://plindenbaum.blogspot.com\'>Pierre Lindenbaum PhD.</a></span>"));
hbox.add(me);

tab.add(vbox, "About");
tab.selectTab(0);


this.choiceCode = new ListBox();


for(int i=0;i< this.geneticCodes.length;++i)
{
this.choiceCode.addItem(this.geneticCodes[i].getName(), String.valueOf(i));
}
this.choiceCode.setSelectedIndex(0);
this.choiceCode.setVisibleItemCount(this.geneticCodes.length);
this.choiceCode.addClickListener(new ClickListener()
{
public void onClick(Widget sender) {
translateInput();
}
});

vbox= new VerticalPanel();
vbox.add(new Label("Genetic Code"));
vbox.add(this.choiceCode);
vbox.add(new Label("User Input"));
this.userInput = new TextArea();
vbox.add(this.userInput);
vbox.add(new Label("Translation"));
this.translated = new TextArea();
vbox.add(translated);

this.userInput.addKeyboardListener(new KeyboardListener()
{
public void onKeyDown(Widget sender, char keyCode, int modifiers) {
translateInput();
}
public void onKeyPress(Widget sender, char keyCode, int modifiers) {
translateInput();
}
public void onKeyUp(Widget sender, char keyCode, int modifiers) {
translateInput();
}
});
tab.add(vbox, "Translate");



RootPanel.get("main-id").add(tab);
}


private void translateInput()
{
int idx= this.choiceCode.getSelectedIndex();
if(idx==-1) return;
GeneticCode code= this.geneticCodes[idx];
String dna= this.userInput.getText().replaceAll("[ \t\n\r]","").toLowerCase().replace('u', 't');
StringBuffer protein= new StringBuffer(dna.length()/3+1);
for(int i=0;i+2< dna.length();i+=3)
{
protein.append(code.translate(dna.charAt(i), dna.charAt(i+1), dna.charAt(i+2)));
if(protein.length()%40==0) protein.append("\n");
}
translated.setText(protein.toString());

}

}

2 comments:

harijay said...

Pierre this is really neat. I have been meaning to start playing with javascript real soon..wondering if you think gwt will stick around for long

Pierre Lindenbaum said...

No idea if the GWT will last for a long time but it will save me hours of javascript programming ! :-) However, I'm still learning javascript (I just bought the book from O'Reilly) and XUL (to write some extensions for mozilla)