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