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.event.KeyEvent;
29 import java.awt.event.KeyListener;
30 import java.math.BigDecimal;
32 import java.text.ParseException;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
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;
51 import org.forester.archaeopteryx.tools.ImageLoader;
52 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
53 import org.forester.phylogeny.PhylogenyNode;
54 import org.forester.phylogeny.data.Accession;
55 import org.forester.phylogeny.data.BranchWidth;
56 import org.forester.phylogeny.data.Confidence;
57 import org.forester.phylogeny.data.Date;
58 import org.forester.phylogeny.data.Distribution;
59 import org.forester.phylogeny.data.Event;
60 import org.forester.phylogeny.data.Identifier;
61 import org.forester.phylogeny.data.MultipleUris;
62 import org.forester.phylogeny.data.PhylogenyData;
63 import org.forester.phylogeny.data.PhylogenyDataUtil;
64 import org.forester.phylogeny.data.Point;
65 import org.forester.phylogeny.data.Reference;
66 import org.forester.phylogeny.data.Sequence;
67 import org.forester.phylogeny.data.Taxonomy;
68 import org.forester.phylogeny.data.Uri;
69 import org.forester.util.FailedConditionCheckException;
70 import org.forester.util.ForesterUtil;
72 class NodeEditPanel extends JPanel {
74 private static final long serialVersionUID = 5120159904388100771L;
75 private final JTree _tree;
76 private final JEditorPane _pane;
77 private final PhylogenyNode _my_node;
78 private final TreePanel _tree_panel;
79 private final Map<DefaultMutableTreeNode, TagNumber> _map;
81 public NodeEditPanel( final PhylogenyNode phylogeny_node, final TreePanel tree_panel ) {
82 _map = new HashMap<DefaultMutableTreeNode, TagNumber>();
83 _my_node = phylogeny_node;
84 _tree_panel = tree_panel;
85 String node_name = "";
86 if ( !ForesterUtil.isEmpty( phylogeny_node.getName() ) ) {
87 node_name = phylogeny_node.getName() + " ";
89 final DefaultMutableTreeNode top = new DefaultMutableTreeNode( "Node " + node_name );
90 createNodes( top, phylogeny_node );
91 _tree = new JTree( top );
92 getJTree().setEditable( true );
93 getJTree().setFocusable( true );
94 getJTree().setToggleClickCount( 1 );
95 getJTree().setInvokesStopCellEditing( true );
96 final JScrollPane tree_view = new JScrollPane( getJTree() );
97 _pane = new JEditorPane();
98 _pane.setEditable( true );
99 final JScrollPane data_view = new JScrollPane( _pane );
100 final JSplitPane split_pane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
101 split_pane.setTopComponent( tree_view );
102 // split_pane.setBottomComponent( data_view );
103 data_view.setMinimumSize( Constants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
104 tree_view.setMinimumSize( Constants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
105 // split_pane.setDividerLocation( 400 );
106 split_pane.setPreferredSize( Constants.NODE_PANEL_SIZE );
108 getJTree().getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
109 getJTree().addKeyListener( new KeyListener() {
112 public void keyPressed( final KeyEvent e ) {
117 public void keyReleased( final KeyEvent e ) {
122 public void keyTyped( final KeyEvent e ) {
126 for( int i = 0; i < getJTree().getRowCount(); i++ ) {
127 getJTree().expandRow( i );
129 collapsePath( NodePanel.BASIC );
130 collapsePath( NodePanel.TAXONOMY );
131 collapsePath( NodePanel.SEQUENCE );
132 collapsePath( NodePanel.EVENTS );
133 collapsePath( NodePanel.DATE );
134 collapsePath( NodePanel.DISTRIBUTION );
135 collapsePath( NodePanel.LIT_REFERENCE );
136 getJTree().addTreeSelectionListener( new TreeSelectionListener() {
139 public void valueChanged( final TreeSelectionEvent e ) {
140 final TreePath new_path = e.getNewLeadSelectionPath();
141 final TreePath old_path = e.getOldLeadSelectionPath();
142 if ( new_path != null ) {
143 writeBack( ( DefaultMutableTreeNode ) new_path.getLastPathComponent() );
145 if ( old_path != null ) {
146 writeBack( ( DefaultMutableTreeNode ) old_path.getLastPathComponent() );
152 private void addBasics( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node, final String name ) {
153 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
155 addSubelementEditable( category, NodePanel.NODE_NAME, phylogeny_node.getName(), PHYLOXML_TAG.NODE_NAME );
157 if ( phylogeny_node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
158 bl = ForesterUtil.FORMATTER_6.format( phylogeny_node.getDistanceToParent() );
160 addSubelementEditable( category, NodePanel.NODE_BRANCH_LENGTH, bl, PHYLOXML_TAG.NODE_BRANCH_LENGTH );
162 if ( phylogeny_node.getBranchData().isHasConfidences() ) {
163 for( int i = phylogeny_node.getBranchData().getConfidences().size() - 1; i >= 0; i-- ) {
164 if ( phylogeny_node.getBranchData().getConfidences().get( i ).getValue() == Confidence.CONFIDENCE_DEFAULT_VALUE ) {
165 phylogeny_node.getBranchData().getConfidences().remove( i );
168 for( final PhylogenyData conf : phylogeny_node.getBranchData().getConfidences() ) {
169 final Confidence my_conf = ( Confidence ) ( conf );
170 addSubelementEditable( category,
171 NodePanel.CONFIDENCE + " [" + counter + "]",
172 ForesterUtil.FORMATTER_6.format( my_conf.getValue() ),
173 PHYLOXML_TAG.CONFIDENCE_VALUE,
174 NodePanel.CONFIDENCE_TYPE,
176 PHYLOXML_TAG.CONFIDENCE_TYPE,
180 addSubelementEditable( category,
181 NodePanel.CONFIDENCE + " [" + counter + "]",
183 PHYLOXML_TAG.CONFIDENCE_VALUE,
184 NodePanel.CONFIDENCE_TYPE,
186 PHYLOXML_TAG.CONFIDENCE_TYPE,
189 if ( ( phylogeny_node.getBranchData().getBranchWidth() != null )
190 && ( phylogeny_node.getBranchData().getBranchWidth().getValue() != BranchWidth.BRANCH_WIDTH_DEFAULT_VALUE ) ) {
191 bw = ForesterUtil.FORMATTER_3.format( phylogeny_node.getBranchData().getBranchWidth().getValue() );
193 addSubelementEditable( category, NodePanel.NODE_BRANCH_WIDTH, bw, PHYLOXML_TAG.NODE_BRANCH_WIDTH );
196 // private void addAnnotation( final DefaultMutableTreeNode top, final Annotation ann, final String name ) {
197 // DefaultMutableTreeNode category;
198 // category = new DefaultMutableTreeNode( name );
199 // top.add( category );
200 // addSubelementEditable( category, "Reference", ann.getRef() , PHYLOXML_TAG.);
201 // addSubelementEditable( category, "Description", ann.getDesc() , PHYLOXML_TAG.);
202 // addSubelementEditable( category, "Source", ann.getSource(), PHYLOXML_TAG. );
203 // addSubelementEditable( category, "Type", ann.getType(), PHYLOXML_TAG. );
204 // addSubelementEditable( category, "Evidence", ann.getEvidence() , PHYLOXML_TAG.);
205 // if ( ann.getConfidence() != null ) {
206 // addSubelementEditable( category, "Confidence", ann.getConfidence().asText().toString() , PHYLOXML_TAG.);
208 // if ( ann.getProperties() != null ) {
209 // addProperties( category, ann.getProperties(), "Properties", PHYLOXML_TAG. );
212 // private void addAnnotations( final DefaultMutableTreeNode top,
213 // final List<PhylogenyData> annotations,
214 // final DefaultMutableTreeNode category ) {
215 // if ( ( annotations != null ) && ( annotations.size() > 0 ) ) {
216 // category.add( new DefaultMutableTreeNode( "Annotations" ) );
217 // final DefaultMutableTreeNode last = top.getLastLeaf();
219 // for( final PhylogenyData ann : annotations ) {
220 // addAnnotation( last, ( Annotation ) ann, "Annotation " + ( i++ ) );
224 private void addDate( final DefaultMutableTreeNode top, Date date, final String name ) {
225 if ( date == null ) {
228 DefaultMutableTreeNode category;
229 category = new DefaultMutableTreeNode( name );
231 addSubelementEditable( category, NodePanel.DATE_DESCRIPTION, date.getDesc(), PHYLOXML_TAG.DATE_DESCRIPTION );
232 addSubelementEditable( category,
233 NodePanel.DATE_VALUE,
234 String.valueOf( date.getValue() != null ? date.getValue() : "" ),
235 PHYLOXML_TAG.DATE_VALUE );
236 addSubelementEditable( category,
238 String.valueOf( date.getMin() != null ? date.getMin() : "" ),
239 PHYLOXML_TAG.DATE_MIN );
240 addSubelementEditable( category,
242 String.valueOf( date.getMax() != null ? date.getMax() : "" ),
243 PHYLOXML_TAG.DATE_MAX );
244 addSubelementEditable( category, NodePanel.DATE_UNIT, date.getUnit(), PHYLOXML_TAG.DATE_UNIT );
247 private void addDistribution( final DefaultMutableTreeNode top, Distribution dist, final String name ) {
248 if ( dist == null ) {
249 dist = new Distribution( "" );
251 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
254 if ( ( dist.getPoints() != null ) && ( dist.getPoints().size() > 0 ) ) {
255 p0 = dist.getPoints().get( 0 );
260 addSubelementEditable( category, NodePanel.DIST_DESCRIPTION, dist.getDesc(), PHYLOXML_TAG.DIST_DESC );
261 addSubelementEditable( category,
262 NodePanel.DIST_GEODETIC_DATUM,
263 p0.getGeodeticDatum(),
264 PHYLOXML_TAG.DIST_GEODETIC );
265 addSubelementEditable( category,
266 NodePanel.DIST_LATITUDE,
267 String.valueOf( p0.getLatitude() != null ? p0.getLatitude() : "" ),
268 PHYLOXML_TAG.DIST_LAT );
269 addSubelementEditable( category,
270 NodePanel.DIST_LONGITUDE,
271 String.valueOf( p0.getLongitude() != null ? p0.getLongitude() : "" ),
272 PHYLOXML_TAG.DIST_LONG );
273 addSubelementEditable( category,
274 NodePanel.DIST_ALTITUDE,
275 String.valueOf( p0.getAltitude() != null ? p0.getAltitude() : "" ),
276 PHYLOXML_TAG.DIST_ALT );
277 addSubelementEditable( category,
278 NodePanel.DIST_ALT_UNIT,
279 String.valueOf( p0.getAltiudeUnit() != null ? p0.getAltiudeUnit() : "" ),
280 PHYLOXML_TAG.DIST_ALT_UNIT );
283 private void addEvents( final DefaultMutableTreeNode top, Event events, final String name ) {
284 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
285 if ( events == null ) {
286 events = new Event();
289 addSubelementEditable( category,
290 NodePanel.EVENTS_DUPLICATIONS,
291 String.valueOf( events.getNumberOfDuplications() >= 0 ? events.getNumberOfDuplications()
293 PHYLOXML_TAG.EVENTS_DUPLICATIONS );
294 addSubelementEditable( category,
295 NodePanel.EVENTS_SPECIATIONS,
296 String.valueOf( events.getNumberOfSpeciations() >= 0 ? events.getNumberOfSpeciations()
298 PHYLOXML_TAG.EVENTS_SPECIATIONS );
299 addSubelementEditable( category,
300 NodePanel.EVENTS_GENE_LOSSES,
301 String.valueOf( events.getNumberOfGeneLosses() >= 0 ? events.getNumberOfGeneLosses() : 0 ),
302 PHYLOXML_TAG.EVENTS_GENE_LOSSES );
305 private void addMapping( final DefaultMutableTreeNode mtn, final TagNumber tag ) {
306 if ( getMap().containsKey( mtn ) ) {
307 throw new IllegalArgumentException( "key " + mtn + " already present" );
309 if ( getMap().containsValue( tag ) ) {
310 throw new IllegalArgumentException( "value " + tag + " already present" );
312 getMap().put( mtn, tag );
315 private void addReference( final DefaultMutableTreeNode top, Reference ref, final String name ) {
317 ref = new Reference( "" );
319 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
321 addSubelementEditable( category,
322 NodePanel.LIT_REFERENCE_DESC,
323 ref.getDescription(),
324 PHYLOXML_TAG.LIT_REFERENCE_DESC );
325 addSubelementEditable( category, NodePanel.LIT_REFERENCE_DOI, ref.getDoi(), PHYLOXML_TAG.LIT_REFERENCE_DOI );
328 private void addSequence( final DefaultMutableTreeNode top, Sequence seq, final String name ) {
330 seq = new Sequence();
332 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
334 Accession acc = seq.getAccession();
336 acc = new Accession( "", "" );
338 addSubelementEditable( category, NodePanel.SEQ_NAME, seq.getName(), PHYLOXML_TAG.SEQ_NAME );
339 addSubelementEditable( category, NodePanel.SEQ_SYMBOL, seq.getSymbol(), PHYLOXML_TAG.SEQ_SYMBOL );
340 addSubelementEditable( category, NodePanel.SEQ_GENE_NAME, seq.getGeneName(), PHYLOXML_TAG.SEQ_GENE_NAME );
341 addSubelementEditable( category,
342 NodePanel.SEQ_ACCESSION,
344 PHYLOXML_TAG.SEQ_ACC_VALUE,
347 PHYLOXML_TAG.SEQ_ACC_SOURCE );
348 addSubelementEditable( category, NodePanel.SEQ_LOCATION, seq.getLocation(), PHYLOXML_TAG.SEQ_LOCATION );
349 addSubelementEditable( category, NodePanel.SEQ_TYPE, seq.getType(), PHYLOXML_TAG.SEQ_TYPE );
350 addSubelementEditable( category, NodePanel.SEQ_MOL_SEQ, seq.getMolecularSequence(), PHYLOXML_TAG.SEQ_MOL_SEQ );
352 if ( seq.getUris() != null ) {
353 for( final Uri uri : seq.getUris() ) {
355 addSubelementEditable( category, NodePanel.SEQ_URI + " [" + uri_counter + "]", uri.getValue()
356 .toString(), PHYLOXML_TAG.SEQ_URI, uri_counter++ );
360 addSubelementEditable( category,
361 NodePanel.SEQ_URI + " [" + uri_counter + "]",
363 PHYLOXML_TAG.SEQ_URI,
365 // addAnnotations( top, seq.getAnnotations(), category );
368 private void addSubelementEditable( final DefaultMutableTreeNode node,
371 final PHYLOXML_TAG phyloxml_tag ) {
372 addSubelementEditable( node, name, value, phyloxml_tag, 0 );
375 private void addSubelementEditable( final DefaultMutableTreeNode node,
378 final PHYLOXML_TAG phyloxml_tag,
380 String my_value = value;
381 if ( ForesterUtil.isEmpty( my_value ) ) {
384 final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
385 final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
386 name_node.add( value_node );
387 node.add( name_node );
388 addMapping( name_node, new TagNumber( phyloxml_tag, number ) );
391 private void addSubelementEditable( final DefaultMutableTreeNode node,
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 addSubelementEditable( node, name, value, phyloxml_value_tag, source_name, source_value, phyloxml_source_tag, 0 );
401 private void addSubelementEditable( final DefaultMutableTreeNode node,
404 final PHYLOXML_TAG phyloxml_value_tag,
405 final String source_name,
406 final String source_value,
407 final PHYLOXML_TAG phyloxml_source_tag,
409 String my_value = value;
410 if ( ForesterUtil.isEmpty( my_value ) ) {
413 String my_source_value = source_value;
414 if ( ForesterUtil.isEmpty( my_source_value ) ) {
415 my_source_value = "";
417 final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
418 final DefaultMutableTreeNode source_name_node = new DefaultMutableTreeNode( source_name );
419 final DefaultMutableTreeNode source_value_node = new DefaultMutableTreeNode( my_source_value );
420 final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
421 name_node.add( source_name_node );
422 source_name_node.add( source_value_node );
423 name_node.add( value_node );
424 node.add( name_node );
425 addMapping( name_node, new TagNumber( phyloxml_value_tag, number ) );
426 addMapping( source_name_node, new TagNumber( phyloxml_source_tag, number ) );
429 private void addTaxonomy( final DefaultMutableTreeNode top, Taxonomy tax, final String name ) {
431 tax = new Taxonomy();
433 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
435 Identifier id = tax.getIdentifier();
437 id = new Identifier();
439 addSubelementEditable( category,
440 NodePanel.TAXONOMY_IDENTIFIER,
442 PHYLOXML_TAG.TAXONOMY_ID_VALUE,
445 PHYLOXML_TAG.TAXONOMY_ID_PROVIDER );
446 addSubelementEditable( category, NodePanel.TAXONOMY_CODE, tax.getTaxonomyCode(), PHYLOXML_TAG.TAXONOMY_CODE );
447 addSubelementEditable( category,
448 NodePanel.TAXONOMY_SCIENTIFIC_NAME,
449 tax.getScientificName(),
450 PHYLOXML_TAG.TAXONOMY_SCIENTIFIC_NAME );
451 addSubelementEditable( category,
452 NodePanel.TAXONOMY_AUTHORITY,
454 PHYLOXML_TAG.TAXONOMY_AUTHORITY );
455 addSubelementEditable( category,
456 NodePanel.TAXONOMY_COMMON_NAME,
458 PHYLOXML_TAG.TAXONOMY_COMMON_NAME );
459 for( int i = tax.getSynonyms().size() - 1; i >= 0; i-- ) {
460 if ( ForesterUtil.isEmpty( tax.getSynonyms().get( i ) ) ) {
461 tax.getSynonyms().remove( i );
465 for( final String syn : tax.getSynonyms() ) {
466 addSubelementEditable( category,
467 NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
469 PHYLOXML_TAG.TAXONOMY_SYNONYM,
472 addSubelementEditable( category,
473 NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
475 PHYLOXML_TAG.TAXONOMY_SYNONYM,
477 addSubelementEditable( category, NodePanel.TAXONOMY_RANK, tax.getRank(), PHYLOXML_TAG.TAXONOMY_RANK );
479 if ( tax.getUris() != null ) {
480 for( final Uri uri : tax.getUris() ) {
482 addSubelementEditable( category, NodePanel.TAXONOMY_URI + " [" + uri_counter + "]", uri.getValue()
483 .toString(), PHYLOXML_TAG.TAXONOMY_URI, uri_counter++ );
487 addSubelementEditable( category,
488 NodePanel.TAXONOMY_URI + " [" + uri_counter + "]",
490 PHYLOXML_TAG.TAXONOMY_URI,
494 private void collapsePath( final String name ) {
495 final TreePath tp = getJTree().getNextMatch( name, 0, Position.Bias.Forward );
497 getJTree().collapsePath( tp );
501 private void createNodes( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node ) {
502 if ( !phylogeny_node.getNodeData().isHasTaxonomy() ) {
503 phylogeny_node.getNodeData().addTaxonomy( new Taxonomy() );
505 if ( !phylogeny_node.getNodeData().isHasSequence() ) {
506 phylogeny_node.getNodeData().addSequence( new Sequence() );
508 if ( !phylogeny_node.getNodeData().isHasDistribution() ) {
509 phylogeny_node.getNodeData().addDistribution( new Distribution( "" ) );
511 if ( !phylogeny_node.getNodeData().isHasReference() ) {
512 phylogeny_node.getNodeData().addReference( new Reference( "" ) );
514 addBasics( top, phylogeny_node, NodePanel.BASIC );
515 addTaxonomy( top, phylogeny_node.getNodeData().getTaxonomy(), NodePanel.TAXONOMY );
516 addSequence( top, phylogeny_node.getNodeData().getSequence(), NodePanel.SEQUENCE );
517 if ( !phylogeny_node.isExternal() ) {
518 addEvents( top, phylogeny_node.getNodeData().getEvent(), NodePanel.EVENTS );
520 addDate( top, phylogeny_node.getNodeData().getDate(), NodePanel.DATE );
521 addDistribution( top, phylogeny_node.getNodeData().getDistribution(), NodePanel.DISTRIBUTION );
522 addReference( top, phylogeny_node.getNodeData().getReference(), NodePanel.LIT_REFERENCE );
523 // addProperties( top, phylogeny_node.getNodeData().getProperties(), "Properties" );
526 private void formatError( final DefaultMutableTreeNode mtn, final PhyloXmlDataFormatException e ) {
527 JOptionPane.showMessageDialog( this, e.getMessage(), "Format error", JOptionPane.ERROR_MESSAGE );
528 mtn.setUserObject( "" );
529 getJTree().repaint();
532 private JTree getJTree() {
536 private Map<DefaultMutableTreeNode, TagNumber> getMap() {
540 private TagNumber getMapping( final DefaultMutableTreeNode mtn ) {
541 return getMap().get( mtn );
544 PhylogenyNode getMyNode() {
548 private DefaultMutableTreeNode getSelectedTreeNode() {
549 final TreePath selectionPath = getJTree().getSelectionPath();
550 if ( selectionPath != null ) {
551 final Object[] path = selectionPath.getPath();
552 if ( path.length > 0 ) {
553 return ( DefaultMutableTreeNode ) path[ path.length - 1 ]; // Last node
559 private TreePanel getTreePanel() {
563 private void keyEvent( final KeyEvent e ) {
564 if ( e.getKeyCode() == KeyEvent.VK_ENTER ) {
565 writeBack( getSelectedTreeNode() );
569 private List<Point> obtainPoints() {
570 ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
571 Distribution d = getMyNode().getNodeData().getDistribution();
572 if ( d.getPoints() == null ) {
573 d = new Distribution( d.getDesc(), new ArrayList<Point>(), d.getPolygons() );
574 getMyNode().getNodeData().setDistribution( d );
576 final List<Point> ps = d.getPoints();
577 if ( ps.isEmpty() ) {
578 ps.add( new Point() );
580 else if ( ps.get( 0 ) == null ) {
581 ps.set( 0, new Point() );
586 private BigDecimal parseBigDecimal( final DefaultMutableTreeNode mtn, final String value ) {
587 if ( ForesterUtil.isEmpty( value ) ) {
588 return new BigDecimal( 0 );
592 i = new BigDecimal( value );
594 catch ( final NumberFormatException e ) {
595 JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
596 mtn.setUserObject( "" );
601 private int parsePositiveInt( final DefaultMutableTreeNode mtn, final String value ) {
602 if ( ForesterUtil.isEmpty( value ) ) {
607 i = ForesterUtil.parseInt( value );
609 catch ( final ParseException e ) {
610 JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
611 mtn.setUserObject( "" );
614 JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
615 mtn.setUserObject( "" );
621 for( int i = 0; i < getJTree().getRowCount(); i++ ) {
622 final TreePath p = getJTree().getPathForRow( i );
623 writeBack( ( DefaultMutableTreeNode ) p.getLastPathComponent() );
627 private void writeBack( final DefaultMutableTreeNode mtn ) {
628 if ( !getMap().containsKey( mtn ) ) {
629 final DefaultMutableTreeNode parent = ( DefaultMutableTreeNode ) mtn.getParent();
630 if ( getMap().containsKey( parent ) ) {
631 writeBack( mtn, getMapping( parent ) );
636 private void writeBack( final DefaultMutableTreeNode mtn, final TagNumber tag_number ) {
637 if ( tag_number == null ) {
640 String value = mtn.toString();
641 if ( value == null ) {
644 value = value.replaceAll( "\\s+", " " );
645 value = value.trim();
646 mtn.setUserObject( value );
647 getJTree().repaint();
648 final PHYLOXML_TAG tag = tag_number.getTag();
649 final int number = tag_number.getNumber();
652 getMyNode().setName( value );
654 case NODE_BRANCH_LENGTH:
655 if ( ForesterUtil.isEmpty( value ) ) {
656 getMyNode().setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
660 getMyNode().setDistanceToParent( ForesterUtil.parseDouble( value ) );
662 catch ( final ParseException e ) {
663 JOptionPane.showMessageDialog( this,
664 "failed to parse branch length from: " + value,
666 JOptionPane.ERROR_MESSAGE );
667 mtn.setUserObject( "" );
671 case NODE_BRANCH_WIDTH:
672 if ( ForesterUtil.isEmpty( value ) || value.equals( "1" ) ) {
673 if ( getMyNode().getBranchData().getBranchWidth() != null ) {
674 getMyNode().getBranchData().setBranchWidth( new BranchWidth() );
679 final double bw = ForesterUtil.parseDouble( value );
681 getMyNode().getBranchData().setBranchWidth( new BranchWidth( bw ) );
684 catch ( final ParseException e ) {
685 JOptionPane.showMessageDialog( this,
686 "failed to parse branch width from: " + value,
688 JOptionPane.ERROR_MESSAGE );
689 mtn.setUserObject( "" );
693 case CONFIDENCE_VALUE:
694 double confidence = Confidence.CONFIDENCE_DEFAULT_VALUE;
695 if ( !ForesterUtil.isEmpty( value ) ) {
697 confidence = ForesterUtil.parseDouble( value );
699 catch ( final ParseException e ) {
700 JOptionPane.showMessageDialog( this,
701 "failed to parse confidence value from: " + value,
703 JOptionPane.ERROR_MESSAGE );
704 mtn.setUserObject( "" );
708 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
709 throw new FailedConditionCheckException();
711 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
712 if ( confidence >= 0 ) {
713 getMyNode().getBranchData().getConfidences().add( new Confidence( confidence, "unknown" ) );
717 final String type = getMyNode().getBranchData().getConfidences().get( number ).getType();
718 final double sd = getMyNode().getBranchData().getConfidences().get( number ).getStandardDeviation();
719 getMyNode().getBranchData().getConfidences().set( number, new Confidence( confidence, type, sd ) );
722 case CONFIDENCE_TYPE:
723 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
724 throw new FailedConditionCheckException();
726 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
727 if ( !ForesterUtil.isEmpty( value ) ) {
728 getMyNode().getBranchData().getConfidences().add( new Confidence( 0, value ) );
732 final double v = getMyNode().getBranchData().getConfidences().get( number ).getValue();
733 final double sd = getMyNode().getBranchData().getConfidences().get( number ).getStandardDeviation();
734 getMyNode().getBranchData().getConfidences().set( number, new Confidence( v, value, sd ) );
738 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
740 getMyNode().getNodeData().getTaxonomy().setTaxonomyCode( value );
742 catch ( final PhyloXmlDataFormatException e ) {
743 formatError( mtn, e );
747 case TAXONOMY_SCIENTIFIC_NAME:
748 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
749 getMyNode().getNodeData().getTaxonomy().setScientificName( value );
751 case TAXONOMY_COMMON_NAME:
752 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
753 getMyNode().getNodeData().getTaxonomy().setCommonName( value );
756 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
758 getMyNode().getNodeData().getTaxonomy().setRank( value.toLowerCase() );
760 catch ( final PhyloXmlDataFormatException e ) {
761 formatError( mtn, e );
765 case TAXONOMY_AUTHORITY:
766 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
767 getMyNode().getNodeData().getTaxonomy().setAuthority( value );
771 if ( !ForesterUtil.isEmpty( value ) ) {
773 uri = new Uri( new URL( value ).toURI() );
775 catch ( final Exception e ) {
776 JOptionPane.showMessageDialog( this,
777 "failed to parse URL from: " + value,
779 JOptionPane.ERROR_MESSAGE );
780 mtn.setUserObject( "" );
784 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
786 addUri( mtn, uri, number, getMyNode().getNodeData().getTaxonomy() );
789 case TAXONOMY_SYNONYM:
790 if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() < number ) {
791 throw new FailedConditionCheckException();
793 else if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() == number ) {
794 if ( !ForesterUtil.isEmpty( value ) ) {
795 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
796 getMyNode().getNodeData().getTaxonomy().getSynonyms().add( value );
800 getMyNode().getNodeData().getTaxonomy().getSynonyms().set( number, value );
803 case TAXONOMY_ID_VALUE:
804 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
805 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
806 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value ) );
809 final String provider = getMyNode().getNodeData().getTaxonomy().getIdentifier().getProvider();
810 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value, provider ) );
813 case TAXONOMY_ID_PROVIDER:
814 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
815 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
816 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( "", value ) );
819 final String v = getMyNode().getNodeData().getTaxonomy().getIdentifier().getValue();
820 getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( v, value ) );
824 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
825 getMyNode().getNodeData().getSequence().setLocation( value );
828 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
829 getMyNode().getNodeData().getSequence().setMolecularSequence( value.replaceAll( "[^a-zA-Z-]", "" ) );
832 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
833 getMyNode().getNodeData().getSequence().setName( value );
836 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
838 getMyNode().getNodeData().getSequence().setSymbol( value );
840 catch ( final PhyloXmlDataFormatException e ) {
841 formatError( mtn, e );
846 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
847 getMyNode().getNodeData().getSequence().setGeneName( value );
850 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
852 getMyNode().getNodeData().getSequence().setType( value.toLowerCase() );
854 catch ( final PhyloXmlDataFormatException e ) {
855 formatError( mtn, e );
860 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
861 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
862 getMyNode().getNodeData().getSequence().setAccession( new Accession( "", value ) );
865 final String v = getMyNode().getNodeData().getSequence().getAccession().getValue();
866 getMyNode().getNodeData().getSequence().setAccession( new Accession( v, value ) );
870 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
871 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
872 getMyNode().getNodeData().getSequence().setAccession( new Accession( value, "" ) );
875 final String source = getMyNode().getNodeData().getSequence().getAccession().getSource();
876 getMyNode().getNodeData().getSequence().setAccession( new Accession( value, source ) );
881 if ( !ForesterUtil.isEmpty( value ) ) {
883 uri = new Uri( new URL( value ).toURI() );
885 catch ( final Exception e ) {
886 JOptionPane.showMessageDialog( this,
887 "failed to parse URL from: " + value,
889 JOptionPane.ERROR_MESSAGE );
890 mtn.setUserObject( "" );
894 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
896 addUri( mtn, uri, number, getMyNode().getNodeData().getSequence() );
899 case LIT_REFERENCE_DESC:
900 if ( !getMyNode().getNodeData().isHasReference() ) {
901 getMyNode().getNodeData().setReference( new Reference( "" ) );
903 getMyNode().getNodeData().getReference().setValue( value );
905 case LIT_REFERENCE_DOI:
906 if ( !getMyNode().getNodeData().isHasReference() ) {
907 getMyNode().getNodeData().setReference( new Reference( "" ) );
910 getMyNode().getNodeData().getReference().setDoi( value );
912 catch ( final PhyloXmlDataFormatException e ) {
913 formatError( mtn, e );
917 case EVENTS_DUPLICATIONS:
918 if ( !getMyNode().getNodeData().isHasEvent() ) {
919 getMyNode().getNodeData().setEvent( new Event() );
921 getMyNode().getNodeData().getEvent().setDuplications( parsePositiveInt( mtn, value ) );
923 case EVENTS_SPECIATIONS:
924 if ( !getMyNode().getNodeData().isHasEvent() ) {
925 getMyNode().getNodeData().setEvent( new Event() );
927 getMyNode().getNodeData().getEvent().setSpeciations( parsePositiveInt( mtn, value ) );
929 case EVENTS_GENE_LOSSES:
930 if ( !getMyNode().getNodeData().isHasEvent() ) {
931 getMyNode().getNodeData().setEvent( new Event() );
933 getMyNode().getNodeData().getEvent().setGeneLosses( parsePositiveInt( mtn, value ) );
935 case DATE_DESCRIPTION:
936 ForesterUtil.ensurePresenceOfDate( getMyNode() );
937 getMyNode().getNodeData().getDate().setDesc( value );
940 ForesterUtil.ensurePresenceOfDate( getMyNode() );
941 getMyNode().getNodeData().getDate().setMax( parseBigDecimal( mtn, value ) );
944 ForesterUtil.ensurePresenceOfDate( getMyNode() );
945 getMyNode().getNodeData().getDate().setMin( parseBigDecimal( mtn, value ) );
948 ForesterUtil.ensurePresenceOfDate( getMyNode() );
949 getMyNode().getNodeData().getDate().setUnit( value );
952 ForesterUtil.ensurePresenceOfDate( getMyNode() );
953 getMyNode().getNodeData().getDate().setValue( parseBigDecimal( mtn, value ) );
956 final BigDecimal new_value = parseBigDecimal( mtn, value );
957 if ( new_value != null ) {
958 final List<Point> ps = obtainPoints();
959 final Point p = ps.get( 0 );
960 final Point p_new = new Point( p.getGeodeticDatum(),
964 ForesterUtil.isEmpty( p.getAltiudeUnit() ) ? "?"
965 : p.getAltiudeUnit() );
971 ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
972 final Distribution d = getMyNode().getNodeData().getDistribution();
973 getMyNode().getNodeData().setDistribution( new Distribution( value, d.getPoints(), d.getPolygons() ) );
976 case DIST_GEODETIC: {
977 if ( !ForesterUtil.isEmpty( value ) ) {
978 final List<Point> ps = obtainPoints();
979 final Point p = ps.get( 0 );
980 final Point p_new = new Point( value,
984 p.getAltiudeUnit() );
989 case DIST_ALT_UNIT: {
990 if ( !ForesterUtil.isEmpty( value ) ) {
991 final List<Point> ps = obtainPoints();
992 final Point p = ps.get( 0 );
993 final Point p_new = new Point( p.getGeodeticDatum(),
1003 final BigDecimal new_value = parseBigDecimal( mtn, value );
1004 if ( new_value != null ) {
1005 final List<Point> ps = obtainPoints();
1006 final Point p = ps.get( 0 );
1007 final Point p_new = new Point( p.getGeodeticDatum(),
1011 p.getAltiudeUnit() );
1017 final BigDecimal new_value = parseBigDecimal( mtn, value );
1018 if ( new_value != null ) {
1019 final List<Point> ps = obtainPoints();
1020 final Point p = ps.get( 0 );
1021 final Point p_new = new Point( p.getGeodeticDatum(),
1025 p.getAltiudeUnit() );
1031 throw new IllegalArgumentException( "unknown: " + tag );
1033 getJTree().repaint();
1034 getTreePanel().setEdited( true );
1035 getTreePanel().repaint();
1038 private void addUri( final DefaultMutableTreeNode mtn, final Uri uri, final int number, final MultipleUris mu ) {
1039 if ( uri != null ) {
1040 if ( mu.getUris() == null ) {
1041 mu.setUris( new ArrayList<Uri>() );
1044 if ( ( uri != null ) && ( mu.getUris() == null ) ) {
1045 mu.setUris( new ArrayList<Uri>() );
1047 if ( ( uri != null ) && ( mu.getUris().size() == number ) ) {
1048 mu.getUris().add( uri );
1050 if ( ( mu.getUris() != null ) && ( mu.getUris().size() != number ) ) {
1051 mu.getUris().set( number, uri );
1053 final ImageLoader il = new ImageLoader( getTreePanel() );
1054 new Thread( il ).start();
1057 private enum PHYLOXML_TAG {
1062 TAXONOMY_SCIENTIFIC_NAME,
1064 TAXONOMY_COMMON_NAME,
1081 TAXONOMY_ID_PROVIDER,
1088 EVENTS_DUPLICATIONS,
1099 private class TagNumber {
1101 final private PHYLOXML_TAG _tag;
1102 final private int _number;
1104 TagNumber( final PHYLOXML_TAG tag, final int number ) {
1113 PHYLOXML_TAG getTag() {
1118 public String toString() {
1119 return getTag() + "_" + getNumber();