inprogress
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanelUtil.java
1
2 package org.forester.archaeopteryx;
3
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.io.UnsupportedEncodingException;
7 import java.net.URLEncoder;
8 import java.util.ArrayList;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Set;
15 import java.util.SortedMap;
16 import java.util.SortedSet;
17 import java.util.TreeMap;
18
19 import javax.swing.JOptionPane;
20
21 import org.forester.analysis.TaxonomyDataManager;
22 import org.forester.phylogeny.Phylogeny;
23 import org.forester.phylogeny.PhylogenyMethods;
24 import org.forester.phylogeny.PhylogenyNode;
25 import org.forester.phylogeny.data.Annotation;
26 import org.forester.phylogeny.data.BranchColor;
27 import org.forester.phylogeny.data.NodeData.NODE_DATA;
28 import org.forester.phylogeny.data.Sequence;
29 import org.forester.phylogeny.data.Taxonomy;
30 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
31 import org.forester.phylogeny.iterators.PreorderTreeIterator;
32 import org.forester.util.ForesterConstants;
33 import org.forester.util.ForesterUtil;
34 import org.forester.util.SequenceIdParser;
35 import org.forester.ws.seqdb.UniProtTaxonomy;
36
37 public class TreePanelUtil {
38
39     static int makeSB( final List<String> data, final Options optz, final StringBuilder sb ) {
40         final SortedMap<String, Integer> map = new TreeMap<String, Integer>();
41         if ( ( optz.getExtDescNodeDataToReturn() != NODE_DATA.SEQUENCE_MOL_SEQ )
42                 && ( optz.getExtDescNodeDataToReturn() != NODE_DATA.SEQUENCE_MOL_SEQ_FASTA ) ) {
43             for( final String d : data ) {
44                 if ( !ForesterUtil.isEmpty( d ) ) {
45                     if ( map.containsKey( d ) ) {
46                         map.put( d, map.get( d ) + 1 );
47                     }
48                     else {
49                         map.put( d, 1 );
50                     }
51                 }
52             }
53         }
54         int size = 0;
55         if ( ( optz.getExtDescNodeDataToReturn() != NODE_DATA.SEQUENCE_MOL_SEQ )
56                 && ( optz.getExtDescNodeDataToReturn() != NODE_DATA.SEQUENCE_MOL_SEQ_FASTA ) ) {
57             for( final Entry<String, Integer> e : map.entrySet() ) {
58                 final String v = e.getKey();
59                 final Object c = e.getValue();
60                 sb.append( v );
61                 sb.append( "\t" );
62                 sb.append( c );
63                 sb.append( ForesterUtil.LINE_SEPARATOR );
64             }
65             size = map.size();
66         }
67         else {
68             for( final String d : data ) {
69                 if ( !ForesterUtil.isEmpty( d ) ) {
70                     sb.append( d );
71                     sb.append( ForesterUtil.LINE_SEPARATOR );
72                 }
73             }
74             size = data.size();
75         }
76         return size;
77     }
78
79     public final static String createUriForSeqWeb( final PhylogenyNode node,
80                                                    final Configuration conf,
81                                                    final TreePanel tp ) {
82         String uri_str = null;
83         final String upkb = ForesterUtil.extractUniProtKbProteinSeqIdentifier( node );
84         if ( !ForesterUtil.isEmpty( upkb ) ) {
85             try {
86                 uri_str = ForesterUtil.UNIPROT_KB + URLEncoder.encode( upkb, ForesterConstants.UTF8 );
87             }
88             catch ( final UnsupportedEncodingException e ) {
89                 AptxUtil.showErrorMessage( tp, e.toString() );
90                 e.printStackTrace();
91             }
92         }
93         if ( ForesterUtil.isEmpty( uri_str ) ) {
94             final String v = ForesterUtil.extractGenbankAccessor( node );
95             if ( !ForesterUtil.isEmpty( v ) ) {
96                 try {
97                     if ( SequenceIdParser.isProtein( v ) ) {
98                         uri_str = ForesterUtil.NCBI_PROTEIN + URLEncoder.encode( v, ForesterConstants.UTF8 );
99                     }
100                     else {
101                         uri_str = ForesterUtil.NCBI_NUCCORE + URLEncoder.encode( v, ForesterConstants.UTF8 );
102                     }
103                 }
104                 catch ( final UnsupportedEncodingException e ) {
105                     AptxUtil.showErrorMessage( tp, e.toString() );
106                     e.printStackTrace();
107                 }
108             }
109         }
110         if ( ForesterUtil.isEmpty( uri_str ) ) {
111             final String v = ForesterUtil.extractRefSeqAccessorAccessor( node );
112             if ( !ForesterUtil.isEmpty( v ) ) {
113                 try {
114                     if ( SequenceIdParser.isProtein( v ) ) {
115                         uri_str = ForesterUtil.NCBI_PROTEIN + URLEncoder.encode( v, ForesterConstants.UTF8 );
116                     }
117                     else {
118                         uri_str = ForesterUtil.NCBI_NUCCORE + URLEncoder.encode( v, ForesterConstants.UTF8 );
119                     }
120                 }
121                 catch ( final UnsupportedEncodingException e ) {
122                     AptxUtil.showErrorMessage( tp, e.toString() );
123                     e.printStackTrace();
124                 }
125             }
126         }
127         if ( ForesterUtil.isEmpty( uri_str ) ) {
128             final String v = ForesterUtil.extractGInumber( node );
129             if ( !ForesterUtil.isEmpty( v ) ) {
130                 try {
131                     uri_str = ForesterUtil.NCBI_GI + URLEncoder.encode( v, ForesterConstants.UTF8 );
132                 }
133                 catch ( final UnsupportedEncodingException e ) {
134                     AptxUtil.showErrorMessage( tp, e.toString() );
135                     e.printStackTrace();
136                 }
137             }
138         }
139         return uri_str;
140     }
141
142     /**
143      * Returns the set of distinct taxonomies of
144      * all external nodes of node.
145      * If at least one the external nodes has no taxonomy,
146      * null is returned.
147      * 
148      */
149     public static Set<Taxonomy> obtainDistinctTaxonomies( final PhylogenyNode node ) {
150         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
151         final Set<Taxonomy> tax_set = new HashSet<Taxonomy>();
152         for( final PhylogenyNode n : descs ) {
153             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
154                 return null;
155             }
156             tax_set.add( n.getNodeData().getTaxonomy() );
157         }
158         return tax_set;
159     }
160
161     public final static void showExtDescNodeDataUserSelectedHelper( final ControlPanel cp,
162                                                                     final PhylogenyNode node,
163                                                                     final List<String> data ) {
164         final StringBuilder sb = new StringBuilder();
165         if ( cp.isShowNodeNames() && !ForesterUtil.isEmpty( node.getName() ) ) {
166             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getName(), sb );
167         }
168         if ( cp.isShowSeqNames() && node.getNodeData().isHasSequence()
169                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) {
170             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getName(), sb );
171         }
172         if ( cp.isShowSeqSymbols() && node.getNodeData().isHasSequence()
173                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) {
174             TreePanelUtil
175                     .showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getSymbol(), sb );
176         }
177         if ( cp.isShowGeneNames() && node.getNodeData().isHasSequence()
178                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getGeneName() ) ) {
179             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getGeneName(),
180                                                                        sb );
181         }
182         if ( cp.isShowSequenceAcc() && node.getNodeData().isHasSequence()
183                 && ( node.getNodeData().getSequence().getAccession() != null )
184                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().toString() ) ) {
185             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getAccession()
186                     .toString(), sb );
187         }
188         if ( cp.isShowTaxonomyCode() && node.getNodeData().isHasTaxonomy()
189                 && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
190             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getTaxonomy()
191                     .getTaxonomyCode(), sb );
192         }
193         if ( cp.isShowTaxonomyScientificNames() && node.getNodeData().isHasTaxonomy()
194                 && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
195             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getTaxonomy()
196                     .getScientificName(), sb );
197         }
198         if ( cp.isShowTaxonomyCommonNames() && node.getNodeData().isHasTaxonomy()
199                 && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) {
200             TreePanelUtil
201                     .showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getTaxonomy().getCommonName(), sb );
202         }
203         if ( ( cp.isShowSeqNames() || cp.isShowSeqSymbols() || cp.isShowSequenceAcc() )
204                 && node.getNodeData().isHasSequence()
205                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) {
206             TreePanelUtil.showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence()
207                     .getMolecularSequence(), sb );
208         }
209         final String s = sb.toString().trim();
210         if ( !ForesterUtil.isEmpty( s ) ) {
211             data.add( s );
212         }
213     }
214
215     public final static void showExtDescNodeDataUserSelectedHelperHelper( final String s, final StringBuilder sb ) {
216         if ( sb.length() > 0 ) {
217             sb.append( "\t" );
218         }
219         sb.append( s );
220     }
221
222     final public static void showInformationMessage( final Component parent, final String title, final String msg ) {
223         JOptionPane.showMessageDialog( parent, msg, title, JOptionPane.INFORMATION_MESSAGE );
224     }
225
226     final static Color calculateColorFromString( final String str, final boolean is_taxonomy ) {
227         final String my_str = str.toUpperCase();
228         char first = my_str.charAt( 0 );
229         char second = ' ';
230         char third = ' ';
231         if ( my_str.length() > 1 ) {
232             if ( is_taxonomy ) {
233                 second = my_str.charAt( 1 );
234             }
235             else {
236                 second = my_str.charAt( my_str.length() - 1 );
237             }
238             if ( is_taxonomy ) {
239                 if ( my_str.length() > 2 ) {
240                     if ( my_str.indexOf( " " ) > 0 ) {
241                         third = my_str.charAt( my_str.indexOf( " " ) + 1 );
242                     }
243                     else {
244                         third = my_str.charAt( 2 );
245                     }
246                 }
247             }
248             else if ( my_str.length() > 2 ) {
249                 third = my_str.charAt( ( my_str.length() - 1 ) / 2 );
250             }
251         }
252         first = TreePanelUtil.normalizeCharForRGB( first );
253         second = TreePanelUtil.normalizeCharForRGB( second );
254         third = TreePanelUtil.normalizeCharForRGB( third );
255         if ( ( first > 235 ) && ( second > 235 ) && ( third > 235 ) ) {
256             first = 0;
257         }
258         else if ( ( first < 60 ) && ( second < 60 ) && ( third < 60 ) ) {
259             second = 255;
260         }
261         return new Color( first, second, third );
262     }
263
264     final static void collapseSpeciesSpecificSubtrees( final Phylogeny phy ) {
265         boolean inferred = false;
266         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
267             final PhylogenyNode n = it.next();
268             if ( !n.isExternal() && !n.isCollapse() && ( n.getNumberOfDescendants() > 1 ) ) {
269                 final Set<Taxonomy> taxs = TreePanelUtil.obtainDistinctTaxonomies( n );
270                 if ( ( taxs != null ) && ( taxs.size() == 1 ) ) {
271                     TreePanelUtil.collapseSubtree( n, true );
272                     if ( !n.getNodeData().isHasTaxonomy() ) {
273                         n.getNodeData().setTaxonomy( ( Taxonomy ) n.getAllExternalDescendants().get( 0 ).getNodeData()
274                                 .getTaxonomy().copy() );
275                     }
276                     inferred = true;
277                 }
278                 else {
279                     n.setCollapse( false );
280                 }
281             }
282         }
283         if ( inferred ) {
284             phy.setRerootable( false );
285         }
286     }
287
288     final static void collapseSubtree( final PhylogenyNode node, final boolean collapse ) {
289         node.setCollapse( collapse );
290         if ( node.isExternal() ) {
291             return;
292         }
293         final PhylogenyNodeIterator it = new PreorderTreeIterator( node );
294         while ( it.hasNext() ) {
295             it.next().setCollapse( collapse );
296         }
297     }
298
299     static void colorizeSubtree( final PhylogenyNode node, final BranchColor c ) {
300         node.getBranchData().setBranchColor( c );
301         final List<PhylogenyNode> descs = PhylogenyMethods.getAllDescendants( node );
302         for( final PhylogenyNode desc : descs ) {
303             desc.getBranchData().setBranchColor( c );
304         }
305     }
306
307     final static void colorPhylogenyAccordingToConfidenceValues( final Phylogeny tree, final TreePanel tree_panel ) {
308         double max_conf = 0.0;
309         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
310             final PhylogenyNode n = it.next();
311             n.getBranchData().setBranchColor( null );
312             if ( n.getBranchData().isHasConfidences() ) {
313                 final double conf = PhylogenyMethods.getConfidenceValue( n );
314                 if ( conf > max_conf ) {
315                     max_conf = conf;
316                 }
317             }
318         }
319         if ( max_conf > 0.0 ) {
320             final Color bg = tree_panel.getTreeColorSet().getBackgroundColor();
321             final Color br = tree_panel.getTreeColorSet().getBranchColor();
322             for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
323                 final PhylogenyNode n = it.next();
324                 if ( n.getBranchData().isHasConfidences() ) {
325                     final double conf = PhylogenyMethods.getConfidenceValue( n );
326                     final BranchColor c = new BranchColor( ForesterUtil.calcColor( conf, 0.0, max_conf, bg, br ) );
327                     TreePanelUtil.colorizeSubtree( n, c );
328                 }
329             }
330         }
331     }
332
333     final static void colorPhylogenyAccordingToExternalTaxonomy( final Phylogeny tree, final TreePanel tree_panel ) {
334         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
335             it.next().getBranchData().setBranchColor( null );
336         }
337         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
338             final PhylogenyNode n = it.next();
339             if ( !n.getBranchData().isHasBranchColor() ) {
340                 final Taxonomy tax = PhylogenyMethods.getExternalDescendantsTaxonomy( n );
341                 if ( tax != null ) {
342                     n.getBranchData().setBranchColor( new BranchColor( tree_panel.calculateTaxonomyBasedColor( tax ) ) );
343                     final List<PhylogenyNode> descs = PhylogenyMethods.getAllDescendants( n );
344                     for( final PhylogenyNode desc : descs ) {
345                         desc.getBranchData()
346                                 .setBranchColor( new BranchColor( tree_panel.calculateTaxonomyBasedColor( tax ) ) );
347                     }
348                 }
349             }
350         }
351     }
352
353     final static int colorPhylogenyAccordingToRanks( final Phylogeny tree, final String rank, final TreePanel tree_panel ) {
354         final Map<String, Color> true_lineage_to_color_map = new HashMap<String, Color>();
355         int colorizations = 0;
356         for( final PhylogenyNodeIterator it = tree.iteratorPostorder(); it.hasNext(); ) {
357             final PhylogenyNode n = it.next();
358             if ( n.getNodeData().isHasTaxonomy()
359                     && ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() )
360                             || !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) || !ForesterUtil
361                             .isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
362                 if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getRank() )
363                         && n.getNodeData().getTaxonomy().getRank().equalsIgnoreCase( rank ) ) {
364                     final BranchColor c = new BranchColor( tree_panel.calculateTaxonomyBasedColor( n.getNodeData()
365                             .getTaxonomy() ) );
366                     TreePanelUtil.colorizeSubtree( n, c );
367                     ++colorizations;
368                     if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
369                         true_lineage_to_color_map.put( n.getNodeData().getTaxonomy().getScientificName(), c.getValue() );
370                     }
371                 }
372             }
373         }
374         for( final PhylogenyNodeIterator it = tree.iteratorPostorder(); it.hasNext(); ) {
375             final PhylogenyNode node = it.next();
376             if ( ( node.getBranchData().getBranchColor() == null ) && node.getNodeData().isHasTaxonomy()
377                     && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getLineage() ) ) {
378                 boolean success = false;
379                 if ( !true_lineage_to_color_map.isEmpty() ) {
380                     for( final String lin : node.getNodeData().getTaxonomy().getLineage() ) {
381                         if ( true_lineage_to_color_map.containsKey( lin ) ) {
382                             TreePanelUtil
383                                     .colorizeSubtree( node, new BranchColor( true_lineage_to_color_map.get( lin ) ) );
384                             ++colorizations;
385                             success = true;
386                             break;
387                         }
388                     }
389                 }
390                 if ( !success ) {
391                     final Map<String, String> lineage_to_rank_map = MainPanel.getLineageToRankMap();
392                     for( final String lin : node.getNodeData().getTaxonomy().getLineage() ) {
393                         final Taxonomy temp_tax = new Taxonomy();
394                         temp_tax.setScientificName( lin );
395                         if ( lineage_to_rank_map.containsKey( lin )
396                                 && !ForesterUtil.isEmpty( lineage_to_rank_map.get( lin ) )
397                                 && lineage_to_rank_map.get( lin ).equalsIgnoreCase( rank ) ) {
398                             final BranchColor c = new BranchColor( tree_panel.calculateTaxonomyBasedColor( temp_tax ) );
399                             TreePanelUtil.colorizeSubtree( node, c );
400                             ++colorizations;
401                             true_lineage_to_color_map.put( lin, c.getValue() );
402                             break;
403                         }
404                         else {
405                             UniProtTaxonomy up = null;
406                             try {
407                                 up = TaxonomyDataManager.obtainUniProtTaxonomy( temp_tax, null, null );
408                             }
409                             catch ( final Exception e ) {
410                                 e.printStackTrace();
411                             }
412                             if ( ( up != null ) && !ForesterUtil.isEmpty( up.getRank() ) ) {
413                                 lineage_to_rank_map.put( lin, up.getRank() );
414                                 if ( up.getRank().equalsIgnoreCase( rank ) ) {
415                                     final BranchColor c = new BranchColor( tree_panel.calculateTaxonomyBasedColor( temp_tax ) );
416                                     TreePanelUtil.colorizeSubtree( node, c );
417                                     ++colorizations;
418                                     true_lineage_to_color_map.put( lin, c.getValue() );
419                                     break;
420                                 }
421                             }
422                         }
423                     }
424                 }
425             }
426         }
427         return colorizations;
428     }
429
430     final static String createAnnotationString( final SortedSet<Annotation> annotations, final boolean show_ref_sources ) {
431         final SortedMap<String, List<Annotation>> m = new TreeMap<String, List<Annotation>>();
432         for( final Annotation an : annotations ) {
433             final String ref_source = ForesterUtil.isEmpty( an.getRefSource() ) ? "?" : an.getRefSource();
434             if ( !m.containsKey( ref_source ) ) {
435                 m.put( ref_source, new ArrayList<Annotation>() );
436             }
437             m.get( ref_source ).add( an );
438         }
439         final StringBuilder sb = new StringBuilder();
440         for( final Entry<String, List<Annotation>> e : m.entrySet() ) {
441             final String ref_source = e.getKey();
442             final List<Annotation> ans = e.getValue();
443             if ( m.size() > 1 ) {
444                 sb.append( "[" );
445             }
446             if ( show_ref_sources && !ref_source.equals( "?" ) ) {
447                 sb.append( ref_source );
448                 sb.append( ": " );
449             }
450             for( int i = 0; i < ans.size(); ++i ) {
451                 final Annotation an = ans.get( i );
452                 if ( !ForesterUtil.isEmpty( an.getRefValue() ) ) {
453                     sb.append( an.getRefValue() );
454                     sb.append( " " );
455                 }
456                 if ( !ForesterUtil.isEmpty( an.getDesc() ) ) {
457                     sb.append( an.getDesc() );
458                 }
459                 if ( sb.charAt( sb.length() - 1 ) == ' ' ) {
460                     sb.deleteCharAt( sb.length() - 1 );
461                 }
462                 if ( i < ans.size() - 1 ) {
463                     sb.append( ", " );
464                 }
465             }
466             if ( m.size() > 1 ) {
467                 sb.append( "] " );
468             }
469         }
470         return sb.toString();
471     }
472
473     final static String getPartAfterColon( final String s ) {
474         final int i = s.indexOf( ':' );
475         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
476             return s;
477         }
478         return s.substring( i + 1, s.length() );
479     }
480
481     final static boolean isHasAssignedEvent( final PhylogenyNode node ) {
482         if ( !node.getNodeData().isHasEvent() ) {
483             return false;
484         }
485         if ( ( node.getNodeData().getEvent() ).isUnassigned() ) {
486             return false;
487         }
488         return true;
489     }
490
491     final static boolean isSequenceEmpty( final Sequence seq ) {
492         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
493                 && ForesterUtil.isEmpty( seq.getGeneName() ) && ForesterUtil.isEmpty( seq.getSymbol() );
494     }
495
496     final static boolean isTaxonomyEmpty( final Taxonomy tax ) {
497         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
498                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
499                 .getSynonyms().isEmpty() );
500     }
501
502     final static char normalizeCharForRGB( char c ) {
503         c -= 65;
504         c *= 10.2;
505         c = c > 255 ? 255 : c;
506         c = c < 0 ? 0 : c;
507         return c;
508     }
509
510     final static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
511         final Phylogeny new_phy = new Phylogeny();
512         new_phy.setRooted( true );
513         new_phy.setName( source_phy.getName() );
514         new_phy.setDescription( source_phy.getDescription() );
515         new_phy.setType( source_phy.getType() );
516         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
517         new_phy.setConfidence( source_phy.getConfidence() );
518         new_phy.setIdentifier( source_phy.getIdentifier() );
519         new_phy.setRoot( new_root.copyNodeDataShallow() );
520         int i = 0;
521         for( final PhylogenyNode n : new_root.getDescendants() ) {
522             new_phy.getRoot().setChildNode( i++, n );
523         }
524         return new_phy;
525     }
526 }