version 0.9910 beta
[jalview.git] / forester / java / src / org / forester / phylogeny / PhylogenyMethods.java
index eba76de..c3fbc95 100644 (file)
@@ -40,12 +40,14 @@ import java.util.Map;
 import java.util.Set;\r
 import java.util.regex.Matcher;\r
 import java.util.regex.Pattern;\r
+import java.util.regex.PatternSyntaxException;\r
 \r
 import org.forester.io.parsers.FastaParser;\r
 import org.forester.io.parsers.PhylogenyParser;\r
 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;\r
 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;\r
 import org.forester.io.parsers.util.PhylogenyParserException;\r
+import org.forester.msa.Msa;\r
 import org.forester.phylogeny.data.Accession;\r
 import org.forester.phylogeny.data.Annotation;\r
 import org.forester.phylogeny.data.BranchColor;\r
@@ -105,7 +107,7 @@ public class PhylogenyMethods {
         return could_extract;\r
     }\r
 \r
-    public static DescriptiveStatistics calculatBranchLengthStatistics( final Phylogeny phy ) {\r
+    public static DescriptiveStatistics calculateBranchLengthStatistics( final Phylogeny phy ) {\r
         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();\r
         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {\r
             final PhylogenyNode n = iter.next();\r
@@ -116,7 +118,7 @@ public class PhylogenyMethods {
         return stats;\r
     }\r
 \r
-    public static List<DescriptiveStatistics> calculatConfidenceStatistics( final Phylogeny phy ) {\r
+    public static List<DescriptiveStatistics> calculateConfidenceStatistics( final Phylogeny phy ) {\r
         final List<DescriptiveStatistics> stats = new ArrayList<DescriptiveStatistics>();\r
         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {\r
             final PhylogenyNode n = iter.next();\r
@@ -146,8 +148,8 @@ public class PhylogenyMethods {
 \r
     /**\r
      * Calculates the distance between PhylogenyNodes node1 and node2.\r
-     * \r
-     * \r
+     *\r
+     *\r
      * @param node1\r
      * @param node2\r
      * @return distance between node1 and node2\r
@@ -161,8 +163,8 @@ public class PhylogenyMethods {
 \r
     /**\r
      * Returns the LCA of PhylogenyNodes node1 and node2.\r
-     * \r
-     * \r
+     *\r
+     *\r
      * @param node1\r
      * @param node2\r
      * @return LCA of node1 and node2\r
@@ -207,8 +209,8 @@ public class PhylogenyMethods {
     /**\r
      * Returns the LCA of PhylogenyNodes node1 and node2.\r
      * Precondition: ids are in pre-order (or level-order).\r
-     * \r
-     * \r
+     *\r
+     *\r
      * @param node1\r
      * @param node2\r
      * @return LCA of node1 and node2\r
@@ -277,7 +279,7 @@ public class PhylogenyMethods {
         }\r
         return max;\r
     }\r
-    \r
+\r
     public static PhylogenyNode calculateNodeWithMaxDistanceToRoot( final Phylogeny phy ) {\r
         double max = 0.0;\r
         PhylogenyNode max_node = phy.getFirstExternalNode();\r
@@ -303,7 +305,7 @@ public class PhylogenyMethods {
         return x;\r
     }\r
 \r
-    public static DescriptiveStatistics calculatNumberOfDescendantsPerNodeStatistics( final Phylogeny phy ) {\r
+    public static DescriptiveStatistics calculateNumberOfDescendantsPerNodeStatistics( final Phylogeny phy ) {\r
         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();\r
         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {\r
             final PhylogenyNode n = iter.next();\r
@@ -498,9 +500,9 @@ public class PhylogenyMethods {
     }\r
 \r
     /**\r
-     * \r
+     *\r
      * Convenience method\r
-     * \r
+     *\r
      * @param node\r
      * @return\r
      */\r
@@ -551,9 +553,9 @@ public class PhylogenyMethods {
     }\r
 \r
     /**\r
-     * Returns taxonomy t if all external descendants have \r
+     * Returns taxonomy t if all external descendants have\r
      * the same taxonomy t, null otherwise.\r
-     * \r
+     *\r
      */\r
     public static Taxonomy getExternalDescendantsTaxonomy( final PhylogenyNode node ) {\r
         final List<PhylogenyNode> descs = node.getAllExternalDescendants();\r
@@ -681,7 +683,7 @@ public class PhylogenyMethods {
 \r
     /*\r
      * This is case insensitive.\r
-     * \r
+     *\r
      */\r
     public synchronized static boolean isTaxonomyHasIdentifierOfGivenProvider( final Taxonomy tax,\r
                                                                                final String[] providers ) {\r
@@ -775,7 +777,7 @@ public class PhylogenyMethods {
      * all external nodes of node.\r
      * If at least one of the external nodes has no taxonomy,\r
      * null is returned.\r
-     * \r
+     *\r
      */\r
     public static Map<Taxonomy, Integer> obtainDistinctTaxonomyCounts( final PhylogenyNode node ) {\r
         final List<PhylogenyNode> descs = node.getAllExternalDescendants();\r
@@ -799,10 +801,10 @@ public class PhylogenyMethods {
      * Arranges the order of childern for each node of this Phylogeny in such a\r
      * way that either the branch with more children is on top (right) or on\r
      * bottom (left), dependent on the value of boolean order.\r
-     * \r
+     *\r
      * @param order\r
      *            decides in which direction to order\r
-     * @param pri \r
+     * @param pri\r
      */\r
     public static void orderAppearance( final PhylogenyNode n,\r
                                         final boolean order,\r
@@ -934,11 +936,46 @@ public class PhylogenyMethods {
         }\r
     }\r
 \r
+    private static enum NDF {\r
+        NodeName( "NN" ),\r
+        TaxonomyCode( "TC" ),\r
+        TaxonomyCommonName( "CN" ),\r
+        TaxonomyScientificName( "TS" ),\r
+        TaxonomyIdentifier( "TI" ),\r
+        TaxonomySynonym( "SY" ),\r
+        SequenceName( "SN" ),\r
+        GeneName( "GN" ),\r
+        SequenceSymbol( "SS" ),\r
+        SequenceAccession( "SA" ),\r
+        Domain( "DO" ),\r
+        Annotation( "AN" ),\r
+        CrossRef( "XR" ),\r
+        BinaryCharacter( "BC" ),\r
+        MolecularSequence( "MS" );\r
+\r
+        private final String _text;\r
+\r
+        NDF( final String text ) {\r
+            _text = text;\r
+        }\r
+\r
+        public static NDF fromString( final String text ) {\r
+            for( final NDF n : NDF.values() ) {\r
+                if ( text.startsWith( n._text ) ) {\r
+                    return n;\r
+                }\r
+            }\r
+            return null;\r
+        }\r
+    }\r
+\r
     public static List<PhylogenyNode> searchData( final String query,\r
                                                   final Phylogeny phy,\r
                                                   final boolean case_sensitive,\r
                                                   final boolean partial,\r
-                                                  final boolean search_domains ) {\r
+                                                  final boolean regex,\r
+                                                  final boolean search_domains,\r
+                                                  final double domains_confidence_threshold ) {\r
         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();\r
         if ( phy.isEmpty() || ( query == null ) ) {\r
             return nodes;\r
@@ -946,120 +983,160 @@ public class PhylogenyMethods {
         if ( ForesterUtil.isEmpty( query ) ) {\r
             return nodes;\r
         }\r
+        String my_query = query;\r
+        NDF ndf = null;\r
+        if ( ( my_query.length() > 2 ) && ( my_query.indexOf( ":" ) == 2 ) ) {\r
+            ndf = NDF.fromString( my_query );\r
+            if ( ndf != null ) {\r
+                my_query = my_query.substring( 3 );\r
+            }\r
+        }\r
         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {\r
             final PhylogenyNode node = iter.next();\r
             boolean match = false;\r
-            if ( match( node.getName(), query, case_sensitive, partial ) ) {\r
+            if ( ( ( ndf == null ) || ( ndf == NDF.NodeName ) )\r
+                    && match( node.getName(), my_query, case_sensitive, partial, regex ) ) {\r
                 match = true;\r
             }\r
-            else if ( node.getNodeData().isHasTaxonomy()\r
-                    && match( node.getNodeData().getTaxonomy().getTaxonomyCode(), query, case_sensitive, partial ) ) {\r
+            else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyCode ) )\r
+                    && node.getNodeData().isHasTaxonomy()\r
+                    && match( node.getNodeData().getTaxonomy().getTaxonomyCode(),\r
+                              my_query,\r
+                              case_sensitive,\r
+                              partial,\r
+                              regex ) ) {\r
                 match = true;\r
             }\r
-            else if ( node.getNodeData().isHasTaxonomy()\r
-                    && match( node.getNodeData().getTaxonomy().getCommonName(), query, case_sensitive, partial ) ) {\r
+            else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyCommonName ) )\r
+                    && node.getNodeData().isHasTaxonomy()\r
+                    && match( node.getNodeData().getTaxonomy().getCommonName(),\r
+                              my_query,\r
+                              case_sensitive,\r
+                              partial,\r
+                              regex ) ) {\r
                 match = true;\r
             }\r
-            else if ( node.getNodeData().isHasTaxonomy()\r
-                    && match( node.getNodeData().getTaxonomy().getScientificName(), query, case_sensitive, partial ) ) {\r
+            else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyScientificName ) )\r
+                    && node.getNodeData().isHasTaxonomy()\r
+                    && match( node.getNodeData().getTaxonomy().getScientificName(),\r
+                              my_query,\r
+                              case_sensitive,\r
+                              partial,\r
+                              regex ) ) {\r
                 match = true;\r
             }\r
-            else if ( node.getNodeData().isHasTaxonomy()\r
+            else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyIdentifier ) )\r
+                    && node.getNodeData().isHasTaxonomy()\r
                     && ( node.getNodeData().getTaxonomy().getIdentifier() != null )\r
                     && match( node.getNodeData().getTaxonomy().getIdentifier().getValue(),\r
-                              query,\r
+                              my_query,\r
                               case_sensitive,\r
-                              partial ) ) {\r
+                              partial,\r
+                              regex ) ) {\r
                 match = true;\r
             }\r
-            else if ( node.getNodeData().isHasTaxonomy() && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {\r
+            else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomySynonym ) ) && node.getNodeData().isHasTaxonomy()\r
+                    && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {\r
                 final List<String> syns = node.getNodeData().getTaxonomy().getSynonyms();\r
                 I: for( final String syn : syns ) {\r
-                    if ( match( syn, query, case_sensitive, partial ) ) {\r
+                    if ( match( syn, my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break I;\r
                     }\r
                 }\r
             }\r
-            if ( !match && node.getNodeData().isHasSequence()\r
-                    && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial ) ) {\r
+            if ( !match && ( ( ndf == null ) || ( ndf == NDF.SequenceName ) ) && node.getNodeData().isHasSequence()\r
+                    && match( node.getNodeData().getSequence().getName(), my_query, case_sensitive, partial, regex ) ) {\r
                 match = true;\r
             }\r
-            if ( !match && node.getNodeData().isHasSequence()\r
-                    && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial ) ) {\r
+            if ( !match && ( ( ndf == null ) || ( ndf == NDF.GeneName ) ) && node.getNodeData().isHasSequence()\r
+                    && match( node.getNodeData().getSequence().getGeneName(), my_query, case_sensitive, partial, regex ) ) {\r
                 match = true;\r
             }\r
-            if ( !match && node.getNodeData().isHasSequence()\r
-                    && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {\r
+            if ( !match && ( ( ndf == null ) || ( ndf == NDF.SequenceSymbol ) ) && node.getNodeData().isHasSequence()\r
+                    && match( node.getNodeData().getSequence().getSymbol(), my_query, case_sensitive, partial, regex ) ) {\r
                 match = true;\r
             }\r
             if ( !match\r
+                    && ( ( ndf == null ) || ( ndf == NDF.SequenceAccession ) )\r
                     && node.getNodeData().isHasSequence()\r
                     && ( node.getNodeData().getSequence().getAccession() != null )\r
                     && match( node.getNodeData().getSequence().getAccession().getValue(),\r
-                              query,\r
+                              my_query,\r
                               case_sensitive,\r
-                              partial ) ) {\r
+                              partial,\r
+                              regex ) ) {\r
                 match = true;\r
             }\r
-            if ( search_domains && !match && node.getNodeData().isHasSequence()\r
+            if ( !match && ( ( ( ndf == null ) && search_domains ) || ( ndf == NDF.Domain ) )\r
+                    && node.getNodeData().isHasSequence()\r
                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {\r
                 final DomainArchitecture da = node.getNodeData().getSequence().getDomainArchitecture();\r
                 I: for( int i = 0; i < da.getNumberOfDomains(); ++i ) {\r
-                    if ( match( da.getDomain( i ).getName(), query, case_sensitive, partial ) ) {\r
+                    if ( ( da.getDomain( i ).getConfidence() <= domains_confidence_threshold )\r
+                            && ( match( da.getDomain( i ).getName(), my_query, case_sensitive, partial, regex ) ) ) {\r
                         match = true;\r
                         break I;\r
                     }\r
                 }\r
             }\r
-            //\r
-            if ( !match && node.getNodeData().isHasSequence()\r
+            if ( !match && ( ( ndf == null ) || ( ndf == NDF.Annotation ) ) && node.getNodeData().isHasSequence()\r
                     && ( node.getNodeData().getSequence().getAnnotations() != null ) ) {\r
                 for( final Annotation ann : node.getNodeData().getSequence().getAnnotations() ) {\r
-                    if ( match( ann.getDesc(), query, case_sensitive, partial ) ) {\r
+                    if ( match( ann.getDesc(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break;\r
                     }\r
-                    if ( match( ann.getRef(), query, case_sensitive, partial ) ) {\r
+                    if ( match( ann.getRef(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break;\r
                     }\r
                 }\r
             }\r
-            if ( !match && node.getNodeData().isHasSequence()\r
+            if ( !match && ( ( ndf == null ) || ( ndf == NDF.CrossRef ) ) && node.getNodeData().isHasSequence()\r
                     && ( node.getNodeData().getSequence().getCrossReferences() != null ) ) {\r
                 for( final Accession x : node.getNodeData().getSequence().getCrossReferences() ) {\r
-                    if ( match( x.getComment(), query, case_sensitive, partial ) ) {\r
+                    if ( match( x.getComment(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break;\r
                     }\r
-                    if ( match( x.getSource(), query, case_sensitive, partial ) ) {\r
+                    if ( match( x.getSource(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break;\r
                     }\r
-                    if ( match( x.getValue(), query, case_sensitive, partial ) ) {\r
+                    if ( match( x.getValue(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break;\r
                     }\r
                 }\r
             }\r
-            //\r
-            if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {\r
+            if ( !match && ( ( ndf == null ) || ( ndf == NDF.BinaryCharacter ) )\r
+                    && ( node.getNodeData().getBinaryCharacters() != null ) ) {\r
                 Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();\r
                 I: while ( it.hasNext() ) {\r
-                    if ( match( it.next(), query, case_sensitive, partial ) ) {\r
+                    if ( match( it.next(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break I;\r
                     }\r
                 }\r
                 it = node.getNodeData().getBinaryCharacters().getGainedCharacters().iterator();\r
                 I: while ( it.hasNext() ) {\r
-                    if ( match( it.next(), query, case_sensitive, partial ) ) {\r
+                    if ( match( it.next(), my_query, case_sensitive, partial, regex ) ) {\r
                         match = true;\r
                         break I;\r
                     }\r
                 }\r
             }\r
+            if ( !match\r
+                    && ( ndf == NDF.MolecularSequence )\r
+                    && node.getNodeData().isHasSequence()\r
+                    && match( node.getNodeData().getSequence().getMolecularSequence(),\r
+                              my_query,\r
+                              case_sensitive,\r
+                              true,\r
+                              regex ) ) {\r
+                match = true;\r
+            }\r
             if ( match ) {\r
                 nodes.add( node );\r
             }\r
@@ -1071,7 +1148,8 @@ public class PhylogenyMethods {
                                                             final Phylogeny phy,\r
                                                             final boolean case_sensitive,\r
                                                             final boolean partial,\r
-                                                            final boolean search_domains ) {\r
+                                                            final boolean search_domains,\r
+                                                            final double domains_confidence_threshold ) {\r
         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();\r
         if ( phy.isEmpty() || ( queries == null ) || ( queries.length < 1 ) ) {\r
             return nodes;\r
@@ -1079,123 +1157,168 @@ public class PhylogenyMethods {
         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {\r
             final PhylogenyNode node = iter.next();\r
             boolean all_matched = true;\r
-            for( final String query : queries ) {\r
+            for( String query : queries ) {\r
+                if ( query == null ) {\r
+                    continue;\r
+                }\r
+                query = query.trim();\r
+                NDF ndf = null;\r
+                if ( ( query.length() > 2 ) && ( query.indexOf( ":" ) == 2 ) ) {\r
+                    ndf = NDF.fromString( query );\r
+                    if ( ndf != null ) {\r
+                        query = query.substring( 3 );\r
+                    }\r
+                }\r
                 boolean match = false;\r
                 if ( ForesterUtil.isEmpty( query ) ) {\r
                     continue;\r
                 }\r
-                if ( match( node.getName(), query, case_sensitive, partial ) ) {\r
+                if ( ( ( ndf == null ) || ( ndf == NDF.NodeName ) )\r
+                        && match( node.getName(), query, case_sensitive, partial, false ) ) {\r
                     match = true;\r
                 }\r
-                else if ( node.getNodeData().isHasTaxonomy()\r
-                        && match( node.getNodeData().getTaxonomy().getTaxonomyCode(), query, case_sensitive, partial ) ) {\r
+                else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyCode ) )\r
+                        && node.getNodeData().isHasTaxonomy()\r
+                        && match( node.getNodeData().getTaxonomy().getTaxonomyCode(),\r
+                                  query,\r
+                                  case_sensitive,\r
+                                  partial,\r
+                                  false ) ) {\r
                     match = true;\r
                 }\r
-                else if ( node.getNodeData().isHasTaxonomy()\r
-                        && match( node.getNodeData().getTaxonomy().getCommonName(), query, case_sensitive, partial ) ) {\r
+                else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyCommonName ) )\r
+                        && node.getNodeData().isHasTaxonomy()\r
+                        && match( node.getNodeData().getTaxonomy().getCommonName(),\r
+                                  query,\r
+                                  case_sensitive,\r
+                                  partial,\r
+                                  false ) ) {\r
                     match = true;\r
                 }\r
-                else if ( node.getNodeData().isHasTaxonomy()\r
-                        && match( node.getNodeData().getTaxonomy().getScientificName(), query, case_sensitive, partial ) ) {\r
+                else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyScientificName ) )\r
+                        && node.getNodeData().isHasTaxonomy()\r
+                        && match( node.getNodeData().getTaxonomy().getScientificName(),\r
+                                  query,\r
+                                  case_sensitive,\r
+                                  partial,\r
+                                  false ) ) {\r
                     match = true;\r
                 }\r
-                else if ( node.getNodeData().isHasTaxonomy()\r
+                else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomyIdentifier ) )\r
+                        && node.getNodeData().isHasTaxonomy()\r
                         && ( node.getNodeData().getTaxonomy().getIdentifier() != null )\r
                         && match( node.getNodeData().getTaxonomy().getIdentifier().getValue(),\r
                                   query,\r
                                   case_sensitive,\r
-                                  partial ) ) {\r
+                                  partial,\r
+                                  false ) ) {\r
                     match = true;\r
                 }\r
-                else if ( node.getNodeData().isHasTaxonomy()\r
+                else if ( ( ( ndf == null ) || ( ndf == NDF.TaxonomySynonym ) ) && node.getNodeData().isHasTaxonomy()\r
                         && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {\r
                     final List<String> syns = node.getNodeData().getTaxonomy().getSynonyms();\r
                     I: for( final String syn : syns ) {\r
-                        if ( match( syn, query, case_sensitive, partial ) ) {\r
+                        if ( match( syn, query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break I;\r
                         }\r
                     }\r
                 }\r
-                if ( !match && node.getNodeData().isHasSequence()\r
-                        && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial ) ) {\r
+                if ( !match && ( ( ndf == null ) || ( ndf == NDF.SequenceName ) ) && node.getNodeData().isHasSequence()\r
+                        && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial, false ) ) {\r
                     match = true;\r
                 }\r
-                if ( !match && node.getNodeData().isHasSequence()\r
-                        && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial ) ) {\r
+                if ( !match\r
+                        && ( ( ndf == null ) || ( ndf == NDF.GeneName ) )\r
+                        && node.getNodeData().isHasSequence()\r
+                        && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial, false ) ) {\r
                     match = true;\r
                 }\r
-                if ( !match && node.getNodeData().isHasSequence()\r
-                        && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {\r
+                if ( !match && ( ( ndf == null ) || ( ndf == NDF.SequenceSymbol ) )\r
+                        && node.getNodeData().isHasSequence()\r
+                        && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial, false ) ) {\r
                     match = true;\r
                 }\r
                 if ( !match\r
+                        && ( ( ndf == null ) || ( ndf == NDF.SequenceAccession ) )\r
                         && node.getNodeData().isHasSequence()\r
                         && ( node.getNodeData().getSequence().getAccession() != null )\r
                         && match( node.getNodeData().getSequence().getAccession().getValue(),\r
                                   query,\r
                                   case_sensitive,\r
-                                  partial ) ) {\r
+                                  partial,\r
+                                  false ) ) {\r
                     match = true;\r
                 }\r
-                if ( search_domains && !match && node.getNodeData().isHasSequence()\r
+                if ( !match && ( ( ( ndf == null ) && search_domains ) || ( ndf == NDF.Domain ) )\r
+                        && node.getNodeData().isHasSequence()\r
                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {\r
                     final DomainArchitecture da = node.getNodeData().getSequence().getDomainArchitecture();\r
                     I: for( int i = 0; i < da.getNumberOfDomains(); ++i ) {\r
-                        if ( match( da.getDomain( i ).getName(), query, case_sensitive, partial ) ) {\r
+                        if ( ( da.getDomain( i ).getConfidence() <= domains_confidence_threshold )\r
+                                && match( da.getDomain( i ).getName(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break I;\r
                         }\r
                     }\r
                 }\r
-                //\r
-                if ( !match && node.getNodeData().isHasSequence()\r
+                if ( !match && ( ( ndf == null ) || ( ndf == NDF.Annotation ) ) && node.getNodeData().isHasSequence()\r
                         && ( node.getNodeData().getSequence().getAnnotations() != null ) ) {\r
                     for( final Annotation ann : node.getNodeData().getSequence().getAnnotations() ) {\r
-                        if ( match( ann.getDesc(), query, case_sensitive, partial ) ) {\r
+                        if ( match( ann.getDesc(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break;\r
                         }\r
-                        if ( match( ann.getRef(), query, case_sensitive, partial ) ) {\r
+                        if ( match( ann.getRef(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break;\r
                         }\r
                     }\r
                 }\r
-                if ( !match && node.getNodeData().isHasSequence()\r
+                if ( !match && ( ( ndf == null ) || ( ndf == NDF.CrossRef ) ) && node.getNodeData().isHasSequence()\r
                         && ( node.getNodeData().getSequence().getCrossReferences() != null ) ) {\r
                     for( final Accession x : node.getNodeData().getSequence().getCrossReferences() ) {\r
-                        if ( match( x.getComment(), query, case_sensitive, partial ) ) {\r
+                        if ( match( x.getComment(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break;\r
                         }\r
-                        if ( match( x.getSource(), query, case_sensitive, partial ) ) {\r
+                        if ( match( x.getSource(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break;\r
                         }\r
-                        if ( match( x.getValue(), query, case_sensitive, partial ) ) {\r
+                        if ( match( x.getValue(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break;\r
                         }\r
                     }\r
                 }\r
-                //\r
-                if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {\r
+                if ( !match && ( ( ndf == null ) || ( ndf == NDF.BinaryCharacter ) )\r
+                        && ( node.getNodeData().getBinaryCharacters() != null ) ) {\r
                     Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();\r
                     I: while ( it.hasNext() ) {\r
-                        if ( match( it.next(), query, case_sensitive, partial ) ) {\r
+                        if ( match( it.next(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break I;\r
                         }\r
                     }\r
                     it = node.getNodeData().getBinaryCharacters().getGainedCharacters().iterator();\r
                     I: while ( it.hasNext() ) {\r
-                        if ( match( it.next(), query, case_sensitive, partial ) ) {\r
+                        if ( match( it.next(), query, case_sensitive, partial, false ) ) {\r
                             match = true;\r
                             break I;\r
                         }\r
                     }\r
                 }\r
+                if ( !match\r
+                        && ( ndf == NDF.MolecularSequence )\r
+                        && node.getNodeData().isHasSequence()\r
+                        && match( node.getNodeData().getSequence().getMolecularSequence(),\r
+                                  query,\r
+                                  case_sensitive,\r
+                                  true,\r
+                                  false ) ) {\r
+                    match = true;\r
+                }\r
                 if ( !match ) {\r
                     all_matched = false;\r
                     break;\r
@@ -1216,7 +1339,7 @@ public class PhylogenyMethods {
 \r
     /**\r
      * Convenience method.\r
-     * Sets value for the first confidence value (created if not present, values overwritten otherwise). \r
+     * Sets value for the first confidence value (created if not present, values overwritten otherwise).\r
      */\r
     public static void setBootstrapConfidence( final PhylogenyNode node, final double bootstrap_confidence_value ) {\r
         setConfidence( node, bootstrap_confidence_value, "bootstrap" );\r
@@ -1238,7 +1361,7 @@ public class PhylogenyMethods {
 \r
     /**\r
      * Convenience method.\r
-     * Sets value for the first confidence value (created if not present, values overwritten otherwise). \r
+     * Sets value for the first confidence value (created if not present, values overwritten otherwise).\r
      */\r
     public static void setConfidence( final PhylogenyNode node, final double confidence_value ) {\r
         setConfidence( node, confidence_value, "" );\r
@@ -1246,7 +1369,7 @@ public class PhylogenyMethods {
 \r
     /**\r
      * Convenience method.\r
-     * Sets value for the first confidence value (created if not present, values overwritten otherwise). \r
+     * Sets value for the first confidence value (created if not present, values overwritten otherwise).\r
      */\r
     public static void setConfidence( final PhylogenyNode node, final double confidence_value, final String type ) {\r
         Confidence c = null;\r
@@ -1270,11 +1393,11 @@ public class PhylogenyMethods {
 \r
     /**\r
      * Convenience method to set the taxonomy code of a phylogeny node.\r
-     * \r
-     * \r
+     *\r
+     *\r
      * @param node\r
      * @param taxonomy_code\r
-     * @throws PhyloXmlDataFormatException \r
+     * @throws PhyloXmlDataFormatException\r
      */\r
     public static void setTaxonomyCode( final PhylogenyNode node, final String taxonomy_code )\r
             throws PhyloXmlDataFormatException {\r
@@ -1285,159 +1408,6 @@ public class PhylogenyMethods {
     }\r
 \r
     final static public void sortNodeDescendents( final PhylogenyNode node, final DESCENDANT_SORT_PRIORITY pri ) {\r
-        class PhylogenyNodeSortTaxonomyPriority implements Comparator<PhylogenyNode> {\r
-\r
-            @Override\r
-            public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {\r
-                if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getTaxonomyCode()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getCommonName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getCommonName() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getCommonName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getCommonName().toLowerCase() );\r
-                    }\r
-                }\r
-                if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getSymbol()\r
-                                .compareTo( n2.getNodeData().getSequence().getSymbol() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getGeneName()\r
-                                .compareTo( n2.getNodeData().getSequence().getGeneName() );\r
-                    }\r
-                    if ( ( n1.getNodeData().getSequence().getAccession() != null )\r
-                            && ( n2.getNodeData().getSequence().getAccession() != null )\r
-                            && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )\r
-                            && !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getAccession().getValue() ) ) {\r
-                        return n1.getNodeData().getSequence().getAccession().getValue()\r
-                                .compareTo( n2.getNodeData().getSequence().getAccession().getValue() );\r
-                    }\r
-                }\r
-                if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {\r
-                    return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );\r
-                }\r
-                return 0;\r
-            }\r
-        }\r
-        class PhylogenyNodeSortSequencePriority implements Comparator<PhylogenyNode> {\r
-\r
-            @Override\r
-            public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {\r
-                if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getSymbol()\r
-                                .compareTo( n2.getNodeData().getSequence().getSymbol() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getGeneName()\r
-                                .compareTo( n2.getNodeData().getSequence().getGeneName() );\r
-                    }\r
-                    if ( ( n1.getNodeData().getSequence().getAccession() != null )\r
-                            && ( n2.getNodeData().getSequence().getAccession() != null )\r
-                            && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )\r
-                            && !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getAccession().getValue() ) ) {\r
-                        return n1.getNodeData().getSequence().getAccession().getValue()\r
-                                .compareTo( n2.getNodeData().getSequence().getAccession().getValue() );\r
-                    }\r
-                }\r
-                if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getTaxonomyCode()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getCommonName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getCommonName() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getCommonName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getCommonName().toLowerCase() );\r
-                    }\r
-                }\r
-                if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {\r
-                    return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );\r
-                }\r
-                return 0;\r
-            }\r
-        }\r
-        class PhylogenyNodeSortNodeNamePriority implements Comparator<PhylogenyNode> {\r
-\r
-            @Override\r
-            public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {\r
-                if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {\r
-                    return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );\r
-                }\r
-                if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getTaxonomyCode()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getCommonName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getCommonName() ) ) ) {\r
-                        return n1.getNodeData().getTaxonomy().getCommonName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getTaxonomy().getCommonName().toLowerCase() );\r
-                    }\r
-                }\r
-                if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getName().toLowerCase()\r
-                                .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getSymbol()\r
-                                .compareTo( n2.getNodeData().getSequence().getSymbol() );\r
-                    }\r
-                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )\r
-                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {\r
-                        return n1.getNodeData().getSequence().getGeneName()\r
-                                .compareTo( n2.getNodeData().getSequence().getGeneName() );\r
-                    }\r
-                    if ( ( n1.getNodeData().getSequence().getAccession() != null )\r
-                            && ( n2.getNodeData().getSequence().getAccession() != null )\r
-                            && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )\r
-                            && !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getAccession().getValue() ) ) {\r
-                        return n1.getNodeData().getSequence().getAccession().getValue()\r
-                                .compareTo( n2.getNodeData().getSequence().getAccession().getValue() );\r
-                    }\r
-                }\r
-                return 0;\r
-            }\r
-        }\r
         Comparator<PhylogenyNode> c;\r
         switch ( pri ) {\r
             case SEQUENCE:\r
@@ -1460,7 +1430,7 @@ public class PhylogenyMethods {
     /**\r
      * Removes from Phylogeny to_be_stripped all external Nodes which are\r
      * associated with a species NOT found in Phylogeny reference.\r
-     * \r
+     *\r
      * @param reference\r
      *            a reference Phylogeny\r
      * @param to_be_stripped\r
@@ -1747,7 +1717,7 @@ public class PhylogenyMethods {
     /**\r
      * Calculates the distance between PhylogenyNodes n1 and n2.\r
      * PRECONDITION: n1 is a descendant of n2.\r
-     * \r
+     *\r
      * @param n1\r
      *            a descendant of n2\r
      * @param n2\r
@@ -1767,21 +1737,54 @@ public class PhylogenyMethods {
     private static boolean match( final String s,\r
                                   final String query,\r
                                   final boolean case_sensitive,\r
-                                  final boolean partial ) {\r
+                                  final boolean partial,\r
+                                  final boolean regex ) {\r
         if ( ForesterUtil.isEmpty( s ) || ForesterUtil.isEmpty( query ) ) {\r
             return false;\r
         }\r
         String my_s = s.trim();\r
         String my_query = query.trim();\r
-        if ( !case_sensitive ) {\r
+        if ( !case_sensitive && !regex ) {\r
             my_s = my_s.toLowerCase();\r
             my_query = my_query.toLowerCase();\r
         }\r
-        if ( partial ) {\r
+        if ( regex ) {\r
+            Pattern p = null;\r
+            try {\r
+                if ( case_sensitive ) {\r
+                    p = Pattern.compile( my_query );\r
+                }\r
+                else {\r
+                    p = Pattern.compile( my_query, Pattern.CASE_INSENSITIVE );\r
+                }\r
+            }\r
+            catch ( final PatternSyntaxException e ) {\r
+                return false;\r
+            }\r
+            if ( p != null ) {\r
+                return p.matcher( my_s ).find();\r
+            }\r
+            else {\r
+                return false;\r
+            }\r
+        }\r
+        else if ( partial ) {\r
             return my_s.indexOf( my_query ) >= 0;\r
         }\r
         else {\r
-            return Pattern.compile( "(\\b|_)" + Pattern.quote( my_query ) + "(\\b|_)" ).matcher( my_s ).find();\r
+            Pattern p = null;\r
+            try {\r
+                p = Pattern.compile( "(\\b|_)" + Pattern.quote( my_query ) + "(\\b|_)" );\r
+            }\r
+            catch ( final PatternSyntaxException e ) {\r
+                return false;\r
+            }\r
+            if ( p != null ) {\r
+                return p.matcher( my_s ).find();\r
+            }\r
+            else {\r
+                return false;\r
+            }\r
         }\r
     }\r
 \r
@@ -1810,4 +1813,142 @@ public class PhylogenyMethods {
         TAXONOMY_ID_UNIPROT_2,\r
         TAXONOMY_SCIENTIFIC_NAME;\r
     }\r
+\r
+    public static void addMolecularSeqsToTree( final Phylogeny phy, final Msa msa ) {\r
+        for( int s = 0; s < msa.getNumberOfSequences(); ++s ) {\r
+            final org.forester.sequence.MolecularSequence seq = msa.getSequence( s );\r
+            final PhylogenyNode node = phy.getNode( seq.getIdentifier() );\r
+            final org.forester.phylogeny.data.Sequence new_seq = new Sequence();\r
+            new_seq.setMolecularSequenceAligned( true );\r
+            new_seq.setMolecularSequence( seq.getMolecularSequenceAsString() );\r
+            new_seq.setName( seq.getIdentifier() );\r
+            try {\r
+                new_seq.setType( PhyloXmlUtil.SEQ_TYPE_PROTEIN );\r
+            }\r
+            catch ( final PhyloXmlDataFormatException ignore ) {\r
+                // do nothing\r
+            }\r
+            node.getNodeData().addSequence( new_seq );\r
+        }\r
+    }\r
+\r
+    final private static class PhylogenyNodeSortTaxonomyPriority implements Comparator<PhylogenyNode> {\r
+\r
+        @Override\r
+        public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {\r
+            if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {\r
+                    return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()\r
+                            .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {\r
+                    return n1.getNodeData().getTaxonomy().getTaxonomyCode()\r
+                            .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );\r
+                }\r
+            }\r
+            if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getName().toLowerCase()\r
+                            .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getGeneName()\r
+                            .compareTo( n2.getNodeData().getSequence().getGeneName() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getSymbol()\r
+                            .compareTo( n2.getNodeData().getSequence().getSymbol() );\r
+                }\r
+            }\r
+            if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {\r
+                return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );\r
+            }\r
+            return 0;\r
+        }\r
+    }\r
+\r
+    final private static class PhylogenyNodeSortSequencePriority implements Comparator<PhylogenyNode> {\r
+\r
+        @Override\r
+        public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {\r
+            if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getName().toLowerCase()\r
+                            .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getGeneName()\r
+                            .compareTo( n2.getNodeData().getSequence().getGeneName() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getSymbol()\r
+                            .compareTo( n2.getNodeData().getSequence().getSymbol() );\r
+                }\r
+            }\r
+            if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {\r
+                    return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()\r
+                            .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {\r
+                    return n1.getNodeData().getTaxonomy().getTaxonomyCode()\r
+                            .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );\r
+                }\r
+            }\r
+            if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {\r
+                return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );\r
+            }\r
+            return 0;\r
+        }\r
+    }\r
+\r
+    final private static class PhylogenyNodeSortNodeNamePriority implements Comparator<PhylogenyNode> {\r
+\r
+        @Override\r
+        public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {\r
+            if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {\r
+                return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );\r
+            }\r
+            if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {\r
+                    return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()\r
+                            .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {\r
+                    return n1.getNodeData().getTaxonomy().getTaxonomyCode()\r
+                            .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );\r
+                }\r
+            }\r
+            if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getName().toLowerCase()\r
+                            .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getGeneName()\r
+                            .compareTo( n2.getNodeData().getSequence().getGeneName() );\r
+                }\r
+                if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )\r
+                        && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {\r
+                    return n1.getNodeData().getSequence().getSymbol()\r
+                            .compareTo( n2.getNodeData().getSequence().getSymbol() );\r
+                }\r
+            }\r
+            return 0;\r
+        }\r
+    }\r
 }\r