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 01-21-2010 021 */ 022package org.biojava.nbio.core.sequence.io; 023 024import org.biojava.nbio.core.exceptions.ParserException; 025import org.biojava.nbio.core.sequence.compound.AminoAcidCompound; 026import org.biojava.nbio.core.sequence.compound.NucleotideCompound; 027import org.biojava.nbio.core.sequence.io.util.ClasspathResource; 028import org.biojava.nbio.core.sequence.io.util.IOUtils; 029import org.biojava.nbio.core.sequence.template.AbstractCompoundSet; 030import org.biojava.nbio.core.sequence.template.CompoundSet; 031import org.biojava.nbio.core.sequence.transcription.Table; 032 033import java.io.InputStream; 034import java.util.*; 035 036 037/** 038 * Available translations 039 * 040 * <ul> 041 * <li>1 - UNIVERSAL</li> 042 * <li>2 - VERTEBRATE_MITOCHONDRIAL</li> 043 * <li>3 - YEAST_MITOCHONDRIAL</li> 044 * <li>4 - MOLD_MITOCHONDRIAL</li> 045 * <li>5 - INVERTEBRATE_MITOCHONDRIAL</li> 046 * <li>6 - CILIATE_NUCLEAR</li> 047 * <li>9 - ECHINODERM_MITOCHONDRIAL</li> 048 * <li>10 - EUPLOTID_NUCLEAR</li> 049 * <li>11 - BACTERIAL</li> 050 * <li>12 - ALTERNATIVE_YEAST_NUCLEAR</li> 051 * <li>13 - ASCIDIAN_MITOCHONDRIAL</li> 052 * <li>14 - FLATWORM_MITOCHONDRIAL</li> 053 * <li>15 - BLEPHARISMA_MACRONUCLEAR</li> 054 * <li>16 - 2CHLOROPHYCEAN_MITOCHONDRIAL</li> 055 * <li>21 - TREMATODE_MITOCHONDRIAL</li> 056 * <li>23 - SCENEDESMUS_MITOCHONDRIAL</li> 057 * </ul> 058 * 059 * Taken from <a 060 * href="https://www.ncbi.nlm.nih.gov/Taxonomy/Utils/wprintgc.cgi?mode=c" 061 * >NCBI</a> with slight modification and put into the classpath resource. 062 * 063 * Takes in an ID, name, amino acid string and the locations of amino acids 064 * which acts as start codons in the translation table. You can give the 3 codon 065 * position strings that correspond to the amino acid string or if you are using 066 * the default IUPAC codes you can use the hardcoded ones which are consistent 067 * amongst all <a 068 * href="https://www.ncbi.nlm.nih.gov/Taxonomy/Utils/wprintgc.cgi?mode=c"> codon 069 * tables</a>. 070 * 071 * The generated {@link IUPACTable} objects do not parse the data further until 072 * requested so if you do not use a translation table your only penalty is the 073 * loading of the IUPAC data from the classpath. 074 * 075 * @author Andy Yates 076 */ 077public class IUPACParser { 078 079 private static class IOD { 080 public static final IUPACParser INSTANCE = new IUPACParser(); 081 } 082 083 public static IUPACParser getInstance() { 084 return IOD.INSTANCE; 085 } 086 087 public static final String IUPAC_LOCATION = "org/biojava/nbio/core/sequence/iupac.txt"; 088 089 private InputStream is; 090 private List<IUPACTable> tables; 091 private Map<String, IUPACTable> nameLookup; 092 private Map<Integer, IUPACTable> idLookup; 093 094 /** 095 * Default version and uses the classpath based IUPAC table 096 */ 097 public IUPACParser() { 098 //use the preCache version to make sure we don't keep a IO handle open 099 is = new ClasspathResource(IUPAC_LOCATION, true).getInputStream(); 100 } 101 102 /** 103 * Allows you to specify a different IUPAC table. 104 */ 105 public IUPACParser(InputStream is) { 106 this.is = is; 107 } 108 109 /** 110 * Returns a list of all available IUPAC tables 111 */ 112 public List<IUPACTable> getTables() { 113 if (tables == null) { 114 tables = parseTables(); 115 } 116 return tables; 117 } 118 119 /** 120 * Returns a table by its name 121 */ 122 public IUPACTable getTable(String name) { 123 populateLookups(); 124 return nameLookup.get(name); 125 } 126 127 /** 128 * Returns a table by its identifier i.e. 1 means universal codon tables 129 */ 130 public IUPACTable getTable(Integer id) { 131 populateLookups(); 132 return idLookup.get(id); 133 } 134 135 private void populateLookups() { 136 if(nameLookup == null) { 137 nameLookup = new HashMap<String, IUPACTable>(); 138 idLookup = new HashMap<Integer, IUPACTable>(); 139 for(IUPACTable t: getTables()) { 140 nameLookup.put(t.getName(), t); 141 idLookup.put(t.getId(), t); 142 } 143 } 144 } 145 146 private List<IUPACTable> parseTables() { 147 List<IUPACTable> localTables = new ArrayList<IUPACTable>(); 148 List<String> lines = IOUtils.getList(is); 149 Integer id = null; 150 String name, aa, starts, baseone, basetwo, basethree; 151 name = aa = starts = baseone = basetwo = basethree = null; 152 for (String line : lines) { 153 if (line.equalsIgnoreCase("//")) { 154 localTables.add(new IUPACTable(name, id, aa, starts, baseone, basetwo, 155 basethree)); 156 name = aa = starts = baseone = basetwo = basethree = null; 157 id = null; 158 } 159 else { 160 String[] keyValue = line.split("\\s*=\\s*"); 161 if (keyValue[0].equals("AAs")) { 162 aa = keyValue[1]; 163 } 164 else if (keyValue[0].equals("Starts")) { 165 starts = keyValue[1]; 166 } 167 else if (keyValue[0].equals("Base1")) { 168 baseone = keyValue[1]; 169 } 170 else if (keyValue[0].equals("Base2")) { 171 basetwo = keyValue[1]; 172 } 173 else if (keyValue[0].equals("Base3")) { 174 basethree = keyValue[1]; 175 } 176 else { 177 name = keyValue[0]; 178 id = Integer.parseInt(keyValue[1]); 179 } 180 } 181 } 182 183 return localTables; 184 } 185 186 /** 187 * Holds the concept of a codon table from the IUPAC format 188 * 189 * @author Andy Yates 190 */ 191 public static class IUPACTable implements Table { 192 193 private final Integer id; 194 private final String name; 195 private final String aminoAcidString; 196 private final String startCodons; 197 private final String baseOne; 198 private final String baseTwo; 199 private final String baseThree; 200 201 private final List<Codon> codons = new ArrayList<Codon>(); 202 private CompoundSet<Codon> compounds = null; 203 204 public IUPACTable(String name, int id, String aminoAcidString, 205 String startCodons, String baseOne, String baseTwo, String baseThree) { 206 this.aminoAcidString = aminoAcidString; 207 this.startCodons = startCodons; 208 this.name = name; 209 this.id = id; 210 this.baseOne = baseOne; 211 this.baseTwo = baseTwo; 212 this.baseThree = baseThree; 213 } 214 215 /** 216 * Constructor which uses the basic IUPAC codon table format. Useful 217 * if you need to specify your own IUPAC table with minimal 218 * definitions from your side. 219 */ 220 public IUPACTable(String name, Integer id, String aminoAcidString, 221 String startCodons) { 222 this(name, id, aminoAcidString, startCodons, 223 "TTTTTTTTTTTTTTTTCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAGGGGGGGGGGGGGGGG", 224 "TTTTCCCCAAAAGGGGTTTTCCCCAAAAGGGGTTTTCCCCAAAAGGGGTTTTCCCCAAAAGGGG", 225 "TCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAGTCAG"); 226 } 227 228 public Integer getId() { 229 return id; 230 } 231 232 public String getName() { 233 return name; 234 } 235 236 /** 237 * Returns true if the given compound was a start codon in this 238 * codon table. This will report true if the compound could ever have 239 * been a start codon. 240 * 241 * @throws IllegalStateException Thrown if 242 * {@link #getCodons(CompoundSet, CompoundSet)} was not called first. 243 */ 244 @Override 245 public boolean isStart(AminoAcidCompound compound) throws IllegalStateException { 246 if(this.codons.isEmpty()) { 247 throw new IllegalStateException("Codons are empty; please request getCodons() fist before asking this"); 248 } 249 for(Codon codon: codons) { 250 //Only check if the codon was a start codon and then ask if the compound was encoded by it 251 if(codon.isStart()) { 252 if(codon.getAminoAcid().equalsIgnoreCase(compound)) { 253 return true; 254 } 255 } 256 } 257 return false; 258 } 259 260 /** 261 * Returns a list of codons where the source and target compounds 262 * are the same as those given by the parameters. 263 * 264 * @param nucleotides The nucleotide set to use when building BioJava 265 * representations of codons 266 * @param aminoAcids The target amino acid compounds objects 267 */ 268 @Override 269 public List<Codon> getCodons(CompoundSet<NucleotideCompound> nucelotides, 270 CompoundSet<AminoAcidCompound> aminoAcids) { 271 272 if (this.codons.isEmpty()) { 273 List<String> aminoAcidStrings = aminoAcids(); 274 List<String> startCodonStrings = startCodons(); 275 List<List<String>> codonStrings = codonStrings(); 276 277 for (int i = 0; i < aminoAcidStrings.size(); i++) { 278 279 List<String> codonString = codonStrings.get(i); 280 NucleotideCompound one = getCompound(codonString, 0, nucelotides); 281 NucleotideCompound two = getCompound(codonString, 1, nucelotides); 282 NucleotideCompound three = getCompound(codonString, 2, nucelotides); 283 boolean start = ("M".equals(startCodonStrings.get(i))); 284 boolean stop = ("*".equals(aminoAcidStrings.get(i))); 285 AminoAcidCompound aminoAcid = aminoAcids 286 .getCompoundForString(aminoAcidStrings.get(i)); 287 codons.add(new Codon(new CaseInsensitiveTriplet(one, two, three), aminoAcid, start, stop)); 288 } 289 } 290 291 return codons; 292 } 293 294 private NucleotideCompound getCompound(List<String> compounds, 295 int position, CompoundSet<NucleotideCompound> nucelotides) { 296 String compound = compounds.get(position); 297 NucleotideCompound returnCompound = nucelotides 298 .getCompoundForString(compound); 299 if (returnCompound == null) { 300 if ("T".equalsIgnoreCase(compound)) { 301 returnCompound = nucelotides.getCompoundForString("U"); 302 } 303 else { 304 throw new ParserException("Cannot find a compound for string " 305 + compound); 306 } 307 } 308 return returnCompound; 309 } 310 311 /** 312 * Returns the compound set of codons 313 */ 314 @Override 315 public CompoundSet<Codon> getCodonCompoundSet( 316 final CompoundSet<NucleotideCompound> rnaCompounds, 317 final CompoundSet<AminoAcidCompound> aminoAcidCompounds) { 318 if (compounds == null) { 319 compounds = new AbstractCompoundSet<Codon>() { 320 { 321 for (Codon c : getCodons(rnaCompounds, aminoAcidCompounds)) { 322 addCompound(c); 323 } 324 } 325 }; 326 } 327 return compounds; 328 } 329 330 private List<List<String>> codonStrings() { 331 List<List<String>> codons = new ArrayList<List<String>>(); 332 for (int i = 0; i < baseOne.length(); i++) { 333 List<String> codon = Arrays.asList(Character 334 .toString(baseOne.charAt(i)), 335 Character.toString(baseTwo.charAt(i)), Character.toString(baseThree 336 .charAt(i))); 337 codons.add(codon); 338 } 339 return codons; 340 } 341 342 private List<String> aminoAcids() { 343 return split(aminoAcidString); 344 } 345 346 private List<String> startCodons() { 347 return split(startCodons); 348 } 349 350 private List<String> split(String string) { 351 List<String> split = new ArrayList<String>(); 352 for (int i = 0; i < string.length(); i++) { 353 split.add(Character.toString(string.charAt(i))); 354 } 355 return split; 356 } 357 } 358}