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