This tutorial will guide you through adding a new example command to PhyloNet that can be executed from within the PHYLONET block of a NEXUS file.

Step 1. Prepare Existing Source

The first step in authoring a new command is to download the PhyloNet source code and its binary dependencies. The most recent version of the tool and its source can be found here.

PhyloNet also has a few binary dependencies: anltr-runtime, mockito, and apache-commons-io.

After downloading the PhyloNet source files and dependency libraries make sure you can compile PhyloNet.  JDK 1.6 or later is required.

Step 2. Create Your Command Class and extend CommandBase

In our example we will create a new command CountNodes that computes the number of nodes in a given network.

The first step in this process is to create our CountNodes class and have it extend edu.rice.cs.bioinfo.programs.phylonet.commands.CommandBase:

package com.example;

import edu.rice.cs.bioinfo.programs.phylonet.commands.*;
import edu.rice.cs.bioinfo.library.language.pyson._1_0.ir.blockcontents.*;
import edu.rice.cs.bioinfo.library.language.richnewick._1_0.ast.*;
import edu.rice.cs.bioinfo.library.programming.*;
import java.io.*;
import java.util.*;

public class CountNodes extends CommandBase
{
   public CountNodes(SyntaxCommand motivatingCommand, ArrayList<Parameter> params,
                     Map<String,NetworkNonEmpty>  sourceIdentToNetwork, Proc3<String, Integer, Integer> errorDetected)
    {
      super(motivatingCommand, params, sourceIdentToNetwork, errorDetected);
    }
    ...
}

The new command class should provide the illustrated public four argument constructor with corresponding call to super. When a command is encountered in a PhyloNet block within a NEXUS file, it is this constructor that will be called to create the command instance.

2.1 Implement Min and Max Params

By inheriting from CommandBase your class will have to implement the following methods:

protected int getMinNumParams()
{
    ...
}

protected int getMaxNumParams()
{
   ...
}

which define the minimum and maximum number of parameters allowed for your command in the NEXUS file. It is important to keep in mind that most commands in PhyloNet allow an optional final parameter for redirecting command output to a file instead of the console. So generally the maximum number of allowable parameters is one more than the number supported by the command itself. Our example CountNodes command will take a minimum of one parameter (the name of the network to scan) and at most two parameters (the second being the optional file destination of our command's output).

protected int getMinNumParams()
{
  return 1;
}

protected int getMaxNumParams()
{
  return 2;
}

2.2 Implement checkParamsForCommand()

By inheriting from CommandBase your class will have to implement checkParamsForCommand() which provides an opportunity to perform context sensitive analysis over the command's parameters prior to execution. The function should return true if no errors are detected and false if errors are detected. It should also report any discovered errors to the inherited errorDetected member.

For our command, we need only to check only that the given network identifier is in fact defined in the NEXUS document:

protected boolean checkParamsForCommand()
{
   int expectedNetworkNameParameterIndex = 0; // network ident should be first parameter in command.

   // automatically checks for network existance and reports any errors to this.errorDetected.
   NetworkNonEmpty network = this.assertAndGetNetwork(expectedNetworkNameParameterIndex);

   if(network == null) // user specified network name is invalid
   {
       return false;
   }
  _networkToScan = network; // added field to class to retain network to scan.
  return true;

}

Note that we have added a field to our class _networkToScan to retain a reference to the specified network to scan. This will be useful in our next step.

2.3 Implement executeCommandHelp.

The actual execution of the command takes place within the executeCommandHelp method. In our example we will put the command execution code in this method directly; however, it is advisable not to implement the core logic of larger commands here but instead to delegate that computation to a helper class. The single displayResult parameter is a function for displaying text results of the command to the user. The first call to displayResult should begin with a newline character to preserve the tool's output formatting. These results will be automatically displayed on the console or sent to an output file based on the user's preference.

protected void executeCommandHelp(Proc<String> displayResult) throws IOException
{
    String eNewickNetwork = NetworkTransformer.toENewick(_networkToScan); // convert NEXUS network to extended newick string

    /*
     * convert extended network string to Network
     */
    edu.rice.cs.bioinfo.programs.phylonet.structs.network.io.ExNewickReader<String> enr =
      new edu.rice.cs.bioinfo.programs.phylonet.structs.network.io.ExNewickReader<String>(
          newStringReader(eNewickNetwork));

    edu.rice.cs.bioinfo.programs.phylonet.structs.network.Network<String> net;
    try
    {
      net = enr.readNetwork();
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }

    /*
     * count and display number of nodes in network
     */
    Iterable<edu.rice.cs.bioinfo.programs.phylonet.structs.network.NetNode<String>> allNodes = net.dfs();
    int nodeCount = 0;
    for(Object node : net.dfs())
    {
      nodeCount++;
    }

    displayResult.execute("\nNetwork contains " + nodeCount + " nodes.");
}

Step 3. Update CommandFactory

The next step is to update edu.rice.cs.bioinfo.programs.phylonet.commands.CommandFactory to construct instances of the CountNodes command. When examining the existing structure of the CommandFactory you will discover an if/else if chain within the make method:

public class CommandFactory {



    public static Command make(SyntaxCommand directive, Map<String,NetworkNonEmpty> sourceIdentToNetwork,
                               Proc3<String, Integer, Integer> errorDetected, Random rand)
    {
        ...

        if(lowerCommandName.equals("symmetricdifference") || lowerCommandName.equals("rf"))
        {
            return new SymmetricDifference(directive, params, sourceIdentToNetwork, errorDetected);
        }
        else if(lowerCommandName.equals("lca"))
        {
            return new LCA(directive, params, sourceIdentToNetwork, errorDetected);
        }
        ...
    }

To add a new command to the factory, select a lower case string name to represent the command in a NEXUS file and append an entry of the command in the chain. In our example we will name the CountNodes command simply "CountNodes":

public class CommandFactory {



    public static Command make(SyntaxCommand directive, Map<String,NetworkNonEmpty> sourceIdentToNetwork,
                               Proc3<String, Integer, Integer> errorDetected, Random rand)
    {
        ...
        else if(lowerCommandName.equals("nexus_out"))
        {
            return new NexusOut(directive, params, sourceIdentToNetwork, errorDetected);
        }
        else if(lowerCommandName.equals("countnodes"))
        {
           return new CountNodes(directive, params, sourceIdentToNetwork, errorDetected);
        }
        ...

This will instruct PhyloNet to create instances of the new command when named within a PHYLONET block.

Step 4. Compile and Test

At this point inclusion of a new command is complete. After recompiling PhyloNet the new command should be available. For example, the following NEXUS file will now be processed by PhyloNet:

#NEXUS

BEGIN NETWORKS;

Network net = ((a,(b,(c)x#1)M)N,((x#1,d)J,e)Z)R;

END;


BEGIN PHYLONET;

CountNodes net;

END;
  • No labels