package Galaxy.Toolbox;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.UnknownFieldException;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.StreamException;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import CLInterface.ConverterConfig;
import CLInterface.Printer;
import Core.Pair;
import Core.Triple;
import FileOps.XStream.XStreamWrapper;
import Galaxy.Tree.Tool.Tool;
import Specification.GalaxySpecification;
/***
* The GalaxyToolDatabase loads the Galaxy Tools given the root directory
* of a galaxy installation. It supports loading existing tools, adding
* new tools, and writing the modified tool library to a directory.
*
* @author viper
*
*/
public class GalaxyToolDatabase {
/***
* INDEX_NAME: name of tool index file.
*/
final String INDEX_NAME = "tool_conf.xml";
/***
* TOOL_DIRECTORY: name of directory where tools are stored.
*/
final String TOOL_DIRECTORY = "tools";
/***
* toolDB: Stores all the tool objects. The key value of the map
* is the tool id. The first string in the triple is the relative path to the tool.
* The second string in the triple is the package name of the tool, and the
* Toolt object in the tripleis the loaded Tool object. This is null by default
* and gets filled in if the tool is requested.
*/
public Map<String, Triple<String,String, Tool>> toolDB;
/***
* toolIndexParser: This is an xml parser for parsing the tool index file.
*/
XStreamWrapper<Toolbox> toolIndexParser;
/***
* toolIndex: This is the object representation of the tool index.
*/
Toolbox toolIndex;
/***
* Get the list of ids contained in the tool database.
* @return A set of tool ids.
*/
public Set<String> getTools(){
return toolDB.keySet();
}
/***
* The Toolbox object contains all the information inside the
* toolbox tag set. This includes a list of sections.
* @author viper
*
*/
static class Toolbox{
List<Section> sections;
public String toString(){
return sections.toString();
}
}
/***
* This corresponds to a section tag set. A section has a name
* (the package name), and an id, as well as a list of entries.
* @author viper
*
*/
static class Section{
List<Entry> entries;
String name;
String id;
public String toString(){
return "<name:"+name+" id:"+id+" entries:"
+entries.toString()+">";
}
}
/***
* The entry class is a dummy class that is extended
* by tags contained within the section tag set.
* @author viper
*
*/
static class Entry{}
/***
* A ToolEntry corresponds to the information contained in the
* tool tag set. Each ToolEntry has a path.
* @author viper
*
*/
static class ToolEntry extends Entry{
String path;
public String toString(){
return "[T] "+path;
}
}
/***
* A LabelEntry corresponds to information inside the label tagset.
* This is only used inside the parsing step. Contains the string label.
* @author viper
*
*/
static class LabelEntry extends Entry{
String label;
public String toString(){
return "[L] "+ label;
}
}
/***
*
* @param id The id of the tool to get a relative path for.
* @param pkg The package the tool is contained in
* @return returns the relative path package/id.xml
*/
private String ToolPairToRelativePath(String id, String pkg){
return pkg+ "/"+ id+".xml";
}
/***
* Turn a relative path into an absolute path.
* directory.
* @param path relative to the root of the tool/ directory
* @return absolute path, using the galaxy input directory as the galaxy directory.
*/
public String relativePathToInputToolPath(String relativePath){
String toolPath;
toolPath = (String) ConverterConfig.GALAXY_INPUT_DIR;
toolPath += TOOL_DIRECTORY + "/";
toolPath += relativePath;
return toolPath;
}
/***
*
* @param path relative to the root of the tool directory.
* @return absolute path, using the galaxy output directory as the galaxy directory.
*/
private String relativePathToOutputToolPath(String relativePath){
String toolPath;
toolPath = (String) ConverterConfig.GALAXY_OUTPUT_DIR;
toolPath += TOOL_DIRECTORY + "/";
toolPath += relativePath;
return toolPath;
}
/**
* Visits all the objects in the parsed tool index tree and populates the database
* object entries in the database.
* @author viper
*
*/
class ToolIndexVisitor{
/**
* Scrapes the tool's XML file to find the id of the tool. The XML file is not
* fully parsed in this operation.
* @param relative path of the tool from the root of the tool directory.
* @return the id of the tool that was scraped from the file.
*/
private String getToolIdFromToolXML(String relativePath){
String toolPath;
XStream xstream;
BufferedReader reader;
toolPath = relativePathToInputToolPath(relativePath);
xstream = new XStream(new StaxDriver());
try {
reader = new BufferedReader(new FileReader(toolPath));
StaxDriver xmlParser = new StaxDriver();
HierarchicalStreamReader xmlPeeker= xmlParser.createReader(reader);
String tool_id= xmlPeeker.getAttribute("id");
reader.close();
return tool_id;
} catch(StreamException e){
System.err.println("Error loading "+ relativePath);
System.err.println(e.getCause().toString());
}
catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println("Error loading "+ relativePath);
System.err.println(e.getCause());
} catch (IOException e) {
// TODO Auto-generated catch block
System.err.println("Error loading "+ relativePath);
System.err.println(e.getCause());
}
return "";
}
/***
* Adds a new tool to the tool database using infomation
* contained in the ToolEntry object and the package string.
*
* @param toolentry The toolEntry to visit
* @param pkg the name of the package the tool iscontained in.
*/
public void visit(ToolEntry toolentry, String pkg){
String tool_id = getToolIdFromToolXML(toolentry.path);
if(tool_id.equals("") == false)
toolDB.put(tool_id, new Triple<String,String, Tool>(
toolentry.path, pkg,
null
));
}
/***
* Visits all the entries inside a section, calling the appropriate
* visit function for each visit. The section id is used as the package
* name.
* @param section The section to visit the entries of.
*/
public void visit(Section section){
for(Entry entry : section.entries){
if(entry instanceof ToolEntry){
ToolEntry toolentry = (ToolEntry) entry;
visit(toolentry, section.id);
}
}
}
/***
* Visit all the sections in a toolbox object.
* @param toolbox Toolbox to visit.
*/
public void visit(Toolbox toolbox){
for(Section section : toolbox.sections){
visit(section);
}
}
}
/**
* Setup the tool index parser to parse the tool index file correctly
* and load the tool database.
*/
public GalaxyToolDatabase(){
//Path, package, tool
toolDB = new HashMap<String, Triple<String,String, Tool>>();
toolIndexParser = new XStreamWrapper<Toolbox>();
toolIndexParser.bindElementToClass(Toolbox.class, "toolbox");
toolIndexParser.bindGroupToList(Toolbox.class, "sections", Section.class);
toolIndexParser.bindElementToClass(Section.class, "section");
toolIndexParser.bindAttributeToClassField(Section.class, "name", "name");
toolIndexParser.bindAttributeToClassField(Section.class, "id", "id");
toolIndexParser.bindGroupToList(Section.class, "entries", Entry.class);
toolIndexParser.bindElementToClass(ToolEntry.class, "tool");
toolIndexParser.bindAttributeToClassField(ToolEntry.class, "path", "file");
toolIndexParser.bindElementToClass(LabelEntry.class, "label");
toolIndexParser.bindAttributeToClassField(LabelEntry.class, "label", "text");
toolIndexParser.bindAttributeToClassField(LabelEntry.class, "label", "text");
loadDatabase();
}
/***
* Clear the database and the tool index object.
*/
public void clear(){
toolDB.clear();
toolIndex = null;
}
/**
* Load the database from the Galaxy input directory.
*/
private void loadDatabase(){
try {
ToolIndexVisitor indexVisitor = new ToolIndexVisitor();
toolIndex = toolIndexParser.parse(new File(ConverterConfig.GALAXY_INPUT_DIR + INDEX_NAME));
indexVisitor.visit(toolIndex);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
Printer.log("Error Parsing "+ConverterConfig.GALAXY_INPUT_DIR + INDEX_NAME);
Printer.log("Galaxy Directory not found: Cannot load tools");
}
}
/**
* Dump the tool database to the Galaxy output directory. Dumps the updated index file
* and all the tools in their appropriate directories. Tools that cannot be dumped are skipped.
*/
public void dumpDatabase(){
String idxPath = ConverterConfig.GALAXY_OUTPUT_DIR + INDEX_NAME;
try {
(new File(idxPath)).createNewFile();
} catch (IOException e1) {
System.err.println("Index file creation failed.");
}
if(toolIndex != null){
try {
toolIndexParser.generate(toolIndex, idxPath);
}
catch (IOException e) {
// TODO Auto-generated catch block
System.err.println(e.getMessage());
e.printStackTrace();
}
}
for(String id : toolDB.keySet()){
Triple<String, String, Tool> currentTool = toolDB.get(id);
String relativePath = currentTool.getElem1();
Tool toolToDump = currentTool.getElem3();
if(toolToDump != null&& relativePath != null ){
File dir = new File(relativePathToOutputToolPath(getDirectory(id)));
File absfile = new File(relativePathToOutputToolPath(relativePath));
dir.mkdirs();
try {
absfile.createNewFile();
} catch (IOException e1) {
System.err.println("File creation failed.");
}
try {
GalaxySpecification.getXMLGenerator().generate(toolToDump, absfile.getAbsolutePath());
} catch (IOException e) {
// TODO Auto-generated catch block
System.err.println(e.getLocalizedMessage());
System.err.println(e.getMessage());
e.printStackTrace();
}
}
}
}
/**
* Get a section of the tool index file by the id of the section.
* return null if the section doesn't exist.
* @param sectionName name of section to get.
* @return section with specified name.
*/
private Section getSection(String sectionName){
if(sectionName == null)
return null;
if(toolIndex == null || toolIndex.sections == null)
return null;
for(Section section : toolIndex.sections){
if(section.id.equals(sectionName))
return section;
}
return null;
}
/***
* Add a new section with the given section id. If the section already
* exists, return the existing section. If it doesn't, create a new section,
* add it to the tool index object and return the new section. The created section
* will have an id and name that corresponds to the specified section name.
* @param sectionName The name the created/retrieved section should have as an id.
* @return The found/created section.
*/
private Section addSection(String sectionName){
if(toolIndex == null)
toolIndex = new Toolbox();
if(toolIndex.sections == null)
toolIndex.sections = new LinkedList<Section>();
for(Section section : toolIndex.sections){
if(section.id.equals(sectionName))
return section;
}
Section newSect = new Section();
newSect.name = sectionName;
newSect.id = sectionName;
newSect.entries = new LinkedList<Entry>();
toolIndex.sections.add(newSect);
return newSect;
}
/**
* Get the enclosing package name given the id of a tool.
* @param id the id of the tool to get the package of.
* @return the name of the package.
*/
public String getPackage(String id){
if(toolDB.containsKey(id))
return toolDB.get(id).getElem2();
return "";
}
/**
* Get the directory of the tool with the specified id. For example if
* the tool's path is foo/a/test.xml, the returned directory will be
* foo/a/
* @param id The id of the tool to get the directory of.
* @return The directory path.
*/
public String getDirectory(String id){
String tooldir = "";
if(toolDB.containsKey(id)){
String path =toolDB.get(id).getElem1();
String[] args = path.split("/");
for(int i=0; i<args.length-1; i++){
tooldir += args[i] + "/";
}
}
return tooldir;
}
/**
* Get the path of a tool from the id. For example if the path of
* some tool is foo/A/test.xml, then, foo/A/test.xml is returned.
* If the tool doesn't exist return an empty string.
* @param id id of the tool to get the path of.
* @return
*/
public String getPath(String id){
;
if(toolDB.containsKey(id))
return toolDB.get(id).getElem1();
return "";
}
/***
* Given the desired id, return a modified version of the desired id
* that does not conflict with any of the existing tools in the database.
* @param baseid The desired id for the tool.
* @return An id based on the desired id that does not conflict with the ids of
* any existing tools.
*/
public String getUniqueToolID(String baseid){
String id = baseid;
int count = 0;
while(toolDB.containsKey(id)){
id = baseid + count;
count++;
}
return id;
}
/***
* Add a tool to the database given the package the tool is a part of and a
* Tool object. Creates a new tool_entry in the tool index file, and
* as adds a the tool to the tool database. the tool's filename is generated
* from the tool id.
* @param packageName The package name of the tool to add.
* @param tool The tool to add.
*/
public void addTool(String packageName,Tool tool){
String id = tool.getID();
String package_ = packageName;
String mypath = ToolPairToRelativePath(id,packageName);
toolDB.put(id, new Triple(mypath, package_, tool));
/*
* Add to index tree.
*/
ToolEntry toolEntry = new ToolEntry();
toolEntry.path = mypath;
Section idxSection = getSection(packageName);
if(idxSection == null){
idxSection = addSection(packageName);
}
idxSection.entries.add(toolEntry);
}
/**
* Retrieves a tool object from the database using the provided tool id. If
* the tool object is not loaded into the database, its .xml specification file is
* parsed and the tool is loaded. If the key is not found, then null is returned.
* @param toolId the id name of the tool to retrieve.
* @return the tool object in the database.
*/
public Tool getTool(String toolId){
if(toolDB.containsKey(toolId)){
Tool tool;
Triple<String, String, Tool> databaseTool= toolDB.get(toolId);
/*
If the tool has never been parsed. Parse it and store it.
*/
if(databaseTool.getElem3() == null){
try {
tool = GalaxySpecification.getXMLParser().parse(
new File(relativePathToInputToolPath(databaseTool.getElem1())
));
if(tool == null)
throw new RuntimeException("Tool did not parse correctly");
databaseTool.setElem3(tool);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.print("FILE: "+databaseTool.getElem1());
System.err.print(toolId);
System.err.print(e.getMessage());
e.printStackTrace();
}
}
return databaseTool.getElem3();
}
return null;
}
}