/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.clothocad.tool.primerdesigner; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; import org.clothocore.api.core.Collector; import org.clothocore.api.data.Annotation; import org.clothocore.api.data.Collection; import org.clothocore.api.data.Feature; import org.clothocore.api.data.NucSeq; import org.clothocore.api.data.Oligo; import org.clothocore.util.chooser.ClothoOpenChooser; import org.openide.windows.TopComponent; /** * * @author Jenhan Tao * Performs primer design calculations */ public class PrimerDesignController { /** * constructor links controller to the view * @param s */ public PrimerDesignController(DesignFrame d) { isTC = true; _frameView = d; _textField = d.getSequenceTextField(); _ns = new NucSeq(""); final JComponent guiContentPane = (JComponent) _frameView.getContentPane(); // JRootPane guiRootPane = _frameView.getRootPane(); final JMenuBar menu = _frameView.getJMenuBar(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { _tcView = new TopComponent(); _tcView.setLayout(new BorderLayout()); JScrollPane sp = new JScrollPane(guiContentPane); _tcView.add(menu, BorderLayout.NORTH); _tcView.add(sp, BorderLayout.CENTER); _tcView.setName("Primer Designer"); _tcView.open(); _tcView.requestActive(); } }); } public String getSequence() { return _sequence; } public void setSequence(String s) { _sequence = s; _textField.setText(s); ((DesignFrame) _frameView).updateLabels(); } /** * * Primers of length +/-3 of target length are generated * @param seq * @param tm * @param length * @param insert1 * @param insert2 * @param spacer1 * @param spacer2 */ public void generatePrimers(String seq, Double tm, int length, String insert1, String insert2, String spacer1, String spacer2) { // System.out.println("Sequence: " + seq); // System.out.println("Target tm: " + tm); // System.out.println("Target length: " + length); // System.out.println("Inserts: " + insert1 + ", " + insert2); // System.out.println("Spacers: " + spacer1 + ", " + spacer2); ArrayList<String> fwdSequences = new ArrayList<String>(); ArrayList<String> revSequences = new ArrayList<String>(); ArrayList<Double> fwdG = new ArrayList<Double>(); ArrayList<Double> revG = new ArrayList<Double>(); if (spacer1 == null) { spacer1 = ""; } if (spacer2 == null) { spacer2 = ""; } if (seq.length() < 60) { String[] yesNoOpt = {"Yes", "No"}; if (javax.swing.JOptionPane.showOptionDialog(new JFrame(), "Template is less than 60 basepairs in length.\nProceed anyways?", "Primer Designer: Warning", javax.swing.JOptionPane.YES_NO_OPTION, javax.swing.JOptionPane.QUESTION_MESSAGE, null, yesNoOpt, yesNoOpt[1]) == 1) { return; } if (seq.length() < 20) { JOptionPane.showMessageDialog(new JFrame(), "Sequence is too short to generate primers", "Primer Designer: Warning", JOptionPane.WARNING_MESSAGE); return; } } if (length < 15) { String[] yesNoOpt = {"Yes", "No"}; if (javax.swing.JOptionPane.showOptionDialog(new JFrame(), "Preferred primer length may be too short to allow for specific annealing.\nProceed anyways?", "Primer Designer: Warning", javax.swing.JOptionPane.YES_NO_OPTION, javax.swing.JOptionPane.QUESTION_MESSAGE, null, yesNoOpt, yesNoOpt[1]) == 1) { return; } if (length < 4) { length = 4; //min target length allowed } } if (spacer1.length() > 5 || spacer2.length() > 5) { String[] yesNoOpt = {"Yes", "No"}; if (javax.swing.JOptionPane.showOptionDialog(new JFrame(), "Spacers seem to be large.\nProceed anyways?", "Primer Designer: Warning", javax.swing.JOptionPane.YES_NO_OPTION, javax.swing.JOptionPane.QUESTION_MESSAGE, null, yesNoOpt, yesNoOpt[1]) == 1) { return; } } String revPrimer = ""; String fwdPrimer = ""; //generates primer sequences with length +/-3 of target length excluding the insert length for (int i = length - 3; i <= length + 3; i++) { fwdPrimer = this.complementSequence(seq.substring(0, i)); fwdG.add(calcDeltaG(fwdPrimer)); if (!insert1.equalsIgnoreCase("none")) { Feature afeat = Feature.retrieveByName(insert1); fwdPrimer = this.complementSequence(afeat.getSeq().toString()) + fwdPrimer; } fwdPrimer = spacer1 + fwdPrimer; fwdSequences.add(fwdPrimer.toUpperCase()); revPrimer = this.flip(seq.substring(seq.length() - i)); revG.add(calcDeltaG(revPrimer)); if (!insert2.equalsIgnoreCase("none")) { Feature afeat = Feature.retrieveByName(insert2); revPrimer = this.flip(afeat.getSeq().toString()) + revPrimer; } revPrimer = spacer2 + revPrimer; revSequences.add(revPrimer.toUpperCase()); } PrimerResultFrame prf = new PrimerResultFrame(this, fwdSequences, revSequences, fwdG, revG); prf.pack(); prf.setVisible(true); } /** * Saves two arraylists of sequences as Clotho oligos uses nameRoot as the base of the name * @param sequences * @param nameRoot */ public void saveAll(ArrayList<String> fwdseq, ArrayList<String> revseq, String nameRoot, String collectionName) { Collection saveTo = Collection.retrieveByName(collectionName); if (saveTo != null) { for (int i = 0; i < fwdseq.size(); i++) { Oligo ol = new Oligo(nameRoot + "F" + i, "primer", Collector.getCurrentUser(), fwdseq.get(i)); saveTo.addObject(ol); } for (int i = 0; i < revseq.size(); i++) { Oligo ol = new Oligo(nameRoot + "R" + i, "primer", Collector.getCurrentUser(), revseq.get(i)); saveTo.addObject(ol); ol.saveDefault(); } saveTo.saveDefault(); } } /** * Complents input string * @param s * @return */ public String complementSequence(String s) { if (s != null) { _ns.changeSeq(s); String seq = _ns.revComp(); String temp = ""; for (int j = seq.length() - 1; j > -1; j--) { temp = temp + seq.charAt(j); } return temp; } return null; } /** * flips input string * @param s * @return */ public String flip(String s) { if (s != null) { String temp = ""; for (int j = s.length() - 1; j > -1; j--) { temp = temp + s.charAt(j); } return temp; } return null; } /** * Checks sequence for restriction sites. * @param seq sequences being scanned * @return a hashmap containing restriction sites indices, key is start, value is end */ public HashMap<Integer, Integer> checkForRESites() { if (_sequence == null) { return null; } _ns.changeSeq(_sequence); HashMap<Integer, Integer> toReturn = new HashMap<Integer, Integer>(); HashSet<Annotation> hs = _ns.getAnnotations(); for (Annotation an : hs) { if (an.getFeature().getSearchTags().contains("restriction enzyme")) { toReturn.put(an.getStart(), an.getEnd()); } } if (toReturn.isEmpty()) { toReturn = null; } return toReturn; } public Double calcMeltingTemp(String seq) { _ns.changeSeq(seq); Double toReturn = _ns.meltingTemp(); return toReturn; } public Double calcGCContent(String seq) { _ns.changeSeq(seq); Double toReturn = _ns.gcContent()[1]; return toReturn; } /** * Calculates delta g value for input string, representing a dna sequence, using nearest neighbor method * Delta g calculations taken from "A unified view of polymer, dumbbell, and oligonucleotide DNA nearest-neighbor " * @param s * @return */ public static Double calcDeltaG(String s) { Double toReturn = 0.0; if (s != null) { s = s.toUpperCase(); if (s.startsWith("C") || s.startsWith("G")) { toReturn = toReturn + 0.98; } else { toReturn = toReturn + 1.03; } if (s.endsWith("C") || s.endsWith("G")) { toReturn = toReturn + 0.98; } else { toReturn = toReturn + 1.03; } for (int i = 0; i < s.length() - 1; i++) { String token = s.substring(i, i + 2); if (token.equals("AA") || token.equals("TT")) { toReturn = toReturn - 1.00; } else if (token.equals("AT")) { toReturn = toReturn - 0.88; } else if (token.equals("TA")) { toReturn = toReturn - -0.58; } else if (token.equals("CA") || token.equals("AC")) { toReturn = toReturn - 1.45; } else if (token.equals("GT") || token.equals("TG")) { toReturn = toReturn - 1.44; } else if (token.equals("CT") || token.equals("TC")) { toReturn = toReturn - 1.28; } else if (token.equals("GA") || token.equals("AG")) { toReturn = toReturn - 1.30; } else if (token.equals("CG")) { toReturn = toReturn - 2.17; } else if (token.equals("GC")) { toReturn = toReturn - 2.24; } else if (token.equals("GG") || token.equals("CC")) { toReturn = toReturn - 1.84; } } return toReturn; } return 0.00; } public String checkForDimers(String fwdSeq, String revSeq, Double fwdG, Double revG) { String result = ""; String alignString = ""; Pattern p; Matcher m; Double dG = 0.0; //check for self dimers result = result + "SELF DIMERS\nfwd primer:"; alignString = Aligner.align(fwdSeq, fwdSeq); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } result = result + "\nDelta G: " + dG; if (dG.intValue() <= fwdG.intValue()) { result = result + "\nWarning: self dimer is likely in forward primer"; } result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; result = result + "\nrev primer:"; dG = 0.0; alignString = Aligner.align(revSeq, revSeq); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } if (dG.intValue() <= revG.intValue()) { result = result + "\nWarning: self dimer is likely in reverse primer"; } result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; //check for dimer with other primer result = result + "\nrev primer:"; dG = 0.0; alignString = Aligner.align(fwdSeq, revSeq); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } if ((dG.intValue() <= fwdG.intValue() || dG.intValue() < revG.intValue())) { result = result + "\nWarning: Dimerization is likely to occur between forward and reverse primer"; } result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; //Check for hairpins result = result + "\nHairpins:\nfwd primer:"; for (int i = 0; i < fwdSeq.length(); i++) { dG = 0.0; alignString = Aligner.align(fwdSeq.substring(0, fwdSeq.length() - i), fwdSeq.substring(i)); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } if (dG.intValue() <= fwdG.intValue()) { result = result + "\nWarning: Forward primer is likely to form a hairpin"; result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; } } result = result + "\nrev primer:"; for (int i = 0; i < revSeq.length(); i++) { dG = 0.0; alignString = Aligner.align(revSeq.substring(0, revSeq.length() - i), revSeq.substring(i)); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } if (dG.intValue() <= revG.intValue()) { result = result + "\nWarning: Reverse primer is likely to form a hairpin"; result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; } } //check against template for portential match result = result + "\nMISMATCH AGAINST TEMPLATE:\nfwd primer:"; dG = 0.0; alignString = Aligner.align(fwdSeq, _sequence); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } if (dG.intValue() <= fwdG.intValue()) { result = result + "\nWarning: Forward primer is likely to mismatch against the template"; } result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; result = result + "\nrev primer:"; dG = 0.0; alignString = Aligner.align(revSeq, _sequence); p = Pattern.compile("[|]+"); m = p.matcher(dimerCheckHelper(alignString)); while (m.find()) { dG = dG + calcDeltaG(alignString.substring(m.start() - 1, m.end() - 1)); } if (dG.intValue() <= revG.intValue()) { result = result + "\nWarning: Reverse primer is likely to mismatch against the template"; } result = result + "\n" + alignString; result = result + "\nDelta G: " + dG; return result; } private static String dimerCheckHelper(String s) { int first = 0; int second = 0; for (int i = 0; i < s.length(); i++) { if (s.substring(i, i + 1).matches("\\n")) { if (first == 0) { first = i; } else { second = i; } } } return (s.substring(first, second)); } public void switchViews() { if (isTC) { Component[] components = _tcView.getComponents(); // for (int i = 0; i < components.length; i++) { // System.out.println(components[i]); // } _frameView = new JFrame(); _frameView.setContentPane((Container) components[1]); _frameView.setJMenuBar((JMenuBar) components[0]); _frameView.pack(); _frameView.setVisible(true); isTC = false; _tcView.close(); } else { final JComponent guiContentPane = (JComponent) _frameView.getContentPane(); // JRootPane guiRootPane = _frameView.getRootPane(); final JMenuBar menu = _frameView.getJMenuBar(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { _tcView = new TopComponent(); _tcView.setLayout(new BorderLayout()); JScrollPane sp = new JScrollPane(guiContentPane); _tcView.add(menu, BorderLayout.NORTH); _tcView.add(sp, BorderLayout.CENTER); _tcView.setName("Primer Designer"); _tcView.open(); _tcView.requestActive(); } }); _frameView.setVisible(false); isTC = true; } } public void close() { if (isTC) { _tcView.close(); } else { _frameView.dispose(); } } public void validateKeyPressed(KeyEvent evt) { if (evt.getKeyCode() == KeyEvent.VK_ENTER || evt.getKeyCode() == KeyEvent.VK_TAB) { evt.consume(); } return; } public void validateKeyTyped(KeyEvent evt) { if (evt.getKeyChar() != 'a' && evt.getKeyChar() != 'g' && evt.getKeyChar() != 't' && evt.getKeyChar() != 'c' && evt.getKeyChar() != 'A' && evt.getKeyChar() != 'G' && evt.getKeyChar() != 'T' && evt.getKeyChar() != 'C') { evt.consume(); } } public void validateNumTyped(KeyEvent evt) { if (evt.getKeyChar() != '1' && evt.getKeyChar() != '2' && evt.getKeyChar() != '3' && evt.getKeyChar() != '4' && evt.getKeyChar() != '5' && evt.getKeyChar() != '6' && evt.getKeyChar() != '7' && evt.getKeyChar() != '8' && evt.getKeyChar() != '9' && evt.getKeyChar() != '0') { evt.consume(); } } public void loadSequence() { FileNameExtensionFilter fastaFilter = new FileNameExtensionFilter("FASTA File", "fa", "mpfa", "fna", "fsa", "fas", "fasta"); FileNameExtensionFilter genbankFilter = new FileNameExtensionFilter("GenBank File", "gen", "gb", "gbank", "genbank"); if (!_fileOpenerInstantiated) { _fileOpener = new ClothoOpenChooser("Load Sequence"); _fileOpener.getFileChooser().addChoosableFileFilter(fastaFilter); _fileOpener.getFileChooser().addChoosableFileFilter(genbankFilter); _fileOpener.getFileChooser().setFileFilter(_fileOpener.getFileChooser().getAcceptAllFileFilter()); _fileOpener.setTitle("Open a Sequence..."); _fileOpenerInstantiated = true; } _fileOpener.open_Window(); if (_fileOpener.fileSelected) { loadSequence(_fileOpener.getFile()); } } public void loadSequence(File toLoad) { String toSeqView = ""; if (toLoad.exists()) { try { java.io.BufferedReader inFile = new java.io.BufferedReader(new java.io.FileReader(toLoad)); String line = inFile.readLine(); // Reads in a FASTA format file if (line.startsWith(">")) { line = line.substring(1, line.length()); line = inFile.readLine(); while (line != null) { toSeqView = toSeqView + line.trim(); line = inFile.readLine(); } setSequence(toSeqView); } // Read in a Genbank format file else if (line.startsWith("LOCUS")) { while (line != null) { if (line.startsWith("ORIGIN")) { line = inFile.readLine().trim(); while (!(line.startsWith("//"))) { ArrayList<String> seq = new ArrayList(Arrays.asList(line.split(" "))); for (int i = 1; i < seq.size(); i++) { toSeqView = toSeqView + seq.get(i); } line = inFile.readLine().trim(); } } line = inFile.readLine(); } setSequence(toSeqView); } else { String[] yesNoOpt = {"Yes", "No"}; if (javax.swing.JOptionPane.showOptionDialog(new JFrame(), "This does not appear to be a Genbank or FASTA formated file.\n Do you want to proceed?", "Clotho: Sequnce View", javax.swing.JOptionPane.YES_NO_OPTION, javax.swing.JOptionPane.QUESTION_MESSAGE, null, yesNoOpt, yesNoOpt[1]) == javax.swing.JOptionPane.YES_OPTION) { while (line != null) { toSeqView = toSeqView + line.trim(); line = inFile.readLine(); } setSequence(toSeqView); System.out.println("WARNING: User decided to open a file that is not in FASTA or Genbank format"); } else { System.out.println("ERROR: User decided to cancel operation since the file is not in FASTA or Genbank format"); } } inFile.close(); } catch (java.io.IOException e) { System.out.println("\n" + e.getMessage() + "\n"); } // Sets the openChooser to open up at the location of the last // opened file } else { System.out.println("File does not exist!"); } } public void updateSequence(String s) { _sequence = s; } private JTextField _textField; private String _sequence; private JFrame _frameView; private boolean isTC; private TopComponent _tcView; private NucSeq _ns; private boolean _fileOpenerInstantiated; private ClothoOpenChooser _fileOpener; }