Team:Freiburg software/Code/SynBioWaveRobotServlet.java

From 2009.igem.org


SynBioWaveRobotServlet

/*
*    Copyright: synbiowave
*     
*    License: GPL
*     
*    Authors: Jörg Wassolesk & Paul Staab
*     
*    Version: 0.1 
*    
*    DESCRIPTION:
*    	This is the main class of the main SynBioWave-Robot. 
*/


package org.synbiowave;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import org.biojavax.bio.db.HashRichSequenceDB;
import org.biojavax.bio.db.RichSequenceDB;
import org.synbiowave.biobrick.BioBrickManager;
import org.synbiowave.biojava.Util;
import org.synbiowave.database.DatastoreManager;
import org.synbiowave.database.Sequence;
import org.synbiowave.menu.MenuItem;
import org.synbiowave.sequence.SequenceView;
import org.synbiowave.servlet.AbstractSbwRobotServlet;

import com.google.wave.api.Annotation;
import com.google.wave.api.Blip;
import com.google.wave.api.Element;
import com.google.wave.api.ElementType;
import com.google.wave.api.Event;
import com.google.wave.api.EventType;
import com.google.wave.api.Gadget;
import com.google.wave.api.GadgetView;
import com.google.wave.api.Image;
import com.google.wave.api.Range;
import com.google.wave.api.RobotMessageBundle;
import com.google.wave.api.TextView;
import com.google.wave.api.Wavelet;

//@SuppressWarnings("serial")
public class SynBioWaveRobotServlet extends AbstractSbwRobotServlet  
{	
	private static final long serialVersionUID = 9128671957338019728L;
	private TreeSet<String> biobrickIDs=null;
	private String bValue=""; 

	//------------------------------------------------------------------------
	//General Part
	//------------------------------------------------------------------------
	@Override
	public String myName() {
		return "synbiowave@appspot.com";
	}

	@Override
	public void createMenu(MenuItem menu) {
		
		MenuItem fileMenu = new MenuItem("menu","File",this.generateKey());
		menu.appendSubItem(fileMenu);
			
			MenuItem fileimportMenu = new MenuItem("menu","Import",this.generateKey());
			fileMenu.appendSubItem(fileimportMenu);

				MenuItem uploadformbutton = new MenuItem("button", "File", this.generateKey());
				fileimportMenu.appendSubItem(uploadformbutton);
					
						MenuItem uploadForm = new MenuItem("form","Upload",this.generateKey());
						uploadformbutton.appendSubItem(uploadForm);
							
							MenuItem upload = new MenuItem("upload","upload",this.generateKey());
							upload.setOption("target", "https://" + this.myName().replace("@", ".") + "/_wave/robot/jsonrpc");
							upload.setOption("enctype", "multipart/form-data");
							uploadForm.appendSubItem(upload);
				
				MenuItem importBioBrick = new MenuItem("button", "BioBrick part", this.generateKey());
				fileimportMenu.appendSubItem(importBioBrick);
				
				MenuItem importDatastore = new MenuItem("button", "Datastore sequence", this.generateKey());
				fileimportMenu.appendSubItem(importDatastore);

			MenuItem fileexportMenu = new MenuItem("menu","Export",this.generateKey());
			fileMenu.appendSubItem(fileexportMenu);				
			
			
				MenuItem downloadFilebutton = new MenuItem("button","File",this.generateKey());
				fileexportMenu.appendSubItem(downloadFilebutton);
					
					MenuItem downloadForm = new MenuItem("form","Download","noreload");
					downloadFilebutton.appendSubItem(downloadForm);
					
						MenuItem download = new MenuItem("download","Blub","Download");
						downloadForm.appendSubItem(download);
						download.setOption("target", "https://" + this.myName().replace("@", ".") + "/_wave/robot/jsonrpc");
					
						downloadForm.createSubItem("radio", "fasta", this.generateKey()).setOption("group", "format");
						downloadForm.createSubItem("radio", "gb", this.generateKey()).setOption("group", "format");
					
		MenuItem sequenceMenu = new MenuItem("menu","Sequence",this.generateKey());
		menu.appendSubItem(sequenceMenu);
			
			sequenceMenu.createSubItem("button","Circular View", this.generateKey());
		
		
		MenuItem helpMenu = new MenuItem("menu","Help",this.generateKey());
		menu.appendSubItem(helpMenu);
			
			helpMenu.createSubItem("checkbox", "debug", this.generateKey());

	}
	
	
	//------------------------------------------------------------------------
	//Menu-Part
	//------------------------------------------------------------------------
	private final String gadgetURL = "http://synbiowave.svn.sourceforge.net/viewvc/synbiowave/trunk/menuGadget/menuGadget.xml";
	private String uiMenuJson = "{}";

