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<>();
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<>();
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 && "on".equals(getAlignmentOption(MEGABLAST));
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         * Blastall equivalent: -d
141         *
142         * @param database a valid name to a NCBI blastable database
143         */
144        public void setBlastDatabase(String database) {
145                setAlignmentOption(DATABASE, database);
146        }
147
148        /**
149         * @return double value of EXPECT parameter used for blast run
150         */
151        public double getBlastExpect() {
152                if (param.containsKey(EXPECT)) {
153                        return Double.parseDouble(getAlignmentOption(EXPECT));
154                }
155                return 10;
156        }
157
158        /**
159         * Sets the EXPECT parameter to be use with blastall
160         * <p>
161         * Example: if you want a EXPECT of 1e-10, pass {@code Double.parseDouble("1e-10")} as a parameter
162         * <p>
163         * Blastall equivalent: -e
164         *
165         * @param expect: a double value of EXPECT parameter
166         */
167        public void setBlastExpect(double expect) {
168                setAlignmentOption(EXPECT, Double.toString(expect));
169        }
170
171        /**
172         * Returns the value of the WORD_SIZE parameter used for this blast run
173         *
174         * @return int value of WORD_SIZE used by this search
175         * @throws IllegalArgumentException when program type is not set and program type is not supported
176         */
177        public int getBlastWordSize() {
178                if (param.containsKey(WORD_SIZE)) {
179                        return Integer.parseInt(getAlignmentOption(WORD_SIZE));
180                }
181
182                // return default word size value
183                try {
184                        BlastProgramEnum programType = getBlastProgram();
185                        switch (programType) {
186                        case blastn:
187                                return 11;
188                        case megablast:
189                                return 28;
190                        case blastp:
191                        case blastx:
192                        case tblastn:
193                        case tblastx:
194                                return 3;
195                        default:
196                                throw new UnsupportedOperationException("Blast program " + programType.name() + " is not supported.");
197                        }
198                } catch (IllegalArgumentException e) {
199                        throw new IllegalArgumentException("Blast program " + getBlastProgram() + " is not supported.", e);
200                }
201        }
202
203        /**
204         * Sets the WORD_SIZE parameter to be use with blastall
205         * <p>
206         * <b>WARNING!!</b> At this point, the method does not verify the validity of your choice; for example, word size of
207         * greater than 5 with blastp returns error messages from QBlast. Word size range depends on the algorithm chosen.
208         * <p>
209         * More at https://www.ncbi.nlm.nih.gov/staff/tao/URLAPI/new/node74.html
210         * <p>
211         * Blastall equivalent: -W
212         *
213         * @param word: an int used to set WORD_SIZE
214         */
215        public void setBlastWordSize(int word) {
216                setAlignmentOption(WORD_SIZE, Integer.toString(word));
217        }
218
219        /**
220         * Returns the value for the GAP_CREATION parameter (first half of GAPCOSTS parameter)
221         *
222         * @return an integer value for gap creation used by this search, -1 if not set or not a number
223         */
224        public int getBlastGapCreation() {
225                String gapCosts = getAlignmentOption(GAPCOSTS);
226                try {
227                        String gapCreation = gapCosts.split("\\+")[0];
228                        return Integer.parseInt(gapCreation);
229                } catch (Exception e) {
230                        return -1;
231                }
232        }
233
234        /**
235         * Returns the value for the gap extension parameter (second half of GAPCOSTS parameter)
236         *
237         * @return an integer for the value for gap extension used by this search, -1 if not set or not a number
238         */
239        public int getBlastGapExtension() {
240                String gapCosts = getAlignmentOption(GAPCOSTS);
241                try {
242                        String gapExtension = gapCosts.split("\\+")[1];
243                        return Integer.parseInt(gapExtension);
244                } catch (Exception e) {
245                        return -1;
246                }
247        }
248
249        /**
250         * Returns the actual string for the GAPCOSTS parameter which is used to build the URL
251         *
252         * @return the string representation of the GAPCOSTS parameter formatted for the URL
253         */
254        public String getBlastGapCosts() {
255                return getAlignmentOption(GAPCOSTS);
256        }
257
258        /**
259         * Sets the GAPCOSTS parameter
260         *
261         * @param gapCreation integer to use as gap creation value
262         * @param gapExtension integer to use as gap extension value
263         */
264        public void setBlastGapCosts(int gapCreation, int gapExtension) {
265                String gc = Integer.toString(gapCreation);
266                String ge = Integer.toString(gapExtension);
267                setAlignmentOption(GAPCOSTS, gc + "+" + ge);
268        }
269
270        /**
271         * Returns the value of the specified substitution matrix
272         *
273         * @return matrix: the name of the specified substitution matrix
274         */
275        public String getBlastMatrix() {
276                return getAlignmentOption(MATRIX_NAME);
277        }
278
279        /**
280         * Sets the value for the MATRIX parameter to use for blastall
281         * <p>
282         * Blastall equivalent: -M
283         *
284         * @param matrix : a String to use as gap creation value
285         * @see BlastMatrixEnum
286         */
287        public void setBlastMatrix(BlastMatrixEnum matrix) {
288                setAlignmentOption(MATRIX_NAME, matrix.name());
289
290                boolean gapCostsSet = getBlastGapCreation() != -1 || getBlastGapExtension() != -1;
291
292                if (!gapCostsSet) {
293                        /*
294                         * Setting default values for -G/-E if no other values have been set is necessary because, since BLOSUM62 is
295                         * default, the expected values are -G 11 -E 1. If your matrix choice is different, the request will fail,
296                         * implicitly expecting GAPCOSTS=11+1
297                         */
298                        switch (matrix) {
299                        case PAM30:
300                                setBlastGapCosts(9, 1);
301                                break;
302                        case PAM70:
303                                setBlastGapCosts(10, 1);
304                                break;
305                        case PAM250:
306                                setBlastGapCosts(14, 2);
307                                break;
308                        case BLOSUM45:
309                                setBlastGapCosts(15, 2);
310                                break;
311                        case BLOSUM50:
312                                setBlastGapCosts(13, 2);
313                                break;
314                        case BLOSUM80:
315                        case BLOSUM90:
316                                setBlastGapCosts(10, 1);
317                                break;
318                        case BLOSUM62:
319                                break;
320                        default:
321                                break;
322                        }
323                }
324        }
325
326        /**
327         * 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/>
328         * Blastall equivalent: -L
329         *
330         * @param start QUERY_FROM parameter
331         * @param end QUERY_TO parameter
332         */
333        public void setBlastFromToPosition(int start, int end) {
334                if (start >= end) {
335                        throw new IllegalArgumentException("Start index must be less than end index");
336                }
337                setAlignmentOption(QUERY_FROM, String.valueOf(start));
338                setAlignmentOption(QUERY_TO, String.valueOf(end));
339        }
340
341        /**
342         * @return an integer value for the QUERY_FROM parameter
343         * @see #setBlastFromToPosition(int, int)
344         */
345        public int getBlastFromPosition() {
346                return Integer.parseInt(getAlignmentOption(QUERY_FROM));
347        }
348
349        /**
350         * @return QUERY_TO parameter
351         * @see #setBlastFromToPosition(int, int)
352         */
353        public int getBlastToPosition() {
354                return Integer.parseInt(getAlignmentOption(QUERY_TO));
355        }
356
357        /**
358         * This method is to be used if a request is to use non-default values at submission. Useful for the following
359         * blastall parameters:
360         * <ul>
361         * <li>-r: integer to reward for match. Default = 1</li>
362         * <li>-q: negative integer for penalty to allow mismatch. Default = -3</li>
363         * <li>-y: dropoff for blast extensions in bits, using default if not specified. Default = 20 for blastn, 7 for all
364         * others (except megablast for which it is not applicable).</li>
365         * <li>-X: X dropoff value for gapped alignment, in bits. Default = 30 for blastn/megablast, 15 for all others.</li>
366         * <li>-Z: final X dropoff value for gapped alignement, in bits. Default = 50 for blastn, 25 for all others (except
367         * megablast for which it is not applicable)</li>
368         * <li>-P: equals 0 for multiple hits 1-pass, 1 for single hit 1-pass. Does not apply to blastn ou megablast.</li>
369         * <li>-A: multiple hits window size. Default = 0 (for single hit algorithm)</li>
370         * <li>-I: number of database sequences to save hits for. Default = 500</li>
371         * <li>-Y: effective length of the search space. Default = 0 (0 represents using the whole space)</li>
372         * <li>-z: a real specifying the effective length of the database to use. Default = 0 (0 represents the real size)</li>
373         * <li>-c: an integer representing pseudocount constant for PSI-BLAST. Default = 7</li>
374         * <li>-F: any filtering directive</li>
375         * </ul>
376         * <p>
377         * WARNING!! This method is still very much in flux and might not work as expected...
378         * </p>
379         * <p>
380         * You have to be aware that at no moment is there any error checking on the use of these parameters by this class.
381         * </p>
382         *
383         * @param advancedOptions : a String with any number of optional parameters with an associated value.
384         */
385        public void setBlastAdvancedOptions(String advancedOptions) {
386                // Escaping white spaces with + char to comply with QBlast specifications
387                setAlignmentOption(OTHER_ADVANCED, advancedOptions.replaceAll(" ", "+"));
388        }
389
390        /**
391         * @return the String with the advanced options
392         */
393        public String getBlastAdvancedOptions() {
394                return getAlignmentOption(OTHER_ADVANCED);
395        }
396}