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;
29 import java.awt.Color;
30 import java.awt.Component;
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
33 import java.util.List;
35 import java.util.SortedMap;
36 import java.util.SortedSet;
38 import javax.swing.BoxLayout;
39 import javax.swing.JButton;
40 import javax.swing.JEditorPane;
41 import javax.swing.JPanel;
42 import javax.swing.JScrollPane;
43 import javax.swing.JSplitPane;
44 import javax.swing.JTree;
45 import javax.swing.event.TreeSelectionEvent;
46 import javax.swing.event.TreeSelectionListener;
47 import javax.swing.text.Position;
48 import javax.swing.tree.DefaultMutableTreeNode;
49 import javax.swing.tree.TreePath;
51 import org.forester.phylogeny.PhylogenyMethods;
52 import org.forester.phylogeny.PhylogenyNode;
53 import org.forester.phylogeny.data.Accession;
54 import org.forester.phylogeny.data.Annotation;
55 import org.forester.phylogeny.data.BinaryCharacters;
56 import org.forester.phylogeny.data.BranchWidth;
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.PhylogenyData;
61 import org.forester.phylogeny.data.PhylogenyDataUtil;
62 import org.forester.phylogeny.data.Point;
63 import org.forester.phylogeny.data.PropertiesMap;
64 import org.forester.phylogeny.data.Property;
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.ForesterUtil;
71 class NodePanel extends JPanel implements TreeSelectionListener {
73 static final String BASIC = "Basic";
74 static final String BINARY_CHARACTERS = "Binary characters";
75 static final String CONFIDENCE = "Confidence";
76 static final String CONFIDENCE_TYPE = "type";
77 static final String DATE = "Date";
78 static final String DATE_DESCRIPTION = "Description";
79 static final String DATE_MAX = "Max";
80 static final String DATE_MIN = "Min";
81 static final String DATE_UNIT = "Unit";
82 static final String DATE_VALUE = "Value";
83 static final String DIST_ALT_UNIT = "Altitude unit";
84 static final String DIST_ALTITUDE = "Altitude";
85 static final String DIST_DESCRIPTION = "Description";
86 static final String DIST_GEODETIC_DATUM = "Geodetic datum";
87 static final String DIST_LATITUDE = "Latitude";
88 static final String DIST_LONGITUDE = "Longitude";
89 static final String DISTRIBUTION = "Distribution";
90 static final String EVENTS = "Events";
91 static final String EVENTS_DUPLICATIONS = "Duplications";
92 static final String EVENTS_GENE_LOSSES = "Gene losses";
93 static final String EVENTS_SPECIATIONS = "Speciations";
94 static final String LIT_REFERENCE = "Reference";
95 static final String LIT_REFERENCE_DESC = "Description";
96 static final String LIT_REFERENCE_DOI = "DOI";
97 static final String NODE_BRANCH_COLOR = "Branch color";
98 static final String NODE_BRANCH_LENGTH = "Branch length";
99 static final String NODE_BRANCH_WIDTH = "Branch width";
100 static final String NODE_NAME = "Name";
101 static final String PROP = "Properties";
102 static final String REFERENCE = "Reference";
103 static final String SEQ_ACCESSION = "Accession";
104 static final String SEQ_LOCATION = "Location";
105 static final String SEQ_MOL_SEQ = "Mol seq";
106 static final String SEQ_NAME = "Name";
107 static final String SEQ_SYMBOL = "Symbol";
108 static final String SEQ_GENE_NAME = "Gene name";
109 static final String SEQ_TYPE = "Type";
110 static final String SEQ_URI = "URI";
111 static final String SEQUENCE = "Sequence";
112 static final String TAXONOMY = "Taxonomy";
113 static final String TAXONOMY_AUTHORITY = "Authority";
114 static final String TAXONOMY_CODE = "Code";
115 static final String TAXONOMY_COMMON_NAME = "Common name";
116 static final String TAXONOMY_IDENTIFIER = "Identifier";
117 static final String TAXONOMY_RANK = "Rank";
118 static final String TAXONOMY_SCIENTIFIC_NAME = "Scientific name";
119 static final String TAXONOMY_SYNONYM = "Synonym";
120 static final String TAXONOMY_URI = "URI";
121 private static final long serialVersionUID = 5120159904388100771L;
122 private final JEditorPane _pane;
123 private final JTree _tree;
125 public NodePanel( final PhylogenyNode phylogeny_node, final NodeFrame parent ) {
126 String node_name = "";
127 if ( !ForesterUtil.isEmpty( phylogeny_node.getName() ) ) {
128 node_name = phylogeny_node.getName() + " ";
130 final DefaultMutableTreeNode top = new DefaultMutableTreeNode( "Node " + node_name );
131 createNodes( top, phylogeny_node );
132 _tree = new JTree( top );
133 _tree.setEditable( false );
134 getJTree().setToggleClickCount( 1 );
136 expandPath( TAXONOMY );
137 expandPath( SEQUENCE );
138 expandPath( EVENTS );
139 final JScrollPane tree_view = new JScrollPane( getJTree() );
141 final JButton close_button = new JButton( "Close" );
142 close_button.setEnabled( true );
144 _pane = new JEditorPane();
145 _pane.setEditable( false );
147 final JScrollPane data_view = new JScrollPane( _pane );
148 final JSplitPane split_pane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
149 split_pane.setTopComponent( tree_view );
150 data_view.add( close_button );
151 split_pane.setBottomComponent( data_view );
152 data_view.setMinimumSize( AptxConstants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
153 tree_view.setMinimumSize( AptxConstants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
154 split_pane.setDividerLocation( 300 );
155 split_pane.setPreferredSize( AptxConstants.NODE_PANEL_SIZE );
158 close_button.addActionListener( new ActionListener() {
159 public void actionPerformed( final ActionEvent e ) {
165 close_button.setAlignmentX( Component.CENTER_ALIGNMENT );
166 split_pane.setAlignmentX( Component.CENTER_ALIGNMENT );
167 final JPanel panel = new JPanel();
168 panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ) );
169 panel.add( split_pane );
170 panel.add( close_button );
175 public void valueChanged( final TreeSelectionEvent e ) {
179 private void expandPath( final String name ) {
180 final TreePath tp = getJTree().getNextMatch( name, 0, Position.Bias.Forward );
182 getJTree().expandPath( tp );
186 private JTree getJTree() {
190 private static void addAnnotation( final DefaultMutableTreeNode top, final Annotation ann, final String name ) {
191 DefaultMutableTreeNode category;
192 category = new DefaultMutableTreeNode( name );
194 addSubelement( category, "Source", ann.getSource() );
195 addSubelement( category, "Type", ann.getType() );
196 addSubelement( category, "Evidence", ann.getEvidence() );
197 if ( ann.getConfidence() != null ) {
198 addSubelement( category, CONFIDENCE, ann.getConfidence().asText().toString() );
200 if ( ann.getProperties() != null ) {
201 addProperties( category, ann.getProperties(), PROP );
205 private static void addAnnotations( final DefaultMutableTreeNode top,
206 final SortedSet<Annotation> annotations,
207 final DefaultMutableTreeNode category ) {
208 if ( ( annotations != null ) && ( annotations.size() > 0 ) ) {
209 category.add( new DefaultMutableTreeNode( "Annotations" ) );
210 final DefaultMutableTreeNode last = top.getLastLeaf();
211 for( final Annotation ann : annotations ) {
212 addAnnotation( last, ann, ann.asText().toString() );
217 private static void addBasics( final DefaultMutableTreeNode top,
218 final PhylogenyNode phylogeny_node,
219 final String name ) {
220 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
222 addSubelement( category, NODE_NAME, phylogeny_node.getName() );
223 if ( phylogeny_node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
224 addSubelement( category,
226 ForesterUtil.FORMATTER_6.format( phylogeny_node.getDistanceToParent() ) );
228 if ( phylogeny_node.getBranchData().isHasConfidences() ) {
229 for( final PhylogenyData conf : phylogeny_node.getBranchData().getConfidences() ) {
230 addSubelement( category, CONFIDENCE, conf.asText().toString() );
233 if ( !phylogeny_node.isExternal() ) {
234 addSubelement( category, "Children", String.valueOf( phylogeny_node.getNumberOfDescendants() ) );
235 addSubelement( category,
237 String.valueOf( phylogeny_node.getAllExternalDescendants().size() ) );
238 final Map<Taxonomy, Integer> distinct_tax = PhylogenyMethods.obtainDistinctTaxonomyCounts( phylogeny_node );
239 if ( distinct_tax != null ) {
240 final int no_tax = PhylogenyMethods.calculateNumberOfExternalNodesWithoutTaxonomy( phylogeny_node );
241 final int tax_count = distinct_tax.size();
242 addSubelement( category, "Distinct external taxonomies", String.valueOf( tax_count ) );
244 addSubelement( category, "External nodes without taxonomy", String.valueOf( no_tax ) );
248 if ( !phylogeny_node.isRoot() ) {
249 addSubelement( category, "Depth", String.valueOf( phylogeny_node.calculateDepth() ) );
250 final double d = phylogeny_node.calculateDistanceToRoot();
252 addSubelement( category, "Distance to root", String.valueOf( ForesterUtil.FORMATTER_6.format( d ) ) );
255 if ( ( phylogeny_node.getBranchData().getBranchWidth() != null )
256 && ( phylogeny_node.getBranchData().getBranchWidth().getValue() != BranchWidth.BRANCH_WIDTH_DEFAULT_VALUE ) ) {
257 addSubelement( category,
259 ForesterUtil.FORMATTER_3.format( phylogeny_node.getBranchData().getBranchWidth().getValue() ) );
261 if ( ( phylogeny_node.getBranchData().getBranchColor() != null ) ) {
262 final Color c = phylogeny_node.getBranchData().getBranchColor().getValue();
263 addSubelement( category, NODE_BRANCH_COLOR, c.getRed() + ", " + c.getGreen() + ", " + c.getBlue() );
267 private static void addBinaryCharacters( final DefaultMutableTreeNode top,
268 final BinaryCharacters bc,
269 final String name ) {
270 DefaultMutableTreeNode category;
271 category = new DefaultMutableTreeNode( name );
273 addSubelement( category, "Gained", String.valueOf( bc.getGainedCount() ) );
274 addSubelement( category, "Lost", String.valueOf( bc.getLostCount() ) );
275 addSubelement( category, "Present", String.valueOf( bc.getPresentCount() ) );
276 final DefaultMutableTreeNode chars = new DefaultMutableTreeNode( "Lists" );
277 category.add( chars );
278 addSubelement( chars, "Gained", bc.getGainedCharactersAsStringBuffer().toString() );
279 addSubelement( chars, "Lost", bc.getLostCharactersAsStringBuffer().toString() );
280 addSubelement( chars, "Present", bc.getPresentCharactersAsStringBuffer().toString() );
283 private static void addCrossReference( final DefaultMutableTreeNode top, final Accession x, final String name ) {
284 DefaultMutableTreeNode category;
285 category = new DefaultMutableTreeNode( name );
289 private static void addCrossReferences( final DefaultMutableTreeNode top,
290 final SortedSet<Accession> xs,
291 final DefaultMutableTreeNode category ) {
292 if ( ( xs != null ) && ( xs.size() > 0 ) ) {
293 category.add( new DefaultMutableTreeNode( "Cross references" ) );
294 final DefaultMutableTreeNode last = top.getLastLeaf();
295 for( final Accession x : xs ) {
296 addCrossReference( last, x, x.asText().toString() );
301 private static void addDate( final DefaultMutableTreeNode top, final Date date, final String name ) {
302 DefaultMutableTreeNode category;
303 category = new DefaultMutableTreeNode( name );
305 addSubelement( category, DATE_DESCRIPTION, date.getDesc() );
306 addSubelement( category, DATE_VALUE, String.valueOf( date.getValue() ) );
307 addSubelement( category, DATE_MIN, String.valueOf( date.getMin() ) );
308 addSubelement( category, DATE_MAX, String.valueOf( date.getMax() ) );
309 addSubelement( category, DATE_UNIT, date.getUnit() );
312 private static void addDistribution( final DefaultMutableTreeNode top, final Distribution dist, final String name ) {
313 DefaultMutableTreeNode category;
314 category = new DefaultMutableTreeNode( name );
316 addSubelement( category, DIST_DESCRIPTION, dist.getDesc() );
317 if ( ( dist.getPoints() != null ) && ( dist.getPoints().size() > 0 ) ) {
318 final Point p0 = dist.getPoints().get( 0 );
319 if ( ( p0 != null ) && !Point.isSeemsEmpty( p0 ) ) {
320 addSubelement( category, DIST_GEODETIC_DATUM, p0.getGeodeticDatum() );
321 addSubelement( category, DIST_LATITUDE, String.valueOf( p0.getLatitude() ) );
322 addSubelement( category, DIST_LONGITUDE, String.valueOf( p0.getLongitude() ) );
323 String alt_unit = p0.getAltiudeUnit();
324 if ( ForesterUtil.isEmpty( alt_unit ) ) {
327 addSubelement( category, DIST_ALTITUDE, String.valueOf( p0.getAltitude() ) + alt_unit );
332 private static void addEvents( final DefaultMutableTreeNode top, final Event events, final String name ) {
333 DefaultMutableTreeNode category;
334 category = new DefaultMutableTreeNode( name );
336 if ( events.getNumberOfDuplications() > 0 ) {
337 addSubelement( category, EVENTS_DUPLICATIONS, String.valueOf( events.getNumberOfDuplications() ) );
339 if ( events.getNumberOfSpeciations() > 0 ) {
340 addSubelement( category, EVENTS_SPECIATIONS, String.valueOf( events.getNumberOfSpeciations() ) );
342 if ( events.getNumberOfGeneLosses() > 0 ) {
343 addSubelement( category, EVENTS_GENE_LOSSES, String.valueOf( events.getNumberOfGeneLosses() ) );
345 addSubelement( category, "Type", events.getEventType().toString() );
346 if ( events.getConfidence() != null ) {
347 addSubelement( category, CONFIDENCE, events.getConfidence().asText().toString() );
351 private static void addLineage( final DefaultMutableTreeNode top,
352 final List<String> lineage,
353 final DefaultMutableTreeNode category ) {
354 if ( ( lineage != null ) && ( lineage.size() > 0 ) ) {
355 final StringBuilder sb = new StringBuilder();
356 for( final String lin : lineage ) {
357 if ( !ForesterUtil.isEmpty( lin ) ) {
363 if ( sb.length() > 1 ) {
364 str = sb.substring( 0, sb.length() - 3 );
366 if ( !ForesterUtil.isEmpty( str ) ) {
367 addSubelement( category, "Lineage", str );
372 private static void addProperties( final DefaultMutableTreeNode top,
373 final PropertiesMap properties,
374 final String string ) {
375 final SortedMap<String, Property> properties_map = properties.getProperties();
376 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( "Properties " );
378 for( final String key : properties_map.keySet() ) {
379 final Property prop = properties_map.get( key );
380 category.add( new DefaultMutableTreeNode( prop.getRef() + "=" + prop.getValue() + " " + prop.getUnit()
381 + " [" + prop.getAppliesTo().toString() + "]" ) );
385 private static void addReference( final DefaultMutableTreeNode top, final Reference ref, final String name ) {
386 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
388 addSubelement( category, LIT_REFERENCE_DOI, ref.getDoi() );
389 addSubelement( category, LIT_REFERENCE_DESC, ref.getDescription() );
392 private static void addSequence( final DefaultMutableTreeNode top, final Sequence seq, final String name ) {
393 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
395 addSubelement( category, SEQ_NAME, seq.getName() );
396 addSubelement( category, SEQ_SYMBOL, seq.getSymbol() );
397 addSubelement( category, SEQ_GENE_NAME, seq.getGeneName() );
398 if ( seq.getAccession() != null ) {
399 addSubelement( category, SEQ_ACCESSION, seq.getAccession().asText().toString() );
401 addSubelement( category, SEQ_LOCATION, seq.getLocation() );
402 addSubelement( category, SEQ_TYPE, seq.getType() );
403 addSubelement( category, SEQ_MOL_SEQ, seq.getMolecularSequence() );
404 if ( ( seq.getAnnotations() != null ) && !seq.getAnnotations().isEmpty() ) {
405 addAnnotations( top, seq.getAnnotations(), category );
407 if ( ( seq.getCrossReferences() != null ) && !seq.getCrossReferences().isEmpty() ) {
408 addCrossReferences( top, seq.getCrossReferences(), category );
410 if ( ( seq.getUris() != null ) && !seq.getUris().isEmpty() ) {
411 addUris( top, seq.getUris(), category );
415 private static void addSubelement( final DefaultMutableTreeNode node, final String name, final String value ) {
416 if ( !ForesterUtil.isEmpty( value ) ) {
417 node.add( new DefaultMutableTreeNode( name + ": " + value ) );
421 private static void addTaxonomy( final DefaultMutableTreeNode top, final Taxonomy tax, final String name ) {
422 final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
424 if ( tax.getIdentifier() != null ) {
425 addSubelement( category, TAXONOMY_IDENTIFIER, tax.getIdentifier().asText().toString() );
427 addSubelement( category, TAXONOMY_CODE, tax.getTaxonomyCode() );
428 addSubelement( category, TAXONOMY_SCIENTIFIC_NAME, tax.getScientificName() );
429 addSubelement( category, TAXONOMY_AUTHORITY, tax.getAuthority() );
430 addSubelement( category, TAXONOMY_COMMON_NAME, tax.getCommonName() );
431 for( final String syn : tax.getSynonyms() ) {
432 addSubelement( category, TAXONOMY_SYNONYM, syn );
434 addSubelement( category, TAXONOMY_RANK, tax.getRank() );
435 if ( ( tax.getUris() != null ) && !tax.getUris().isEmpty() ) {
436 addUris( top, tax.getUris(), category );
438 if ( ( tax.getLineage() != null ) && !tax.getLineage().isEmpty() ) {
439 addLineage( top, tax.getLineage(), category );
443 private static void addUri( final DefaultMutableTreeNode top, final Uri uri, final String name ) {
444 DefaultMutableTreeNode category;
445 category = new DefaultMutableTreeNode( name );
447 addSubelement( category, "Description", uri.getDescription() );
448 addSubelement( category, "Type", uri.getType() );
449 addSubelement( category, "URI", uri.getValue().toString() );
452 private static void addUris( final DefaultMutableTreeNode top,
453 final List<Uri> uris,
454 final DefaultMutableTreeNode category ) {
455 if ( ( uris != null ) && ( uris.size() > 0 ) ) {
456 category.add( new DefaultMutableTreeNode( "URIs" ) );
457 final DefaultMutableTreeNode last = top.getLastLeaf();
459 for( final Uri uri : uris ) {
461 addUri( last, uri, "URI " + ( i++ ) );
467 private static void createNodes( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node ) {
468 addBasics( top, phylogeny_node, BASIC );
470 if ( phylogeny_node.getNodeData().isHasTaxonomy() ) {
471 addTaxonomy( top, phylogeny_node.getNodeData().getTaxonomy(), TAXONOMY );
474 if ( phylogeny_node.getNodeData().isHasSequence() ) {
475 addSequence( top, phylogeny_node.getNodeData().getSequence(), SEQUENCE );
478 if ( phylogeny_node.getNodeData().isHasEvent() ) {
479 addEvents( top, phylogeny_node.getNodeData().getEvent(), EVENTS );
482 if ( phylogeny_node.getNodeData().isHasDate() ) {
483 addDate( top, phylogeny_node.getNodeData().getDate(), DATE );
486 if ( phylogeny_node.getNodeData().isHasDistribution() ) {
487 addDistribution( top, phylogeny_node.getNodeData().getDistribution(), DISTRIBUTION );
490 if ( phylogeny_node.getNodeData().isHasReference() ) {
491 addReference( top, phylogeny_node.getNodeData().getReference(), LIT_REFERENCE );
494 if ( phylogeny_node.getNodeData().isHasBinaryCharacters() ) {
495 addBinaryCharacters( top, phylogeny_node.getNodeData().getBinaryCharacters(), BINARY_CHARACTERS );
498 if ( phylogeny_node.getNodeData().isHasProperties() ) {
499 addProperties( top, phylogeny_node.getNodeData().getProperties(), PROP );