X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fws%2Fjws2%2FRNAalifoldClient.java;h=769100e28295ce326e3bce7ad8ff74c93eb84735;hb=17e77c3f2949a0729322b4a8d907f3f34b6a9914;hp=959214241209c0870cb94d6ddb878fb75d65b984;hpb=24c4dce749cbfccc4435e442090d04013e3e1b99;p=jalview.git diff --git a/src/jalview/ws/jws2/RNAalifoldClient.java b/src/jalview/ws/jws2/RNAalifoldClient.java index 9592142..769100e 100644 --- a/src/jalview/ws/jws2/RNAalifoldClient.java +++ b/src/jalview/ws/jws2/RNAalifoldClient.java @@ -1,186 +1,405 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9) + * Copyright (C) 2015 The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.ws.jws2; import jalview.api.AlignCalcWorkerI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.gui.AlignFrame; -import jalview.schemes.NucleotideColourScheme; +import jalview.util.MessageManager; import jalview.ws.jws2.jabaws2.Jws2Instance; import jalview.ws.params.WsParamSetI; +import jalview.ws.uimodel.AlignAnalysisUIText; +import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.TreeMap; import java.util.TreeSet; +import java.util.regex.Pattern; -import compbio.data.msa.jaxws.Align; +import compbio.data.sequence.FastaSequence; +import compbio.data.sequence.RNAStructReader.AlifoldResult; +import compbio.data.sequence.RNAStructScoreManager; import compbio.data.sequence.Range; import compbio.data.sequence.Score; -import compbio.data.sequence.RNAStructScoreManager; -import compbio.data.sequence.ScoreManager; import compbio.metadata.Argument; -public class RNAalifoldClient extends JabawsAlignCalcWorker implements - AlignCalcWorkerI +/** + * Client for the JABA RNA Alifold Service + * + * @author daluke - Daniel Barton + * + */ + +public class RNAalifoldClient extends JabawsCalcWorker implements + AlignCalcWorkerI { - - // test - - String methodName; - - AlignFrame af; - - public RNAalifoldClient(Jws2Instance sh, AlignFrame alignFrame, - WsParamSetI preset, List paramset) - { - super(sh, alignFrame, preset, paramset); - - if (arguments == null) arguments = new ArrayList(); - arguments.add(sh.getRunnerConfig().getArgumentByOptionName("-p")); - - - af = alignFrame; - methodName = sh.serviceType; - - // defult false. Which one here? - // submitGaps = true; - nucleotidesAllowed = true; - proteinAllowed = false; - - arguments.add(sh.getRunnerConfig().getArgumentByOptionName("-p")); - } - - @Override - public String getServiceActionText() - { - return "Submitting RNA alignment for Secondary Structure prediction using " - + "RNAalifold Service"; - } - - @Override - public void updateResultAnnotation(boolean immediate) - { - - - if (immediate || !calcMan.isWorking(this) && scoremanager != null) - { - - List ourAnnot = new ArrayList(); - // ourAnnots = new ArrayList(); - - // So I don't have to do any more casting - List structs = ((RNAStructScoreManager) scoremanager).getStructs(); - List> data = ((RNAStructScoreManager) scoremanager).getData(); - - - // I think this will never find an annotation at the moment. It will always create... - AlignmentAnnotation annotation = alignViewport.getAlignment() - .findOrCreateAnnotation("Consensus Structure", getCalcId(), false, null , null); - - // construct Annotation from scoremanager - - // Deal with the consensus structure and (?)BasePair Probabilities - Annotation[] anns = new Annotation[structs.get(1).length()]; - - // check if the first Score object is populated with base pair probabilities - Score fscore = data.get(0).first(); - boolean BPScores = (fscore.getScores().size() > 0 - && fscore.getRanges() != null); - - TreeMap basePairs = null; - if (BPScores) { - // The base pair probabilities are stored in a set in scoremanager... we want a map - basePairs = new TreeMap(); - for (Score score : data.get(0)) { - // The Score objects contain a set of size one containing the range and - // an ArrayList of size one containing the probabilty - basePairs.put(score.getRanges().first(), new Float(score.getScores().get(0))); - } - } - - // ignoring the Consensus alignemnt for now, get the Structure and make an AlignmentAnnotation - String struct = structs.get(1); // get(1) - for (int i = 0; i < struct.length(); i++) { - - if (BPScores) { - // Return all the contacts associated with position i - List contacts = isContact(basePairs, i+1); - - if (contacts.size() == 0) { - anns[i] = new Annotation(struct.substring(i, i+1), "", struct.charAt(i), 0f); - } - else if (contacts.size() == 1) { - // There is only one contact associated with this base - float prob = basePairs.get(contacts.get(0)); - anns[i] = new Annotation(struct.substring(i, i+1), "", struct.charAt(i), prob); - } - else if (contacts.size() > 1) { - // For now we will simply deal with alternate contact information by mentioning its - // existance in the description - float prob = basePairs.get(contacts.get(0)); - anns[i] = new Annotation(struct.substring(i, i+1), "This base has alternate contacts", - struct.charAt(i), prob); - } - } - else { - // Same as the first if from the previous block - anns[i] = new Annotation(struct.substring(i, i+1), "", struct.charAt(i), 0f); - } - } - - System.out.println("size of anns: " + anns.length); - - // Set the annotation to the AlignmentAnnotation object - annotation.annotations = anns; - - // Set the probability - annotation.setScore(data.get(1).first().getScores().get(0)); - - - - // old -// AlignmentAnnotation annot = new AlignmentAnnotation("Consensus Structure", "Free Energy", anns); - - - - - System.out.println("RNAalifoldClient - annotation:\n"); - for (Annotation ann : annotation.annotations) { - System.out.print(ann.toString()+"|"); - } - System.out.println(); - - // Instead of this look at existing methods for creating annotations - ourAnnot.add(annotation); - - - if (ourAnnot.size() > 0) { - updateOurAnnots(ourAnnot); - } - - } - } - - // Check whether, at position i there is a base contact and return all the - // contacts at this position. Should be in order of descending probability. - private List isContact(TreeMap basePairs, int i) { - - List contacts = new ArrayList(); - - for (Range contact : basePairs.keySet()) { - // finds the contacts associtated with position i ordered by the natural - // ordering of the Scores TreeSet in ScoreManager which is, descending probability - if (contact.from == i || contact.to == i) contacts.add(contact); - } - - return contacts; - } - - public String getCalcId() + + String methodName; + + AlignFrame af; + + // keeps track of whether the RNAalifold result includes base contact + // probabilities + boolean bpScores; + + public RNAalifoldClient(Jws2Instance sh, AlignFrame alignFrame, + WsParamSetI preset, List paramset) + { + super(sh, alignFrame, preset, paramset); + af = alignFrame; + methodName = sh.serviceType; + alignedSeqs = true; + submitGaps = true; + nucleotidesAllowed = true; + proteinAllowed = false; + initViewportParams(); + } + + public String getCalcId() + { + return CALC_ID; + } + + private static String CALC_ID = "jalview.ws.jws2.RNAalifoldClient"; + + public static AlignAnalysisUIText getAlignAnalysisUITest() + { + return new AlignAnalysisUIText( + compbio.ws.client.Services.RNAalifoldWS.toString(), + jalview.ws.jws2.RNAalifoldClient.class, CALC_ID, true, false, + true, MessageManager.getString("label.rnalifold_calculations"), + MessageManager.getString("tooltip.rnalifold_calculations"), + MessageManager.getString("label.rnalifold_settings"), + MessageManager.getString("tooltip.rnalifold_settings")); + } + + @Override + public String getServiceActionText() + { + return "Submitting RNA alignment for Secondary Structure prediction using " + + "RNAalifold Service"; + } + + @Override + boolean checkValidInputSeqs(boolean dynamic, List seqs) + { + return (seqs.size() > 1); + } + + @Override + public void updateResultAnnotation(boolean immediate) + { + + if (immediate || !calcMan.isWorking(this) && scoremanager != null) + { + + List ourAnnot = new ArrayList(); + + // Unpack the ScoreManager + List structs = ((RNAStructScoreManager) scoremanager) + .getStructs(); + List> data = ((RNAStructScoreManager) scoremanager) + .getData(); + + // test to see if this data object contains base pair contacts + Score fscore = data.get(0).first(); + this.bpScores = (fscore.getMethod() + .equals(AlifoldResult.contactProbabilities.toString())); + + // add annotation for the consensus sequence alignment + createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), + structs.get(0), null, null); + + // Add annotations for the mfe Structure + createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), + structs.get(1), data.get(1), null); + + // decide whether to add base pair contact probability histogram + int count = 2; + if (bpScores) + { + createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), + structs.get(2), data.get(0), data.get(2)); + count++; + } + + // Now loop for the rest of the Annotations (if there it isn't stochastic + // output + // only the centroid and MEA structures remain anyway) + for (int i = count; i < structs.size(); i++) + { + // The ensemble values should be displayed in the description of the + // first (or all?) Stochastic Backtrack Structures. + if (!data.get(i).first().getMethod() + .equals(AlifoldResult.ensembleValues.toString())) + { + + createAnnotationRowforScoreHolder(ourAnnot, getCalcId(), + structs.get(i), data.get(i), null); + } + } + + if (ourAnnot.size() > 0) + { + + updateOurAnnots(ourAnnot); + ap.adjustAnnotationHeight(); + } + } + } + + protected void createAnnotationRowforScoreHolder( + List ourAnnot, String calcId, String struct, + TreeSet data, TreeSet descriptionData) + { + /* + * If contactProbability information is returned from RNAalifold it is + * stored in the first TreeSet object corresponding to the String Id + * which holds the consensus alignment. The method enumeration is then + * updated to AlifoldResult.contactProbabilties. This line recreates the + * same data object as was overwritten with the contact probabilites data. + */ + if (data == null) + { + data = compbio.data.sequence.RNAStructReader + .newEmptyScore(AlifoldResult.consensusAlignment); + } + + if (descriptionData == null) + { + descriptionData = data; + } + + String[] typenameAndDescription = constructTypenameAndDescription(descriptionData + .first()); + String typename = typenameAndDescription[0]; + String description = typenameAndDescription[1]; + + AlignmentAnnotation annotation = alignViewport.getAlignment() + .findOrCreateAnnotation(typename, calcId, false, null, null); + + constructAnnotationFromScoreHolder(annotation, struct, data); + + /* + * update annotation description with the free Energy, frequency in ensemble + * or other data where appropriate. + * + * Doesnt deal with AlifoldResult.ensembleValues, the free energy of + * ensemble and frequency of mfe structure in ensemble. How to deal with + * these? + */ + annotation.description = description; + + annotation.belowAlignment = false; + // annotation.showAllColLabels = true; + + alignViewport.getAlignment().validateAnnotation(annotation); + af.setMenusForViewport(); + + ourAnnot.add(annotation); + } + + private AlignmentAnnotation constructAnnotationFromScoreHolder( + AlignmentAnnotation annotation, String struct, TreeSet data) + { + Annotation[] anns = new Annotation[gapMap != null ? gapMap.length + 1 + : struct.length()]; + + if (data != null + && data.size() > 1 + && data.first().getMethod() + .equals(AlifoldResult.contactProbabilities.toString())) + { + + // The base pair probabilities are stored in a set in scoreholder. we want + // a map + LinkedHashMap basePairs = new LinkedHashMap(); + for (Score score : data) + { + // The Score objects contain a set of size one containing the range and + // an ArrayList of size one containing the probabilty + basePairs.put(score.getRanges().first(), new Float(score + .getScores().get(0))); + } + + for (int i = 0, ri = 0, iEnd = struct.length(); i < iEnd; i++, ri++) + { + if (gapMap != null) + { + // skip any gapped columns in the input data + while (!gapMap[ri]) + { + ri++; + } + } + // Return all the contacts associated with position i + LinkedHashMap contacts = isContact(basePairs, i + 1); + + String description = ""; + float prob = 0f; + + if (contacts.size() == 0) + { + description = "No Data"; + } + else + { + for (Range contact : contacts.keySet()) + { + float t = contacts.get(contact); + if (t > prob) + { + prob = t; + } + description += Integer.toString(contact.from) + "->" + + Integer.toString(contact.to) + ": " + + Float.toString(t) + "% | "; + } + } + + anns[ri] = new Annotation(struct.substring(i, i + 1), description, + isSS(struct.charAt(i)), prob); + } + } + else if (data == null || data.size() == 1) + { + for (int i = 0, ri = 0, iEnd = struct.length(); i < iEnd; i++, ri++) + { + if (gapMap != null) + { + // skip any gapped columns in the input data + while (!gapMap[ri] && ri < gapMap.length) + { + ri++; + } + if (ri == gapMap.length) + { + break; + } + } + anns[ri] = new Annotation(struct.substring(i, i + 1), "", + isSS(struct.charAt(i)), Float.NaN); + } + + annotation.graph = 0; // No graph + } + + annotation.annotations = anns; + + return annotation; + } + + private String[] constructTypenameAndDescription(Score score) + { + String description = ""; + String typename = ""; + String datatype = score.getMethod(); + + // Look up java switch syntax and use one here + if (datatype.equals(AlifoldResult.mfeStructure.toString())) + { + + description = MessageFormat.format( + "Minimum Free Energy Structure. Energy: {0} = {1} + {2}", + score.getScores().get(0), score.getScores().get(1), score + .getScores().get(2)); + typename = "MFE Structure"; + } + else if (datatype.equals(AlifoldResult.contactProbabilityStructure + .toString())) + { + description = MessageFormat + .format("Base Pair Contact Probabilities. " + + "Energy of Ensemble: {0} Frequency of Ensemble: {1}", + score.getScores().get(0), score.getScores().get(1)); + typename = "Contact Probabilities"; + } + else if (datatype.equals(AlifoldResult.centroidStructure.toString())) + { + description = MessageFormat.format( + "Centroid Structure. Energy: {0} = {1} + {2}", score + .getScores().get(0), score.getScores().get(1), score + .getScores().get(2)); + typename = "Centroid Structure"; + } + else if (datatype.equals(AlifoldResult.stochBTStructure.toString())) + { + if (score.getScores().size() > 0) + { + description = MessageFormat.format("Probability: {0} Energy: {1}", + score.getScores().get(0), score.getScores().get(1)); + } + else + { + description = "Stochastic Backtrack Structure"; + } + } + else if (datatype.equals(AlifoldResult.MEAStucture.toString())) + { + description = MessageFormat.format( + "Maximum Expected Accuracy Values: '{' {0} MEA={1} '}", score + .getScores().get(0), score.getScores().get(1)); + typename = "MEA Structure"; + } + else if (datatype.equals(AlifoldResult.consensusAlignment.toString())) + { + typename = "RNAalifold Consensus"; + description = "Consensus Alignment Produced by RNAalifold"; + } + else + { + typename = datatype; + description = typename; + } + + return new String[] { typename, description }; + } + + // Check whether, at position i there is a base contact and return all the + // contacts at this position. Should be in order of descending probability. + private LinkedHashMap isContact( + LinkedHashMap basePairs, int i) + { + LinkedHashMap contacts = new LinkedHashMap(); + + for (Range contact : basePairs.keySet()) + { + // finds the contacts associtated with position i ordered by the natural + // ordering of the Scores TreeSet in ScoreManager which is, descending + // probability + if (contact.from == i || contact.to == i) + { + contacts.put(contact, basePairs.get(contact)); + } + } + + return contacts; + } + + private char isSS(char chr) { - return SequenceAnnotationWSClient.AAConCalcId; + String regex = "\\(|\\)|\\{|\\}|\\[|\\]"; + char ss = (Pattern.matches(regex, Character.toString(chr))) ? 'S' : ' '; + return ss; } - }