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