1c17c0594903916d5f5d56b92948583abfbebe26
[jalview.git] / src / jalview / io / GenBankFile.java
1 package jalview.io;
2
3 import jalview.datamodel.Alignment;
4 import jalview.datamodel.AlignmentI;
5 import jalview.datamodel.DBRefEntry;
6 import jalview.datamodel.DBRefSource;
7 import jalview.datamodel.Mapping;
8 import jalview.datamodel.Sequence;
9 import jalview.datamodel.SequenceFeature;
10 import jalview.datamodel.SequenceI;
11 import jalview.io.xdb.genbank.GenBankFeature;
12 import jalview.io.xdb.genbank.GenBankLocation;
13 import jalview.io.xdb.genbank.GenBankLocationPoint;
14 import jalview.io.xdb.genbank.GenBankLocationRange;
15 import jalview.io.xdb.genbank.GenBankLocations;
16 import jalview.io.xdb.genbank.GenBankLocus;
17 import jalview.io.xdb.genbank.GenBankReference;
18 import jalview.io.xdb.genbank.GenBankSequence;
19 import jalview.io.xdb.genbank.GenBankSource;
20 import jalview.io.xdb.genbank.GenBankVersion;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Enumeration;
25 import java.util.Hashtable;
26 import java.util.List;
27 import java.util.Vector;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32
33 import org.apache.james.mime4j.field.ParsedField;
34
35 public class GenBankFile extends AlignFile
36 {
37   private static final Logger log = Logger.getLogger(GenBankFile.class
38           .getName());
39
40   private GenBankVersion version = new GenBankVersion();
41
42   private GenBankLocus locus = new GenBankLocus();
43
44   private GenBankSource source = new GenBankSource();
45
46   private static final Pattern patLocation = Pattern
47           .compile("(\\d+)\\.\\.(\\d+)");
48
49   private static final Pattern patLocationComp = Pattern
50           .compile("(complement)\\((\\d+)\\.\\.(\\d+)\\)");
51
52   private static final Pattern patLocus = Pattern
53           .compile("^LOCUS +([a-z|A-Z|0-9|_]+) +([0-9]+) bp ( {3}|ss\\-|ds\\-|ms\\-)([a-z|A-Z|-|\\s]+) ([a-z| ]{8}) ([A-Z| ]{3}) ([0-9]+-[A-Z]+-[0-9]+)");
54
55   private static final Pattern patQualifierKey = Pattern.compile("/(.*?)=");
56
57   private static final Pattern patFeatureKey = Pattern
58           .compile("^\\s{5}([A-Za-z0-9\\_\\']+)\\s+");
59
60   private String definition;
61
62   private String accession;
63
64   private String keywords;
65
66   private String dblink;
67
68   private String baseCount;
69
70   private Vector<GenBankFeature> features;
71
72   private Vector<String> comments;
73
74   // Items under origin
75   private Vector<GenBankSequence> sequences;
76
77   private Vector<GenBankReference> references;
78
79   private SequenceI genBankSequence;
80
81   public GenBankFile()
82   {
83   }
84
85   public GenBankFile(String inFile, String type) throws IOException
86   {
87     super(inFile, type);
88   }
89
90   public GenBankFile(FileParse source) throws IOException
91   {
92     super(source);
93   }
94
95   public void initData()
96   {
97     super.initData();
98     features = new Vector<GenBankFeature>();
99     comments = new Vector<String>();
100     sequences = new Vector<GenBankSequence>();
101     references = new Vector<GenBankReference>();
102   }
103
104   public void parse() throws IOException
105   {
106     String line;
107     boolean featureMode = false; // FEATURES found
108     boolean seqMode = false; // Parsing Sequences from SOURCE
109     boolean referenceMode = false; // REFERENCE found
110     boolean sourceMode = false; // SOURCE found
111     boolean commentMode = false; // COMMENT found
112     boolean parsingAuthors = false; // Parsing authors (multiline)
113     boolean parsingDefinition = false; // Parsing definition (multiline)
114     boolean parsingKeywords = false; // Parsing keywords (multiline)
115     boolean parsingDbLink = false; // Parsing DBLINK (multiline)
116     boolean parsingTitle = false; // Parsing title (multiline)
117     boolean parsingQualifier = false; // Parsing feature qualifier (multine)
118     String currentQualifierName = "";
119     GenBankReference reference = null;
120     GenBankFeature feature = null;
121     List<String> sourceLines = new ArrayList<String>();
122
123     if (this.isValid())
124     {
125
126       while ((line = nextLine()) != null)
127       {
128         // We only process lines if they have contents within
129         if (line.length() == 0)
130           continue;
131
132         if (line.startsWith("FEATURES"))
133         {
134           featureMode = true;
135           seqMode = false;
136           referenceMode = false;
137           sourceMode = false;
138           commentMode = false;
139           feature = new GenBankFeature();
140           source = parseSource(sourceLines);
141         }
142
143         if (seqMode)
144         {
145           if (!line.startsWith("//"))
146           {
147             GenBankSequence seq = processSequenceLine(line);
148             sequences.add(seq);
149           }
150           featureMode = false;
151           referenceMode = false;
152           sourceMode = false;
153         }
154
155         if (line.startsWith("ORIGIN"))
156         {
157           if (feature.getType() != null)
158             features.add(feature);
159           featureMode = false;
160           referenceMode = false;
161           sourceMode = false;
162           seqMode = true;
163         }
164
165         if (featureMode)
166         {
167           // Process feature line
168           if (!line.startsWith("FEATURES")
169                   && !line.startsWith("BASE COUNT"))
170           {
171             // Parse type
172             if (!line.trim().startsWith("/"))
173             {
174               Matcher featuresMatch = patFeatureKey.matcher(line);
175               if (featuresMatch.find())
176               {
177                 if (feature.getType() != null)
178                   features.add(feature); // Hay que a�adirlo s�lo si no se est�
179                                          // a mitad de un qualif o una feature
180                 // It's a feature
181                 String type = featuresMatch.group(0);
182                 feature = new GenBankFeature();
183                 feature.setType(type);
184                 GenBankLocation loc = parserFeatureLocation(feature,
185                         line.replace(type, ""));
186                 feature.setLocation(loc);
187                 parsingQualifier = false;
188                 continue;
189               }
190               else if (parsingQualifier)
191               { // If not a feature, it's another part of a qualifier
192                 String qValue = feature.getQualifier(currentQualifierName);
193                 StringBuffer sb = new StringBuffer().append(qValue).append(
194                         ltrim(line));
195                 feature.updateQualifier(currentQualifierName, sb.toString());
196                 continue;
197               }
198             }
199             else
200             {
201               // It's the begining of a qualifier line
202               Matcher matcher = patQualifierKey.matcher(line);
203               if (matcher.find())
204               {
205                 String qName = matcher.group(1);
206                 currentQualifierName = qName.replace("/", "");
207                 line = line.replace(qName, "").replace("/", "")
208                         .replace("=", "");
209                 feature.addQualifier(currentQualifierName, ltrim(line));
210                 parsingQualifier = true;
211                 continue;
212               }
213             }
214           }
215         }
216         // Process REFERENCE line
217         if (line.startsWith("REFERENCE"))
218         {
219           if (!referenceMode)
220           {
221             // This is line is the REFERENCE line
222             referenceMode = true;
223             featureMode = false;
224             sourceMode = false;
225             seqMode = false;
226           }
227           else
228           {
229             // We were at referenceMode, then add current reference to the list
230             // and create a new one
231             references.add(reference);
232           }
233           reference = new GenBankReference();
234           String desc = processReferenceLine(line, "REFERENCE");
235           int[] ranges = parseReferenceDescriptor(desc);
236           reference.setDescriptor(desc);
237           reference.setOrder(ranges[0]);
238           reference.setBegin(ranges[1]);
239           reference.setEnd(ranges[2]);
240           parsingAuthors = false;
241           parsingTitle = false;
242           continue;
243         }
244
245         if (line.startsWith("  AUTHORS"))
246         {
247           if (referenceMode)
248           {
249             reference.setAuthors(processReferenceLine(line, "AUTHORS"));
250             parsingAuthors = true;
251             parsingTitle = false;
252           }
253           continue;
254         }
255         if (line.startsWith("  TITLE"))
256         {
257           if (referenceMode)
258           {
259             reference.setTitle(processReferenceLine(line, "TITLE"));
260             parsingAuthors = false;
261             parsingTitle = true;
262           }
263           continue;
264         }
265         if (line.startsWith("  JOURNAL"))
266         {
267           if (referenceMode)
268           {
269             reference.setJournal(processReferenceLine(line, "JOURNAL"));
270             parsingTitle = false;
271             parsingAuthors = false;
272           }
273           continue;
274         }
275         if (line.startsWith("   PUBMED"))
276         {
277           if (referenceMode)
278           {
279             reference.setPubmed(processReferenceLine(line, "PUBMED"));
280             parsingTitle = false;
281             parsingAuthors = false;
282           }
283           continue;
284         }
285
286         if (line.startsWith("   MEDLINE"))
287         {
288           if (referenceMode)
289           {
290             reference.setMedline(processReferenceLine(line, "MEDLINE"));
291             parsingTitle = false;
292             parsingAuthors = false;
293           }
294           continue;
295         }
296         if (line.startsWith("  REMARK"))
297         {
298           if (referenceMode)
299           {
300             reference.setRemark(processReferenceLine(line, "REMARK"));
301             parsingTitle = false;
302             parsingAuthors = false;
303           }
304           continue;
305         }
306         if (line.startsWith("  CONSRTM"))
307         {
308           if (referenceMode)
309           {
310             reference.setConsortia(processReferenceLine(line, "CONSRTM"));
311             parsingTitle = false;
312             parsingAuthors = false;
313           }
314           continue;
315         }
316
317         if (line.startsWith("SOURCE"))
318         {
319           parsingKeywords = false;
320           sourceMode = true;
321           commentMode = false;
322           if (sourceMode)
323           {
324             sourceLines.add(line);
325           }
326           continue;
327         }
328         if (line.indexOf("ORGANISM") != -1)
329         {
330           if (sourceMode)
331           {
332             sourceLines.add(line);
333             continue;
334           }
335         }
336
337         if (line.startsWith("COMMENT"))
338         {
339           if (reference != null)
340             references.add(reference);
341           commentMode = true;
342           sourceMode = false;
343           referenceMode = false;
344           sourceMode = false;
345           seqMode = false;
346           comments.add(processCommentLine(line));
347           continue;
348         }
349         // Process LOCUS line
350         if (line.startsWith("LOCUS"))
351         {
352           locus = parseLocus(line);
353           continue;
354         }
355         // Process BASE COUNT line
356         if (line.startsWith("BASE COUNT"))
357         {
358           baseCount = processHeaderLine(line, "BASE COUNT");
359           featureMode = false;
360           continue;
361         }
362         // Process DEFINITION line
363         if (line.startsWith("DEFINITION"))
364         {
365           definition = processHeaderLine(line, "DEFINITION");
366           parsingDefinition = true;
367           continue;
368         }
369         // Process ACCESSION line
370         if (line.startsWith("ACCESSION"))
371         {
372           accession = processHeaderLine(line, "ACCESSION");
373           parsingDefinition = false;
374           continue;
375         }
376         // Process VERSION line
377         if (line.startsWith("VERSION"))
378         {
379           version = parseVersion(line);
380           // headers.put("VERSION", processHeaderLine(line,"VERSION"));
381           continue;
382         }
383         // Process DBLINK line
384         if (line.startsWith("DBLINK"))
385         {
386           dblink = processHeaderLine(line, "DBLINK");
387           parsingDbLink = true;
388           continue;
389         }
390         // Process KEYWORDS line
391         if (line.startsWith("KEYWORDS"))
392         {
393           keywords = processHeaderLine(line, "KEYWORDS");
394           parsingKeywords = true;
395           parsingDbLink = false;
396           continue;
397         }
398         if (sourceMode)
399         {
400           sourceLines.add(line);
401           continue;
402         }
403         if (parsingDefinition)
404         {
405           StringBuffer sb = new StringBuffer().append(definition).append(
406                   line);
407           definition = sb.toString();
408           continue;
409         }
410         if (referenceMode && parsingAuthors)
411         {
412           if (reference != null)
413           {
414             StringBuffer authors = new StringBuffer().append(
415                     reference.getAuthors()).append(line);
416             reference.setAuthors(authors.toString());
417           }
418           continue;
419         }
420         if (referenceMode && parsingTitle)
421         {
422           if (reference != null)
423           {
424             StringBuffer title = new StringBuffer().append(
425                     reference.getTitle()).append(line);
426             reference.setTitle(title.toString());
427           }
428           continue;
429         }
430         if (parsingKeywords)
431         {
432           StringBuffer sb = new StringBuffer().append(keywords)
433                   .append(line);
434           keywords = sb.toString();
435           continue;
436         }
437         if (parsingDbLink)
438         {
439           StringBuffer sb = new StringBuffer().append(dblink).append(line);
440           dblink = sb.toString();
441           continue;
442         }
443         if (commentMode)
444         {
445           comments.add(line);
446         }
447       }
448       setEntries();
449     }
450     else
451     {
452       // File is not valid
453       throw new IOException("GenBankFile is not valid.");
454     }
455   }
456
457   protected void setEntries()
458   {
459     StringBuffer result = new StringBuffer();
460     // Mapping GenBank info into Jalview data model
461     genBankSequence = new Sequence(accession,
462             DnaUtils.getNucleotidesFromSequenceVector(sequences));
463     // Mapping DBRefEntry
464     DBRefEntry dbRef = new DBRefEntry();
465     dbRef.setSource(DBRefSource.GENBANK);
466     dbRef.setVersion(version == null ? "" : version.toString());
467     dbRef.setAccessionId(accession);
468     // add map to indicate the sequence is a valid coordinate frame for the
469     // dbref
470     dbRef.setMap(new Mapping(null, new int[]
471     { 1, genBankSequence.getLength() }, new int[]
472     { 1, genBankSequence.getLength() }, 1, 1));
473     genBankSequence.addDBRef(dbRef);
474
475     // add header info as non-positional features
476     // add LOCUS
477     SequenceFeature locusF = new SequenceFeature("LOCUS",
478             (locus == null ? "" : locus.toString()), null, 1,
479             genBankSequence.getLength(), DBRefSource.GENBANK);
480     genBankSequence.addSequenceFeature(locusF);
481     // add DEFNITION
482     SequenceFeature defF = new SequenceFeature("DEFINITION", definition,
483             null, 1, genBankSequence.getLength(), DBRefSource.GENBANK);
484     genBankSequence.addSequenceFeature(defF);
485     // add ACCESSION
486     SequenceFeature accessionF = new SequenceFeature("ACCESSION",
487             accession, null, 1, genBankSequence.getLength(),
488             DBRefSource.GENBANK);
489     genBankSequence.addSequenceFeature(accessionF);
490     // add VERSION
491     SequenceFeature versionF = new SequenceFeature("VERSION",
492             (version == null ? "" : version.toString()), null, 1,
493             genBankSequence.getLength(), DBRefSource.GENBANK);
494     genBankSequence.addSequenceFeature(versionF);
495     // add DBLINK
496     SequenceFeature dblinkF = new SequenceFeature("DBLINK",
497             (dblink == null ? "" : dblink.toString()), null, 1,
498             genBankSequence.getLength(), DBRefSource.GENBANK);
499     genBankSequence.addSequenceFeature(dblinkF);
500     // add KEYWORDS
501     SequenceFeature keywordsF = new SequenceFeature("KEYWORDS", keywords,
502             null, 1, genBankSequence.getLength(), DBRefSource.GENBANK);
503     genBankSequence.addSequenceFeature(keywordsF);
504     // add SOURCE
505     SequenceFeature sourceF = new SequenceFeature("SOURCE",
506             (source == null ? "" : source.toString()), null, 1,
507             genBankSequence.getLength(), DBRefSource.GENBANK);
508     genBankSequence.addSequenceFeature(sourceF);
509     // add BASE COUNT
510     SequenceFeature baseCountF = new SequenceFeature("BASE COUNT",
511             (baseCount == null ? "" : baseCount.toString()), null, 1,
512             genBankSequence.getLength(), DBRefSource.GENBANK);
513     genBankSequence.addSequenceFeature(baseCountF);
514
515     // add literature and database cross references in the file
516     for (GenBankReference gbRef : references)
517     {
518       // They are non-positional features
519       SequenceFeature refFeature = new SequenceFeature("REFERENCE",
520               gbRef.toString(), null, gbRef.getBegin(), gbRef.getEnd(),
521               DBRefSource.GENBANK);
522       genBankSequence.addSequenceFeature(refFeature);
523     }
524     // add COMMENTS
525     if (comments.size() > 0)
526     {
527       StringBuffer sb = new StringBuffer();
528       for (String comment : comments)
529       {
530         sb.append(comment).append(newline);
531       }
532       SequenceFeature commentF = new SequenceFeature("COMMENT",
533               sb.toString(), null, 1, genBankSequence.getLength(),
534               DBRefSource.GENBANK);
535       genBankSequence.addSequenceFeature(commentF);
536     }
537     // Mapping FEATURES
538     for (GenBankFeature feature : features)
539     {
540       if (feature.getType() != null)
541       {
542         SequenceFeature sf = new SequenceFeature();
543         sf.setType(feature.getType());
544         sf.setDescription(feature.getType());
545
546         sf.setBegin(feature.getLocation() == null ? 0 : feature
547                 .getLocation().getMinor());
548         sf.setEnd(feature.getLocation() == null ? 0 : feature.getLocation()
549                 .getMajor());
550         Enumeration<String> names = feature.getQualifiersNames();
551         while (names.hasMoreElements())
552         {
553           String qName = names.nextElement();
554           String qValue = feature.getQualifier(qName);
555           sf.setValue(qName, qValue);
556         }
557         genBankSequence.addSequenceFeature(sf);
558       }
559     }
560     SequenceI[] parsedSeqs = new SequenceI[1];
561     parsedSeqs[0] = genBankSequence;
562     this.setSeqs(parsedSeqs);
563   }
564
565   private GenBankVersion parseVersion(String line)
566   {
567     // VERSION U00096.2 GI:48994873
568     if (line.trim().equalsIgnoreCase("VERSION"))
569     {
570       return null;
571     }
572     else
573     {
574       GenBankVersion ver = new GenBankVersion();
575       String v = line.substring(11, line.indexOf(" ", 12)).trim();
576       ver.setVersion(v);
577       int posGI = line.indexOf("GI:", 11 + v.length());
578       if (posGI > -1)
579       {
580         ver.setGI(line.substring(posGI));
581       }
582       return ver;
583     }
584   }
585
586   private GenBankLocus parseLocus(String line)
587   {
588     GenBankLocus loc = new GenBankLocus();
589     Matcher mat = patLocus.matcher(line);
590     if (mat.find())
591     {
592       String name = mat.group(1);
593       String len = mat.group(2);
594       String strand = mat.group(3);
595       String mtype = mat.group(4);
596       String linear = mat.group(5);
597       String division = mat.group(6);
598       String date = mat.group(7);
599
600       loc.setName(name == null ? "" : name.trim());
601       loc.setSequenceLength(len == null ? 0 : Integer.parseInt(len));
602       loc.setStrand(strand == null ? "" : strand);
603       loc.setMoleculeType(mtype == null ? "" : mtype);
604       loc.setLinearSequence("linear".equals(linear));
605       loc.setDivision(division == null ? "" : division);
606       loc.setModificationDate(date == null ? "" : date);
607     }
608     return loc;
609   }
610
611   private GenBankSource parseSource(List<String> lines)
612   {
613     StringBuffer sb = new StringBuffer();
614     for (String line : lines)
615     {
616       sb.append(line).append(newline);
617     }
618     // Source section
619     GenBankSource sou = new GenBankSource();
620     String aux = sb.toString().substring(11);
621     int fim1 = aux.indexOf("\n");
622     if (fim1 > -1)
623     {
624       sou.setSource(aux.substring(0, fim1));
625       int ini2 = aux.indexOf("ORGANISM");
626       if (ini2 > -1)
627       {
628         fim1 = aux.indexOf("\n", ini2 + 10);
629         if (fim1 > -1)
630         {
631           sou.setOrganism(aux.substring(ini2 + 10, fim1));
632           sou.setTaxonomic(aux.substring(fim1)
633                   .replaceAll("            ", "").replaceAll("\\s+", ""));
634         }
635         else
636         {
637           sou.setOrganism(aux);
638         }
639       }
640     }
641     else
642     {
643       sou.setSource(aux);
644     }
645     return sou;
646   }
647
648   /**
649    * Possible situations:
650    * 
651    * 467 Points to a single base in the presented sequence 340..565 Points to a
652    * continuous range of bases bounded by and including the starting and ending
653    * bases &lt;345..500 Indicates that the exact lower boundary point of a
654    * feature is unknown. The location begins at some base previous to the first
655    * base specified (which need not be contained in the presented sequence) and
656    * continues to and includes the ending base &lt;1..888 The feature starts
657    * before the first sequenced base and continues to and includes base 888
658    * 1..&gt;888 The feature starts at the first sequenced base and continues
659    * beyond base 888 102.110 Indicates that the exact location is unknown but
660    * that it is one of the bases between bases 102 and 110, inclusive 123^124
661    * Points to a site between bases 123 and 124 join(12..78,134..202) Regions 12
662    * to 78 and 134 to 202 should be joined to form one contiguous sequence
663    * complement(34..126) Start at the base complementary to 126 and finish at
664    * the base complementary to base 34 (the feature is on the strand
665    * complementary to the presented strand)
666    * complement(join(2691..4571,4918..5163)) Joins regions 2691 to 4571 and 4918
667    * to 5163, then complements the joined segments (the feature is on the strand
668    * complementary to the presented strand)
669    * join(complement(4918..5163),complement(2691..4571)) Complements regions
670    * 4918 to 5163 and 2691 to 4571, then joins the complemented segments (the
671    * feature is on the strand complementary to the presented strand)
672    * J00194.1:100..202 Points to bases 100 to 202, inclusive, in the entry (in
673    * this database) with primary accession number 'J00194'
674    * join(1..100,J00194.1:100..202) Joins region 1..100 of the existing entry
675    * with the region 100..202 of remote entry J00194
676    * 
677    * @param fea
678    * @param localiza
679    */
680   private GenBankLocation parserFeatureLocation(GenBankFeature fea,
681           String localiza)
682   {
683     // remove os espaços, quebra de linhas etc
684     String buf = localiza.replaceAll("\\s", "");
685
686     // checks if there is a comma present between ranges
687     // complement(100..110),complement(90..100)
688     char[] buf2 = buf.toCharArray();
689     int abertos = 0;
690     java.util.List<String> lista = new java.util.ArrayList<String>();
691     int pinicial = 0;
692     for (int i = 0; i < buf2.length; i++)
693     {
694       if (buf2[i] == '(')
695       {
696         abertos++;
697       }
698       else if (buf2[i] == ')')
699       {
700         abertos--;
701       }
702       else if (buf2[i] == ',' && abertos == 0)
703       {
704         lista.add(buf.substring(pinicial, i));
705         pinicial = i + 1;
706       }
707     }
708     if (lista.size() > 0)
709     {
710       lista.add(buf.substring(pinicial));
711       GenBankLocations um = new GenBankLocations();
712       um.setOperator(GenBankLocations.NONE);
713       for (String s : lista)
714       {
715         um.getUnits().add(parserFeatureLocation(fea, s));
716       }
717       fea.setLocation(um);
718       return um;
719     }
720
721     // trata as funcoes: complement(location,location...),
722     // join(location,location...), order(location,location...)
723     if (buf.contains("("))
724     {
725       GenBankLocations um = new GenBankLocations();
726       int ini = buf.indexOf("(");
727       int fim = buf.lastIndexOf(")");
728       String token = buf.substring(0, ini);
729       if ("complement".equalsIgnoreCase(token))
730       {
731         String inter = buf.substring(ini + 1, fim);
732         GenBankLocation interno = parserFeatureLocation(fea, inter);
733         interno.setComplement(true);
734         um.setOperator(GenBankLocations.COMPLEMENT);
735         um.getUnits().add(interno);
736         fea.setLocation(um);
737       }
738       else if ("join".equalsIgnoreCase(token))
739       {
740         String inter = buf.substring(ini + 1, fim);
741         GenBankLocation interno = parserFeatureLocation(fea, inter);
742         um.setOperator(GenBankLocations.JOIN);
743         um.getUnits().add(interno);
744         fea.setLocation(um);
745       }
746       else if ("order".equalsIgnoreCase(token))
747       {
748         String inter = buf.substring(ini + 1, fim);
749         GenBankLocation interno = parserFeatureLocation(fea, inter);
750         um.setOperator(GenBankLocations.ORDER);
751         um.getUnits().add(interno);
752         fea.setLocation(um);
753       }
754       else
755       {
756         log.log(Level.WARNING,
757                 "Token desconhecido em location/features - {0}", token);
758         String inter = buf.substring(ini + 1, fim);
759         fea.setLocation(parserFeatureLocation(fea, inter));
760       }
761       return fea.getLocation();
762     }
763     else
764     {
765       // trata quando tiver uma lista de location
766       if (buf.contains(","))
767       {
768         String[] partes = buf.split(",");
769         GenBankLocations um = new GenBankLocations();
770         for (String p : partes)
771         {
772           um.getUnits().add(parserFeatureLocation(fea, p));
773         }
774         fea.setLocation(um);
775         return um;
776       }
777       else
778       {
779         // trata quando tiver range
780         if (buf.contains(".."))
781         {
782           String[] partes = buf.split("\\.\\.");
783           GenBankLocationRange range = new GenBankLocationRange();
784           if (buf.contains(":"))
785           {
786             for (int i = 0; i < partes.length; i++)
787             {
788               int pos = partes[i].indexOf(":");
789               if (pos > 0)
790               {
791                 String entry = partes[i].substring(0, pos);
792                 partes[i] = partes[i].substring(pos + 1);
793                 range.setEntry(entry);
794               }
795             }
796           }
797           GenBankLocationPoint gp0 = (GenBankLocationPoint) parserFeatureLocation(
798                   fea, partes[0]);
799           range.setStart(gp0);
800           GenBankLocationPoint gp1 = (GenBankLocationPoint) parserFeatureLocation(
801                   fea, partes[1]);
802           range.setEnd(gp1);
803           fea.setLocation(range);
804           return range;
805         }
806         else
807         {
808           // trata um ponto
809           // possibilidades consideradas:
810           // 467
811           // 102.110
812           // 123^124
813           // <345
814           // >400
815           // 345>
816           // 400<
817           // ou uma combinacao dessas
818           GenBankLocationPoint gp = new GenBankLocationPoint();
819           if (buf.contains(":"))
820           {
821             int pos = buf.indexOf(":");
822             if (pos > 0)
823             {
824               String entry = buf.substring(0, pos);
825               buf = buf.substring(pos + 1);
826               gp.setEntry(entry);
827             }
828           }
829           int pos = 0;
830           // verifica os simb < e > antes do primeiro numero
831           if (buf.charAt(pos) == '<' || buf.charAt(pos) == '>')
832           {
833             gp.setPrefix(buf.charAt(pos));
834             pos++;
835           }
836           // pega o primeiro numero
837           int ini = pos;
838           while (pos < buf.length() && buf.charAt(pos) >= '0'
839                   && buf.charAt(pos) <= '9')
840           {
841             pos++;
842           }
843           if (buf.subSequence(ini, pos).length() < 1)
844           {
845             System.out.println(localiza);
846           }
847           int num = Integer.parseInt(buf.substring(ini, pos));
848           int num2 = num;
849           // o primeiro numero pode ser o unico numero
850           if (pos < buf.length())
851           {
852             // verifica se tem os sinais < e > apos o primeiro numero
853             if (buf.charAt(pos) == '<' || buf.charAt(pos) == '>')
854             {
855               if (buf.contains(".") || buf.contains("^"))
856               {
857                 gp.setPrefix(buf.charAt(pos));
858               }
859               else
860               {
861                 gp.setSufix(buf.charAt(pos));
862               }
863               pos++;
864             }
865
866             // verifica a separacao dos numeros . ou ^
867             if (pos < buf.length()
868                     && (buf.charAt(pos) == '.' || buf.charAt(pos) == '^'))
869             {
870               // separação localizada, possibilidade de mais numero
871               gp.setSymbol(buf.charAt(pos));
872               pos++;
873
874               // verifica os simb < e > antes do segundo numero
875               if (buf.charAt(pos) == '<' || buf.charAt(pos) == '>')
876               {
877                 gp.setSufix(buf.charAt(pos));
878                 pos++;
879               }
880
881               // pega o segundo numero
882               ini = pos;
883               while (pos < buf.length() && buf.charAt(pos) >= '0'
884                       && buf.charAt(pos) <= '9')
885               {
886                 pos++;
887               }
888               num2 = Integer.parseInt(buf.substring(ini, pos));
889
890               // verifica os simb < e > após o segundo numero
891               if (pos < buf.length()
892                       && (buf.charAt(pos) == '<' || buf.charAt(pos) == '>'))
893               {
894                 gp.setSufix(buf.charAt(pos));
895                 pos++;
896               }
897             }
898           }
899           gp.setMin(num);
900           gp.setMax(num2);
901           fea.setLocation(gp);
902           return gp;
903         }
904       }
905     }
906   }
907
908   private int[] parseReferenceDescriptor(String descriptor)
909   {
910     // 1 (bases 1 to 1609)
911     int[] resultado = new int[3];
912     descriptor = descriptor.replace("(bases", ",").replace("to", ",")
913             .replace(")", "");
914     String[] args = descriptor.split(",");
915     resultado[0] = Integer.parseInt(args[0].trim());
916     resultado[1] = Integer.parseInt(args[1].trim());
917     resultado[2] = Integer.parseInt(args[2].trim());
918     return resultado;
919   }
920
921   private String processReferenceLine(String line, String component)
922   {
923     int init = line.indexOf(component);
924     if (init != -1)
925     {
926       line = line.replace(component, "");
927     }
928     return line;
929   }
930
931   private String processHeaderLine(String line, String header)
932   {
933     int init = line.indexOf(header);
934     if (init != -1)
935     {
936       line = line.replace(header, "");
937     }
938     return line;
939   }
940
941   private GenBankSequence processSequenceLine(String line)
942   {
943     GenBankSequence gbs = new GenBankSequence();
944     line = ltrim(line);
945     String[] args = line.split(" ");
946     gbs.setId(Integer.parseInt(args[0]));
947     int len = args.length - 1;
948     Vector<String> seqs = new Vector<String>();
949     for (int i = 0; i < len; i++)
950       seqs.add(args[i + 1]);
951     gbs.setSequences(seqs);
952     return gbs;
953   }
954
955   private String processCommentLine(String line)
956   {
957     int init = line.indexOf("COMMENT");
958     if (init != -1)
959     {
960       line = line.replace("COMMENT", "");
961     }
962     return line;
963   }
964
965   public String rtrim(String s)
966   {
967     int i = s.length() - 1;
968     while (i >= 0 && Character.isWhitespace(s.charAt(i)))
969     {
970       i--;
971     }
972     return s.substring(0, i + 1);
973   }
974
975   public String ltrim(String s)
976   {
977     int i = 0;
978     while (i < s.length() && Character.isWhitespace(s.charAt(i)))
979     {
980       i++;
981     }
982     return s.substring(i);
983   }
984
985   public String print()
986   {
987     StringBuffer out = new StringBuffer();
988     for (SequenceI seq : this.getSeqs())
989     {
990       SequenceFeature[] seqFeatures = seq.getSequenceFeatures();
991       boolean featureLinePrinted = false;
992       for (SequenceFeature sf : seqFeatures)
993       {
994         if (sf.getType().equals("LOCUS"))
995         {
996           out.append(sf.getDescription()).append(newline);
997         }
998         else if (sf.getType().equals("DEFINITION"))
999         {
1000           out.append("DEFINITION  ").append(sf.getDescription())
1001                   .append(newline);
1002         }
1003         else if (sf.getType().equals("VERSION"))
1004         {
1005           out.append("VERSION     ").append(sf.getDescription())
1006                   .append(newline);
1007         }
1008         else if (sf.getType().equals("ACCESSION"))
1009         {
1010           out.append("ACCESSION  ").append(sf.getDescription())
1011                   .append(newline);
1012         }
1013         else if (sf.getType().equals("DBLINK"))
1014         {
1015           out.append("DBLINK ").append(sf.getDescription()).append(newline);
1016         }
1017         else if (sf.getType().equals("KEYWORDS"))
1018         {
1019           out.append("KEYWORDS  ").append(sf.getDescription())
1020                   .append(newline);
1021         }
1022         else if (sf.getType().equals("SOURCE"))
1023         {
1024           out.append("SOURCE      ").append(sf.getDescription())
1025                   .append(newline);
1026         }
1027         else if (sf.getType().equals("REFERENCE"))
1028         {
1029           out.append(sf.getDescription()).append(newline);
1030         }
1031         else if (sf.getType().equals("COMMENT"))
1032         {
1033           out.append("COMMENT     ").append(sf.getDescription())
1034                   .append(newline);
1035         }
1036         else if (sf.getType().equals("BASE COUNT"))
1037         {
1038           out.append("BASE COUNT     ").append(sf.getDescription())
1039                   .append(newline);
1040         }
1041         else
1042         {
1043           if (!featureLinePrinted)
1044           {
1045             out.append("FEATURES             Location/Qualifiers").append(
1046                     newline);
1047             featureLinePrinted = true;
1048           }
1049           out.append("     ").append(sf.getType()).append("          ")
1050                   .append(sf.getBegin()).append("..").append(sf.getEnd())
1051                   .append(newline);
1052           Hashtable<String, String> qualifiers = sf.otherDetails;
1053           if (qualifiers != null)
1054           {
1055             Enumeration<String> keys = qualifiers.keys();
1056             while (keys.hasMoreElements())
1057             {
1058               String key = keys.nextElement();
1059               String value = qualifiers.get(key);
1060               if (value != null)
1061               {
1062                 out.append("                     /").append(key)
1063                         .append("=").append(value).append(newline);
1064               }
1065             }
1066           }
1067         }
1068       }
1069       out.append("ORIGIN").append(newline);
1070       // We have to divide sequence in groups of 6x10 chars
1071       String sequenceString = seq.getSequenceAsString();
1072       int howManyGroups = (int) Math.floor(sequenceString.length() / 60);
1073       for (int i = 0; i <= howManyGroups; i++)
1074       {
1075         String sequenceSegment = sequenceString.substring(i * 60,
1076                 Math.min((i + 1) * 60, sequenceString.length()));
1077         if ((!"".equals(sequenceSegment) && (sequenceSegment != null) && (sequenceSegment
1078                 .length() > 0)))
1079         {
1080           out.append("        ").append(60 * i + 1).append(" ");
1081         }
1082         int segmentLength = sequenceSegment.length();
1083         if (segmentLength >= 10)
1084         {
1085           out.append(sequenceSegment.substring(0, 10)).append(" ");
1086           if (segmentLength >= 20)
1087           {
1088             out.append(sequenceSegment.substring(10, 20)).append(" ");
1089             if (segmentLength >= 30)
1090             {
1091               out.append(sequenceSegment.substring(20, 30)).append(" ");
1092               if (segmentLength >= 40)
1093               {
1094                 out.append(sequenceSegment.substring(30, 40)).append(" ");
1095                 if (segmentLength >= 50)
1096                 {
1097                   out.append(sequenceSegment.substring(40, 50)).append(" ");
1098                   if (segmentLength <= 60)
1099                   {
1100                     out.append(sequenceSegment.substring(50,
1101                             sequenceSegment.length()));
1102                   }
1103                 }
1104                 else
1105                 {
1106                   out.append(sequenceSegment.substring(40,
1107                           sequenceSegment.length()));
1108                 }
1109               }
1110               else
1111               {
1112                 out.append(sequenceSegment.substring(30,
1113                         sequenceSegment.length()));
1114               }
1115             }
1116             else
1117             {
1118               out.append(sequenceSegment.substring(20,
1119                       sequenceSegment.length()));
1120             }
1121           }
1122           else
1123           {
1124             out.append(sequenceSegment.substring(10,
1125                     sequenceSegment.length()));
1126           }
1127         }
1128         else if ((!"".equals(sequenceSegment) && (sequenceSegment != null) && (sequenceSegment
1129                 .length() > 0)))
1130         {
1131           out.append(sequenceSegment);
1132         }
1133         out.append(newline);
1134       }
1135       out.append("//");
1136     }
1137     return out.toString();
1138   }
1139 }