2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
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.
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.
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
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
26 package org.forester.archaeopteryx;
28 import java.awt.Component;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.KeyListener;
33 import java.math.BigDecimal;
35 import java.text.ParseException;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.List;
41 import javax.swing.BoxLayout;
42 import javax.swing.JButton;
43 import javax.swing.JEditorPane;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPanel;
46 import javax.swing.JScrollPane;
47 import javax.swing.JTree;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.text.Position;
51 import javax.swing.tree.DefaultMutableTreeNode;
52 import javax.swing.tree.TreePath;
53 import javax.swing.tree.TreeSelectionModel;
55 import org.forester.archaeopteryx.tools.ImageLoader;
56 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
57 import org.forester.phylogeny.PhylogenyNode;
58 import org.forester.phylogeny.data.Accession;
59 import org.forester.phylogeny.data.BranchWidth;
60 import org.forester.phylogeny.data.Confidence;
61 import org.forester.phylogeny.data.Date;
62 import org.forester.phylogeny.data.Distribution;
63 import org.forester.phylogeny.data.Event;
64 import org.forester.phylogeny.data.Identifier;
65 import org.forester.phylogeny.data.MultipleUris;
66 import org.forester.phylogeny.data.PhylogenyData;
67 import org.forester.phylogeny.data.PhylogenyDataUtil;
68 import org.forester.phylogeny.data.Point;
69 import org.forester.phylogeny.data.Reference;
70 import org.forester.phylogeny.data.Sequence;
71 import org.forester.phylogeny.data.Taxonomy;
72 import org.forester.phylogeny.data.Uri;
73 import org.forester.util.FailedConditionCheckException;
74 import org.forester.util.ForesterUtil;
76 class NodeEditPanel extends JPanel {
78 private enum PHYLOXML_TAG {
83 TAXONOMY_SCIENTIFIC_NAME,
102 TAXONOMY_ID_PROVIDER,
120 private class TagNumber {
122 final private PHYLOXML_TAG _tag;
123 final private int _number;
125 TagNumber( final PHYLOXML_TAG tag, final int number ) {
131 public String toString() {
132 return getTag() + "_" + getNumber();
139 PHYLOXML_TAG getTag() {
143 private static final long serialVersionUID = 5120159904388100771L;
144 private final JTree _tree;
145 private final JEditorPane _pane;
146 private final PhylogenyNode _my_node;
147 private final TreePanel _tree_panel;
148 private final Map<DefaultMutableTreeNode, TagNumber> _map;
150 public NodeEditPanel( final PhylogenyNode phylogeny_node,
151 final TreePanel tree_panel,
152 final NodeFrame parent ) {
153 _map = new HashMap<DefaultMutableTreeNode, TagNumber>();
154 _my_node = phylogeny_node;
155 _tree_panel = tree_panel;
156 String node_name = "";
157 if ( !ForesterUtil.isEmpty( phylogeny_node.getName() ) ) {
158 node_name = phylogeny_node.getName() + " ";
160 final DefaultMutableTreeNode top = new DefaultMutableTreeNode( "Node " + node_name );
161 createNodes( top, phylogeny_node );
162 _tree = new JTree( top );
163 getJTree().setEditable( true );
164 getJTree().setFocusable( true );
165 getJTree().setToggleClickCount( 1 );
166 getJTree().setInvokesStopCellEditing( true );
167 final JScrollPane tree_view = new JScrollPane( getJTree() );
169 final JButton close_button = new JButton( "Close" );
170 close_button.setToolTipText( "This only closes this window; to write values back to the phylogeny, press ENTER after editing a field." );
171 close_button.setEnabled( true );
173 close_button.addActionListener( new ActionListener() {
174 public void actionPerformed( final ActionEvent e ) {
180 _pane = new JEditorPane();
181 _pane.setEditable( true );
182 tree_view.setMinimumSize( AptxConstants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
183 tree_view.setPreferredSize( AptxConstants.NODE_PANEL_SIZE );
185 close_button.setAlignmentX( Component.CENTER_ALIGNMENT );
186 tree_view.setAlignmentX( Component.CENTER_ALIGNMENT );
188 final JPanel panel = new JPanel();
189 panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ) );
190 panel.add( tree_view );
191 panel.add( close_button );
194 getJTree().getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
195 getJTree().addKeyListener( new KeyListener() {
198 public void keyPressed( final KeyEvent e ) {
203 public void keyReleased( final KeyEvent e ) {
208 public void keyTyped( final KeyEvent e ) {
212 for( int i = 0; i < getJTree().getRowCount(); i++ ) {
213 getJTree().expandRow( i );
215 collapsePath( NodePanel.BASIC );
216 collapsePath( NodePanel.TAXONOMY );
217 collapsePath( NodePanel.SEQUENCE );
218 collapsePath( NodePanel.EVENTS );
219 collapsePath( NodePanel.DATE );
220 collapsePath( NodePanel.DISTRIBUTION );
221 collapsePath( NodePanel.LIT_REFERENCE );
222 getJTree().addTreeSelectionListener( new TreeSelectionListener() {
225 public void valueChanged( final TreeSelectionEvent e ) {
226 final TreePath new_path = e.getNewLeadSelectionPath();
227 final TreePath old_path = e.getOldLeadSelectionPath();
228 if ( new_path != null ) {
229 writeBack( ( DefaultMutableTreeNode ) new_path.getLastPathComponent() );
231 if ( old_path != null ) {
232 writeBack( ( DefaultMutableTreeNode ) old_path.getLastPathComponent() );
234 AptxUtil.lookAtRealBranchLengthsForAptxControlSettings( tree_panel.getPhylogeny(),
235 tree_panel.getControlPanel() );
236 getTreePanel().repaint();
244 private void addBasics( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node, final String name ) {
245 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
247 addSubelementEditable( category, NodePanel.NODE_NAME, phylogeny_node.getName(), PHYLOXML_TAG.NODE_NAME );
249 if ( phylogeny_node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
250 bl = ForesterUtil.FORMATTER_6.format( phylogeny_node.getDistanceToParent() );
252 addSubelementEditable( category, NodePanel.NODE_BRANCH_LENGTH, bl, PHYLOXML_TAG.NODE_BRANCH_LENGTH );
254 if ( phylogeny_node.getBranchData().isHasConfidences() ) {
255 for( int i = phylogeny_node.getBranchData().getConfidences().size() - 1; i >= 0; i-- ) {
256 if ( phylogeny_node.getBranchData().getConfidences().get( i ).getValue() == Confidence.CONFIDENCE_DEFAULT_VALUE ) {
257 phylogeny_node.getBranchData().getConfidences().remove( i );
260 for( final PhylogenyData conf : phylogeny_node.getBranchData().getConfidences() ) {
261 final Confidence my_conf = ( Confidence ) ( conf );
262 addSubelementEditable( category,
263 NodePanel.CONFIDENCE + " [" + counter + "]",
264 ForesterUtil.FORMATTER_6.format( my_conf.getValue() ),
265 PHYLOXML_TAG.CONFIDENCE_VALUE,
266 NodePanel.CONFIDENCE_TYPE,
268 PHYLOXML_TAG.CONFIDENCE_TYPE,
272 addSubelementEditable( category,
273 NodePanel.CONFIDENCE + " [" + counter + "]",
275 PHYLOXML_TAG.CONFIDENCE_VALUE,
276 NodePanel.CONFIDENCE_TYPE,
278 PHYLOXML_TAG.CONFIDENCE_TYPE,
281 if ( ( phylogeny_node.getBranchData().getBranchWidth() != null )
282 && ( phylogeny_node.getBranchData().getBranchWidth().getValue() != BranchWidth.BRANCH_WIDTH_DEFAULT_VALUE ) ) {
283 bw = ForesterUtil.FORMATTER_3.format( phylogeny_node.getBranchData().getBranchWidth().getValue() );
285 addSubelementEditable( category, NodePanel.NODE_BRANCH_WIDTH, bw, PHYLOXML_TAG.NODE_BRANCH_WIDTH );
288 // private void addAnnotation( final DefaultMutableTreeNode top, final Annotation ann, final String name ) {
289 // DefaultMutableTreeNode category;
290 // category = new DefaultMutableTreeNode( name );
291 // top.add( category );
292 // addSubelementEditable( category, "Reference", ann.getRef() , PHYLOXML_TAG.);
293 // addSubelementEditable( category, "Description", ann.getDesc() , PHYLOXML_TAG.);
294 // addSubelementEditable( category, "Source", ann.getSource(), PHYLOXML_TAG. );
295 // addSubelementEditable( category, "Type", ann.getType(), PHYLOXML_TAG. );
296 // addSubelementEditable( category, "Evidence", ann.getEvidence() , PHYLOXML_TAG.);
297 // if ( ann.getConfidence() != null ) {
298 // addSubelementEditable( category, "Confidence", ann.getConfidence().asText().toString() , PHYLOXML_TAG.);
300 // if ( ann.getProperties() != null ) {
301 // addProperties( category, ann.getProperties(), "Properties", PHYLOXML_TAG. );
304 // private void addAnnotations( final DefaultMutableTreeNode top,
305 // final List<PhylogenyData> annotations,
306 // final DefaultMutableTreeNode category ) {
307 // if ( ( annotations != null ) && ( annotations.size() > 0 ) ) {
308 // category.add( new DefaultMutableTreeNode( "Annotations" ) );
309 // final DefaultMutableTreeNode last = top.getLastLeaf();
311 // for( final PhylogenyData ann : annotations ) {
312 // addAnnotation( last, ( Annotation ) ann, "Annotation " + ( i++ ) );
316 private void addDate( final DefaultMutableTreeNode top, Date date, final String name ) {
317 if ( date == null ) {
320 DefaultMutableTreeNode category;
321 category = new DefaultMutableTreeNode( name );
323 addSubelementEditable( category, NodePanel.DATE_DESCRIPTION, date.getDesc(), PHYLOXML_TAG.DATE_DESCRIPTION );
324 addSubelementEditable( category,
325 NodePanel.DATE_VALUE,
326 String.valueOf( date.getValue() != null ? date.getValue() : "" ),
327 PHYLOXML_TAG.DATE_VALUE );
328 addSubelementEditable( category,
330 String.valueOf( date.getMin() != null ? date.getMin() : "" ),
331 PHYLOXML_TAG.DATE_MIN );
332 addSubelementEditable( category,
334 String.valueOf( date.getMax() != null ? date.getMax() : "" ),
335 PHYLOXML_TAG.DATE_MAX );
336 addSubelementEditable( category, NodePanel.DATE_UNIT, date.getUnit(), PHYLOXML_TAG.DATE_UNIT );
339 private void addDistribution( final DefaultMutableTreeNode top, Distribution dist, final String name ) {
340 if ( dist == null ) {
341 dist = new Distribution( "" );
343 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
346 if ( ( dist.getPoints() != null ) && ( dist.getPoints().size() > 0 ) ) {
347 p0 = dist.getPoints().get( 0 );
352 addSubelementEditable( category, NodePanel.DIST_DESCRIPTION, dist.getDesc(), PHYLOXML_TAG.DIST_DESC );
353 addSubelementEditable( category,
354 NodePanel.DIST_GEODETIC_DATUM,
355 p0.getGeodeticDatum(),
356 PHYLOXML_TAG.DIST_GEODETIC );
357 addSubelementEditable( category,
358 NodePanel.DIST_LATITUDE,
359 String.valueOf( p0.getLatitude() != null ? p0.getLatitude() : "" ),
360 PHYLOXML_TAG.DIST_LAT );
361 addSubelementEditable( category,
362 NodePanel.DIST_LONGITUDE,
363 String.valueOf( p0.getLongitude() != null ? p0.getLongitude() : "" ),
364 PHYLOXML_TAG.DIST_LONG );
365 addSubelementEditable( category,
366 NodePanel.DIST_ALTITUDE,
367 String.valueOf( p0.getAltitude() != null ? p0.getAltitude() : "" ),
368 PHYLOXML_TAG.DIST_ALT );
369 addSubelementEditable( category,
370 NodePanel.DIST_ALT_UNIT,
371 String.valueOf( p0.getAltiudeUnit() != null ? p0.getAltiudeUnit() : "" ),
372 PHYLOXML_TAG.DIST_ALT_UNIT );
375 private void addEvents( final DefaultMutableTreeNode top, Event events, final String name ) {
376 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
377 if ( events == null ) {
378 events = new Event();
381 addSubelementEditable( category,
382 NodePanel.EVENTS_DUPLICATIONS,
383 String.valueOf( events.getNumberOfDuplications() >= 0 ? events.getNumberOfDuplications()
385 PHYLOXML_TAG.EVENTS_DUPLICATIONS );
386 addSubelementEditable( category,
387 NodePanel.EVENTS_SPECIATIONS,
388 String.valueOf( events.getNumberOfSpeciations() >= 0 ? events.getNumberOfSpeciations()
390 PHYLOXML_TAG.EVENTS_SPECIATIONS );
391 addSubelementEditable( category,
392 NodePanel.EVENTS_GENE_LOSSES,
393 String.valueOf( events.getNumberOfGeneLosses() >= 0 ? events.getNumberOfGeneLosses() : 0 ),
394 PHYLOXML_TAG.EVENTS_GENE_LOSSES );
397 private void addMapping( final DefaultMutableTreeNode mtn, final TagNumber tag ) {
398 if ( getMap().containsKey( mtn ) ) {
399 throw new IllegalArgumentException( "key " + mtn + " already present" );
401 if ( getMap().containsValue( tag ) ) {
402 throw new IllegalArgumentException( "value " + tag + " already present" );
404 getMap().put( mtn, tag );
407 private void addReference( final DefaultMutableTreeNode top, Reference ref, final String name ) {
409 ref = new Reference( "" );
411 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
413 addSubelementEditable( category,
414 NodePanel.LIT_REFERENCE_DESC,
415 ref.getDescription(),
416 PHYLOXML_TAG.LIT_REFERENCE_DESC );
417 addSubelementEditable( category, NodePanel.LIT_REFERENCE_DOI, ref.getDoi(), PHYLOXML_TAG.LIT_REFERENCE_DOI );
420 private void addSequence( final DefaultMutableTreeNode top, Sequence seq, final String name ) {
422 seq = new Sequence();
424 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
426 Accession acc = seq.getAccession();
428 acc = new Accession( "", "" );
430 addSubelementEditable( category, NodePanel.SEQ_NAME, seq.getName(), PHYLOXML_TAG.SEQ_NAME );
431 addSubelementEditable( category, NodePanel.SEQ_SYMBOL, seq.getSymbol(), PHYLOXML_TAG.SEQ_SYMBOL );
432 addSubelementEditable( category, NodePanel.SEQ_GENE_NAME, seq.getGeneName(), PHYLOXML_TAG.SEQ_GENE_NAME );
433 addSubelementEditable( category,
434 NodePanel.SEQ_ACCESSION,
436 PHYLOXML_TAG.SEQ_ACC_VALUE,
439 PHYLOXML_TAG.SEQ_ACC_SOURCE );
440 addSubelementEditable( category, NodePanel.SEQ_LOCATION, seq.getLocation(), PHYLOXML_TAG.SEQ_LOCATION );
441 addSubelementEditable( category, NodePanel.SEQ_TYPE, seq.getType(), PHYLOXML_TAG.SEQ_TYPE );
442 addSubelementEditable( category, NodePanel.SEQ_MOL_SEQ, seq.getMolecularSequence(), PHYLOXML_TAG.SEQ_MOL_SEQ );
444 if ( seq.getUris() != null ) {
445 for( final Uri uri : seq.getUris() ) {
447 addSubelementEditable( category, NodePanel.SEQ_URI + " [" + uri_counter + "]", uri.getValue()
448 .toString(), PHYLOXML_TAG.SEQ_URI, uri_counter++ );
452 addSubelementEditable( category,
453 NodePanel.SEQ_URI + " [" + uri_counter + "]",
455 PHYLOXML_TAG.SEQ_URI,
457 // addAnnotations( top, seq.getAnnotations(), category );
460 private void addSubelementEditable( final DefaultMutableTreeNode node,
463 final PHYLOXML_TAG phyloxml_tag ) {
464 addSubelementEditable( node, name, value, phyloxml_tag, 0 );
467 private void addSubelementEditable( final DefaultMutableTreeNode node,
470 final PHYLOXML_TAG phyloxml_tag,
472 String my_value = value;
473 if ( ForesterUtil.isEmpty( my_value ) ) {
476 final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
477 final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
478 name_node.add( value_node );
479 node.add( name_node );
480 addMapping( name_node, new TagNumber( phyloxml_tag, number ) );
483 private void addSubelementEditable( final DefaultMutableTreeNode node,
486 final PHYLOXML_TAG phyloxml_value_tag,
487 final String source_name,
488 final String source_value,
489 final PHYLOXML_TAG phyloxml_source_tag ) {
490 addSubelementEditable( node, name, value, phyloxml_value_tag, source_name, source_value, phyloxml_source_tag, 0 );
493 private void addSubelementEditable( final DefaultMutableTreeNode node,
496 final PHYLOXML_TAG phyloxml_value_tag,
497 final String source_name,
498 final String source_value,
499 final PHYLOXML_TAG phyloxml_source_tag,
501 String my_value = value;
502 if ( ForesterUtil.isEmpty( my_value ) ) {
505 String my_source_value = source_value;
506 if ( ForesterUtil.isEmpty( my_source_value ) ) {
507 my_source_value = "";
509 final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
510 final DefaultMutableTreeNode source_name_node = new DefaultMutableTreeNode( source_name );
511 final DefaultMutableTreeNode source_value_node = new DefaultMutableTreeNode( my_source_value );
512 final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
513 name_node.add( source_name_node );
514 source_name_node.add( source_value_node );
515 name_node.add( value_node );
516 node.add( name_node );
517 addMapping( name_node, new TagNumber( phyloxml_value_tag, number ) );
518 addMapping( source_name_node, new TagNumber( phyloxml_source_tag, number ) );
521 private void addTaxonomy( final DefaultMutableTreeNode top, Taxonomy tax, final String name ) {
523 tax = new Taxonomy();
525 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
527 Identifier id = tax.getIdentifier();
529 id = new Identifier();
531 addSubelementEditable( category,
532 NodePanel.TAXONOMY_IDENTIFIER,
534 PHYLOXML_TAG.TAXONOMY_ID_VALUE,
537 PHYLOXML_TAG.TAXONOMY_ID_PROVIDER );
538 addSubelementEditable( category, NodePanel.TAXONOMY_CODE, tax.getTaxonomyCode(), PHYLOXML_TAG.TAXONOMY_CODE );
539 addSubelementEditable( category,
540 NodePanel.TAXONOMY_SCIENTIFIC_NAME,
541 tax.getScientificName(),
542 PHYLOXML_TAG.TAXONOMY_SCIENTIFIC_NAME );
543 addSubelementEditable( category,
544 NodePanel.TAXONOMY_AUTHORITY,
546 PHYLOXML_TAG.TAXONOMY_AUTHORITY );
547 addSubelementEditable( category,
548 NodePanel.TAXONOMY_COMMON_NAME,
550 PHYLOXML_TAG.TAXONOMY_COMMON_NAME );
551 for( int i = tax.getSynonyms().size() - 1; i >= 0; i-- ) {
552 if ( ForesterUtil.isEmpty( tax.getSynonyms().get( i ) ) ) {
553 tax.getSynonyms().remove( i );
557 for( final String syn : tax.getSynonyms() ) {
558 addSubelementEditable( category,
559 NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
561 PHYLOXML_TAG.TAXONOMY_SYNONYM,
564 addSubelementEditable( category,
565 NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
567 PHYLOXML_TAG.TAXONOMY_SYNONYM,
569 addSubelementEditable( category, NodePanel.TAXONOMY_RANK, tax.getRank(), PHYLOXML_TAG.TAXONOMY_RANK );
571 if ( tax.getUris() != null ) {
572 for( final Uri uri : tax.getUris() ) {
574 addSubelementEditable( category, NodePanel.TAXONOMY_URI + " [" + uri_counter + "]", uri.getValue()
575 .toString(), PHYLOXML_TAG.TAXONOMY_URI, uri_counter++ );
579 addSubelementEditable( category,
580 NodePanel.TAXONOMY_URI + " [" + uri_counter + "]",
582 PHYLOXML_TAG.TAXONOMY_URI,
586 private void addUri( final DefaultMutableTreeNode mtn, final Uri uri, final int number, final MultipleUris mu ) {
588 if ( mu.getUris() == null ) {
589 mu.setUris( new ArrayList<Uri>() );
592 if ( ( uri != null ) && ( mu.getUris() == null ) ) {
593 mu.setUris( new ArrayList<Uri>() );
595 if ( ( uri != null ) && ( mu.getUris().size() == number ) ) {
596 mu.getUris().add( uri );
598 if ( ( mu.getUris() != null ) && ( mu.getUris().size() != number ) ) {
599 mu.getUris().set( number, uri );
601 final ImageLoader il = new ImageLoader( getTreePanel() );
602 new Thread( il ).start();
605 private void collapsePath( final String name ) {
606 final TreePath tp = getJTree().getNextMatch( name, 0, Position.Bias.Forward );
608 getJTree().collapsePath( tp );
612 private void createNodes( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node ) {
613 if ( !phylogeny_node.getNodeData().isHasTaxonomy() ) {
614 phylogeny_node.getNodeData().addTaxonomy( new Taxonomy() );
616 if ( !phylogeny_node.getNodeData().isHasSequence() ) {
617 phylogeny_node.getNodeData().addSequence( new Sequence() );
619 if ( !phylogeny_node.getNodeData().isHasDistribution() ) {
620 phylogeny_node.getNodeData().addDistribution( new Distribution( "" ) );
622 if ( !phylogeny_node.getNodeData().isHasReference() ) {
623 phylogeny_node.getNodeData().addReference( new Reference( "" ) );
625 addBasics( top, phylogeny_node, NodePanel.BASIC );
626 addTaxonomy( top, phylogeny_node.getNodeData().getTaxonomy(), NodePanel.TAXONOMY );
627 addSequence( top, phylogeny_node.getNodeData().getSequence(), NodePanel.SEQUENCE );
628 if ( !phylogeny_node.isExternal() ) {
629 addEvents( top, phylogeny_node.getNodeData().getEvent(), NodePanel.EVENTS );
631 addDate( top, phylogeny_node.getNodeData().getDate(), NodePanel.DATE );
632 addDistribution( top, phylogeny_node.getNodeData().getDistribution(), NodePanel.DISTRIBUTION );
633 addReference( top, phylogeny_node.getNodeData().getReference(), NodePanel.LIT_REFERENCE );
634 // addProperties( top, phylogeny_node.getNodeData().getProperties(), "Properties" );
637 private void formatError( final DefaultMutableTreeNode mtn, final PhyloXmlDataFormatException e ) {
638 JOptionPane.showMessageDialog( this, e.getMessage(), "Format error", JOptionPane.ERROR_MESSAGE );
639 mtn.setUserObject( "" );
640 getJTree().repaint();
643 private JTree getJTree() {
647 private Map<DefaultMutableTreeNode, TagNumber> getMap() {
651 private TagNumber getMapping( final DefaultMutableTreeNode mtn ) {
652 return getMap().get( mtn );
655 private DefaultMutableTreeNode getSelectedTreeNode() {
656 final TreePath selectionPath = getJTree().getSelectionPath();
657 if ( selectionPath != null ) {
658 final Object[] path = selectionPath.getPath();
659 if ( path.length > 0 ) {
660 return ( DefaultMutableTreeNode ) path[ path.length - 1 ]; // Last node
666 private TreePanel getTreePanel() {
670 private void keyEvent( final KeyEvent e ) {
671 if ( e.getKeyCode() == KeyEvent.VK_ENTER ) {
672 writeBack( getSelectedTreeNode() );
676 private List<Point> obtainPoints() {
677 ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
678 Distribution d = getMyNode().getNodeData().getDistribution();
679 if ( d.getPoints() == null ) {
680 d = new Distribution( d.getDesc(), new ArrayList<Point>(), d.getPolygons() );
681 getMyNode().getNodeData().setDistribution( d );
683 final List<Point> ps = d.getPoints();
684 if ( ps.isEmpty() ) {
685 ps.add( new Point() );
687 else if ( ps.get( 0 ) == null ) {
688 ps.set( 0, new Point() );
693 private BigDecimal parseBigDecimal( final DefaultMutableTreeNode mtn, final String value ) {
694 if ( ForesterUtil.isEmpty( value ) ) {
695 return new BigDecimal( 0 );
699 i = new BigDecimal( value );
701 catch ( final NumberFormatException e ) {
702 JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
703 mtn.setUserObject( "" );
708 private int parsePositiveInt( final DefaultMutableTreeNode mtn, final String value ) {
709 if ( ForesterUtil.isEmpty( value ) ) {
714 i = ForesterUtil.parseInt( value );
716 catch ( final ParseException e ) {
717 JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
718 mtn.setUserObject( "" );
721 JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
722 mtn.setUserObject( "" );
727 private void writeBack( final DefaultMutableTreeNode mtn ) {
728 if ( !getMap().containsKey( mtn ) ) {
729 final DefaultMutableTreeNode parent = ( DefaultMutableTreeNode ) mtn.getParent();
730 if ( getMap().containsKey( parent ) ) {
731 writeBack( mtn, getMapping( parent ) );
736 private void writeBack( final DefaultMutableTreeNode mtn, final TagNumber tag_number ) {
737 if ( tag_number == null ) {
740 String value = mtn.toString();
741 if ( value == null ) {
744 value = value.replaceAll( "\\s+", " " );
745 value = value.trim();
746 mtn.setUserObject( value );
747 getJTree().repaint();
748 final PHYLOXML_TAG tag = tag_number.getTag();
749 final int number = tag_number.getNumber();
752 getMyNode().setName( value );
754 case NODE_BRANCH_LENGTH:
755 if ( ForesterUtil.isEmpty( value ) ) {
756 getMyNode().setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
760 getMyNode().setDistanceToParent( ForesterUtil.parseDouble( value ) );
762 catch ( final ParseException e ) {
763 JOptionPane.showMessageDialog( this,
764 "failed to parse branch length from: " + value,
766 JOptionPane.ERROR_MESSAGE );
767 mtn.setUserObject( "" );
771 case NODE_BRANCH_WIDTH:
772 if ( ForesterUtil.isEmpty( value ) || value.equals( "1" ) ) {
773 if ( getMyNode().getBranchData().getBranchWidth() != null ) {
774 getMyNode().getBranchData().setBranchWidth( new BranchWidth() );
779 final double bw = ForesterUtil.parseDouble( value );
781 getMyNode().getBranchData().setBranchWidth( new BranchWidth( bw ) );
784 catch ( final ParseException e ) {
785 JOptionPane.showMessageDialog( this,
786 "failed to parse branch width from: " + value,
788 JOptionPane.ERROR_MESSAGE );
789 mtn.setUserObject( "" );
793 case CONFIDENCE_VALUE:
794 double confidence = Confidence.CONFIDENCE_DEFAULT_VALUE;
795 if ( !ForesterUtil.isEmpty( value ) ) {
797 confidence = ForesterUtil.parseDouble( value );
799 catch ( final ParseException e ) {
800 JOptionPane.showMessageDialog( this,
801 "failed to parse confidence value from: " + value,
803 JOptionPane.ERROR_MESSAGE );
804 mtn.setUserObject( "" );
808 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
809 throw new FailedConditionCheckException();
811 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
812 if ( confidence >= 0 ) {
813 getMyNode().getBranchData().getConfidences().add( new Confidence( confidence, "unknown" ) );
817 final String type = getMyNode().getBranchData().getConfidences().get( number ).getType();
818 final double sd = getMyNode().getBranchData().getConfidences().get( number ).getStandardDeviation();
819 getMyNode().getBranchData().getConfidences().set( number, new Confidence( confidence, type, sd ) );
822 case CONFIDENCE_TYPE:
823 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
824 throw new FailedConditionCheckException();
826 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
827 if ( !ForesterUtil.isEmpty( value ) ) {
828 getMyNode().getBranchData().getConfidences().add( new Confidence( 0, value ) );
832 final double v = getMyNode().getBranchData().getConfidences().get( number ).getValue();
833 final double sd = getMyNode().getBranchData().getConfidences().get( number ).getStandardDeviation();
834 getMyNode().getBranchData().getConfidences().set( number, new Confidence( v, value, sd ) );
838 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
840 getMyNode().getNodeData().getTaxonomy().setTaxonomyCode( value );
842 catch ( final PhyloXmlDataFormatException e ) {
843 formatError( mtn, e );
847 case TAXONOMY_SCIENTIFIC_NAME:
848 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
849 getMyNode().getNodeData().getTaxonomy().setScientificName( value );
851 case TAXONOMY_COMMON_NAME:
852 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
853 getMyNode().getNodeData().getTaxonomy().setCommonName( value );
856 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
858 getMyNode().getNodeData().getTaxonomy().setRank( value.toLowerCase() );
860 catch ( final PhyloXmlDataFormatException e ) {
861 formatError( mtn, e );
865 case TAXONOMY_AUTHORITY:
866 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
867 getMyNode().getNodeData().getTaxonomy().setAuthority( value );
871 if ( !ForesterUtil.isEmpty( value ) ) {
873 uri = new Uri( new URL( value ).toURI() );
875 catch ( final Exception e ) {
876 JOptionPane.showMessageDialog( this,
877 "failed to parse URL from: " + value,
879 JOptionPane.ERROR_MESSAGE );
880 mtn.setUserObject( "" );
884 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
886 addUri( mtn, uri, number, getMyNode().getNodeData().getTaxonomy() );
889 case TAXONOMY_SYNONYM:
890 if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() < number ) {
891 throw new FailedConditionCheckException();
893 else if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() == number ) {
894 if ( !ForesterUtil.isEmpty( value ) ) {
895 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
896 getMyNode().getNodeData().getTaxonomy().getSynonyms().add( value );
900 getMyNode().getNodeData().getTaxonomy().getSynonyms().set( number, value );
903 case TAXONOMY_ID_VALUE:
904 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
905 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
906 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value ) );
909 final String provider = getMyNode().getNodeData().getTaxonomy().getIdentifier().getProvider();
910 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value, provider ) );
913 case TAXONOMY_ID_PROVIDER:
914 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
915 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
916 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( "", value ) );
919 final String v = getMyNode().getNodeData().getTaxonomy().getIdentifier().getValue();
920 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( v, value ) );
924 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
925 getMyNode().getNodeData().getSequence().setLocation( value );
928 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
929 getMyNode().getNodeData().getSequence().setMolecularSequence( value.replaceAll( "[^a-zA-Z-]", "" ) );
932 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
933 getMyNode().getNodeData().getSequence().setName( value );
936 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
938 getMyNode().getNodeData().getSequence().setSymbol( value );
940 catch ( final PhyloXmlDataFormatException e ) {
941 formatError( mtn, e );
946 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
947 getMyNode().getNodeData().getSequence().setGeneName( value );
950 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
952 getMyNode().getNodeData().getSequence().setType( value.toLowerCase() );
954 catch ( final PhyloXmlDataFormatException e ) {
955 formatError( mtn, e );
960 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
961 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
962 getMyNode().getNodeData().getSequence().setAccession( new Accession( "", value ) );
965 final String v = getMyNode().getNodeData().getSequence().getAccession().getValue();
966 getMyNode().getNodeData().getSequence().setAccession( new Accession( v, value ) );
970 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
971 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
972 getMyNode().getNodeData().getSequence().setAccession( new Accession( value, "" ) );
975 final String source = getMyNode().getNodeData().getSequence().getAccession().getSource();
976 getMyNode().getNodeData().getSequence().setAccession( new Accession( value, source ) );
981 if ( !ForesterUtil.isEmpty( value ) ) {
983 uri = new Uri( new URL( value ).toURI() );
985 catch ( final Exception e ) {
986 JOptionPane.showMessageDialog( this,
987 "failed to parse URL from: " + value,
989 JOptionPane.ERROR_MESSAGE );
990 mtn.setUserObject( "" );
994 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
996 addUri( mtn, uri, number, getMyNode().getNodeData().getSequence() );
999 case LIT_REFERENCE_DESC:
1000 if ( !getMyNode().getNodeData().isHasReference() ) {
1001 getMyNode().getNodeData().setReference( new Reference( "" ) );
1003 getMyNode().getNodeData().getReference().setValue( value );
1005 case LIT_REFERENCE_DOI:
1006 if ( !getMyNode().getNodeData().isHasReference() ) {
1007 getMyNode().getNodeData().setReference( new Reference( "" ) );
1010 getMyNode().getNodeData().getReference().setDoi( value );
1012 catch ( final PhyloXmlDataFormatException e ) {
1013 formatError( mtn, e );
1017 case EVENTS_DUPLICATIONS:
1018 if ( !getMyNode().getNodeData().isHasEvent() ) {
1019 getMyNode().getNodeData().setEvent( new Event() );
1021 getMyNode().getNodeData().getEvent().setDuplications( parsePositiveInt( mtn, value ) );
1023 case EVENTS_SPECIATIONS:
1024 if ( !getMyNode().getNodeData().isHasEvent() ) {
1025 getMyNode().getNodeData().setEvent( new Event() );
1027 getMyNode().getNodeData().getEvent().setSpeciations( parsePositiveInt( mtn, value ) );
1029 case EVENTS_GENE_LOSSES:
1030 if ( !getMyNode().getNodeData().isHasEvent() ) {
1031 getMyNode().getNodeData().setEvent( new Event() );
1033 getMyNode().getNodeData().getEvent().setGeneLosses( parsePositiveInt( mtn, value ) );
1035 case DATE_DESCRIPTION:
1036 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1037 getMyNode().getNodeData().getDate().setDesc( value );
1040 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1041 getMyNode().getNodeData().getDate().setMax( parseBigDecimal( mtn, value ) );
1044 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1045 getMyNode().getNodeData().getDate().setMin( parseBigDecimal( mtn, value ) );
1048 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1049 getMyNode().getNodeData().getDate().setUnit( value );
1052 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1053 getMyNode().getNodeData().getDate().setValue( parseBigDecimal( mtn, value ) );
1056 final BigDecimal new_value = parseBigDecimal( mtn, value );
1057 if ( new_value != null ) {
1058 final List<Point> ps = obtainPoints();
1059 final Point p = ps.get( 0 );
1060 final Point p_new = new Point( p.getGeodeticDatum(),
1064 ForesterUtil.isEmpty( p.getAltiudeUnit() ) ? "?"
1065 : p.getAltiudeUnit() );
1071 ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
1072 final Distribution d = getMyNode().getNodeData().getDistribution();
1073 getMyNode().getNodeData().setDistribution( new Distribution( value, d.getPoints(), d.getPolygons() ) );
1076 case DIST_GEODETIC: {
1077 if ( !ForesterUtil.isEmpty( value ) ) {
1078 final List<Point> ps = obtainPoints();
1079 final Point p = ps.get( 0 );
1080 final Point p_new = new Point( value,
1084 p.getAltiudeUnit() );
1089 case DIST_ALT_UNIT: {
1090 if ( !ForesterUtil.isEmpty( value ) ) {
1091 final List<Point> ps = obtainPoints();
1092 final Point p = ps.get( 0 );
1093 final Point p_new = new Point( p.getGeodeticDatum(),
1103 final BigDecimal new_value = parseBigDecimal( mtn, value );
1104 if ( new_value != null ) {
1105 final List<Point> ps = obtainPoints();
1106 final Point p = ps.get( 0 );
1107 final Point p_new = new Point( p.getGeodeticDatum(),
1111 p.getAltiudeUnit() );
1117 final BigDecimal new_value = parseBigDecimal( mtn, value );
1118 if ( new_value != null ) {
1119 final List<Point> ps = obtainPoints();
1120 final Point p = ps.get( 0 );
1121 final Point p_new = new Point( p.getGeodeticDatum(),
1125 p.getAltiudeUnit() );
1131 throw new IllegalArgumentException( "unknown: " + tag );
1134 getJTree().repaint();
1135 getTreePanel().setEdited( true );
1136 getTreePanel().repaint();
1139 PhylogenyNode getMyNode() {
1144 //TODO this does not do what it should do.
1145 for( int i = 0; i < getJTree().getRowCount(); i++ ) {
1146 final TreePath p = getJTree().getPathForRow( i );
1147 writeBack( ( DefaultMutableTreeNode ) p.getLastPathComponent() );