/*
* Copyright (C) 2011 apurv
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package nescent.phylogeoref.reader;
import java.io.File;
import static java.lang.System.out;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.forester.phylogeny.Phylogeny;
import org.forester.phylogeny.PhylogenyNode;
import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
/**
* Reads the raw, skeletal tree structure from a treeFile.
* Cooks it along with metadata from metaFile
*
* @author apurv
*/
public class GrandUnifiedReader {
private final static Logger LOGGER = Logger.getLogger("nescent");
private File treeFile;
private File[] metaFile;
private char delim; // Delimiter character.
private int cladeDiv; // Clade Classifier.
private Integer[] args;
private Map mouldMapArray[] = null;
//Values to be returned by this class.
Phylogeny[] phylogenies = null;
public GrandUnifiedReader() {
}
/**
*
* @param treeFile
* @param metaFile
* @param delim delimiter character e.g. '\t ', '\s '
* @param cladeDiv The column whose values have to be taken as the clade.
*/
public GrandUnifiedReader(File treeFile, File[] metaFile, char delim, int cladeDiv) {
this.treeFile = treeFile;
this.metaFile = metaFile;
this.delim = delim;
this.cladeDiv = cladeDiv;
}
public Phylogeny[] getPhylogenyArray()throws NullPointerException {
if (phylogenies !=null)
return phylogenies;
else
throw new NullPointerException("getPhylogenyArray() should be called after buildUnifiedPhylogeny()");
}
public Phylogeny getPhylogeny()throws NullPointerException {
if (phylogenies !=null)
return phylogenies[0];
else
throw new NullPointerException("getPhylogeny() should be called after buildUnifiedPhylogeny()");
}
/**
*
* @return the array of mould maps which contain label, mould binding for each labeled node.
* @throws NullPointerException
*/
public Map[] getMouldMaps()throws NullPointerException{
if(mouldMapArray != null){
return mouldMapArray;
}else{
throw new NullPointerException("getMouldMaps() should be called after buildUnifiedPhylogeny()");
}
}
/**
*
* @return the first map in the array of mould maps.
* @throws NullPointerException
*/
public Map getMouldMap()throws NullPointerException{
if(mouldMapArray != null){
return mouldMapArray[0];
}else{
throw new NullPointerException("getMouldMaps() should be called after buildUnifiedPhylogeny()");
}
}
/**
* Sets the column whose values will be takes as the clade division.<br>
* Specify 0, if no clade division is to be specified.
* @param cladeDiv
* @return
*/
public GrandUnifiedReader setCladeDiv(int cladeDiv) {
this.cladeDiv = cladeDiv;
return this;
}
public GrandUnifiedReader setDelim(char delim) {
this.delim = delim;
return this;
}
public GrandUnifiedReader setMetaFile(File[] metaFile) {
this.metaFile = metaFile;
return this;
}
public GrandUnifiedReader setTreeFile(File treeFile) {
this.treeFile = treeFile;
return this;
}
/**
* args[0] should be the column number of Label in the input file. <br>
* args[1] should be the column number of Latitude in the input file <br>
* and so on...<br>
* args[i] = 0 implies that the property is not being given in the input file.<br>
* The values in the example here have been fed according to the file,
* samples/mammals/mammals_in_tree.txt<br>
* If you are using this file pass the example values.
*
* <table border="1">
* <tr>
* <th> args[i] </th>
* <th> Property </th>
* <th> Example </th>
* </tr>
* <tr>
* <td> args[0]</td>
* <td> Label </td>
* <td> 5, if the fifth column has to be read as the label </td>
* </tr>
* <tr>
* <td> args[1] </td>
* <td> Latitude </td>
* <td> 4, third column is to be read as latitude </td>
* </tr>
* <tr>
* <td> args[2] </td>
* <td> Longitude </td>
* <td> 3, fourth column to be read as longitude </td>
* </tr>
* <tr>
* <td> args[3] </td>
* <td> Id </td>
* <td> 1 </td>
* </tr>
* <tr>
* <td> args[4] </td>
* <td> Scientific Name </td>
* <td> 2 </td>
* </tr>
* <tr>
* <td> args[5] </td>
* <td> Common Name </td>
* <td> 0, since this value is not being given. </td>
* </tr>
* </table>
* <br>
* The trailing unused values can be left unspecified.
*/
public GrandUnifiedReader setArgs(Integer...args) {
//TODO: Add checks here whether or not user is specifying the arguments correctly.
this.args = args;
return this;
}
/**
* Builds the full fledged Phylogeny.
*/
public void buildUnifiedPhylogeny(){
//Get an array of raw phylogenies.
UniversalTreeReader utr = new UniversalTreeReader();
phylogenies = utr.readPhylogenyArray(treeFile);
mouldMapArray = new HashMap[phylogenies.length];
int i = 0;
//Iterate over each phylogeny and cook it up in the kitchen to get
//full fledged phylogenies.
for(Phylogeny phylogeny:phylogenies){
mouldMapArray[i] = buildMouldMap(phylogeny);
// Prepare a new kitchen
PhylogenyKitchen kitchen = new PhylogenyKitchen(metaFile[i], phylogeny, mouldMapArray[i], delim, cladeDiv);
// Set the recipe by matching the column values.
kitchen.setRecipe(args);
// Cook the tree
kitchen.cookDish();
i++;
}
}
/**
* Builds the mouldMap corresponding to the PhylogenyNode phy.
* @param phy
* @return
*/
private Map<String,PhylogenyMould> buildMouldMap(Phylogeny phy){
Map<String,PhylogenyMould> mouldMap = new HashMap<String, PhylogenyMould>();
for( PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext();){
PhylogenyNode node = it.next();
String label = node.getNodeName();
if(!label.equals("") && label != null){
PhylogenyMould mould = new PhylogenyMould();
mould.setAssociatedNode(node);
mouldMap.put(label, mould);
}
}
return mouldMap;
}
}