in progress
[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.util.ForesterUtil;
41
42 public class Sequence implements PhylogenyData, MultipleUris, Comparable<Sequence> {
43
44     private Accession              _accession;
45     private SortedSet<Annotation>  _annotations;
46     private DomainArchitecture     _da;
47     private String                 _gene_name;
48     private String                 _location;
49     private String                 _mol_sequence;
50     private boolean                _mol_sequence_is_aligned;
51     private String                 _name;
52     private List<SequenceRelation> _seq_relations;
53     private String                 _source_id;
54     private String                 _symbol;
55     private String                 _type;
56     private List<Uri>              _uris;
57     private SortedSet<Accession>   _xrefs;
58
59     public Sequence() {
60         init();
61     }
62
63     public void addAnnotation( final Annotation annotation ) {
64         getAnnotations().add( annotation );
65     }
66
67     public void addCrossReference( final Accession cross_reference ) {
68         if ( getCrossReferences() == null ) {
69             setCrossReferences( new TreeSet<Accession>() );
70         }
71         getCrossReferences().add( cross_reference );
72     }
73
74     public void addSequenceRelation( final SequenceRelation sr ) {
75         getSequenceRelations().add( sr );
76     }
77
78     @Override
79     public void addUri( final Uri uri ) {
80         if ( getUris() == null ) {
81             setUris( new ArrayList<Uri>() );
82         }
83         getUris().add( uri );
84     }
85
86     @Override
87     public StringBuffer asSimpleText() {
88         final StringBuffer sb = new StringBuffer();
89         if ( getAccession() != null ) {
90             sb.append( "[" );
91             sb.append( getAccession() );
92             sb.append( "] " );
93         }
94         if ( !ForesterUtil.isEmpty( getName() ) ) {
95             sb.append( getName() );
96             sb.append( " " );
97         }
98         if ( !ForesterUtil.isEmpty( getLocation() ) ) {
99             sb.append( getLocation() );
100         }
101         return sb;
102     }
103
104     @Override
105     public StringBuffer asText() {
106         return asSimpleText();
107     }
108
109     @Override
110     public int compareTo( final Sequence o ) {
111         if ( ( !ForesterUtil.isEmpty( getName() ) ) && ( !ForesterUtil.isEmpty( o.getName() ) ) ) {
112             return getName().compareTo( o.getName() );
113         }
114         if ( ( !ForesterUtil.isEmpty( getSymbol() ) ) && ( !ForesterUtil.isEmpty( o.getSymbol() ) ) ) {
115             return getSymbol().compareTo( o.getSymbol() );
116         }
117         if ( ( !ForesterUtil.isEmpty( getGeneName() ) ) && ( !ForesterUtil.isEmpty( o.getGeneName() ) ) ) {
118             return getGeneName().compareTo( o.getGeneName() );
119         }
120         if ( ( getAccession() != null ) && ( o.getAccession() != null )
121                 && !ForesterUtil.isEmpty( getAccession().getValue() )
122                 && !ForesterUtil.isEmpty( o.getAccession().getValue() ) ) {
123             return getAccession().getValue().compareTo( o.getAccession().getValue() );
124         }
125         if ( ( !ForesterUtil.isEmpty( getMolecularSequence() ) )
126                 && ( !ForesterUtil.isEmpty( o.getMolecularSequence() ) ) ) {
127             return getMolecularSequence().compareTo( o.getMolecularSequence() );
128         }
129         return 0;
130     }
131
132     /**
133      * Not a deep copy.
134      * 
135      */
136     @Override
137     public PhylogenyData copy() {
138         final Sequence seq = new Sequence();
139         seq.setAnnotations( getAnnotations() );
140         seq.setName( getName() );
141         seq.setGeneName( getGeneName() );
142         try {
143             seq.setSymbol( getSymbol() );
144         }
145         catch ( final PhyloXmlDataFormatException e ) {
146             e.printStackTrace();
147         }
148         seq.setMolecularSequence( getMolecularSequence() );
149         seq.setMolecularSequenceAligned( isMolecularSequenceAligned() );
150         seq.setLocation( getLocation() );
151         if ( getAccession() != null ) {
152             seq.setAccession( ( Accession ) getAccession().copy() );
153         }
154         else {
155             seq.setAccession( null );
156         }
157         try {
158             seq.setType( getType() );
159         }
160         catch ( final PhyloXmlDataFormatException e ) {
161             e.printStackTrace();
162         }
163         if ( getUris() != null ) {
164             seq.setUris( new ArrayList<Uri>() );
165             for( final Uri uri : getUris() ) {
166                 if ( uri != null ) {
167                     seq.getUris().add( uri );
168                 }
169             }
170         }
171         if ( getDomainArchitecture() != null ) {
172             seq.setDomainArchitecture( ( DomainArchitecture ) getDomainArchitecture().copy() );
173         }
174         else {
175             seq.setDomainArchitecture( null );
176         }
177         if ( getCrossReferences() != null ) {
178             seq.setCrossReferences( new TreeSet<Accession>() );
179             for( final Accession x : getCrossReferences() ) {
180                 if ( x != null ) {
181                     seq.getCrossReferences().add( x );
182                 }
183             }
184         }
185         return seq;
186     }
187
188     @Override
189     public boolean equals( final Object o ) {
190         if ( this == o ) {
191             return true;
192         }
193         else if ( o == null ) {
194             return false;
195         }
196         else if ( o.getClass() != this.getClass() ) {
197             throw new IllegalArgumentException( "attempt to check [" + this.getClass() + "] equality to " + o + " ["
198                     + o.getClass() + "]" );
199         }
200         else {
201             return isEqual( ( Sequence ) o );
202         }
203     }
204
205     public Accession getAccession() {
206         return _accession;
207     }
208
209     public Annotation getAnnotation( final int i ) {
210         return ( Annotation ) getAnnotations().toArray()[ i ];
211     }
212
213     public SortedSet<Annotation> getAnnotations() {
214         if ( _annotations == null ) {
215             _annotations = new TreeSet<Annotation>();
216         }
217         return _annotations;
218     }
219
220     public SortedSet<Accession> getCrossReferences() {
221         return _xrefs;
222     }
223
224     public DomainArchitecture getDomainArchitecture() {
225         return _da;
226     }
227
228     public String getGeneName() {
229         return _gene_name;
230     }
231
232     public String getLocation() {
233         return _location;
234     }
235
236     public String getMolecularSequence() {
237         return _mol_sequence;
238     }
239
240     public String getName() {
241         return _name;
242     }
243
244     public List<SequenceRelation> getSequenceRelations() {
245         if ( _seq_relations == null ) {
246             _seq_relations = new ArrayList<SequenceRelation>();
247         }
248         return _seq_relations;
249     }
250
251     public String getSourceId() {
252         return _source_id;
253     }
254
255     public String getSymbol() {
256         return _symbol;
257     }
258
259     public String getType() {
260         return _type;
261     }
262
263     @Override
264     public Uri getUri( final int index ) {
265         return getUris().get( index );
266     }
267
268     @Override
269     public List<Uri> getUris() {
270         return _uris;
271     }
272
273     @Override
274     public int hashCode() {
275         if ( getAccession() != null ) {
276             return getAccession().hashCode();
277         }
278         int result = getName().hashCode();
279         if ( getSymbol().length() > 0 ) {
280             result ^= getName().hashCode();
281         }
282         if ( getGeneName().length() > 0 ) {
283             result ^= getGeneName().hashCode();
284         }
285         if ( getMolecularSequence().length() > 0 ) {
286             result ^= getMolecularSequence().hashCode();
287         }
288         return result;
289     }
290
291     public boolean hasSequenceRelations() {
292         return _seq_relations.size() > 0;
293     }
294
295     public void init() {
296         setName( "" );
297         setGeneName( "" );
298         setMolecularSequence( "" );
299         setMolecularSequenceAligned( false );
300         setLocation( "" );
301         setAccession( null );
302         try {
303             setSymbol( "" );
304         }
305         catch ( final PhyloXmlDataFormatException e ) {
306             e.printStackTrace();
307         }
308         try {
309             setType( "" );
310         }
311         catch ( final PhyloXmlDataFormatException e ) {
312             e.printStackTrace();
313         }
314         setDomainArchitecture( null );
315         setUris( null );
316         setSequenceRelations( null );
317         setSourceId( null );
318         setCrossReferences( null );
319         setAnnotations( null );
320     }
321
322     public boolean isEmpty() {
323         return ( getAccession() == null ) && ForesterUtil.isEmpty( getName() ) && ForesterUtil.isEmpty( getSymbol() )
324                 && ForesterUtil.isEmpty( getGeneName() ) && ForesterUtil.isEmpty( getType() )
325                 && ForesterUtil.isEmpty( getLocation() ) && ForesterUtil.isEmpty( getSourceId() )
326                 && ForesterUtil.isEmpty( getMolecularSequence() ) && ( getDomainArchitecture() == null )
327                 && ForesterUtil.isEmpty( _annotations ) && ForesterUtil.isEmpty( _uris )
328                 && ForesterUtil.isEmpty( _seq_relations )
329                 && ( ( getCrossReferences() == null ) || getCrossReferences().isEmpty() );
330     }
331
332     @Override
333     public boolean isEqual( final PhylogenyData data ) {
334         if ( this == data ) {
335             return true;
336         }
337         final Sequence s = ( Sequence ) data;
338         if ( ( getAccession() != null ) && ( s.getAccession() != null ) ) {
339             return getAccession().isEqual( s.getAccession() );
340         }
341         return s.getMolecularSequence().equals( getMolecularSequence() ) && s.getName().equals( getName() )
342                 && s.getSymbol().equals( getSymbol() ) && s.getGeneName().equals( getGeneName() );
343     }
344
345     public boolean isMolecularSequenceAligned() {
346         return _mol_sequence_is_aligned;
347     }
348
349     public void setAccession( final Accession accession ) {
350         _accession = accession;
351     }
352
353     public void setDomainArchitecture( final DomainArchitecture ds ) {
354         _da = ds;
355     }
356
357     public void setGeneName( final String gene_name ) {
358         _gene_name = gene_name;
359     }
360
361     public void setLocation( final String description ) {
362         _location = description;
363     }
364
365     public void setMolecularSequence( final String mol_sequence ) {
366         _mol_sequence = mol_sequence;
367     }
368
369     public void setMolecularSequenceAligned( final boolean aligned ) {
370         _mol_sequence_is_aligned = aligned;
371     }
372
373     public void setName( final String name ) {
374         _name = name;
375     }
376
377     public void setSourceId( final String source_id ) {
378         _source_id = source_id;
379     }
380
381     public void setSymbol( final String symbol ) throws PhyloXmlDataFormatException {
382         if ( !ForesterUtil.isEmpty( symbol ) && !PhyloXmlUtil.SEQUENCE_SYMBOL_PATTERN.matcher( symbol ).matches() ) {
383             throw new PhyloXmlDataFormatException( "illegal sequence symbol: [" + symbol + "]" );
384         }
385         _symbol = symbol;
386     }
387
388     public void setType( final String type ) throws PhyloXmlDataFormatException {
389         if ( !ForesterUtil.isEmpty( type ) && !PhyloXmlUtil.SEQUENCE_TYPES.contains( type ) ) {
390             throw new PhyloXmlDataFormatException( "illegal sequence type: [" + type + "]" );
391         }
392         _type = type;
393     }
394
395     @Override
396     public void setUris( final List<Uri> uris ) {
397         _uris = uris;
398     }
399
400     @Override
401     public StringBuffer toNHX() {
402         final StringBuffer sb = new StringBuffer();
403         if ( getName().length() > 0 ) {
404             sb.append( ":" );
405             sb.append( NHXtags.GENE_NAME );
406             sb.append( ForesterUtil.replaceIllegalNhxCharacters( getName() ) );
407         }
408         if ( getAccession() != null ) {
409             getAccession().toNHX();
410         }
411         return sb;
412     }
413
414     @Override
415     public void toPhyloXML( final Writer writer, final int level, final String indentation ) throws IOException {
416         if ( isEmpty() ) {
417             return;
418         }
419         final String my_ind = indentation + PhylogenyWriter.PHYLO_XML_INTENDATION_BASE;
420         writer.write( ForesterUtil.LINE_SEPARATOR );
421         writer.write( indentation );
422         PhylogenyDataUtil.appendOpen( writer, PhyloXmlMapping.SEQUENCE, PhyloXmlMapping.SEQUENCE_TYPE, getType() );
423         if ( !ForesterUtil.isEmpty( getSymbol() ) ) {
424             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_SYMBOL, getSymbol(), indentation );
425         }
426         if ( ( getAccession() != null ) && !ForesterUtil.isEmpty( getAccession().getValue() ) ) {
427             getAccession().toPhyloXML( writer, level, indentation );
428         }
429         if ( !ForesterUtil.isEmpty( getName() ) ) {
430             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_NAME, getName(), indentation );
431         }
432         if ( !ForesterUtil.isEmpty( getGeneName() ) ) {
433             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_GENE_NAME, getGeneName(), indentation );
434         }
435         if ( !ForesterUtil.isEmpty( getLocation() ) ) {
436             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.SEQUENCE_LOCATION, getLocation(), indentation );
437         }
438         if ( !ForesterUtil.isEmpty( getMolecularSequence() ) ) {
439             PhylogenyDataUtil.appendElement( writer,
440                                              PhyloXmlMapping.SEQUENCE_MOL_SEQ,
441                                              getMolecularSequence(),
442                                              PhyloXmlMapping.SEQUENCE_MOL_SEQ_ALIGNED_ATTR,
443                                              String.valueOf( isMolecularSequenceAligned() ),
444                                              indentation );
445         }
446         if ( ( getUris() != null ) && !getUris().isEmpty() ) {
447             for( final Uri uri : getUris() ) {
448                 if ( uri != null ) {
449                     uri.toPhyloXML( writer, level, indentation );
450                 }
451             }
452         }
453         if ( ( getAnnotations() != null ) && !getAnnotations().isEmpty() ) {
454             for( final PhylogenyData annotation : getAnnotations() ) {
455                 annotation.toPhyloXML( writer, level, my_ind );
456             }
457         }
458         if ( ( getCrossReferences() != null ) && !getCrossReferences().isEmpty() ) {
459             writer.write( ForesterUtil.LINE_SEPARATOR );
460             writer.write( my_ind );
461             PhylogenyDataUtil.appendOpen( writer, PhyloXmlMapping.SEQUENCE_X_REFS );
462             for( final PhylogenyData x : getCrossReferences() ) {
463                 x.toPhyloXML( writer, level, my_ind );
464             }
465             writer.write( ForesterUtil.LINE_SEPARATOR );
466             writer.write( my_ind );
467             PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.SEQUENCE_X_REFS );
468         }
469         if ( getDomainArchitecture() != null ) {
470             getDomainArchitecture().toPhyloXML( writer, level, my_ind );
471         }
472         writer.write( ForesterUtil.LINE_SEPARATOR );
473         writer.write( indentation );
474         PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.SEQUENCE );
475     }
476
477     @Override
478     public String toString() {
479         return asText().toString();
480     }
481
482     private void setAnnotations( final SortedSet<Annotation> annotations ) {
483         _annotations = annotations;
484     }
485
486     private void setCrossReferences( final TreeSet<Accession> cross_references ) {
487         _xrefs = cross_references;
488     }
489
490     private void setSequenceRelations( final List<SequenceRelation> seq_relations ) {
491         _seq_relations = seq_relations;
492     }
493 }