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: 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
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         try {
113             t.setTaxonomyCode( getTaxonomyCode() );
114         }
115         catch ( final PhyloXmlDataFormatException e ) {
116             e.printStackTrace();
117         }
118         t.setScientificName( getScientificName() );
119         t.setCommonName( getCommonName() );
120         t.setAuthority( getAuthority() );
121         for( final String syn : getSynonyms() ) {
122             t.getSynonyms().add( syn );
123         }
124         if ( getIdentifier() != null ) {
125             t.setIdentifier( ( Identifier ) getIdentifier().copy() );
126         }
127         else {
128             t.setIdentifier( null );
129         }
130         try {
131             t.setRank( new String( getRank() ) );
132         }
133         catch ( final PhyloXmlDataFormatException e ) {
134             e.printStackTrace();
135         }
136         if ( getUris() != null ) {
137             t.setUris( new ArrayList<Uri>() );
138             for( final Uri uri : getUris() ) {
139                 if ( uri != null ) {
140                     t.getUris().add( uri );
141                 }
142             }
143         }
144         if ( getLineage() != null ) {
145             t.setLineage( new ArrayList<String>() );
146             for( final String l : getLineage() ) {
147                 if ( l != null ) {
148                     t.getLineage().add( l );
149                 }
150             }
151         }
152         return t;
153     }
154
155     @Override
156     public boolean equals( final Object o ) {
157         if ( this == o ) {
158             return true;
159         }
160         else if ( o == null ) {
161             return false;
162         }
163         else if ( o.getClass() != this.getClass() ) {
164             throw new IllegalArgumentException( "attempt to check [" + this.getClass() + "] equality to " + o + " ["
165                     + o.getClass() + "]" );
166         }
167         else {
168             return isEqual( ( Taxonomy ) o );
169         }
170     }
171
172     public String getAuthority() {
173         return _authority;
174     }
175
176     public String getCommonName() {
177         return _common_name;
178     }
179
180     public Identifier getIdentifier() {
181         return _identifier;
182     }
183
184     public String getRank() {
185         return _rank;
186     }
187
188     public String getScientificName() {
189         return _scientific_name;
190     }
191
192     public List<String> getSynonyms() {
193         if ( _synonyms == null ) {
194             _synonyms = new ArrayList<String>();
195         }
196         return _synonyms;
197     }
198
199     public String getTaxonomyCode() {
200         return _taxonomy_code;
201     }
202
203     @Override
204     public List<Uri> getUris() {
205         return _uris;
206     }
207
208     @Override
209     public int hashCode() {
210         if ( ( getIdentifier() != null ) && !ForesterUtil.isEmpty( getIdentifier().getValue() ) ) {
211             return getIdentifier().hashCode();
212         }
213         else if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
214             return getTaxonomyCode().hashCode();
215         }
216         else if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
217             if ( !ForesterUtil.isEmpty( getAuthority() ) ) {
218                 return ( getScientificName().toLowerCase() + getAuthority().toLowerCase() ).hashCode();
219             }
220             return getScientificName().toLowerCase().hashCode();
221         }
222         else {
223             return getCommonName().toLowerCase().hashCode();
224         }
225     }
226
227     public void init() {
228         setScientificName( "" );
229         setCommonName( "" );
230         setIdentifier( null );
231         try {
232             setRank( "" );
233         }
234         catch ( final PhyloXmlDataFormatException e ) {
235             e.printStackTrace();
236         }
237         try {
238             setTaxonomyCode( "" );
239         }
240         catch ( final PhyloXmlDataFormatException e ) {
241             e.printStackTrace();
242         }
243         setAuthority( "" );
244         setSynonyms( null );
245         setUris( null );
246         setLineage( null );
247     }
248
249     public boolean isEmpty() {
250         return ( ( getIdentifier() == null ) && ForesterUtil.isEmpty( getTaxonomyCode() )
251                 && ForesterUtil.isEmpty( getCommonName() ) && ForesterUtil.isEmpty( getScientificName() ) && ForesterUtil
252                     .isEmpty( _lineage ) );
253     }
254
255     /**
256      * 
257      * If this and taxonomy 'data' has an identifier, comparison will be based on that.
258      * Otherwise,  if this and taxonomy 'data' has a code, comparison will be based on that.
259      * Otherwise,  if Taxonomy 'data' has a scientific name, comparison will be
260      * based on that (case insensitive!).
261      * Otherwise,  if Taxonomy 'data' has a common  name, comparison will be
262      * based on that (case insensitive!).
263      * (Note. This is important and should not be change without a very good reason.)
264      * 
265      */
266     @Override
267     public boolean isEqual( final PhylogenyData data ) {
268         if ( this == data ) {
269             return true;
270         }
271         final Taxonomy tax = ( Taxonomy ) data;
272         if ( ( getIdentifier() != null ) && ( tax.getIdentifier() != null )
273                 && !ForesterUtil.isEmpty( getIdentifier().getValue() )
274                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
275             return getIdentifier().isEqual( tax.getIdentifier() );
276         }
277         else if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) && !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
278             return getTaxonomyCode().equals( tax.getTaxonomyCode() );
279         }
280         else if ( !ForesterUtil.isEmpty( getScientificName() ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
281             if ( !ForesterUtil.isEmpty( getAuthority() ) && !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
282                 return ( getScientificName().equalsIgnoreCase( tax.getScientificName() ) )
283                         && ( getAuthority().equalsIgnoreCase( tax.getAuthority() ) );
284             }
285             return getScientificName().equalsIgnoreCase( tax.getScientificName() );
286         }
287         else if ( !ForesterUtil.isEmpty( getCommonName() ) && !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
288             return getCommonName().equalsIgnoreCase( tax.getCommonName() );
289         }
290         //throw new RuntimeException( "comparison not possible with empty fields" );
291         return false;
292     }
293
294     public void setAuthority( final String authority ) {
295         _authority = authority;
296     }
297
298     public void setCommonName( final String common_name ) {
299         _common_name = common_name;
300     }
301
302     public void setIdentifier( final Identifier identifier ) {
303         _identifier = identifier;
304     }
305
306     public void setRank( final String rank ) throws PhyloXmlDataFormatException {
307         if ( !ForesterUtil.isEmpty( rank ) && !PhyloXmlUtil.TAXONOMY_RANKS_SET.contains( rank ) ) {
308             throw new PhyloXmlDataFormatException( "illegal rank: [" + rank + "]" );
309         }
310         _rank = rank;
311     }
312
313     public void setScientificName( final String scientific_name ) {
314         _scientific_name = scientific_name;
315     }
316
317     private void setSynonyms( final List<String> synonyms ) {
318         _synonyms = synonyms;
319     }
320
321     public void setTaxonomyCode( String taxonomy_code ) throws PhyloXmlDataFormatException {
322         if ( !ForesterUtil.isEmpty( taxonomy_code )
323                 && !PhyloXmlUtil.TAXOMONY_CODE_PATTERN.matcher( taxonomy_code ).matches() ) {
324             throw new PhyloXmlDataFormatException( "illegal taxonomy code: [" + taxonomy_code + "]" );
325         }
326         //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
327         //TODO FIXME (added on 13-11-18) remove me eventually
328         if ( taxonomy_code.equals( "ACIBL" ) ) {
329             taxonomy_code = "KORVE";
330         }
331         else if ( taxonomy_code.equals( "PYRKO" ) ) {
332             taxonomy_code = "THEKO";
333         }
334         //TODO FIXME (added on 13-11-18) remove me eventually
335         //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
336         _taxonomy_code = taxonomy_code;
337     }
338
339     @Override
340     public void setUris( final List<Uri> uris ) {
341         _uris = uris;
342     }
343
344     @Override
345     public StringBuffer toNHX() {
346         final StringBuffer sb = new StringBuffer();
347         if ( getIdentifier() != null ) {
348             sb.append( ':' + NHXtags.TAXONOMY_ID );
349             sb.append( ForesterUtil.replaceIllegalNhxCharacters( getIdentifier().getValue() ) );
350         }
351         final StringBuffer species = new StringBuffer();
352         if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
353             species.append( ForesterUtil.replaceIllegalNhxCharacters( getTaxonomyCode() ) );
354         }
355         if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
356             ForesterUtil.appendSeparatorIfNotEmpty( species, '|' );
357             species.append( ForesterUtil.replaceIllegalNhxCharacters( getScientificName() ) );
358         }
359         if ( !ForesterUtil.isEmpty( getCommonName() ) ) {
360             ForesterUtil.appendSeparatorIfNotEmpty( species, '|' );
361             species.append( ForesterUtil.replaceIllegalNhxCharacters( getCommonName() ) );
362         }
363         if ( species.length() > 0 ) {
364             sb.append( ':' + NHXtags.SPECIES_NAME );
365             sb.append( species );
366         }
367         return sb;
368     }
369
370     @Override
371     public void toPhyloXML( final Writer writer, final int level, final String indentation ) throws IOException {
372         if ( isEmpty() ) {
373             return;
374         }
375         writer.write( ForesterUtil.LINE_SEPARATOR );
376         writer.write( indentation );
377         PhylogenyDataUtil.appendOpen( writer, PhyloXmlMapping.TAXONOMY );
378         if ( ( getIdentifier() != null ) && !ForesterUtil.isEmpty( getIdentifier().getValue() ) ) {
379             getIdentifier().toPhyloXML( writer, level, indentation );
380         }
381         if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) ) {
382             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_CODE, getTaxonomyCode(), indentation );
383         }
384         if ( !ForesterUtil.isEmpty( getScientificName() ) ) {
385             PhylogenyDataUtil.appendElement( writer,
386                                              PhyloXmlMapping.TAXONOMY_SCIENTIFIC_NAME,
387                                              getScientificName(),
388                                              indentation );
389         }
390         if ( !ForesterUtil.isEmpty( getAuthority() ) ) {
391             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_AUTHORITY, getAuthority(), indentation );
392         }
393         if ( !ForesterUtil.isEmpty( getCommonName() ) ) {
394             PhylogenyDataUtil
395                     .appendElement( writer, PhyloXmlMapping.TAXONOMY_COMMON_NAME, getCommonName(), indentation );
396         }
397         if ( _synonyms != null ) {
398             for( final String syn : getSynonyms() ) {
399                 if ( !ForesterUtil.isEmpty( syn ) ) {
400                     PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_SYNONYM, syn, indentation );
401                 }
402             }
403         }
404         if ( !ForesterUtil.isEmpty( getRank() ) ) {
405             PhylogenyDataUtil.appendElement( writer, PhyloXmlMapping.TAXONOMY_RANK, getRank(), indentation );
406         }
407         if ( getUris() != null ) {
408             for( final Uri uri : getUris() ) {
409                 if ( uri != null ) {
410                     uri.toPhyloXML( writer, level, indentation );
411                 }
412             }
413         }
414         writer.write( ForesterUtil.LINE_SEPARATOR );
415         writer.write( indentation );
416         PhylogenyDataUtil.appendClose( writer, PhyloXmlMapping.TAXONOMY );
417     }
418
419     @Override
420     public String toString() {
421         return asText().toString();
422     }
423
424     @Override
425     public int compareTo( final Taxonomy o ) {
426         if ( equals( o ) ) {
427             return 0;
428         }
429         if ( ( getIdentifier() != null ) && ( o.getIdentifier() != null )
430                 && !ForesterUtil.isEmpty( getIdentifier().getValue() )
431                 && !ForesterUtil.isEmpty( o.getIdentifier().getValue() ) ) {
432             final int x = getIdentifier().getValuePlusProvider().compareTo( o.getIdentifier().getValuePlusProvider() );
433             if ( x != 0 ) {
434                 return x;
435             }
436         }
437         if ( !ForesterUtil.isEmpty( getScientificName() ) && !ForesterUtil.isEmpty( o.getScientificName() ) ) {
438             return getScientificName().compareToIgnoreCase( o.getScientificName() );
439         }
440         if ( !ForesterUtil.isEmpty( getCommonName() ) && !ForesterUtil.isEmpty( o.getCommonName() ) ) {
441             return getCommonName().compareToIgnoreCase( o.getCommonName() );
442         }
443         if ( !ForesterUtil.isEmpty( getTaxonomyCode() ) && !ForesterUtil.isEmpty( o.getTaxonomyCode() ) ) {
444             return getTaxonomyCode().compareToIgnoreCase( o.getTaxonomyCode() );
445         }
446         if ( ( getIdentifier() != null ) && ( o.getIdentifier() != null )
447                 && !ForesterUtil.isEmpty( getIdentifier().getValue() )
448                 && !ForesterUtil.isEmpty( o.getIdentifier().getValue() ) ) {
449             return getIdentifier().getValuePlusProvider().compareTo( o.getIdentifier().getValuePlusProvider() );
450         }
451         return 1;
452     }
453
454     public void setLineage( final List<String> lineage ) {
455         _lineage = lineage;
456     }
457
458     public List<String> getLineage() {
459         return _lineage;
460     }
461 }