(no commit message)
[jalview.git] / forester / java / src / org / forester / phylogeny / data / Sequence.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.phylogeny.data;
27
28 import java.io.IOException;
29 import java.io.Writer;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.SortedSet;
33 import java.util.TreeSet;
34
35 import org.forester.io.parsers.nhx.NHXtags;
36 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
37 import org.forester.io.parsers.phyloxml.PhyloXmlMapping;
38 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
39 import org.forester.io.writers.PhylogenyWriter;
40 import org.forester.sequence.MolecularSequence;
41 import org.forester.sequence.MolecularSequence.TYPE;
42 import org.forester.util.ForesterUtil;
43
44 public class Sequence implements PhylogenyData, MultipleUris, Comparable<Sequence> {
45
46     private Accession              _accession;
47     private SortedSet<Annotation>  _annotations;
48     private DomainArchitecture     _da;
49     private String                 _gene_name;
50     private String                 _location;
51     private String                 _mol_sequence;
52     private boolean                _mol_sequence_is_aligned;
53     private String                 _name;
54     private List<SequenceRelation> _seq_relations;
55     private String                 _source_id;
56     private String                 _symbol;
57     private String                 _type;
58     private List<Uri>              _uris;
59     private SortedSet<Accession>   _xrefs;
60
61     public Sequence() {
62         init();
63     }
64
65     public Sequence( final MolecularSequence mol_seq ) {
66         init();
67         setMolecularSequence( mol_seq.getMolecularSequenceAsString() );
68         setName( mol_seq.getIdentifier() );
69         String type;
70         if ( mol_seq.getType() == TYPE.AA ) {
71             type = "protein";
72         }
73         else if ( mol_seq.getType() == TYPE.DNA ) {
74             type = "dna";
75         }
76         else if ( mol_seq.getType() == TYPE.RNA ) {
77             type = "rna";
78         }
79         else {
80             throw new IllegalArgumentException( "unknown sequence type " + mol_seq.getType() );
81         }
82         try {
83             setType( type );
84         }
85         catch ( final PhyloXmlDataFormatException e ) {
86             throw new IllegalArgumentException( "don't know how to handle type " + mol_seq.getType() );
87         }
88     }
89
90     public void addAnnotation( final Annotation annotation ) {
91         getAnnotations().add( annotation );
92     }
93
94     public void addCrossReference( final Accession cross_reference ) {
95         if ( getCrossReferences() == null ) {
96             setCrossReferences( new TreeSet<Accession>() );
97         }
98         getCrossReferences().add( cross_reference );
99     }
100
101     public void addSequenceRelation( final SequenceRelation sr ) {
102         getSequenceRelations().add( sr );
103     }
104
105     @Override
106     public void addUri( final Uri uri ) {
107         if ( getUris() == null ) {
108             setUris( new ArrayList<Uri>() );
109         }
110         getUris().add( uri );
111     }
112
113     @Override
114     public StringBuffer asSimpleText() {
115         final StringBuffer sb = new StringBuffer();
116         if ( getAccession() != null ) {
117             sb.append( "[" );
118             sb.append( getAccession() );
119             sb.append( "] " );
120         }
121         if ( !ForesterUtil.isEmpty( getName() ) ) {
122             sb.append( getName() );
123             sb.append( " " );
124         }
125         if ( !ForesterUtil.isEmpty( getLocation() ) ) {
126             sb.append( getLocation() );
127         }
128         return sb;
129     }
130
131     @Override
132     public StringBuffer asText() {
133         return asSimpleText();
134     }
135
136     @Override
137     public int compareTo( final Sequence o ) {
138         if ( ( !ForesterUtil.isEmpty( getName() ) ) && ( !ForesterUtil.isEmpty( o.getName() ) ) ) {
139             return getName().compareTo( o.getName() );
140         }
141         if ( ( !ForesterUtil.isEmpty( getSymbol() ) ) && ( !ForesterUtil.isEmpty( o.getSymbol() ) ) ) {
142             return getSymbol().compareTo( o.getSymbol() );
143         }
144         if ( ( !ForesterUtil.isEmpty( getGeneName() ) ) && ( !ForesterUtil.isEmpty( o.getGeneName() ) ) ) {
145             return getGeneName().compareTo( o.getGeneName() );
146         }
147         if ( ( getAccession() != null ) && ( o.getAccession() != null )
148                 && !ForesterUtil.isEmpty( getAccession().getValue() )
149                 && !ForesterUtil.isEmpty( o.getAccession().getValue() ) ) {
150             return getAccession().getValue().compareTo( o.getAccession().getValue() );
151         }
152         if ( ( !ForesterUtil.isEmpty( getMolecularSequence() ) )
153                 && ( !ForesterUtil.isEmpty( o.getMolecularSequence() ) ) ) {
154             return getMolecularSequence().compareTo( o.getMolecularSequence() );
155         }
156         return 0;
157     }
158
159     /**
160      * Not a deep copy.
161      *
162      */
163     @Override
164     public PhylogenyData copy() {
165         final Sequence seq = new Sequence();
166         seq.setAnnotations( getAnnotations() );
167         seq.setName( getName() );
168         seq.setGeneName( getGeneName() );
169         try {
170             seq.setSymbol( getSymbol() );
171         }
172         catch ( final PhyloXmlDataFormatException e ) {
173             e.printStackTrace();
174         }
175         seq.setMolecularSequence( getMolecularSequence() );
176         seq.setMolecularSequenceAligned( isMolecularSequenceAligned() );
177         seq.setLocation( getLocation() );
178         if ( getAccession() != null ) {
179             seq.setAccession( ( Accession ) getAccession().copy() );
180         }
181         else {
182             seq.setAccession( null );
183         }
184         try {
185             seq.setType( getType() );
186         }
187         catch ( final PhyloXmlDataFormatException e ) {
188             e.printStackTrace();
189         }
190         if ( getUris() != null ) {
191             seq.setUris( new ArrayList<Uri>() );
192             for( final Uri uri : getUris() ) {
193                 if ( uri != null ) {
194                     seq.getUris().add( uri );
195                 }
196             }
197         }
198         if ( getDomainArchitecture() != null ) {
199             seq.setDomainArchitecture( ( DomainArchitecture ) getDomainArchitecture().copy() );
200         }
201         else {
202             seq.setDomainArchitecture( null );
203         }
204         if ( getCrossReferences() != null ) {
205             seq.setCrossReferences( new TreeSet<Accession>() );
206             for( final Accession x : getCrossReferences() ) {
207                 if ( x != null ) {
208                     seq.getCrossReferences().add( x );
209                 }
210             }
211         }
212         return seq;
213     }
214
215     @Override
216     public boolean equals( final Object o ) {
217         if ( this == o ) {
218             return true;
219         }
220         else if ( o == null ) {
221             return false;
222         }
223         else if ( o.getClass() != this.getClass() ) {
224             throw new IllegalArgumentException( "attempt to check [" + this.getClass() + "] equality to " + o + " ["
225                     + o.getClass() + "]" );
226         }
227         else {
228             return isEqual( ( Sequence ) o );
229         }
230     }
231
232     public Accession getAccession() {
233         return _accession;
234     }
235
236     public Annotation getAnnotation( final int i ) {
237         return ( Annotation ) getAnnotations().toArray()[ i ];
238     }
239
240     public SortedSet<Annotation> getAnnotations() {
241         if ( _annotations == null ) {
242             _annotations = new TreeSet<Annotation>();
243         }
244         return _annotations;
245     }
246
247     public SortedSet<Accession> getCrossReferences() {
248         return _xrefs;
249     }
250
251     public DomainArchitecture getDomainArchitecture() {
252         return _da;
253     }
254
255     public String getGeneName() {
256         return _gene_name;
257     }
258
259     public String getLocation() {
260         return _location;
261     }
262
263     public String getMolecularSequence() {
264         return _mol_sequence;
265     }
266
267     public String getName() {
268         return _name;
269     }
270
271     public List<SequenceRelation> getSequenceRelations() {
272         if ( _seq_relations == null ) {
273             _seq_relations = new ArrayList<SequenceRelation>();
274         }
275         return _seq_relations;
276     }
277
278     public String getSourceId() {
279         return _source_id;
280     }
281
282     public String getSymbol() {
283         return _symbol;
284     }
285
286     public String getType() {
287         return _type;
288     }
289
290     @Override
291     public Uri getUri( final int index ) {
292         return getUris().get( index );
293     }
294
295     @Override
296     public List<Uri> getUris() {
297         return _uris;
298     }
299
300     @Override
301     public int hashCode() {
302         if ( getAccession() != null ) {
303             return getAccession().hashCode();
304         }
305         int result = getName().hashCode();
306         if ( getSymbol().length() > 0 ) {
307             result ^= getName().hashCode();
308         }
309         if ( getGeneName().length() > 0 ) {
310             result ^= getGeneName().hashCode();
311         }
312         if ( getMolecularSequence().length() > 0 ) {
313             result ^= getMolecularSequence().hashCode();
314         }
315         return result;
316     }
317
318     public boolean hasSequenceRelations() {
319         return _seq_relations.size() > 0;
320     }
321
322     public void init() {
323         setName( "" );
324         setGeneName( "" );
325         setMolecularSequence( "" );
326         setMolecularSequenceAligned( false );
327         setLocation( "" );
328         setAccession( null );
329         try {
330             setSymbol( "" );
331         }
332         catch ( final PhyloXmlDataFormatException e ) {
333             e.printStackTrace();
334         }
335         try {
336             setType( "" );
337         }
338         catch ( final PhyloXmlDataFormatException e ) {
339             e.printStackTrace();
340         }
341         setDomainArchitecture( null );
342         setUris( null );
343         setSequenceRelations( null );
344         setSourceId( null );
345         setCrossReferences( null );
346         setAnnotations( null );
347     }
348
349     public boolean isEmpty() {
350         return ( getAccession() == null ) && ForesterUtil.isEmpty( getName() ) && ForesterUtil.isEmpty( getSymbol() )
351                 && ForesterUtil.isEmpty( getGeneName() ) && ForesterUtil.isEmpty( getType() )
352                 && ForesterUtil.isEmpty( getLocation() ) && ForesterUtil.isEmpty( getSourceId() )
353                 && ForesterUtil.isEmpty( getMolecularSequence() ) && ( getDomainArchitecture() == null )
354                 && ForesterUtil.isEmpty( _annotations ) && ForesterUtil.isEmpty( _uris )
355                 && ForesterUtil.isEmpty( _seq_relations )
356                 && ( ( getCrossReferences() == null ) || getCrossReferences().isEmpty() );
357     }
358
359     @Override
360     public boolean isEqual( final PhylogenyData data ) {
361         if ( this == data ) {
362             return true;
363         }
364         final Sequence s = ( Sequence ) data;
365         if ( ( getAccession() != null ) && ( s.getAccession() != null ) ) {
366             return getAccession().isEqual( s.getAccession() );
367         }
368         return s.getMolecularSequence().equals( getMolecularSequence() ) && s.getName().equals( getName() )
369                 && s.getSymbol().equals( getSymbol() ) && s.getGeneName().equals( getGeneName() );
370     }
371
372     public boolean isMolecularSequenceAligned() {
373         return _mol_sequence_is_aligned;
374     }
375
376     public void setAccession( final Accession accession ) {
377         _accession = accession;
378     }
379
380     public void setDomainArchitecture( final DomainArchitecture ds ) {
381         _da = ds;
382     }
383
384     public void setGeneName( final String gene_name ) {
385         _gene_name = gene_name;
386     }
387
388     public void setLocation( final String description ) {
389         _location = description;
390     }
391
392     public void setMolecularSequence( final String mol_sequence ) {
393         _mol_sequence = mol_sequence;
394     }
395
396     public void setMolecularSequenceAligned( final boolean aligned ) {
397         _mol_sequence_is_aligned = aligned;
398     }
399
400     public void setName( final String name ) {
401         _name = name;
402     }
403
404     public void setSourceId( final String source_id ) {
405         _source_id = source_id;
406     }
407
408     public void setSymbol( final String symbol ) throws PhyloXmlDataFormatException {
409         if ( !ForesterUtil.isEmpty( symbol ) && !PhyloXmlUtil.SEQUENCE_SYMBOL_PATTERN.matcher( symbol ).matches() ) {
410             throw new PhyloXmlDataFormatException( "illegal sequence symbol: [" + symbol + "]" );
411         }
412         _symbol = symbol;
413     }
414
415     public void setType( final String type ) throws PhyloXmlDataFormatException {
416         if ( !ForesterUtil.isEmpty( type ) && !PhyloXmlUtil.SEQUENCE_TYPES.contains( type ) ) {
417             throw new PhyloXmlDataFormatException( "illegal sequence type: [" + type + "]" );
418         }
419         _type = type;
420     }
421
422     @Override
423     public void setUris( final List<Uri> uris ) {
424         _uris = uris;
425     }
426
427     @Override
428     public StringBuffer toNHX() {
429         final StringBuffer sb = new StringBuffer();
430         if ( getName().length() > 0 ) {
431             sb.append( ":" );
432             sb.append( NHXtags.GENE_NAME );
433             sb.append( ForesterUtil.replaceIllegalNhxCharacters( getName() ) );
434         }
435         if ( getAccession() != null ) {
436             getAccession().toNHX();
437         }
438         return sb;
439     }
440
441     @Override
442     public void toPhyloXML( final Writer writer, final int level, final String indentation ) throws IOException {
443         if ( isEmpty() ) {
444             return;
445         }
446         final String my_ind = indentation + PhylogenyWriter.PHYLO_XML_INTENDATION_BASE;
447         writer.write( ForesterUtil.LINE_SEPARATOR );
448         writer.write( indentation );
449         PhylogenyDataUtil.appendOpen( writer, PhyloXmlMapping.SEQUENCE, PhyloXmlMapping.SEQUENCE_TYPE, getType() );
450         if ( !ForesterUtil.isEmpty( getSymbol() ) ) {
451             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_SYMBOL, getSymbol(), indentation );
452         }
453         if ( ( getAccession() != null ) && !ForesterUtil.isEmpty( getAccession().getValue() ) ) {
454             getAccession().toPhyloXML( writer, level, indentation );
455         }
456         if ( !ForesterUtil.isEmpty( getName() ) ) {
457             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_NAME, getName(), indentation );
458         }
459         if ( !ForesterUtil.isEmpty( getGeneName() ) ) {
460             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_GENE_NAME, getGeneName(), indentation );
461         }
462         if ( !ForesterUtil.isEmpty( getLocation() ) ) {
463             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_LOCATION, getLocation(), indentation );
464         }
465         if ( !ForesterUtil.isEmpty( getMolecularSequence() ) ) {
466             PhylogenyDataUtil.appendElement( writer,
467                                              PhyloXmlMapping.SEQUENCE_MOL_SEQ,
468                                              getMolecularSequence(),
469                                              PhyloXmlMapping.SEQUENCE_MOL_SEQ_ALIGNED_ATTR,
470                                              String.valueOf( isMolecularSequenceAligned() ),
471                                              indentation );
472         }
473         if ( ( getUris() != null ) && !getUris().isEmpty() ) {
474             for( final Uri uri : getUris() ) {
475                 if ( uri != null ) {
476                     uri.toPhyloXML( writer, level, indentation );
477                 }
478             }
479         }
480         if ( ( getAnnotations() != null ) && !getAnnotations().isEmpty() ) {
481             for( final PhylogenyData annotation : getAnnotations() ) {
482                 annotation.toPhyloXML( writer, level, my_ind );
483             }
484         }
485         if ( ( getCrossReferences() != null ) && !getCrossReferences().isEmpty() ) {
486             writer.write( ForesterUtil.LINE_SEPARATOR );
487             writer.write( my_ind );
488             PhylogenyDataUtil.appendOpen( writer, PhyloXmlMapping.SEQUENCE_X_REFS );
489             for( final PhylogenyData x : getCrossReferences() ) {
490                 x.toPhyloXML( writer, level, my_ind );
491             }
492             writer.write( ForesterUtil.LINE_SEPARATOR );
493             writer.write( my_ind );
494             PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.SEQUENCE_X_REFS );
495         }
496         if ( getDomainArchitecture() != null ) {
497             getDomainArchitecture().toPhyloXML( writer, level, my_ind );
498         }
499         writer.write( ForesterUtil.LINE_SEPARATOR );
500         writer.write( indentation );
501         PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.SEQUENCE );
502     }
503
504     @Override
505     public String toString() {
506         return asText().toString();
507     }
508
509     private void setAnnotations( final SortedSet<Annotation> annotations ) {
510         _annotations = annotations;
511     }
512
513     private void setCrossReferences( final TreeSet<Accession> cross_references ) {
514         _xrefs = cross_references;
515     }
516
517     private void setSequenceRelations( final List<SequenceRelation> seq_relations ) {
518         _seq_relations = seq_relations;
519     }
520 }