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