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