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