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