improving GSDI, under construction...
[jalview.git] / forester / java / src / org / forester / phylogeny / data / Taxonomy.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: www.phylosoft.org/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
33 import org.forester.io.parsers.nhx.NHXtags;
34 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
35 import org.forester.io.parsers.phyloxml.PhyloXmlMapping;
36 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
37 import org.forester.util.ForesterUtil;
38
39 public class Taxonomy implements PhylogenyData, MultipleUris, Comparable<Taxonomy> {
40
41     private String       _scientific_name;
42     private String       _common_name;
43     private List<String> _synonyms;
44     private String       _authority;
45     private Identifier   _identifier;
46     private String       _taxonomy_code;
47     private String       _rank;
48     private List<Uri>    _uris;
49     private List<String> _lineage;
50
51     public Taxonomy() {
52         init();
53     }
54
55     @Override
56     public StringBuffer asSimpleText() {
57         return asText();
58     }
59
60     @Override
61     public Uri getUri( final int index ) {
62         return getUris().get( index );
63     }
64
65     @Override
66     public void addUri( final Uri uri ) {
67         if ( getUris() == null ) {
68             setUris( new ArrayList<Uri>() );
69         }
70         getUris().add( uri );
71     }
72
73     @Override
74     public StringBuffer asText() {
75         final StringBuffer sb = new StringBuffer();
76         if ( getIdentifier() != null ) {
77             sb.append( "[" );
78             sb.append( getIdentifier().asSimpleText() );
79             sb.append( "]" );
80         }
81         if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
82             if ( sb.length() > 0 ) {
83                 sb.append( " " );
84             }
85             sb.append( "[" );
86             sb.append( getTaxonomyCode() );
87             sb.append( "]" );
88         }
89         if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
90             if ( sb.length() > 0 ) {
91                 sb.append( " " );
92             }
93             sb.append( getScientificName() );
94             if ( !ForesterUtil.isEmpty( getAuthority() ) ) {
95                 sb.append( " (" );
96                 sb.append( getAuthority() );
97                 sb.append( ")" );
98             }
99         }
100         if ( !ForesterUtil.isEmpty( getCommonName() ) ) {
101             if ( sb.length() > 0 ) {
102                 sb.append( " " );
103             }
104             sb.append( getCommonName() );
105         }
106         return sb;
107     }
108
109     @Override
110     public PhylogenyData copy() {
111         final Taxonomy t = new Taxonomy();
112         t.setTaxonomyCode( getTaxonomyCode() );
113         t.setScientificName( getScientificName() );
114         t.setCommonName( getCommonName() );
115         t.setAuthority( getAuthority() );
116         for( final String syn : getSynonyms() ) {
117             t.getSynonyms().add( syn );
118         }
119         if ( getIdentifier() != null ) {
120             t.setIdentifier( ( Identifier ) getIdentifier().copy() );
121         }
122         else {
123             t.setIdentifier( null );
124         }
125         t.setRank( new String( getRank() ) );
126         if ( getUris() != null ) {
127             t.setUris( new ArrayList<Uri>() );
128             for( final Uri uri : getUris() ) {
129                 if ( uri != null ) {
130                     t.getUris().add( uri );
131                 }
132             }
133         }
134         if ( getLineage() != null ) {
135             t.setLineage( new ArrayList<String>() );
136             for( final String l : getLineage() ) {
137                 if ( l != null ) {
138                     t.getLineage().add( l );
139                 }
140             }
141         }
142         return t;
143     }
144
145     @Override
146     public boolean equals( final Object o ) {
147         if ( this == o ) {
148             return true;
149         }
150         else if ( o == null ) {
151             return false;
152         }
153         else if ( o.getClass() != this.getClass() ) {
154             throw new IllegalArgumentException( "attempt to check [" + this.getClass() + "] equality to " + o + " ["
155                     + o.getClass() + "]" );
156         }
157         else {
158             return isEqual( ( Taxonomy ) o );
159         }
160     }
161
162     public String getAuthority() {
163         return _authority;
164     }
165
166     public String getCommonName() {
167         return _common_name;
168     }
169
170     public Identifier getIdentifier() {
171         return _identifier;
172     }
173
174     public String getRank() {
175         return _rank;
176     }
177
178     public String getScientificName() {
179         return _scientific_name;
180     }
181
182     public List<String> getSynonyms() {
183         if ( _synonyms == null ) {
184             _synonyms = new ArrayList<String>();
185         }
186         return _synonyms;
187     }
188
189     public String getTaxonomyCode() {
190         return _taxonomy_code;
191     }
192
193     @Override
194     public List<Uri> getUris() {
195         return _uris;
196     }
197
198     @Override
199     public int hashCode() {
200         if ( ( getIdentifier() != null ) && !ForesterUtil.isEmpty( getIdentifier().getValue() ) ) {
201             return getIdentifier().hashCode();
202         }
203         else if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
204             return getTaxonomyCode().hashCode();
205         }
206         else if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
207             if ( !ForesterUtil.isEmpty( getAuthority() ) ) {
208                 return ( getScientificName().toLowerCase() + getAuthority().toLowerCase() ).hashCode();
209             }
210             return getScientificName().toLowerCase().hashCode();
211         }
212         else {
213             return getCommonName().toLowerCase().hashCode();
214         }
215     }
216
217     public void init() {
218         setScientificName( "" );
219         setCommonName( "" );
220         setIdentifier( null );
221         setRank( "" );
222         setTaxonomyCode( "" );
223         setAuthority( "" );
224         setSynonyms( null );
225         setUris( null );
226         setLineage( null );
227     }
228
229     public boolean isEmpty() {
230         return ( ( getIdentifier() == null ) && ForesterUtil.isEmpty( getTaxonomyCode() )
231                 && ForesterUtil.isEmpty( getCommonName() ) && ForesterUtil.isEmpty( getScientificName() )
232                 && ForesterUtil.isEmpty( getRank() ) && ForesterUtil.isEmpty( _uris )
233                 && ForesterUtil.isEmpty( getAuthority() ) && ForesterUtil.isEmpty( _synonyms ) && ForesterUtil
234                 .isEmpty( _lineage ) );
235     }
236
237     /**
238      * 
239      * If this and taxonomy 'data' has an identifier, comparison will be based on that.
240      * Otherwise,  if this and taxonomy 'data' has a code, comparison will be based on that.
241      * Otherwise,  if Taxonomy 'data' has a scientific name, comparison will be
242      * based on that (case insensitive!).
243      * Otherwise,  if Taxonomy 'data' has a common  name, comparison will be
244      * based on that (case insensitive!).
245      * (Note. This is important and should not be change without a very good reason.)
246      * 
247      */
248     @Override
249     public boolean isEqual( final PhylogenyData data ) {
250         if ( this == data ) {
251             return true;
252         }
253         final Taxonomy tax = ( Taxonomy ) data;
254         if ( ( getIdentifier() != null ) && ( tax.getIdentifier() != null )
255                 && !ForesterUtil.isEmpty( getIdentifier().getValue() )
256                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
257             return getIdentifier().isEqual( tax.getIdentifier() );
258         }
259         else if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) && !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
260             return getTaxonomyCode().equals( tax.getTaxonomyCode() );
261         }
262         else if ( !ForesterUtil.isEmpty( getScientificName() ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
263             if ( !ForesterUtil.isEmpty( getAuthority() ) && !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
264                 return ( getScientificName().equalsIgnoreCase( tax.getScientificName() ) )
265                         && ( getAuthority().equalsIgnoreCase( tax.getAuthority() ) );
266             }
267             return getScientificName().equalsIgnoreCase( tax.getScientificName() );
268         }
269         else if ( !ForesterUtil.isEmpty( getCommonName() ) && !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
270             return getCommonName().equalsIgnoreCase( tax.getCommonName() );
271         }
272         else if ( !ForesterUtil.isEmpty( getScientificName() ) && !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
273             return getScientificName().equalsIgnoreCase( tax.getCommonName() );
274         }
275         else if ( !ForesterUtil.isEmpty( getCommonName() ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
276             return getCommonName().equalsIgnoreCase( tax.getScientificName() );
277         }
278         throw new RuntimeException( "comparison not possible with empty fields" );
279     }
280
281     public void setAuthority( final String authority ) {
282         _authority = authority;
283     }
284
285     public void setCommonName( final String common_name ) {
286         _common_name = common_name;
287     }
288
289     public void setIdentifier( final Identifier identifier ) {
290         _identifier = identifier;
291     }
292
293     public void setRank( final String rank ) {
294         if ( !ForesterUtil.isEmpty( rank ) && !PhyloXmlUtil.TAXONOMY_RANKS_SET.contains( rank ) ) {
295             throw new PhyloXmlDataFormatException( "illegal rank: [" + rank + "]" );
296         }
297         _rank = rank;
298     }
299
300     public void setScientificName( final String scientific_name ) {
301         _scientific_name = scientific_name;
302     }
303
304     private void setSynonyms( final List<String> synonyms ) {
305         _synonyms = synonyms;
306     }
307
308     public void setTaxonomyCode( final String taxonomy_code ) {
309         if ( !ForesterUtil.isEmpty( taxonomy_code )
310                 && !PhyloXmlUtil.TAXOMONY_CODE_PATTERN.matcher( taxonomy_code ).matches() ) {
311             throw new PhyloXmlDataFormatException( "illegal taxonomy code: [" + taxonomy_code + "]" );
312         }
313         _taxonomy_code = taxonomy_code;
314     }
315
316     @Override
317     public void setUris( final List<Uri> uris ) {
318         _uris = uris;
319     }
320
321     @Override
322     public StringBuffer toNHX() {
323         final StringBuffer sb = new StringBuffer();
324         if ( getIdentifier() != null ) {
325             sb.append( ':' + NHXtags.TAXONOMY_ID );
326             sb.append( ForesterUtil.replaceIllegalNhxCharacters( getIdentifier().getValue() ) );
327         }
328         final StringBuffer species = new StringBuffer();
329         if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
330             species.append( ForesterUtil.replaceIllegalNhxCharacters( getTaxonomyCode() ) );
331         }
332         if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
333             ForesterUtil.appendSeparatorIfNotEmpty( species, '|' );
334             species.append( ForesterUtil.replaceIllegalNhxCharacters( getScientificName() ) );
335         }
336         if ( !ForesterUtil.isEmpty( getCommonName() ) ) {
337             ForesterUtil.appendSeparatorIfNotEmpty( species, '|' );
338             species.append( ForesterUtil.replaceIllegalNhxCharacters( getCommonName() ) );
339         }
340         if ( species.length() > 0 ) {
341             sb.append( ':' + NHXtags.SPECIES_NAME );
342             sb.append( species );
343         }
344         return sb;
345     }
346
347     @Override
348     public void toPhyloXML( final Writer writer, final int level, final String indentation ) throws IOException {
349         if ( isEmpty() ) {
350             return;
351         }
352         writer.write( ForesterUtil.LINE_SEPARATOR );
353         writer.write( indentation );
354         PhylogenyDataUtil.appendOpen( writer, PhyloXmlMapping.TAXONOMY );
355         if ( ( getIdentifier() != null ) && !ForesterUtil.isEmpty( getIdentifier().getValue() ) ) {
356             getIdentifier().toPhyloXML( writer, level, indentation );
357         }
358         if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
359             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_CODE, getTaxonomyCode(), indentation );
360         }
361         if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
362             PhylogenyDataUtil.appendElement( writer,
363                                              PhyloXmlMapping.TAXONOMY_SCIENTIFIC_NAME,
364                                              getScientificName(),
365                                              indentation );
366         }
367         if ( !ForesterUtil.isEmpty( getAuthority() ) ) {
368             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_AUTHORITY, getAuthority(), indentation );
369         }
370         if ( !ForesterUtil.isEmpty( getCommonName() ) ) {
371             PhylogenyDataUtil
372                     .appendElement( writer, PhyloXmlMapping.TAXONOMY_COMMON_NAME, getCommonName(), indentation );
373         }
374         if ( _synonyms != null ) {
375             for( final String syn : getSynonyms() ) {
376                 if ( !ForesterUtil.isEmpty( syn ) ) {
377                     PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_SYNONYM, syn, indentation );
378                 }
379             }
380         }
381         if ( !ForesterUtil.isEmpty( getRank() ) ) {
382             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_RANK, getRank(), indentation );
383         }
384         if ( getUris() != null ) {
385             for( final Uri uri : getUris() ) {
386                 if ( uri != null ) {
387                     uri.toPhyloXML( writer, level, indentation );
388                 }
389             }
390         }
391         writer.write( ForesterUtil.LINE_SEPARATOR );
392         writer.write( indentation );
393         PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.TAXONOMY );
394     }
395
396     @Override
397     public String toString() {
398         return asText().toString();
399     }
400
401     @Override
402     public int compareTo( final Taxonomy o ) {
403         if ( equals( o ) ) {
404             return 0;
405         }
406         else if ( !ForesterUtil.isEmpty( getScientificName() ) && !ForesterUtil.isEmpty( o.getScientificName() ) ) {
407             return getScientificName().compareToIgnoreCase( o.getScientificName() );
408         }
409         else if ( !ForesterUtil.isEmpty( getCommonName() ) && !ForesterUtil.isEmpty( o.getCommonName() ) ) {
410             return getCommonName().compareToIgnoreCase( o.getCommonName() );
411         }
412         else if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) && !ForesterUtil.isEmpty( o.getTaxonomyCode() ) ) {
413             return getTaxonomyCode().compareToIgnoreCase( o.getTaxonomyCode() );
414         }
415         return 0;
416     }
417
418     public void setLineage( final List<String> lineage ) {
419         _lineage = lineage;
420     }
421
422     public List<String> getLineage() {
423         return _lineage;
424     }
425 }