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