in progress
[jalview.git] / forester / java / src / org / forester / archaeopteryx / NodeEditPanel.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/
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.event.KeyEvent;
29 import java.awt.event.KeyListener;
30 import java.math.BigDecimal;
31 import java.net.URL;
32 import java.text.ParseException;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37
38 import javax.swing.JEditorPane;
39 import javax.swing.JOptionPane;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42 import javax.swing.JSplitPane;
43 import javax.swing.JTree;
44 import javax.swing.event.TreeSelectionEvent;
45 import javax.swing.event.TreeSelectionListener;
46 import javax.swing.text.Position;
47 import javax.swing.tree.DefaultMutableTreeNode;
48 import javax.swing.tree.TreePath;
49 import javax.swing.tree.TreeSelectionModel;
50
51 import org.forester.archaeopteryx.tools.ImageLoader;
52 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
53 import org.forester.phylogeny.PhylogenyNode;
54 import org.forester.phylogeny.data.Accession;
55 import org.forester.phylogeny.data.Confidence;
56 import org.forester.phylogeny.data.Date;
57 import org.forester.phylogeny.data.Distribution;
58 import org.forester.phylogeny.data.Event;
59 import org.forester.phylogeny.data.Identifier;
60 import org.forester.phylogeny.data.MultipleUris;
61 import org.forester.phylogeny.data.PhylogenyData;
62 import org.forester.phylogeny.data.Point;
63 import org.forester.phylogeny.data.Reference;
64 import org.forester.phylogeny.data.Sequence;
65 import org.forester.phylogeny.data.Taxonomy;
66 import org.forester.phylogeny.data.Uri;
67 import org.forester.util.FailedConditionCheckException;
68 import org.forester.util.ForesterUtil;
69
70 class NodeEditPanel extends JPanel {
71
72     private static final long                            serialVersionUID = 5120159904388100771L;
73     private final JTree                                  _tree;
74     private final JEditorPane                            _pane;
75     private final PhylogenyNode                          _my_node;
76     private final TreePanel                              _tree_panel;
77     private final Map<DefaultMutableTreeNode, TagNumber> _map;
78
79     public NodeEditPanel( final PhylogenyNode phylogeny_node, final TreePanel tree_panel ) {
80         _map = new HashMap<DefaultMutableTreeNode, TagNumber>();
81         _my_node = phylogeny_node;
82         _tree_panel = tree_panel;
83         String node_name = "";
84         if ( !ForesterUtil.isEmpty( phylogeny_node.getName() ) ) {
85             node_name = phylogeny_node.getName() + " ";
86         }
87         final DefaultMutableTreeNode top = new DefaultMutableTreeNode( "Node " + node_name );
88         createNodes( top, phylogeny_node );
89         _tree = new JTree( top );
90         getJTree().setEditable( true );
91         getJTree().setFocusable( true );
92         getJTree().setToggleClickCount( 1 );
93         getJTree().setInvokesStopCellEditing( true );
94         final JScrollPane tree_view = new JScrollPane( getJTree() );
95         _pane = new JEditorPane();
96         _pane.setEditable( true );
97         final JScrollPane data_view = new JScrollPane( _pane );
98         final JSplitPane split_pane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
99         split_pane.setTopComponent( tree_view );
100         // split_pane.setBottomComponent( data_view );
101         data_view.setMinimumSize( Constants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
102         tree_view.setMinimumSize( Constants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
103         // split_pane.setDividerLocation( 400 );
104         split_pane.setPreferredSize( Constants.NODE_PANEL_SIZE );
105         add( split_pane );
106         getJTree().getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
107         getJTree().addKeyListener( new KeyListener() {
108
109             @Override
110             public void keyPressed( final KeyEvent e ) {
111                 keyEvent( e );
112             }
113
114             @Override
115             public void keyReleased( final KeyEvent e ) {
116                 keyEvent( e );
117             }
118
119             @Override
120             public void keyTyped( final KeyEvent e ) {
121                 keyEvent( e );
122             }
123         } );
124         for( int i = 0; i < getJTree().getRowCount(); i++ ) {
125             getJTree().expandRow( i );
126         }
127         collapsePath( NodePanel.BASIC );
128         collapsePath( NodePanel.TAXONOMY );
129         collapsePath( NodePanel.SEQUENCE );
130         collapsePath( NodePanel.EVENTS );
131         collapsePath( NodePanel.DATE );
132         collapsePath( NodePanel.DISTRIBUTION );
133         collapsePath( NodePanel.LIT_REFERENCE );
134         getJTree().addTreeSelectionListener( new TreeSelectionListener() {
135
136             @Override
137             public void valueChanged( final TreeSelectionEvent e ) {
138                 final TreePath new_path = e.getNewLeadSelectionPath();
139                 final TreePath old_path = e.getOldLeadSelectionPath();
140                 if ( new_path != null ) {
141                     writeBack( ( DefaultMutableTreeNode ) new_path.getLastPathComponent() );
142                 }
143                 if ( old_path != null ) {
144                     writeBack( ( DefaultMutableTreeNode ) old_path.getLastPathComponent() );
145                 }
146             }
147         } );
148     }
149
150     private void addBasics( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node, final String name ) {
151         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
152         top.add( category );
153         addSubelementEditable( category, NodePanel.NODE_NAME, phylogeny_node.getName(), PHYLOXML_TAG.NODE_NAME );
154         String bl = "";
155         if ( phylogeny_node.getDistanceToParent() != PhylogenyNode.DISTANCE_DEFAULT ) {
156             bl = ForesterUtil.FORMATTER_6.format( phylogeny_node.getDistanceToParent() );
157         }
158         addSubelementEditable( category, NodePanel.NODE_BRANCH_LENGTH, bl, PHYLOXML_TAG.NODE_BRANCH_LENGTH );
159         int counter = 0;
160         if ( phylogeny_node.getBranchData().isHasConfidences() ) {
161             for( int i = phylogeny_node.getBranchData().getConfidences().size() - 1; i >= 0; i-- ) {
162                 if ( phylogeny_node.getBranchData().getConfidences().get( i ).getValue() == Confidence.CONFIDENCE_DEFAULT_VALUE ) {
163                     phylogeny_node.getBranchData().getConfidences().remove( i );
164                 }
165             }
166             for( final PhylogenyData conf : phylogeny_node.getBranchData().getConfidences() ) {
167                 final Confidence my_conf = ( Confidence ) ( conf );
168                 addSubelementEditable( category,
169                                        NodePanel.CONFIDENCE + " [" + counter + "]",
170                                        ForesterUtil.FORMATTER_6.format( my_conf.getValue() ),
171                                        PHYLOXML_TAG.CONFIDENCE_VALUE,
172                                        NodePanel.CONFIDENCE_TYPE,
173                                        my_conf.getType(),
174                                        PHYLOXML_TAG.CONFIDENCE_TYPE,
175                                        counter++ );
176             }
177         }
178         addSubelementEditable( category,
179                                NodePanel.CONFIDENCE + " [" + counter + "]",
180                                "",
181                                PHYLOXML_TAG.CONFIDENCE_VALUE,
182                                NodePanel.CONFIDENCE_TYPE,
183                                "",
184                                PHYLOXML_TAG.CONFIDENCE_TYPE,
185                                counter );
186     }
187
188     //    private void addAnnotation( final DefaultMutableTreeNode top, final Annotation ann, final String name ) {
189     //        DefaultMutableTreeNode category;
190     //        category = new DefaultMutableTreeNode( name );
191     //        top.add( category );
192     //        addSubelementEditable( category, "Reference", ann.getRef() , PHYLOXML_TAG.);
193     //        addSubelementEditable( category, "Description", ann.getDesc() , PHYLOXML_TAG.);
194     //        addSubelementEditable( category, "Source", ann.getSource(), PHYLOXML_TAG. );
195     //        addSubelementEditable( category, "Type", ann.getType(), PHYLOXML_TAG. );
196     //        addSubelementEditable( category, "Evidence", ann.getEvidence() , PHYLOXML_TAG.);
197     //        if ( ann.getConfidence() != null ) {
198     //            addSubelementEditable( category, "Confidence", ann.getConfidence().asText().toString() , PHYLOXML_TAG.);
199     //        }
200     //        if ( ann.getProperties() != null ) {
201     //            addProperties( category, ann.getProperties(), "Properties", PHYLOXML_TAG. );
202     //        }
203     //    }
204     //    private void addAnnotations( final DefaultMutableTreeNode top,
205     //                                 final List<PhylogenyData> annotations,
206     //                                 final DefaultMutableTreeNode category ) {
207     //        if ( ( annotations != null ) && ( annotations.size() > 0 ) ) {
208     //            category.add( new DefaultMutableTreeNode( "Annotations" ) );
209     //            final DefaultMutableTreeNode last = top.getLastLeaf();
210     //            int i = 0;
211     //            for( final PhylogenyData ann : annotations ) {
212     //                addAnnotation( last, ( Annotation ) ann, "Annotation " + ( i++ ) );
213     //            }
214     //        }
215     //    }
216     private void addDate( final DefaultMutableTreeNode top, Date date, final String name ) {
217         if ( date == null ) {
218             date = new Date();
219         }
220         DefaultMutableTreeNode category;
221         category = new DefaultMutableTreeNode( name );
222         top.add( category );
223         addSubelementEditable( category, NodePanel.DATE_DESCRIPTION, date.getDesc(), PHYLOXML_TAG.DATE_DESCRIPTION );
224         addSubelementEditable( category,
225                                NodePanel.DATE_VALUE,
226                                String.valueOf( date.getValue() != null ? date.getValue() : "" ),
227                                PHYLOXML_TAG.DATE_VALUE );
228         addSubelementEditable( category,
229                                NodePanel.DATE_MIN,
230                                String.valueOf( date.getMin() != null ? date.getMin() : "" ),
231                                PHYLOXML_TAG.DATE_MIN );
232         addSubelementEditable( category,
233                                NodePanel.DATE_MAX,
234                                String.valueOf( date.getMax() != null ? date.getMax() : "" ),
235                                PHYLOXML_TAG.DATE_MAX );
236         addSubelementEditable( category, NodePanel.DATE_UNIT, date.getUnit(), PHYLOXML_TAG.DATE_UNIT );
237     }
238
239     private void addDistribution( final DefaultMutableTreeNode top, Distribution dist, final String name ) {
240         if ( dist == null ) {
241             dist = new Distribution( "" );
242         }
243         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
244         top.add( category );
245         Point p0 = null;
246         if ( ( dist.getPoints() != null ) && ( dist.getPoints().size() > 0 ) ) {
247             p0 = dist.getPoints().get( 0 );
248         }
249         else {
250             p0 = new Point();
251         }
252         addSubelementEditable( category, NodePanel.DIST_DESCRIPTION, dist.getDesc(), PHYLOXML_TAG.DIST_DESC );
253         addSubelementEditable( category,
254                                NodePanel.DIST_GEODETIC_DATUM,
255                                p0.getGeodeticDatum(),
256                                PHYLOXML_TAG.DIST_GEODETIC );
257         addSubelementEditable( category,
258                                NodePanel.DIST_LATITUDE,
259                                String.valueOf( p0.getLatitude() != null ? p0.getLatitude() : "" ),
260                                PHYLOXML_TAG.DIST_LAT );
261         addSubelementEditable( category,
262                                NodePanel.DIST_LONGITUDE,
263                                String.valueOf( p0.getLongitude() != null ? p0.getLongitude() : "" ),
264                                PHYLOXML_TAG.DIST_LONG );
265         addSubelementEditable( category,
266                                NodePanel.DIST_ALTITUDE,
267                                String.valueOf( p0.getAltitude() != null ? p0.getAltitude() : "" ),
268                                PHYLOXML_TAG.DIST_ALT );
269         addSubelementEditable( category,
270                                NodePanel.DIST_ALT_UNIT,
271                                String.valueOf( p0.getAltiudeUnit() != null ? p0.getAltiudeUnit() : "" ),
272                                PHYLOXML_TAG.DIST_ALT_UNIT );
273     }
274
275     private void addEvents( final DefaultMutableTreeNode top, Event events, final String name ) {
276         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
277         if ( events == null ) {
278             events = new Event();
279         }
280         top.add( category );
281         addSubelementEditable( category,
282                                NodePanel.EVENTS_DUPLICATIONS,
283                                String.valueOf( events.getNumberOfDuplications() >= 0 ? events.getNumberOfDuplications()
284                                        : 0 ),
285                                PHYLOXML_TAG.EVENTS_DUPLICATIONS );
286         addSubelementEditable( category,
287                                NodePanel.EVENTS_SPECIATIONS,
288                                String.valueOf( events.getNumberOfSpeciations() >= 0 ? events.getNumberOfSpeciations()
289                                        : 0 ),
290                                PHYLOXML_TAG.EVENTS_SPECIATIONS );
291         addSubelementEditable( category,
292                                NodePanel.EVENTS_GENE_LOSSES,
293                                String.valueOf( events.getNumberOfGeneLosses() >= 0 ? events.getNumberOfGeneLosses() : 0 ),
294                                PHYLOXML_TAG.EVENTS_GENE_LOSSES );
295     }
296
297     private void addMapping( final DefaultMutableTreeNode mtn, final TagNumber tag ) {
298         if ( getMap().containsKey( mtn ) ) {
299             throw new IllegalArgumentException( "key " + mtn + " already present" );
300         }
301         if ( getMap().containsValue( tag ) ) {
302             throw new IllegalArgumentException( "value " + tag + " already present" );
303         }
304         getMap().put( mtn, tag );
305     }
306
307     private void addReference( final DefaultMutableTreeNode top, Reference ref, final String name ) {
308         if ( ref == null ) {
309             ref = new Reference( "" );
310         }
311         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
312         top.add( category );
313         addSubelementEditable( category,
314                                NodePanel.LIT_REFERENCE_DESC,
315                                ref.getDescription(),
316                                PHYLOXML_TAG.LIT_REFERENCE_DESC );
317         addSubelementEditable( category, NodePanel.LIT_REFERENCE_DOI, ref.getDoi(), PHYLOXML_TAG.LIT_REFERENCE_DOI );
318     }
319
320     private void addSequence( final DefaultMutableTreeNode top, Sequence seq, final String name ) {
321         if ( seq == null ) {
322             seq = new Sequence();
323         }
324         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
325         top.add( category );
326         Accession acc = seq.getAccession();
327         if ( acc == null ) {
328             acc = new Accession( "", "" );
329         }
330         addSubelementEditable( category, NodePanel.SEQ_NAME, seq.getName(), PHYLOXML_TAG.SEQ_NAME );
331         addSubelementEditable( category, NodePanel.SEQ_SYMBOL, seq.getSymbol(), PHYLOXML_TAG.SEQ_SYMBOL );
332         addSubelementEditable( category,
333                                NodePanel.SEQ_ACCESSION,
334                                acc.getValue(),
335                                PHYLOXML_TAG.SEQ_ACC_VALUE,
336                                "Source",
337                                acc.getSource(),
338                                PHYLOXML_TAG.SEQ_ACC_SOURCE );
339         addSubelementEditable( category, NodePanel.SEQ_LOCATION, seq.getLocation(), PHYLOXML_TAG.SEQ_LOCATION );
340         addSubelementEditable( category, NodePanel.SEQ_TYPE, seq.getType(), PHYLOXML_TAG.SEQ_TYPE );
341         addSubelementEditable( category, NodePanel.SEQ_MOL_SEQ, seq.getMolecularSequence(), PHYLOXML_TAG.SEQ_MOL_SEQ );
342         int uri_counter = 0;
343         if ( seq.getUris() != null ) {
344             for( final Uri uri : seq.getUris() ) {
345                 if ( uri != null ) {
346                     addSubelementEditable( category, NodePanel.SEQ_URI + " [" + uri_counter + "]", uri.getValue()
347                             .toString(), PHYLOXML_TAG.SEQ_URI, uri_counter++ );
348                 }
349             }
350         }
351         addSubelementEditable( category,
352                                NodePanel.SEQ_URI + " [" + uri_counter + "]",
353                                "",
354                                PHYLOXML_TAG.SEQ_URI,
355                                uri_counter );
356         //  addAnnotations( top, seq.getAnnotations(), category );
357     }
358
359     private void addSubelementEditable( final DefaultMutableTreeNode node,
360                                         final String name,
361                                         final String value,
362                                         final PHYLOXML_TAG phyloxml_tag ) {
363         addSubelementEditable( node, name, value, phyloxml_tag, 0 );
364     }
365
366     private void addSubelementEditable( final DefaultMutableTreeNode node,
367                                         final String name,
368                                         final String value,
369                                         final PHYLOXML_TAG phyloxml_tag,
370                                         final int number ) {
371         String my_value = value;
372         if ( ForesterUtil.isEmpty( my_value ) ) {
373             my_value = "";
374         }
375         final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
376         final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
377         name_node.add( value_node );
378         node.add( name_node );
379         addMapping( name_node, new TagNumber( phyloxml_tag, number ) );
380     }
381
382     private void addSubelementEditable( final DefaultMutableTreeNode node,
383                                         final String name,
384                                         final String value,
385                                         final PHYLOXML_TAG phyloxml_value_tag,
386                                         final String source_name,
387                                         final String source_value,
388                                         final PHYLOXML_TAG phyloxml_source_tag ) {
389         addSubelementEditable( node, name, value, phyloxml_value_tag, source_name, source_value, phyloxml_source_tag, 0 );
390     }
391
392     private void addSubelementEditable( final DefaultMutableTreeNode node,
393                                         final String name,
394                                         final String value,
395                                         final PHYLOXML_TAG phyloxml_value_tag,
396                                         final String source_name,
397                                         final String source_value,
398                                         final PHYLOXML_TAG phyloxml_source_tag,
399                                         final int number ) {
400         String my_value = value;
401         if ( ForesterUtil.isEmpty( my_value ) ) {
402             my_value = "";
403         }
404         String my_source_value = source_value;
405         if ( ForesterUtil.isEmpty( my_source_value ) ) {
406             my_source_value = "";
407         }
408         final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
409         final DefaultMutableTreeNode source_name_node = new DefaultMutableTreeNode( source_name );
410         final DefaultMutableTreeNode source_value_node = new DefaultMutableTreeNode( my_source_value );
411         final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
412         name_node.add( source_name_node );
413         source_name_node.add( source_value_node );
414         name_node.add( value_node );
415         node.add( name_node );
416         addMapping( name_node, new TagNumber( phyloxml_value_tag, number ) );
417         addMapping( source_name_node, new TagNumber( phyloxml_source_tag, number ) );
418     }
419
420     private void addTaxonomy( final DefaultMutableTreeNode top, Taxonomy tax, final String name ) {
421         if ( tax == null ) {
422             tax = new Taxonomy();
423         }
424         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
425         top.add( category );
426         Identifier id = tax.getIdentifier();
427         if ( id == null ) {
428             id = new Identifier();
429         }
430         addSubelementEditable( category,
431                                NodePanel.TAXONOMY_IDENTIFIER,
432                                id.getValue(),
433                                PHYLOXML_TAG.TAXONOMY_ID_VALUE,
434                                "Provider",
435                                id.getProvider(),
436                                PHYLOXML_TAG.TAXONOMY_ID_PROVIDER );
437         addSubelementEditable( category, NodePanel.TAXONOMY_CODE, tax.getTaxonomyCode(), PHYLOXML_TAG.TAXONOMY_CODE );
438         addSubelementEditable( category,
439                                NodePanel.TAXONOMY_SCIENTIFIC_NAME,
440                                tax.getScientificName(),
441                                PHYLOXML_TAG.TAXONOMY_SCIENTIFIC_NAME );
442         addSubelementEditable( category,
443                                NodePanel.TAXONOMY_AUTHORITY,
444                                tax.getAuthority(),
445                                PHYLOXML_TAG.TAXONOMY_AUTHORITY );
446         addSubelementEditable( category,
447                                NodePanel.TAXONOMY_COMMON_NAME,
448                                tax.getCommonName(),
449                                PHYLOXML_TAG.TAXONOMY_COMMON_NAME );
450         for( int i = tax.getSynonyms().size() - 1; i >= 0; i-- ) {
451             if ( ForesterUtil.isEmpty( tax.getSynonyms().get( i ) ) ) {
452                 tax.getSynonyms().remove( i );
453             }
454         }
455         int syn_counter = 0;
456         for( final String syn : tax.getSynonyms() ) {
457             addSubelementEditable( category,
458                                    NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
459                                    syn,
460                                    PHYLOXML_TAG.TAXONOMY_SYNONYM,
461                                    syn_counter++ );
462         }
463         addSubelementEditable( category,
464                                NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
465                                "",
466                                PHYLOXML_TAG.TAXONOMY_SYNONYM,
467                                syn_counter );
468         addSubelementEditable( category, NodePanel.TAXONOMY_RANK, tax.getRank(), PHYLOXML_TAG.TAXONOMY_RANK );
469         int uri_counter = 0;
470         if ( tax.getUris() != null ) {
471             for( final Uri uri : tax.getUris() ) {
472                 if ( uri != null ) {
473                     addSubelementEditable( category, NodePanel.TAXONOMY_URI + " [" + uri_counter + "]", uri.getValue()
474                             .toString(), PHYLOXML_TAG.TAXONOMY_URI, uri_counter++ );
475                 }
476             }
477         }
478         addSubelementEditable( category,
479                                NodePanel.TAXONOMY_URI + " [" + uri_counter + "]",
480                                "",
481                                PHYLOXML_TAG.TAXONOMY_URI,
482                                uri_counter );
483     }
484
485     private void collapsePath( final String name ) {
486         final TreePath tp = getJTree().getNextMatch( name, 0, Position.Bias.Forward );
487         if ( tp != null ) {
488             getJTree().collapsePath( tp );
489         }
490     }
491
492     private void createNodes( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node ) {
493         if ( !phylogeny_node.getNodeData().isHasTaxonomy() ) {
494             phylogeny_node.getNodeData().addTaxonomy( new Taxonomy() );
495         }
496         if ( !phylogeny_node.getNodeData().isHasSequence() ) {
497             phylogeny_node.getNodeData().addSequence( new Sequence() );
498         }
499         if ( !phylogeny_node.getNodeData().isHasDistribution() ) {
500             phylogeny_node.getNodeData().addDistribution( new Distribution( "" ) );
501         }
502         if ( !phylogeny_node.getNodeData().isHasReference() ) {
503             phylogeny_node.getNodeData().addReference( new Reference( "" ) );
504         }
505         addBasics( top, phylogeny_node, NodePanel.BASIC );
506         addTaxonomy( top, phylogeny_node.getNodeData().getTaxonomy(), NodePanel.TAXONOMY );
507         addSequence( top, phylogeny_node.getNodeData().getSequence(), NodePanel.SEQUENCE );
508         if ( !phylogeny_node.isExternal() ) {
509             addEvents( top, phylogeny_node.getNodeData().getEvent(), NodePanel.EVENTS );
510         }
511         addDate( top, phylogeny_node.getNodeData().getDate(), NodePanel.DATE );
512         addDistribution( top, phylogeny_node.getNodeData().getDistribution(), NodePanel.DISTRIBUTION );
513         addReference( top, phylogeny_node.getNodeData().getReference(), NodePanel.LIT_REFERENCE );
514         //  addProperties( top, phylogeny_node.getNodeData().getProperties(), "Properties" );
515     }
516
517     private void formatError( final DefaultMutableTreeNode mtn, final PhyloXmlDataFormatException e ) {
518         JOptionPane.showMessageDialog( this, e.getMessage(), "Format error", JOptionPane.ERROR_MESSAGE );
519         mtn.setUserObject( "" );
520         getJTree().repaint();
521     }
522
523     private JTree getJTree() {
524         return _tree;
525     }
526
527     private Map<DefaultMutableTreeNode, TagNumber> getMap() {
528         return _map;
529     }
530
531     private TagNumber getMapping( final DefaultMutableTreeNode mtn ) {
532         return getMap().get( mtn );
533     }
534
535     PhylogenyNode getMyNode() {
536         return _my_node;
537     }
538
539     private DefaultMutableTreeNode getSelectedTreeNode() {
540         final TreePath selectionPath = getJTree().getSelectionPath();
541         if ( selectionPath != null ) {
542             final Object[] path = selectionPath.getPath();
543             if ( path.length > 0 ) {
544                 return ( DefaultMutableTreeNode ) path[ path.length - 1 ]; // Last node
545             }
546         }
547         return null;
548     }
549
550     private TreePanel getTreePanel() {
551         return _tree_panel;
552     }
553
554     private void keyEvent( final KeyEvent e ) {
555         if ( e.getKeyCode() == KeyEvent.VK_ENTER ) {
556             writeBack( getSelectedTreeNode() );
557         }
558     }
559
560     private List<Point> obtainPoints() {
561         ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
562         Distribution d = getMyNode().getNodeData().getDistribution();
563         if ( d.getPoints() == null ) {
564             d = new Distribution( d.getDesc(), new ArrayList<Point>(), d.getPolygons() );
565             getMyNode().getNodeData().setDistribution( d );
566         }
567         final List<Point> ps = d.getPoints();
568         if ( ps.isEmpty() ) {
569             ps.add( new Point() );
570         }
571         else if ( ps.get( 0 ) == null ) {
572             ps.set( 0, new Point() );
573         }
574         return ps;
575     }
576
577     private BigDecimal parseBigDecimal( final DefaultMutableTreeNode mtn, final String value ) {
578         if ( ForesterUtil.isEmpty( value ) ) {
579             return new BigDecimal( 0 );
580         }
581         BigDecimal i = null;
582         try {
583             i = new BigDecimal( value );
584         }
585         catch ( final NumberFormatException e ) {
586             JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
587             mtn.setUserObject( "" );
588         }
589         return i;
590     }
591
592     private int parsePositiveInt( final DefaultMutableTreeNode mtn, final String value ) {
593         if ( ForesterUtil.isEmpty( value ) ) {
594             return 0;
595         }
596         int i = -1;
597         try {
598             i = ForesterUtil.parseInt( value );
599         }
600         catch ( final ParseException e ) {
601             JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
602             mtn.setUserObject( "" );
603         }
604         if ( i < 0 ) {
605             JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
606             mtn.setUserObject( "" );
607         }
608         return i;
609     }
610
611     void writeAll() {
612         for( int i = 0; i < getJTree().getRowCount(); i++ ) {
613             final TreePath p = getJTree().getPathForRow( i );
614             writeBack( ( DefaultMutableTreeNode ) p.getLastPathComponent() );
615         }
616     }
617
618     private void writeBack( final DefaultMutableTreeNode mtn ) {
619         if ( !getMap().containsKey( mtn ) ) {
620             final DefaultMutableTreeNode parent = ( DefaultMutableTreeNode ) mtn.getParent();
621             if ( getMap().containsKey( parent ) ) {
622                 writeBack( mtn, getMapping( parent ) );
623             }
624         }
625     }
626
627     private void writeBack( final DefaultMutableTreeNode mtn, final TagNumber tag_number ) {
628         if ( tag_number == null ) {
629             return;
630         }
631         String value = mtn.toString();
632         if ( value == null ) {
633             value = "";
634         }
635         value = value.replaceAll( "\\s+", " " );
636         value = value.trim();
637         mtn.setUserObject( value );
638         getJTree().repaint();
639         final PHYLOXML_TAG tag = tag_number.getTag();
640         final int number = tag_number.getNumber();
641         switch ( tag ) {
642             case NODE_NAME:
643                 getMyNode().setName( value );
644                 break;
645             case NODE_BRANCH_LENGTH:
646                 if ( ForesterUtil.isEmpty( value ) ) {
647                     getMyNode().setDistanceToParent( PhylogenyNode.DISTANCE_DEFAULT );
648                 }
649                 else {
650                     try {
651                         getMyNode().setDistanceToParent( ForesterUtil.parseDouble( value ) );
652                     }
653                     catch ( final ParseException e ) {
654                         JOptionPane.showMessageDialog( this,
655                                                        "failed to parse branch length from: " + value,
656                                                        "Error",
657                                                        JOptionPane.ERROR_MESSAGE );
658                         mtn.setUserObject( "" );
659                     }
660                 }
661                 break;
662             case CONFIDENCE_VALUE:
663                 double confidence = Confidence.CONFIDENCE_DEFAULT_VALUE;
664                 if ( !ForesterUtil.isEmpty( value ) ) {
665                     try {
666                         confidence = ForesterUtil.parseDouble( value );
667                     }
668                     catch ( final ParseException e ) {
669                         JOptionPane.showMessageDialog( this,
670                                                        "failed to parse confidence value from: " + value,
671                                                        "Error",
672                                                        JOptionPane.ERROR_MESSAGE );
673                         mtn.setUserObject( "" );
674                         break;
675                     }
676                 }
677                 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
678                     throw new FailedConditionCheckException();
679                 }
680                 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
681                     if ( confidence >= 0 ) {
682                         getMyNode().getBranchData().getConfidences().add( new Confidence( confidence, "unknown" ) );
683                     }
684                 }
685                 else {
686                     final String type = getMyNode().getBranchData().getConfidences().get( number ).getType();
687                     getMyNode().getBranchData().getConfidences().set( number, new Confidence( confidence, type ) );
688                 }
689                 break;
690             case CONFIDENCE_TYPE:
691                 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
692                     throw new FailedConditionCheckException();
693                 }
694                 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
695                     if ( !ForesterUtil.isEmpty( value ) ) {
696                         getMyNode().getBranchData().getConfidences().add( new Confidence( 0, value ) );
697                     }
698                 }
699                 else {
700                     final double v = getMyNode().getBranchData().getConfidences().get( number ).getValue();
701                     getMyNode().getBranchData().getConfidences().set( number, new Confidence( v, value ) );
702                 }
703                 break;
704             case TAXONOMY_CODE:
705                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
706                 try {
707                     getMyNode().getNodeData().getTaxonomy().setTaxonomyCode( value );
708                 }
709                 catch ( final PhyloXmlDataFormatException e ) {
710                     formatError( mtn, e );
711                     break;
712                 }
713                 break;
714             case TAXONOMY_SCIENTIFIC_NAME:
715                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
716                 getMyNode().getNodeData().getTaxonomy().setScientificName( value );
717                 break;
718             case TAXONOMY_COMMON_NAME:
719                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
720                 getMyNode().getNodeData().getTaxonomy().setCommonName( value );
721                 break;
722             case TAXONOMY_RANK:
723                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
724                 try {
725                     getMyNode().getNodeData().getTaxonomy().setRank( value.toLowerCase() );
726                 }
727                 catch ( final PhyloXmlDataFormatException e ) {
728                     formatError( mtn, e );
729                     break;
730                 }
731                 break;
732             case TAXONOMY_AUTHORITY:
733                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
734                 getMyNode().getNodeData().getTaxonomy().setAuthority( value );
735                 break;
736             case TAXONOMY_URI: {
737                 Uri uri = null;
738                 if ( !ForesterUtil.isEmpty( value ) ) {
739                     try {
740                         uri = new Uri( new URL( value ).toURI() );
741                     }
742                     catch ( final Exception e ) {
743                         JOptionPane.showMessageDialog( this,
744                                                        "failed to parse URL from: " + value,
745                                                        "Error",
746                                                        JOptionPane.ERROR_MESSAGE );
747                         mtn.setUserObject( "" );
748                     }
749                 }
750                 if ( uri != null ) {
751                     ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
752                 }
753                 addUri( mtn, uri, number, getMyNode().getNodeData().getTaxonomy() );
754                 break;
755             }
756             case TAXONOMY_SYNONYM:
757                 if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() < number ) {
758                     throw new FailedConditionCheckException();
759                 }
760                 else if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() == number ) {
761                     if ( !ForesterUtil.isEmpty( value ) ) {
762                         ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
763                         getMyNode().getNodeData().getTaxonomy().getSynonyms().add( value );
764                     }
765                 }
766                 else {
767                     getMyNode().getNodeData().getTaxonomy().getSynonyms().set( number, value );
768                 }
769                 break;
770             case TAXONOMY_ID_VALUE:
771                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
772                 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
773                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value ) );
774                 }
775                 else {
776                     final String provider = getMyNode().getNodeData().getTaxonomy().getIdentifier().getProvider();
777                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value, provider ) );
778                 }
779                 break;
780             case TAXONOMY_ID_PROVIDER:
781                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
782                 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
783                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( "", value ) );
784                 }
785                 else {
786                     final String v = getMyNode().getNodeData().getTaxonomy().getIdentifier().getValue();
787                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( v, value ) );
788                 }
789                 break;
790             case SEQ_LOCATION:
791                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
792                 getMyNode().getNodeData().getSequence().setLocation( value );
793                 break;
794             case SEQ_MOL_SEQ:
795                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
796                 getMyNode().getNodeData().getSequence().setMolecularSequence( value );
797                 break;
798             case SEQ_NAME:
799                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
800                 getMyNode().getNodeData().getSequence().setName( value );
801                 break;
802             case SEQ_SYMBOL:
803                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
804                 try {
805                     getMyNode().getNodeData().getSequence().setSymbol( value );
806                 }
807                 catch ( final PhyloXmlDataFormatException e ) {
808                     formatError( mtn, e );
809                     break;
810                 }
811                 break;
812             case SEQ_TYPE:
813                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
814                 try {
815                     getMyNode().getNodeData().getSequence().setType( value.toLowerCase() );
816                 }
817                 catch ( final PhyloXmlDataFormatException e ) {
818                     formatError( mtn, e );
819                     break;
820                 }
821                 break;
822             case SEQ_ACC_SOURCE:
823                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
824                 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
825                     getMyNode().getNodeData().getSequence().setAccession( new Accession( "", value ) );
826                 }
827                 else {
828                     final String v = getMyNode().getNodeData().getSequence().getAccession().getValue();
829                     getMyNode().getNodeData().getSequence().setAccession( new Accession( v, value ) );
830                 }
831                 break;
832             case SEQ_ACC_VALUE:
833                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
834                 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
835                     getMyNode().getNodeData().getSequence().setAccession( new Accession( value, "" ) );
836                 }
837                 else {
838                     final String source = getMyNode().getNodeData().getSequence().getAccession().getSource();
839                     getMyNode().getNodeData().getSequence().setAccession( new Accession( value, source ) );
840                 }
841                 break;
842             case SEQ_URI: {
843                 Uri uri = null;
844                 if ( !ForesterUtil.isEmpty( value ) ) {
845                     try {
846                         uri = new Uri( new URL( value ).toURI() );
847                     }
848                     catch ( final Exception e ) {
849                         JOptionPane.showMessageDialog( this,
850                                                        "failed to parse URL from: " + value,
851                                                        "Error",
852                                                        JOptionPane.ERROR_MESSAGE );
853                         mtn.setUserObject( "" );
854                     }
855                 }
856                 if ( uri != null ) {
857                     ForesterUtil.ensurePresenceOfSequence( getMyNode() );
858                 }
859                 addUri( mtn, uri, number, getMyNode().getNodeData().getSequence() );
860                 break;
861             }
862             case LIT_REFERENCE_DESC:
863                 if ( !getMyNode().getNodeData().isHasReference() ) {
864                     getMyNode().getNodeData().setReference( new Reference( "" ) );
865                 }
866                 getMyNode().getNodeData().getReference().setValue( value );
867                 break;
868             case LIT_REFERENCE_DOI:
869                 if ( !getMyNode().getNodeData().isHasReference() ) {
870                     getMyNode().getNodeData().setReference( new Reference( "" ) );
871                 }
872                 try {
873                     getMyNode().getNodeData().getReference().setDoi( value );
874                 }
875                 catch ( final PhyloXmlDataFormatException e ) {
876                     formatError( mtn, e );
877                     break;
878                 }
879                 break;
880             case EVENTS_DUPLICATIONS:
881                 if ( !getMyNode().getNodeData().isHasEvent() ) {
882                     getMyNode().getNodeData().setEvent( new Event() );
883                 }
884                 getMyNode().getNodeData().getEvent().setDuplications( parsePositiveInt( mtn, value ) );
885                 break;
886             case EVENTS_SPECIATIONS:
887                 if ( !getMyNode().getNodeData().isHasEvent() ) {
888                     getMyNode().getNodeData().setEvent( new Event() );
889                 }
890                 getMyNode().getNodeData().getEvent().setSpeciations( parsePositiveInt( mtn, value ) );
891                 break;
892             case EVENTS_GENE_LOSSES:
893                 if ( !getMyNode().getNodeData().isHasEvent() ) {
894                     getMyNode().getNodeData().setEvent( new Event() );
895                 }
896                 getMyNode().getNodeData().getEvent().setGeneLosses( parsePositiveInt( mtn, value ) );
897                 break;
898             case DATE_DESCRIPTION:
899                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
900                 getMyNode().getNodeData().getDate().setDesc( value );
901                 break;
902             case DATE_MAX:
903                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
904                 getMyNode().getNodeData().getDate().setMax( parseBigDecimal( mtn, value ) );
905                 break;
906             case DATE_MIN:
907                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
908                 getMyNode().getNodeData().getDate().setMin( parseBigDecimal( mtn, value ) );
909                 break;
910             case DATE_UNIT:
911                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
912                 getMyNode().getNodeData().getDate().setUnit( value );
913                 break;
914             case DATE_VALUE:
915                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
916                 getMyNode().getNodeData().getDate().setValue( parseBigDecimal( mtn, value ) );
917                 break;
918             case DIST_ALT: {
919                 final BigDecimal new_value = parseBigDecimal( mtn, value );
920                 if ( new_value != null ) {
921                     final List<Point> ps = obtainPoints();
922                     final Point p = ps.get( 0 );
923                     final Point p_new = new Point( p.getGeodeticDatum(),
924                                                    p.getLatitude(),
925                                                    p.getLongitude(),
926                                                    new_value,
927                                                    ForesterUtil.isEmpty( p.getAltiudeUnit() ) ? "?"
928                                                            : p.getAltiudeUnit() );
929                     ps.set( 0, p_new );
930                 }
931                 break;
932             }
933             case DIST_DESC: {
934                 ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
935                 final Distribution d = getMyNode().getNodeData().getDistribution();
936                 getMyNode().getNodeData().setDistribution( new Distribution( value, d.getPoints(), d.getPolygons() ) );
937                 break;
938             }
939             case DIST_GEODETIC: {
940                 if ( !ForesterUtil.isEmpty( value ) ) {
941                     final List<Point> ps = obtainPoints();
942                     final Point p = ps.get( 0 );
943                     final Point p_new = new Point( value,
944                                                    p.getLatitude(),
945                                                    p.getLongitude(),
946                                                    p.getAltitude(),
947                                                    p.getAltiudeUnit() );
948                     ps.set( 0, p_new );
949                 }
950                 break;
951             }
952             case DIST_ALT_UNIT: {
953                 if ( !ForesterUtil.isEmpty( value ) ) {
954                     final List<Point> ps = obtainPoints();
955                     final Point p = ps.get( 0 );
956                     final Point p_new = new Point( p.getGeodeticDatum(),
957                                                    p.getLatitude(),
958                                                    p.getLongitude(),
959                                                    p.getAltitude(),
960                                                    value );
961                     ps.set( 0, p_new );
962                 }
963                 break;
964             }
965             case DIST_LAT: {
966                 final BigDecimal new_value = parseBigDecimal( mtn, value );
967                 if ( new_value != null ) {
968                     final List<Point> ps = obtainPoints();
969                     final Point p = ps.get( 0 );
970                     final Point p_new = new Point( p.getGeodeticDatum(),
971                                                    new_value,
972                                                    p.getLongitude(),
973                                                    p.getAltitude(),
974                                                    p.getAltiudeUnit() );
975                     ps.set( 0, p_new );
976                 }
977                 break;
978             }
979             case DIST_LONG: {
980                 final BigDecimal new_value = parseBigDecimal( mtn, value );
981                 if ( new_value != null ) {
982                     final List<Point> ps = obtainPoints();
983                     final Point p = ps.get( 0 );
984                     final Point p_new = new Point( p.getGeodeticDatum(),
985                                                    p.getLatitude(),
986                                                    new_value,
987                                                    p.getAltitude(),
988                                                    p.getAltiudeUnit() );
989                     ps.set( 0, p_new );
990                 }
991                 break;
992             }
993             default:
994                 throw new IllegalArgumentException( "unknown: " + tag );
995         }
996         getJTree().repaint();
997         getTreePanel().setEdited( true );
998         getTreePanel().repaint();
999     }
1000
1001     private void addUri( final DefaultMutableTreeNode mtn, final Uri uri, final int number, final MultipleUris mu ) {
1002         if ( uri != null ) {
1003             if ( mu.getUris() == null ) {
1004                 mu.setUris( new ArrayList<Uri>() );
1005             }
1006         }
1007         if ( ( uri != null ) && ( mu.getUris() == null ) ) {
1008             mu.setUris( new ArrayList<Uri>() );
1009         }
1010         if ( ( uri != null ) && ( mu.getUris().size() == number ) ) {
1011             mu.getUris().add( uri );
1012         }
1013         if ( ( mu.getUris() != null ) && ( mu.getUris().size() != number ) ) {
1014             mu.getUris().set( number, uri );
1015         }
1016         final ImageLoader il = new ImageLoader( getTreePanel() );
1017         new Thread( il ).start();
1018     }
1019
1020     private enum PHYLOXML_TAG {
1021         NODE_NAME,
1022         NODE_BRANCH_LENGTH,
1023         TAXONOMY_CODE,
1024         TAXONOMY_SCIENTIFIC_NAME,
1025         TAXONOMY_AUTHORITY,
1026         TAXONOMY_COMMON_NAME,
1027         TAXONOMY_SYNONYM,
1028         TAXONOMY_RANK,
1029         TAXONOMY_URI,
1030         SEQ_SYMBOL,
1031         SEQ_NAME,
1032         SEQ_LOCATION,
1033         SEQ_TYPE,
1034         SEQ_MOL_SEQ,
1035         SEQ_URI,
1036         DATE_DESCRIPTION,
1037         DATE_VALUE,
1038         DATE_MIN,
1039         DATE_MAX,
1040         DATE_UNIT,
1041         TAXONOMY_ID_VALUE,
1042         TAXONOMY_ID_PROVIDER,
1043         SEQ_ACC_VALUE,
1044         SEQ_ACC_SOURCE,
1045         CONFIDENCE_VALUE,
1046         CONFIDENCE_TYPE,
1047         LIT_REFERENCE_DESC,
1048         LIT_REFERENCE_DOI,
1049         EVENTS_DUPLICATIONS,
1050         EVENTS_SPECIATIONS,
1051         EVENTS_GENE_LOSSES,
1052         DIST_DESC,
1053         DIST_GEODETIC,
1054         DIST_LAT,
1055         DIST_LONG,
1056         DIST_ALT,
1057         DIST_ALT_UNIT
1058     }
1059
1060     private class TagNumber {
1061
1062         final private PHYLOXML_TAG _tag;
1063         final private int          _number;
1064
1065         TagNumber( final PHYLOXML_TAG tag, final int number ) {
1066             _tag = tag;
1067             _number = number;
1068         }
1069
1070         int getNumber() {
1071             return _number;
1072         }
1073
1074         PHYLOXML_TAG getTag() {
1075             return _tag;
1076         }
1077
1078         @Override
1079         public String toString() {
1080             return getTag() + "_" + getNumber();
1081         }
1082     }
1083 }