001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on 2011-11-20
021 *
022 */
023
024package org.biojava.nbio.ws.alignment.qblast;
025
026import org.biojava.nbio.ws.alignment.RemotePairwiseAlignmentProperties;
027
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.Map;
031import java.util.Set;
032
033import static org.biojava.nbio.ws.alignment.qblast.BlastAlignmentParameterEnum.*;
034
035/**
036 * This class wraps a QBlast search request parameter {@code Map} by adding several convenient parameter addition
037 * methods. Other QBlast URL API parameters should be added using
038 * {@link #setAlignmentOption(BlastAlignmentParameterEnum, String)}
039 * <p/>
040 * Required parameters are {@code PROGRAM} and {@code DATABASE}, other parameters are optional
041 *
042 * @author Sylvain Foisy, Diploide BioIT
043 * @author Gediminas Rimsa
044 */
045public class NCBIQBlastAlignmentProperties implements RemotePairwiseAlignmentProperties {
046        private static final long serialVersionUID = 7158270364392309841L;
047
048        private Map<BlastAlignmentParameterEnum, String> param = new HashMap<BlastAlignmentParameterEnum, String>();
049
050        /**
051         * This method forwards to {@link #getAlignmentOption(BlastAlignmentParameterEnum)}. Consider using it instead.
052         */
053        @Override
054        public String getAlignmentOption(String key) {
055                return getAlignmentOption(BlastAlignmentParameterEnum.valueOf(key));
056        }
057
058        /**
059         * This method forwards to {@link #setAlignmentOption(BlastAlignmentParameterEnum, String)}. Consider using it
060         * instead.
061         */
062        @Override
063        public void setAlignementOption(String key, String val) {
064                setAlignmentOption(BlastAlignmentParameterEnum.valueOf(key), val);
065        }
066
067        /**
068         * Gets parameters, which are currently set
069         */
070        @Override
071        public Set<String> getAlignmentOptions() {
072                Set<String> result = new HashSet<String>();
073                for (BlastAlignmentParameterEnum parameter : param.keySet()) {
074                        result.add(parameter.name());
075                }
076                return result;
077        }
078
079        /**
080         * Gets the value of specified parameter or {@code null} if it is not set.
081         */
082        public String getAlignmentOption(BlastAlignmentParameterEnum key) {
083                return param.get(key);
084        }
085
086        /**
087         * Sets the value of specified parameter
088         */
089        public void setAlignmentOption(BlastAlignmentParameterEnum key, String val) {
090                param.put(key, val);
091        }
092
093        /**
094         * Removes given parameter
095         */
096        public void removeAlignmentOption(BlastAlignmentParameterEnum key) {
097                param.remove(key);
098        }
099
100        /**
101         * @return {@linkplain BlastProgramEnum} used for blast run
102         */
103        public BlastProgramEnum getBlastProgram() {
104                BlastProgramEnum program = BlastProgramEnum.valueOf(getAlignmentOption(PROGRAM));
105                boolean isMegablast = BlastProgramEnum.blastn == program && getAlignmentOption(MEGABLAST).equals("on");
106                return !isMegablast ? program : BlastProgramEnum.megablast;
107        }
108
109        /**
110         * Sets the program to be used with blastall
111         *
112         * @param program : one of blastall programs
113         */
114        public void setBlastProgram(BlastProgramEnum program) {
115                if (BlastProgramEnum.megablast != program) {
116                        setAlignmentOption(PROGRAM, program.name());
117                        removeAlignmentOption(MEGABLAST);
118                } else {
119                        setAlignmentOption(PROGRAM, BlastProgramEnum.blastn.name());
120                        setAlignmentOption(MEGABLAST, "on");
121                }
122        }
123
124        /**
125         * @return name of database used with blastall
126         */
127        public String getBlastDatabase() {
128                return getAlignmentOption(DATABASE);
129        }
130
131        /*
132         * TODO: update comment when URL is available:
133         * A quite exhaustive list of the databases available for QBlast
134         * requests can be found here: <p> http://&lt to_be_completed &gt <p> Blastall equivalent: -d
135         */
136
137        /**
138         * Sets the database to be used with blastall
139         * <p>
140         * A list of available databases can be acquired by calling {@link NCBIQBlastService#printRemoteBlastInfo()}
141         * <p>
142         * Blastall equivalent: -d
143         *
144         * @param db : a valid name to a NCBI blastable database
145         */
146        public void setBlastDatabase(String database) {
147                setAlignmentOption(DATABASE, database);
148        }
149
150        /**
151         * @return double value of EXPECT parameter used for blast run
152         */
153        public double getBlastExpect() {
154                if (param.containsKey(EXPECT)) {
155                        return Double.parseDouble(getAlignmentOption(EXPECT));
156                }
157                return 10;
158        }
159
160        /**
161         * Sets the EXPECT parameter to be use with blastall
162         * <p>
163         * Example: if you want a EXPECT of 1e-10, pass {@code Double.parseDouble("1e-10")} as a parameter
164         * <p>
165         * Blastall equivalent: -e
166         *
167         * @param expect: a double value of EXPECT parameter
168         */
169        public void setBlastExpect(double expect) {
170                setAlignmentOption(EXPECT, Double.toString(expect));
171        }
172
173        /**
174         * Returns the value of the WORD_SIZE parameter used for this blast run
175         *
176         * @return int value of WORD_SIZE used by this search
177         * @throws IllegalArgumentException when program type is not set and program type is not supported
178         */
179        public int getBlastWordSize() {
180                if (param.containsKey(WORD_SIZE)) {
181                        return Integer.parseInt(getAlignmentOption(WORD_SIZE));
182                }
183
184                // return default word size value
185                try {
186                        BlastProgramEnum programType = getBlastProgram();
187                        switch (programType) {
188                        case blastn:
189                                return 11;
190                        case megablast:
191                                return 28;
192                        case blastp:
193                        case blastx:
194                        case tblastn:
195                        case tblastx:
196                                return 3;
197                        default:
198                                throw new UnsupportedOperationException("Blast program " + programType.name() + " is not supported.");
199                        }
200                } catch (IllegalArgumentException e) {
201                        throw new IllegalArgumentException("Blast program " + getBlastProgram() + " is not supported.", e);
202                }
203        }
204
205        /**
206         * Sets the WORD_SIZE parameter to be use with blastall
207         * <p>
208         * <b>WARNING!!</b> At this point, the method does not verify the validity of your choice; for example, word size of
209         * greater than 5 with blastp returns error messages from QBlast. Word size range depends on the algorithm chosen.
210         * <p>
211         * More at https://www.ncbi.nlm.nih.gov/staff/tao/URLAPI/new/node74.html
212         * <p>
213         * Blastall equivalent: -W
214         *
215         * @param word: an int used to set WORD_SIZE
216         */
217        public void setBlastWordSize(int word) {
218                setAlignmentOption(WORD_SIZE, Integer.toString(word));
219        }
220
221        /**
222         * Returns the value for the GAP_CREATION parameter (first half of GAPCOSTS parameter)
223         *
224         * @return an integer value for gap creation used by this search, -1 if not set or not a number
225         */
226        public int getBlastGapCreation() {
227                String gapCosts = getAlignmentOption(GAPCOSTS);
228                try {
229                        String gapCreation = gapCosts.split("\\+")[0];
230                        return Integer.parseInt(gapCreation);
231                } catch (Exception e) {
232                        return -1;
233                }
234        }
235
236        /**
237         * Returns the value for the gap extension parameter (second half of GAPCOSTS parameter)
238         *
239         * @return an integer for the value for gap extension used by this search, -1 if not set or not a number
240         */
241        public int getBlastGapExtension() {
242                String gapCosts = getAlignmentOption(GAPCOSTS);
243                try {
244                        String gapExtension = gapCosts.split("\\+")[1];
245                        return Integer.parseInt(gapExtension);
246                } catch (Exception e) {
247                        return -1;
248                }
249        }
250
251        /**
252         * Returns the actual string for the GAPCOSTS parameter which is used to build the URL
253         *
254         * @return the string representation of the GAPCOSTS parameter formatted for the URL
255         */
256        public String getBlastGapCosts() {
257                return getAlignmentOption(GAPCOSTS);
258        }
259
260        /**
261         * Sets the GAPCOSTS parameter
262         *
263         * @param gapCreation integer to use as gap creation value
264         * @param gapExtension integer to use as gap extension value
265         */
266        public void setBlastGapCosts(int gapCreation, int gapExtension) {
267                String gc = Integer.toString(gapCreation);
268                String ge = Integer.toString(gapExtension);
269                setAlignmentOption(GAPCOSTS, gc + "+" + ge);
270        }
271
272        /**
273         * Returns the value of the specified substitution matrix
274         *
275         * @return matrix: the name of the specified substitution matrix
276         */
277        public String getBlastMatrix() {
278                return getAlignmentOption(MATRIX_NAME);
279        }
280
281        /**
282         * Sets the value for the MATRIX parameter to use for blastall
283         * <p>
284         * Blastall equivalent: -M
285         *
286         * @param matrix : a String to use as gap creation value
287         * @see BlastMatrixEnum
288         */
289        public void setBlastMatrix(BlastMatrixEnum matrix) {
290                setAlignmentOption(MATRIX_NAME, matrix.name());
291
292                boolean gapCostsSet = getBlastGapCreation() != -1 || getBlastGapExtension() != -1;
293
294                if (!gapCostsSet) {
295                        /*
296                         * Setting default values for -G/-E if no other values have been set is necessary because, since BLOSUM62 is
297                         * default, the expected values are -G 11 -E 1. If your matrix choice is different, the request will fail,
298                         * implicitly expecting GAPCOSTS=11+1
299                         */
300                        switch (matrix) {
301                        case PAM30:
302                                setBlastGapCosts(9, 1);
303                                break;
304                        case PAM70:
305                                setBlastGapCosts(10, 1);
306                                break;
307                        case PAM250:
308                                setBlastGapCosts(14, 2);
309                                break;
310                        case BLOSUM45:
311                                setBlastGapCosts(15, 2);
312                                break;
313                        case BLOSUM50:
314                                setBlastGapCosts(13, 2);
315                                break;
316                        case BLOSUM80:
317                        case BLOSUM90:
318                                setBlastGapCosts(10, 1);
319                                break;
320                        case BLOSUM62:
321                                break;
322                        default:
323                                break;
324                        }
325                }
326        }
327
328        /**
329         * Sets the QUERY_FROM and QUERY_TO parameters to be use by blast. Do not use if you want to use the whole sequence.<br/>
330         * Blastall equivalent: -L
331         *
332         * @param start QUERY_FROM parameter
333         * @param end QUERY_TO parameter
334         */
335        public void setBlastFromToPosition(int start, int end) {
336                if (start >= end) {
337                        throw new IllegalArgumentException("Start index must be less than end index");
338                }
339                setAlignmentOption(QUERY_FROM, String.valueOf(start));
340                setAlignmentOption(QUERY_TO, String.valueOf(end));
341        }
342
343        /**
344         * @return an integer value for the QUERY_FROM parameter
345         * @see #setBlastFromToPosition(int, int)
346         */
347        public int getBlastFromPosition() {
348                return Integer.parseInt(getAlignmentOption(QUERY_FROM));
349        }
350
351        /**
352         * @return QUERY_TO parameter
353         * @see #setBlastFromToPosition(int, int)
354         */
355        public int getBlastToPosition() {
356                return Integer.parseInt(getAlignmentOption(QUERY_TO));
357        }
358
359        /**
360         * This method is to be used if a request is to use non-default values at submission. Useful for the following
361         * blastall parameters:
362         * <ul>
363         * <li>-r: integer to reward for match. Default = 1</li>
364         * <li>-q: negative integer for penalty to allow mismatch. Default = -3</li>
365         * <li>-y: dropoff for blast extensions in bits, using default if not specified. Default = 20 for blastn, 7 for all
366         * others (except megablast for which it is not applicable).</li>
367         * <li>-X: X dropoff value for gapped alignment, in bits. Default = 30 for blastn/megablast, 15 for all others.</li>
368         * <li>-Z: final X dropoff value for gapped alignement, in bits. Default = 50 for blastn, 25 for all others (except
369         * megablast for which it is not applicable)</li>
370         * <li>-P: equals 0 for multiple hits 1-pass, 1 for single hit 1-pass. Does not apply to blastn ou megablast.</li>
371         * <li>-A: multiple hits window size. Default = 0 (for single hit algorithm)</li>
372         * <li>-I: number of database sequences to save hits for. Default = 500</li>
373         * <li>-Y: effective length of the search space. Default = 0 (0 represents using the whole space)</li>
374         * <li>-z: a real specifying the effective length of the database to use. Default = 0 (0 represents the real size)</li>
375         * <li>-c: an integer representing pseudocount constant for PSI-BLAST. Default = 7</li>
376         * <li>-F: any filtering directive</li>
377         * </ul>
378         * <p>
379         * WARNING!! This method is still very much in flux and might not work as expected...
380         * </p>
381         * <p>
382         * You have to be aware that at no moment is there any error checking on the use of these parameters by this class.
383         * </p>
384         *
385         * @param advancedOptions : a String with any number of optional parameters with an associated value.
386         */
387        public void setBlastAdvancedOptions(String advancedOptions) {
388                // Escaping white spaces with + char to comply with QBlast specifications
389                setAlignmentOption(OTHER_ADVANCED, advancedOptions.replaceAll(" ", "+"));
390        }
391
392        /**
393         * @return the String with the advanced options
394         */
395        public String getBlastAdvancedOptions() {
396                return getAlignmentOption(OTHER_ADVANCED);
397        }
398}