	private void appendMenuGadgetIfNeccessary(Blip blip)
	{
		GadgetView gv = blip.getDocument().getGadgetView();
		boolean hasmenu = false;
		for ( Gadget gadget : gv.getGadgets() )
		{
			if ( gadget.getField("url").contentEquals(this.gadgetURL) )
			{
				hasmenu = true;
			}
		}
		if ( ! hasmenu )
		{
			Gadget menuGadged = new Gadget(this.gadgetURL);					  
			menuGadged.setField("ui.structure", this.uiMenuJson );
			blip.getDocument().insert(0, "SynBioWave \n");
			blip.getDocument().insertElement(12, menuGadged);
		}
	}
	
	private void generateMenuData(Wavelet wavelet) throws Exception
	{
		MenuItem menu = new MenuItem("ui","ui", this.generateKey());
		Map<String,String> data = wavelet.getDataDocuments();
		for ( String key : data.keySet() )
		{
			if ( key.contains("ui.menu.") )
			{ 
				menu.importMenu( new MenuItem( data.get(key) ) );
			}
		}
		this.uiMenuJson = menu.writeJson();
		wavelet.setDataDocument("ui.completemenu", this.uiMenuJson);
	}
	
	private void updateMenusIfNeccessary(Wavelet wavelet) throws Exception
	{
		if (wavelet.getDataDocument("menu.update").contains("1"))
		{
			this.generateMenuData(wavelet);
			for ( Blip blip : new RecChildBlipListBuilder(wavelet.getRootBlip()).getRecChildList() )
			{
				//blip.getDocument().append("trying to update this blip\n");
				GadgetView gv = blip.getDocument().getGadgetView();
				for ( Gadget gadget : gv.getGadgets() )
				{
					if ( gadget.getField("url").contentEquals(this.gadgetURL) )
					{
						Gadget menuGadget = new Gadget(this.gadgetURL);
						menuGadget.setField("ui.structure", this.uiMenuJson );
						gv.replace(gadget, menuGadget);
					}
				}
				wavelet.setDataDocument("menu.update", "0");
			}
		}	
	}
	
	
	//------------------------------------------------------------------------
	//Basic functions
	//------------------------------------------------------------------------
	private void appendCircView(Event event) throws Exception {
		Sequence sequence=DatastoreManager.getByID(new Sequence(),super.currentSequence);
		URL url=new URL("http://samserver.homedns.org:8080/Rebase/RebaseServlet");
		URLConnection conn=url.openConnection();
		conn.setDoOutput(true);
		org.biojava.bio.seq.Sequence s= Util.convertToRichSequence(sequence);
		RichSequenceDB rsdb=new HashRichSequenceDB();
		rsdb.addSequence(s);
		Util.write(conn.getOutputStream(),rsdb.getRichSequenceIterator(),"genbank");
		BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));
		String name=br.readLine();
		event.getWavelet().appendBlip().getDocument().appendElement(new Image("http://samserver.homedns.org/"+name,500,500,name));
		br.close();}
	
	private void importBioBrick(Event event) throws Exception
	{
		biobrickIDs=BioBrickManager.readBioBrickIDs();
		Element bbInput=new Element(ElementType.INPUT);
		bbInput.setProperty("name","bbInput");
		bbInput.setProperty("defaultValue","");
		Element bbButton=new Element(ElementType.BUTTON);
		bbButton.setProperty("name","bbButton");
		bbButton.setProperty("value","Import");
		TextView rtv=event.getWavelet().getRootBlip().getDocument();
		rtv.insert(rtv.getText().indexOf("Active Sequence:"),"\nPlease insert parts name:\n");
		rtv.insertElement(rtv.getText().indexOf("Active Sequence:"),bbInput);
		rtv.insertElement(rtv.getText().indexOf("Active Sequence:"),bbButton);
		rtv.insert(rtv.getText().indexOf("Active Sequence:"),"\n");
		int maxElements=4;
		Iterator<String> tailSetIterator=biobrickIDs.iterator();
		while(tailSetIterator.hasNext()&&maxElements>0){rtv.insert(rtv.getText().indexOf("Active Sequence:"),tailSetIterator.next()+"\n");maxElements--;}
		rtv.insert(rtv.getText().indexOf("Active Sequence:"),"...\n");
	}
	
	private void importDatastore(Event event) throws Exception
	{
		Element dbInput=new Element(ElementType.INPUT);
		dbInput.setProperty("name","dbInput");
		dbInput.setProperty("defaultValue","");
		Element dbButton=new Element(ElementType.BUTTON);
		dbButton.setProperty("name","dbButton");
		dbButton.setProperty("value","Import");
		TextView rtv=event.getWavelet().getRootBlip().getDocument();
		rtv.insert(rtv.getText().indexOf("Active Sequence:"),"Please insert sequence name:\n");
		rtv.insertElement(rtv.getText().indexOf("Active Sequence:"),dbInput);
		rtv.insertElement(rtv.getText().indexOf("Active Sequence:"),dbButton);
		rtv.insert(rtv.getText().indexOf("Active Sequence:"),"\n");
		int maxElements=4;
		Iterator<Sequence> tailSetIterator=DatastoreManager.getAll(new Sequence()).iterator();
		while(tailSetIterator.hasNext()&&maxElements>0){rtv.insert(rtv.getText().indexOf("Active Sequence:"),tailSetIterator.next().getName()+"\n");maxElements--;}
		rtv.insert(rtv.getText().indexOf("Active Sequence:"),"...\n");
	}
	
	//------------------------------------------------------------------------
	//Events
	//------------------------------------------------------------------------
	@Override
	public void processSbwEvents(RobotMessageBundle bundle) {
        for(Event event:bundle.getEvents()){	
        	try{
	    		Blip blip=event.getBlip();
				TextView tv=blip.getDocument();
			
				if(event.getType()==EventType.WAVELET_SELF_ADDED){
					try{
						this.generateMenuData(event.getWavelet());
						event.getWavelet().setDataDocument("menu.update","0");
						this.appendMenuGadgetIfNeccessary(event.getBlip());
						event.getWavelet().getRootBlip().getDocument().append("Active Sequence: none ");}
					catch(Exception e){event.getBlip().getDocument().append("Error generating menu:"+e.toString());}}
	    		
				else if(event.getType()==EventType.FORM_BUTTON_CLICKED){
					if(event.getButtonName().contains("bbButton")){
						String value = getInputValue(tv);
						tv.delete(new Range(tv.getText().indexOf("\nPlease insert parts name:\n"),tv.getText().indexOf("Active Sequence:")));
						String id=biobrickIDs.ceiling(value);
						if(id!=null){
							try{
								String s=BioBrickManager.readBioBrickSequence(id);
								List<Map<String,String>> features=BioBrickManager.readBioBrickFeatures(id);
								Sequence sequence=new Sequence("biobrick",event.getTimestamp(),event.getWavelet().getWaveId(),event.getWavelet().getWaveletId(),event.getBlip().getBlipId(),s,SequenceView.isSequence(s));
								sequence.setName(id);
								for(Map<String,String> feature:features){
									if(feature.get("END").equals("0")){sequence.addAnnotation(new Annotation(feature.get("TYPE_CATEGORY"),feature.get("TYPE"),new Range(Integer.parseInt(feature.get("START")),s.length())));}	
									else{sequence.addAnnotation(new Annotation(feature.get("TYPE_CATEGORY"),feature.get("TYPE"),new Range(Integer.parseInt(feature.get("START")),Integer.parseInt(feature.get("END")))));}}
								super.currentSequence=DatastoreManager.store(sequence).getId();
								String gadgetView=SequenceView.getHTMLView(sequence.getSequence(),sequence.getType());
								Gadget seqView=new Gadget("http://samnode.de/SequenceView.xml");
								seqView.setField("names","(br /)(br /)(br /)"+sequence.getName()+"(br /)(br /)");
								seqView.setField("sequences",gadgetView);
								event.getWavelet().appendBlip().getDocument().getGadgetView().append(seqView);}
							catch(Exception exc){tv.replace("Error receiving biobrick data:"+exc.toString());}}
						else{tv.append("BioBrick part not found, please choose other part ...");}}
					
					else if(event.getButtonName().contains("dbButton")){
						String value = getInputValue(tv);
						tv.delete(new Range(tv.getText().indexOf("\nPlease insert parts name:\n"),tv.getText().indexOf("Active Sequence:")));
						try{
							Sequence sequence=DatastoreManager.getGreaterThanKeyValue(new Sequence(),"name",value).get(0);
							super.currentSequence=sequence.getId();
							if(!sequence.getWaveletID().get(0).equals(event.getWavelet().getWaveletId())){
								String gadgetView=SequenceView.getHTMLView(sequence.getSequence(),sequence.getType());
								Gadget seqView=new Gadget("http://samnode.de/SequenceView.xml");
								seqView.setField("names","(br /)(br /)(br /)"+sequence.getName()+"(br /)(br /)");
								seqView.setField("sequences",gadgetView);
								event.getWavelet().appendBlip().getDocument().getGadgetView().append(seqView);}}
						catch(Exception exc){tv.replace("Error receiving sequence:"+exc.toString());}}}
		
				else if(event.getType()==EventType.BLIP_SUBMITTED){
					
					if(event.getBlip().getDocument().getText().contains("#save")){
						event.getBlip().getDocument().getGadgetView().append(new Gadget("http://samnode.de/downgadget.xml"));}
					
					else if(event.getBlip().getDocument().getText().contains("#cgView")){
						try{ appendCircView(event); }
						catch(Exception e1){event.getBlip().getDocument().append("Error displaying circular view:"+e1.toString());}}
					
					else if(event.getBlip().getDocument().getText().contains("#biobrick")){
						try{ this.importBioBrick(event); }
						catch(Exception exc){event.getBlip().getDocument().append(exc.toString());}}
					
					else if(event.getBlip().getDocument().getText().contains("#msa")){
						ArrayList<String> sequences=new ArrayList<String>();
						sequences.add("tcgcgcgatcgcgatcgcatagcatacgcatcatcatctcatcatgcgacgcattatacgacgatagactacgacgactacgcgacgacatagcgcagcgagcatagcagcagcatagcagcagcagctacgactacgactacgactcatctcagc");
						sequences.add("jdhfksjfhskjfhkfjshfuheuhfksuehfksjhsjzegfjhseifhkjcshegzfesfkjgfjhbesjhrvfjehfksuegfshvfbshegvhfgvsejhfsjhvsehfbjhesvfhsebfhsbfhsejhekfjbvehsvbjhesfvbjshevsehvhsvehvsejhsejhbsejfhbsjhefvbesjfbjeshvsehbfjsehfbsejfhbsejhvsehevbj");
						sequences.add("ugcugcugacugauguacguagcgagcagucguagcugaucguagucgaugcgacgaugcuagcuagcuagcuagcuagcuagcgagcuagcuagcugaugcuagcuag");
						sequences.add("tacgagctacgactagcatcagcgagcggacatcgacgatcgactacgatcagctac");
						String gadgetView=SequenceView.getHTMLView(sequences);
						Gadget seqView=new Gadget("http://samnode.de/SequenceView.xml");
						seqView.setField("names","(br /)(br /)seq1(br /)seq2(br /)seq3(br /)seq4(br /)(br /)");
						seqView.setField("sequences",gadgetView);
						event.getWavelet().appendBlip().getDocument().getGadgetView().append(seqView);}
					
					else if(event.getBlip().getDocument().getText().contains("#datastore")){
						try{ this.importDatastore(event); }
						catch(Exception exc){event.getBlip().getDocument().append(exc.toString());}}}
				
				else if(event.getType()==EventType.DOCUMENT_CHANGED){
					
					try{this.updateMenusIfNeccessary(event.getWavelet());} 
					catch(Exception e){event.getBlip().getDocument().append("Error updating  menu:"+e.toString());}
					
					if(!tv.getAnnotations("style/backgroundColor").isEmpty()){
						Range range=tv.getAnnotations("style/backgroundColor").get(0).getRange();
						tv.deleteAnnotations("style/backgroundColor");
						String sequence=tv.getText().substring(range.getStart(),range.getEnd());
						try{
							Sequence nSequence=new Sequence(event.getModifiedBy(),event.getTimestamp(),event.getWavelet().getWaveId(),event.getWavelet().getWaveId(),event.getBlip().getBlipId(),sequence,SequenceView.isSequence(sequence));
							nSequence.setName(event.getModifiedBy()+"("+new Date(event.getTimestamp()).toString()+")");
							super.currentSequence=DatastoreManager.store(nSequence).getId();}
						catch(Exception e){event.getBlip().getDocument().append("Error storing sequence:"+e.toString());}
						super.replaceWithSimpleView(event,range,tv.getText().substring(range.getStart(),range.getEnd()));}
					
					String type=null;
					String value=null;
					int start=0;
					List<Element> elements=tv.getElements();
					Map<String,String> properties=null;
					for(Element element:elements){
						properties=element.getProperties();
						if(properties.containsKey("name")){
							if(properties.get("name").equals("bbInput")||properties.get("name").equals("dbInput")){
								type=properties.get("name");
								value=properties.get("value").trim();}
							else if(properties.get("name").equals("bbButton")||properties.get("name").equals("dbButton")){
								start=tv.getPosition(element)+2;}}}
					
					if(!value.equals(this.bValue)){
						this.bValue=value;
						if(type!=null){
							String preview="";
							int maxElements=4;
							if(type.equals("bbInput")){
								Iterator<String> tailSetIterator=biobrickIDs.tailSet(value).iterator();
								while(tailSetIterator.hasNext()&&maxElements>0){preview+=tailSetIterator.next()+"\n";maxElements--;}}
							if(type.equals("dbInput")){
								Iterator<Sequence> tailSetIterator=DatastoreManager.getGreaterThanKeyValue(new Sequence(),"name",value).iterator();
								while(tailSetIterator.hasNext()&&maxElements>0){preview+=tailSetIterator.next()+"\n";maxElements--;}}
							preview+="...\n";
							tv.replace(new Range(start,tv.getText().indexOf("Active Sequence:")),preview);}}}}
						catch(Exception e){}}}//event.getBlip().getDocument().append("Error prossesing events:"+e.toString());}}}

	private String getInputValue(TextView tv) {
		String value=null;
		List<Element> elements=tv.getElements();
		Map<String,String> properties=null;
		for(Element element:elements){
			properties=element.getProperties();
			if(properties.containsKey("name")){
				if(properties.get("name").contains("Input")){
					value=properties.get("value").trim();}}}
		return value;
	}
			
	@Override
	public void processSbwMenuEvents(String buttonLabel,LinkedHashMap<String,String> formValues,Event event){	
		if(buttonLabel.contains("upload")){
			try{
				while(super.currentSequence==null||DatastoreManager.getByID(new Sequence(),super.currentSequence)==null){}
				Sequence sequence=DatastoreManager.getByID(new Sequence(),super.currentSequence);
				String gadgetView=SequenceView.getHTMLView(sequence.getSequence(),sequence.getType());
				Gadget seqView=new Gadget("http://samnode.de/SequenceView.xml");
				seqView.setField("names","(br /)(br /)(br /)"+sequence.getName()+"(br /)(br /)");
				seqView.setField("sequences",gadgetView);
				event.getWavelet().appendBlip().getDocument().getGadgetView().append(seqView);}
			catch(Exception exc){event.getWavelet().appendBlip().getDocument().append("Error on upload:"+exc.toString());}}
		
		else if (buttonLabel.contains("Circular View"))
		{
			try
			{ 
				this.appendCircView(event); 
			}
			catch(Exception exc)
			{
				event.getBlip().getDocument().append("Error displaying circular view:"+exc.toString());
			}
		}
		
		else if (buttonLabel.contains("BioBrick"))
		{
			try
			{ 
				this.importBioBrick(event);
			}
			catch(Exception exc)
			{
				event.getBlip().getDocument().append("BioBrick-Error: " + exc.toString());
			}
		}
		
		else if (buttonLabel.contains("Datastore"))
		{
			try
			{ 
				this.importDatastore(event);
			}
			catch(Exception exc)
			{
				event.getBlip().getDocument().append("Datastore-Error: " + exc.toString());
			}
		}
	}
}