/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.util.blat;
import org.broad.igv.Globals;
import org.broad.igv.feature.PSLRecord;
import org.broad.igv.feature.Strand;
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.feature.genome.GenomeManager;
import org.broad.igv.feature.tribble.PSLCodec;
import org.broad.igv.prefs.Constants;
import org.broad.igv.prefs.PreferencesManager;
import org.broad.igv.track.*;
import org.broad.igv.ui.IGV;
import org.broad.igv.ui.util.MessageUtils;
import org.broad.igv.util.HttpUtils;
import org.broad.igv.util.LongRunningTask;
import org.broad.igv.util.NamedRunnable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Port of perl script blatPlot.pl http://genomewiki.cse.ucsc.edu/index.php/Blat_Scripts
*
* @author jrobinso
* Date: 11/21/12
* Time: 8:28 AM
*/
public class BlatClient {
static int sleepTime = 15 * 1000; // # milli seconds to wait between requests
static String hgsid; // cached, not sure what this is for but apparently its best to reuse it.
static long lastQueryTime = 0;
public static void main(String[] args) throws IOException {
if (args.length != 6) {
Usage();
System.exit(255);
}
String org = args[0];
String db = args[1];
String searchType = args[2];
String sortOrder = args[3];
String outputType = args[4];
String userSeq = args[5];
blat(org, db, searchType, sortOrder, outputType, userSeq);
}
static void Usage() {
System.out.println("usage: BlatBot <organism> <db> <searchType> <sortOrder>");
System.out.println(" <outputType> <querySequence>");
System.out.println("\tSpecify organism using the common name with first letter");
System.out.println("capitalized.");
System.out.println("\te.g. Human, Mouse, Rat etc.");
System.out.println("\tDb is database or assembly name e.g hg17, mm5, rn3 etc.");
System.out.println("\tsearchType can be BLATGuess, DNA, RNA, transDNA or transRNA");
System.out.println("\tsortOrder can be query,score; query,start; chrom,score");
System.out.println("\tchrom,start; score.");
System.out.println("\toutputType can be pslNoHeader, psl or hyperlink.");
System.out.println("\tblats will be run in groups of $batchCount sequences, all");
}
public static List<String> blat(String org, String db, String userSeq) throws IOException {
String searchType = "DNA";
String sortOrder = "query,score";
String outputType = "psl";
List<String> blatRecords = blat(org, db, searchType, sortOrder, outputType, userSeq);
return blatRecords;
}
public static List<String> blat(String org, String db, String searchType, String sortOrder, String outputType, String userSeq) throws IOException {
if (searchType.equals("BLATGuess")) {
searchType = "Blat's Guess";
} else if (searchType.equals("transDNA")) {
searchType = "translated DNA";
} else if (searchType.equals("transRNA")) {
searchType = "translated RNA";
} else if (searchType.equals("DNA") || (searchType.equals("RNA"))) {
} else {
System.out.println("ERROR: have not specified an acceptable search type - it should be BLATGuess, transDNA, transRNA, DNA or RNA.");
Usage();
System.exit(255);
}
if (outputType.equals("pslNoHeader")) {
outputType = "psl no header";
} else if (outputType.equals("psl") || outputType.equals("hyperlink")) {
} else {
System.out.println("ERROR: have not specified an acceptable output type - it should be pslNoHeader, psl or hyperlink.");
Usage();
System.exit(255);
}
//$response;
String $url = PreferencesManager.getPreferences().get(Constants.BLAT_URL);
//if an hgsid was obtained from the output of the first batch
//then use this.
String urlString = ($url + "?org=" + org + "&db=" + db + "&type=" + searchType + "&sort=" + sortOrder +
"&output=" + outputType); // + "&hgsid=" + hgsid);
if (hgsid != null) {
urlString += "&hgsid=" + hgsid;
}
long dt = System.currentTimeMillis() - lastQueryTime;
if (dt < sleepTime) {
try {
Thread.sleep(dt);
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
lastQueryTime = System.currentTimeMillis();
Map<String, String> params = new HashMap();
params.put("userSeq", userSeq);
String result = HttpUtils.getInstance().doPost(new URL(urlString), params);
return parseResult(result);
}
/**
* Return the parsed results as an array of PSL records, where each record is simply an array of tokens.
*
* @param result
* @return
* @throws IOException
*/
static List<String> parseResult(String result) throws IOException {
ArrayList<String> records = new ArrayList<String>();
BufferedReader br = new BufferedReader(new StringReader(result));
String line;
int headerLineCount = 0;
boolean header = false;
boolean hgsidFound = false;
boolean pslSectionFound = false;
while ((line = br.readLine()) != null) {
if (line.contains("hgsid=") && !hgsidFound) {
int startIDX = line.indexOf("hgsid=") + 6;
String sub = line.substring(startIDX);
int endIDX = sub.indexOf("\"");
if (endIDX < 0) endIDX = sub.indexOf("&");
if (endIDX > 0) {
hgsid = sub.substring(0, endIDX);
hgsidFound = true;
}
}
if (line.trim().startsWith("<TT><PRE>")) {
pslSectionFound = true;
if (line.contains("psLayout") && line.contains("version")) {
header = true;
headerLineCount++;
}
} else if (line.trim().startsWith("</PRE></TT>")) {
break;
}
if (pslSectionFound) {
if (header && headerLineCount < 6) {
headerLineCount++;
continue;
}
String[] tokens = Globals.whitespacePattern.split(line);
if (tokens.length != 21) {
System.err.println("Unexpected number of fields (" + tokens.length + ")");
System.err.println(line);
} else {
records.add(line);
}
}
}
return records;
}
public static void doBlatQuery(final String chr, final int start, final int end, Strand strand) {
if((end - start) > 8000) {
MessageUtils.showMessage("BLAT searches are limited to 8kb. Please try a shorter sequence.");
return;
}
Genome genome = GenomeManager.getInstance().getCurrentGenome();
final byte[] seqBytes = genome.getSequence(chr, start, end);
String userSeq = new String(seqBytes);
if(strand == Strand.NEGATIVE) {
userSeq = SequenceTrack.getReverseComplement(userSeq);
}
doBlatQuery(userSeq);
}
public static void doBlatQuery(final String userSeq) {
LongRunningTask.submit(new NamedRunnable() {
public String getName() {
return "Blat sequence";
}
public void run() {
try {
Genome genome = IGV.hasInstance() ? GenomeManager.getInstance().getCurrentGenome() : null;
PSLCodec codec = new PSLCodec(genome, true);
String db = genome.getId();
String species = genome.getSpecies();
if (species == null) {
MessageUtils.showMessage("Cannot determine species name for genome: " + genome.getDisplayName());
return;
}
List<String> tokensList = blat(species, db, userSeq);
// Convert tokens to features
List<PSLRecord> features = new ArrayList<PSLRecord>(tokensList.size());
for (String tokens : tokensList) {
PSLRecord f = codec.decode(tokens);
if (f != null) {
features.add(f);
}
}
if (features.isEmpty()) {
MessageUtils.showMessage("No features found");
} else {
FeatureSource<PSLRecord> source = new FeatureCollectionSource(features, genome);
FeatureTrack newTrack = new FeatureTrack("Blat", "Blat", source);
newTrack.setUseScore(true);
newTrack.setDisplayMode(Track.DisplayMode.SQUISHED);
IGV.getInstance().getTrackPanel(IGV.FEATURE_PANEL_NAME).addTrack(newTrack);
BlatQueryWindow win = new BlatQueryWindow(IGV.getMainFrame(), userSeq, features);
win.setVisible(true);
}
} catch (IOException e1) {
MessageUtils.showErrorMessage("Error running blat", e1);
}
}
});
}
public static JMenuItem getMenuItem() {
JMenuItem menuItem = new JMenuItem("BLAT ...");
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String blatSequence = MessageUtils.showInputDialog("Enter sequence to blat:");
if(blatSequence != null) {
doBlatQuery(blatSequence);
}
}
});
return menuItem;
}
}