f1ca0e386cb5a53fbf3200e7c1a32735b6e0dd41
[jalview.git] / src / jalview / io / GenBankFile.java
1 package jalview.io;
2
3 import java.io.IOException;
4
5 /**
6  * A class that provides selective parsing of the GenBank flatfile format.
7  * <p>
8  * The initial implementation is limited to extracting fields used by Jalview
9  * after fetching an EMBL or EMBLCDS entry:
10  * 
11  * <pre>
12  * accession, version, sequence, xref
13  * and (for CDS feature) location, protein_id, product, codon_start, translation
14  * </pre>
15  * 
16  * @author gmcarstairs
17  * @see https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html
18  */
19 public class GenBankFile extends EMBLLikeFlatFile
20 {
21   private static final String DEFINITION = "DEFINITION";
22
23   /**
24    * Constructor given a data source and the id of the source database
25    * 
26    * @param fp
27    * @param sourceId
28    * @throws IOException
29    */
30   public GenBankFile(FileParse fp, String sourceId) throws IOException
31   {
32     super(fp, sourceId);
33   }
34
35   /**
36    * Parses the flatfile, and if successful, saves as an annotated sequence
37    * which may be retrieved by calling {@code getSequence()}
38    * 
39    * @throws IOException
40    * @see https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html
41    */
42   @Override
43   public void parse() throws IOException
44   {
45     String line = nextLine();
46     while (line != null)
47     {
48       if (line.startsWith("LOCUS"))
49       {
50         line = parseLocus(line);
51       }
52       else if (line.startsWith(DEFINITION))
53       {
54         line = parseDefinition(line);
55       }
56       else if (line.startsWith("ACCESSION"))
57       {
58         this.accession = line.split(WHITESPACE)[1];
59         line = nextLine();
60       }
61       else if (line.startsWith("VERSION"))
62       {
63         line = parseVersion(line);
64       }
65       else if (line.startsWith("ORIGIN"))
66       {
67         line = parseSequence();
68       }
69       else if (line.startsWith("FEATURES"))
70       {
71         line = nextLine();
72         while (line.startsWith(" "))
73         {
74           line = parseFeature(line);
75         }
76       }
77       else
78       {
79         line = nextLine();
80       }
81     }
82     buildSequence();
83   }
84
85   /**
86    * Extracts and saves the primary accession and version (SV value) from an ID
87    * line, or null if not found. Returns the next line after the one processed.
88    * 
89    * @param line
90    * @throws IOException
91    */
92   String parseLocus(String line) throws IOException
93   {
94     String[] tokens = line.split(WHITESPACE);
95
96     /*
97      * first should be "LOCUS"
98      */
99     if (tokens.length < 2 || !"LOCUS".equals(tokens[0]))
100     {
101       return nextLine();
102     }
103     /*
104      * second is primary accession
105      */
106     String token = tokens[1].trim();
107     if (!token.isEmpty())
108     {
109       this.accession = token;
110     }
111
112     // not going to guess the rest just yet, but third is length with unit (bp)
113
114     return nextLine();
115   }
116
117   /**
118    * Reads sequence description from DEFINITION lines. Any trailing period is
119    * discarded. Returns the next line after the definition line(s).
120    * 
121    * @param line
122    * @return
123    * @throws IOException
124    */
125   String parseDefinition(String line) throws IOException
126   {
127     String desc = line.substring(DEFINITION.length()).trim();
128     if (desc.endsWith("."))
129     {
130       desc = desc.substring(0, desc.length() - 1);
131     }
132
133     /*
134      * pass over any additional DE lines
135      */
136     while ((line = nextLine()) != null)
137     {
138       if (line.startsWith(" "))
139       {
140         // definition continuation line
141         desc += line.trim();
142       }
143       else
144       {
145         break;
146       }
147     }
148     this.description = desc;
149
150     return line;
151   }
152
153   /**
154    * Parses the VERSION line e.g.
155    * 
156    * <pre>
157    * VERSION     X81322.1
158    * </pre>
159    * 
160    * and returns the next line
161    * 
162    * @param line
163    * @throws IOException
164    */
165   String parseVersion(String line) throws IOException
166   {
167     /*
168      * extract version part of <accession>.<version>
169      * https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html#VersionB
170      */
171     String[] tokens = line.split(WHITESPACE);
172     if (tokens.length > 1)
173     {
174       tokens = tokens[1].split("\\.");
175       if (tokens.length > 1)
176       {
177         this.version = tokens[1];
178       }
179     }
180
181     return nextLine();
182   }
183
184   @Override
185   protected boolean isFeatureContinuationLine(String line)
186   {
187     return line.startsWith("      "); // 6 spaces
188   }
189 }