PageRenderTime 68ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/scripts/tr_export_species_tree.pl

https://github.com/jestill/iplant-treerec
Perl | 747 lines | 498 code | 94 blank | 155 comment | 37 complexity | 8e157916c696aba19985bb99329c6886 MD5 | raw file
  1. #!/usr/bin/perl -w
  2. #-----------------------------------------------------------+
  3. # |
  4. # tr_export_species_tree.pl |
  5. # |
  6. #-----------------------------------------------------------+
  7. # |
  8. # CONTACT: JamesEstill_at_gmail.com |
  9. # STARTED: 04/11/2012 |
  10. # UPDATED: 04/12/2012 |
  11. # |
  12. # DESCRIPTION: |
  13. # Export a species tree that has been loaded to the TR |
  14. # database. This generates a species tree with internal |
  15. # node identifiers that are usable for input into |
  16. # TREEBEST analyses to that reconciled gene trees can be |
  17. # loaded into the database using the internal nodes ids |
  18. # as used in the database. |
  19. # |
  20. # LICENSE: |
  21. # Simplified BSD License |
  22. # http://tinyurl.com/iplant-tr-license |
  23. # |
  24. #-----------------------------------------------------------+
  25. #
  26. #
  27. # TEST USE:
  28. # ./tr_export_species_tree.pl -o test_out.nwk -u jestill --host localhost --dbname tr_test --driver mysql -n bowers_rosids
  29. # Set package in order to pass tree object to recursive subfunction. If
  30. # this is changed will need to change object name in load_tree_nodes
  31. # below.
  32. package IPlantTR;
  33. #-----------------------------+
  34. # INCLUDES |
  35. #-----------------------------+
  36. use strict;
  37. use DBI;
  38. use Getopt::Long;
  39. use Bio::TreeIO; # BioPerl Tree I/O
  40. use Bio::Tree::TreeI;
  41. # The following needed for printing help
  42. use Pod::Select; # Print subsections of POD documentation
  43. use Pod::Text; # Print POD doc as formatted text file
  44. use IO::Scalar; # For print_help subfunction
  45. use IO::Pipe; # Pipe for STDIN, STDOUT for POD docs
  46. use File::Spec; # Convert a relative path to an abosolute path
  47. #-----------------------------+
  48. # VARIABLES |
  49. #-----------------------------+
  50. my ($VERSION) = 0.1;
  51. my $outfile; # Path to the output species tree
  52. my $format = "newick"; # Assumes newick is preferred output
  53. # DATABASE VARS
  54. my $db; # Database name (ie. iplant_tr)
  55. my $host; # Database host (ie. localhost)
  56. my $driver; # Database driver (ie. mysql)
  57. my $statement; # Database statement
  58. my $sth; # Database statement handle
  59. # OPTIONS SET IN USER ENVIRONMENT
  60. my $usrname = $ENV{TR_USERNAME}; # User name to connect to database
  61. my $pass = $ENV{TR_PASSWORD}; # Password to connect to database
  62. my $dsn = $ENV{TR_DSN}; # DSN for database connection
  63. # BOOLEANS
  64. my $quiet = 0;
  65. my $verbose = 0;
  66. my $show_help = 0;
  67. my $show_usage = 0;
  68. my $show_man = 0;
  69. my $show_version = 0;
  70. my $do_test = 0; # Run the program in test mode
  71. # Optional data for the tree
  72. my $tree_name; # Name for species_tree_attributes table
  73. my $tree_version; # Tree version number to assign
  74. #-----------------------------+
  75. # COMMAND LINE OPTIONS |
  76. #-----------------------------+
  77. my $ok = GetOptions(# REQUIRED OPTIONS
  78. "o|outfile=s" => \$outfile,
  79. "n|t|tree-name=s" => \$tree_name,
  80. # DSN REQUIRED UNLESS PARTS USED
  81. "d|dsn=s" => \$dsn,
  82. # ALTERNATIVE TO --dsn
  83. "driver=s" => \$driver,
  84. "dbname=s" => \$db,
  85. "host=s" => \$host,
  86. # THE FOLLOWING CAN BE DEFINED IN ENV
  87. "u|dbuser=s" => \$usrname,
  88. "p|dbpass=s" => \$pass,
  89. # ADDITIONAL OPTIONS
  90. "tree-version=i" => \$tree_version,
  91. "format=s" => \$format,
  92. "q|quiet" => \$quiet,
  93. "verbose" => \$verbose,
  94. # ADDITIONAL INFORMATION
  95. "usage" => \$show_usage,
  96. "test" => \$do_test,
  97. "version" => \$show_version,
  98. "man" => \$show_man,
  99. "h|help" => \$show_help,);
  100. #-----------------------------+
  101. # SHOW REQUESTED HELP |
  102. #-----------------------------+
  103. if ( ($show_usage) ) {
  104. # print_help ("usage", File::Spec->rel2abs($0) );
  105. print_help ("usage", $0 );
  106. }
  107. if ( ($show_help) || (!$ok) ) {
  108. # print_help ("help", File::Spec->rel2abs($0) );
  109. print_help ("help", $0 );
  110. }
  111. if ($show_man) {
  112. # User perldoc to generate the man documentation.
  113. system ("perldoc $0");
  114. exit($ok ? 0 : 2);
  115. }
  116. if ($show_version) {
  117. print "\n$0:\n".
  118. "Version: $VERSION\n\n";
  119. exit;
  120. }
  121. #-----------------------------------------------------------+
  122. # DATABASE CONNECTION |
  123. #-----------------------------------------------------------+
  124. if ( ($db) && ($host) && ($driver) ) {
  125. # Set default values if none given at command line
  126. $db = "biosql" unless $db;
  127. $host = "localhost" unless $host;
  128. $driver = "mysql" unless $driver;
  129. $dsn = "DBI:$driver:database=$db;host=$host";
  130. }
  131. elsif ($dsn) {
  132. # We need to parse the database name, driver etc from the dsn string
  133. # in the form of DBI:$driver:database=$db;host=$host
  134. # Other dsn strings will not be parsed properly
  135. # Split commands are often faster then regular expressions
  136. # However, a regexp may offer a more stable parse then splits do
  137. my ($cruft, $prefix, $suffix, $predb, $prehost);
  138. ($prefix, $driver, $suffix) = split(/:/,$dsn);
  139. ($predb, $prehost) = split(/;/, $suffix);
  140. ($cruft, $db) = split(/=/,$predb);
  141. ($cruft, $host) = split(/=/,$prehost);
  142. # Print for debug
  143. print STDERR "\tPRE:\t$prefix\n" if $verbose;
  144. print STDERR "\tDRIVER:\t$driver\n" if $verbose;
  145. print STDERR "\tSUF:\t$suffix\n" if $verbose;
  146. print STDERR "\tDB:\t$db\n" if $verbose;
  147. print STDERR "\tHOST:\t$host\n" if $verbose;
  148. }
  149. else {
  150. # The variables to create a dsn have not been passed
  151. print "ERROR: A valid dsn can not be created\n";
  152. exit;
  153. }
  154. #-----------------------------+
  155. # GET DB PASSWORD |
  156. #-----------------------------+
  157. unless ($pass) {
  158. print "\nEnter password for the user $usrname\n";
  159. system('stty', '-echo') == 0 or die "can't turn off echo: $?";
  160. $pass = <STDIN>;
  161. system('stty', 'echo') == 0 or die "can't turn on echo: $?";
  162. chomp $pass;
  163. }
  164. #-----------------------------+
  165. # CONNECT TO THE DATABASE |
  166. #-----------------------------+
  167. # Commented out while I work on fetching tree structure
  168. my $dbh = &connect_to_db($dsn, $usrname, $pass);
  169. #-----------------------------+
  170. # GET TREE ID |
  171. #-----------------------------+
  172. # Will do this separately in case there are muliple
  173. # trees with the same name, this will allow for warning that version
  174. # should be include in the query. I will load all results to the
  175. # trees vaiable so that other values can be returned if necessary.
  176. my $sel_trees_sql = "SELECT species_tree_id".
  177. " FROM species_tree ".
  178. " WHERE species_tree_name ='".$tree_name."'";
  179. my $sel_trees = prepare_sth($dbh,$sel_trees_sql);
  180. execute_sth($sel_trees);
  181. my @trees = ();
  182. while (my $row = $sel_trees->fetchrow_arrayref) {
  183. push(@trees,$row->[0]);
  184. }
  185. my $num_trees = @trees;
  186. # We are going to expect a single tree for the variables given
  187. # as input
  188. if ($num_trees > 1 ) {
  189. print STDERR "More than one species tree matches the query:\n";
  190. print STDERR "$sel_trees_sql\n";
  191. print STDERR "You may need to include tree version in query.\n";
  192. # Could print out all trees that match name criteria here ...
  193. }
  194. elsif ($num_trees == 1) {
  195. print STDERR "Matching tree id is: ".$trees[0]."\n";
  196. }
  197. else {
  198. print STDERR "ERROR: No tree matches name: ".$tree_name."\n";
  199. exit 1;
  200. }
  201. #-----------------------------+
  202. # CREATE A NEW TREE OBJECT |
  203. #-----------------------------+
  204. print "\tCreating a new tree object.\n";
  205. # Using our tree here to make tree available to recursive subfunction
  206. # below.
  207. our $tree = new Bio::Tree::Tree() ||
  208. die "Can not create the BioPerl tree object.\n";
  209. #-----------------------------+
  210. # GET THE ROOT NODE ID FOR |
  211. # THE TREE AND ADD ROOT OBJECT|
  212. # TO TREE OBJECT |
  213. #-----------------------------+
  214. my $sel_root_sql = "SELECT root_node_id".
  215. " FROM species_tree".
  216. " WHERE species_tree_id='".$trees[0]."'";
  217. my $sel_root = prepare_sth($dbh,$sel_root_sql);
  218. execute_sth($sel_root);
  219. my $root_node_id = $sel_root->fetchrow_arrayref;
  220. if ($root_node_id) {
  221. my $root_node = new Bio::Tree::Node( '-id' => $root_node_id->[0]);
  222. $tree->set_root_node($root_node);
  223. }
  224. #-----------------------------+
  225. # LOAD TREE NODES TO TREE |
  226. # OBJECT |
  227. #-----------------------------+
  228. my $sel_child_sql = "SELECT species_tree_node_id, label".
  229. " FROM species_tree_node".
  230. " WHERE parent_id = ?";
  231. my $sel_child = prepare_sth($dbh, $sel_child_sql);
  232. # Given the root node id, we can recurse to
  233. # get all children usind the load_tree_nodes subfunction below.
  234. # Since the root node has a unique id as assigned by the database,
  235. # and this id is loaded as the id of the root node id above, this
  236. # value can be used to refer to the node itself without needing
  237. # to include tree_id as part of the query.
  238. load_tree_nodes ($sel_child,$root_node_id);
  239. #-----------------------------+
  240. # LOAD NODE ATTRIBUTES |
  241. #-----------------------------+
  242. # At this point, all of the nodes should be loaded to the tree object
  243. # and we can use nod IDs to add information to the tree
  244. # This could make direct use of a specified ontology to do this.
  245. my @all_nodes = $tree->get_nodes;
  246. # Select node attribute values
  247. #my $sel_attrs_sql = " SELECT t.name, eav.value "
  248. # ."FROM term t, edge_attribute_value eav "
  249. # ."WHERE t.term_id = eav.term_id "
  250. # ."AND eav.edge_id = ?";
  251. #my $sel_attrs = &prepare_sth($dbh, $sel_attrs_sql);
  252. my $sel_label_sql = " SELECT label".
  253. " FROM species_tree_node".
  254. " WHERE species_tree_node_id = ?";
  255. print STDERR "Adding node annotations ..\n";
  256. foreach my $ind_node (@all_nodes) {
  257. print STDERR "\tAnnotating node : ".$ind_node->id."\n"
  258. if $verbose;
  259. # Get label if one exits, this should be the
  260. # name that was used in the orginal file input
  261. # to tag the nodes (ie species name_
  262. my $sel_label = prepare_sth($dbh, $sel_label_sql);
  263. &execute_sth( $sel_label, $ind_node->id );
  264. # If node label exists, the database id with node label
  265. my $node_label = $sel_label->fetchrow_arrayref;
  266. if ($node_label) {
  267. $ind_node->id($node_label->[0]);
  268. }
  269. # TO DO:
  270. # Get all generic attributes. This will be the
  271. # attributes tagged by the ontology derived name.
  272. #&execute_sth($sel_attrs,$ind_node);
  273. #my %attrs = ();
  274. }
  275. #-------------------------------+
  276. # WRITE TREE TO FILE OR STDOUT |
  277. #-------------------------------+
  278. if ($outfile) {
  279. my $treeio = Bio::TreeIO->new( -format => $format,
  280. -file => '>'.$outfile)
  281. || die "Could not open output file:\n$outfile\n";
  282. $treeio->write_tree($tree);
  283. }
  284. else {
  285. my $treeout_here = Bio::TreeIO->new( -format => $format );
  286. $treeout_here->write_tree($tree);
  287. }
  288. exit 0;
  289. #-----------------------------------------------------------+
  290. # SUBFUNCTIONS |
  291. #-----------------------------------------------------------+
  292. sub load_tree_nodes {
  293. my $sel_chld_sth = shift;# SQL to select children
  294. my $subroot = shift; # reference to the root
  295. my @children = ();
  296. &execute_sth($sel_chld_sth,$subroot->[0]);
  297. # Push results to the children array
  298. while (my $child = $sel_chld_sth->fetchrow_arrayref) {
  299. push(@children, [@$child]);
  300. }
  301. # For all of the children, add the descendent node to
  302. # the tree object and call the load_tree_nodes subfunction
  303. # recursively for the resulting children nodes
  304. for(my $i = 0; $i < @children; $i++) {
  305. # The following used for debug
  306. print STDERR "\t|".$subroot->[0]."--->".$children[$i][0]."|\n"
  307. if $verbose;
  308. #
  309. my ($par_node) = $IPlantTR::tree->find_node( '-id' => $subroot->[0] );
  310. # Check here that @par_node contains only a single node object
  311. my $node_child = new Bio::Tree::Node( '-id' => $children[$i][0] );
  312. $par_node->add_Descendent($node_child);
  313. &load_tree_nodes($sel_chld_sth, $children[$i]);
  314. }
  315. } # end of load_tree_nodes
  316. sub print_help {
  317. my ($help_msg, $podfile) = @_;
  318. # help_msg is the type of help msg to use (ie. help vs. usage)
  319. print "\n";
  320. #-----------------------------+
  321. # PIPE WITHIN PERL |
  322. #-----------------------------+
  323. #my $podfile = $0;
  324. my $scalar = '';
  325. tie *STDOUT, 'IO::Scalar', \$scalar;
  326. if ($help_msg =~ "usage") {
  327. podselect({-sections => ["SYNOPSIS|MORE"]}, $0);
  328. }
  329. else {
  330. podselect({-sections => ["SYNOPSIS|ARGUMENTS|OPTIONS|MORE"]}, $0);
  331. }
  332. untie *STDOUT;
  333. # now $scalar contains the pod from $podfile you can see this below
  334. #print $scalar;
  335. my $pipe = IO::Pipe->new()
  336. or die "failed to create pipe: $!";
  337. my ($pid,$fd);
  338. if ( $pid = fork() ) { #parent
  339. open(TMPSTDIN, "<&STDIN")
  340. or die "failed to dup stdin to tmp: $!";
  341. $pipe->reader();
  342. $fd = $pipe->fileno;
  343. open(STDIN, "<&=$fd")
  344. or die "failed to dup \$fd to STDIN: $!";
  345. my $pod_txt = Pod::Text->new (sentence => 0, width => 78);
  346. $pod_txt->parse_from_filehandle;
  347. # END AT WORK HERE
  348. open(STDIN, "<&TMPSTDIN")
  349. or die "failed to restore dup'ed stdin: $!";
  350. }
  351. else { #child
  352. $pipe->writer();
  353. $pipe->print($scalar);
  354. $pipe->close();
  355. exit 0;
  356. }
  357. $pipe->close();
  358. close TMPSTDIN;
  359. print "\n";
  360. exit 0;
  361. }
  362. sub connect_to_db {
  363. my ($cstr) = @_;
  364. return connect_to_mysql(@_) if $cstr =~ /:mysql:/i;
  365. return connect_to_pg(@_) if $cstr =~ /:pg:/i;
  366. die "can't understand driver in connection string: $cstr\n";
  367. }
  368. sub connect_to_pg {
  369. my ($cstr, $user, $pass) = @_;
  370. my $dbh = DBI->connect($cstr, $user, $pass,
  371. {PrintError => 0,
  372. RaiseError => 1,
  373. AutoCommit => 0});
  374. $dbh || &error("DBI connect failed : ",$dbh->errstr);
  375. return($dbh);
  376. } # End of ConnectToPG subfunction
  377. sub connect_to_mysql {
  378. my ($cstr, $user, $pass) = @_;
  379. my $dbh = DBI->connect($cstr,
  380. $user,
  381. $pass,
  382. {PrintError => 0,
  383. RaiseError => 1,
  384. AutoCommit => 0});
  385. $dbh || &error("DBI connect failed : ",$dbh->errstr);
  386. return($dbh);
  387. }
  388. sub prepare_sth {
  389. my $dbh = shift;
  390. # my ($dbh) = @_;
  391. my $sth = $dbh->prepare(@_);
  392. die "failed to prepare statement '$_[0]': ".$dbh->errstr."\n" unless $sth;
  393. return $sth;
  394. }
  395. sub execute_sth {
  396. # I would like to return the statement string here to figure
  397. # out where problems are.
  398. # Takes a statement handle
  399. my $sth = shift;
  400. my $rv = $sth->execute(@_);
  401. unless ($rv) {
  402. $dbh->disconnect();
  403. die "failed to execute statement: ".$sth->errstr."\n"
  404. }
  405. return $rv;
  406. } # End of execute_sth subfunction
  407. sub last_insert_id {
  408. #my ($dbh,$table_name,$driver) = @_;
  409. # The use of last_insert_id assumes that the no one
  410. # is interleaving nodes while you are working with the db
  411. my $dbh = shift;
  412. my $table_name = shift;
  413. my $driver = shift;
  414. # The following replace by sending driver info to the sufunction
  415. #my $driver = $dbh->get_info(SQL_DBMS_NAME);
  416. if (lc($driver) eq 'mysql') {
  417. return $dbh->{'mysql_insertid'};
  418. }
  419. elsif ((lc($driver) eq 'pg') || ($driver eq 'PostgreSQL')) {
  420. my $sql = "SELECT currval('${table_name}_pk_seq')";
  421. my $stmt = $dbh->prepare_cached($sql);
  422. my $rv = $stmt->execute;
  423. die "failed to retrieve last ID generated\n" unless $rv;
  424. my $row = $stmt->fetchrow_arrayref;
  425. $stmt->finish;
  426. return $row->[0];
  427. }
  428. else {
  429. die "don't know what to do with driver $driver\n";
  430. }
  431. } # End of last_insert_id subfunction
  432. __END__
  433. # The following pulled directly from the DBI module
  434. # this is an attempt to see if I can get the DSNs to parse
  435. # for some reason, this is returning the driver information in the
  436. # place of scheme
  437. sub parse_dsn {
  438. my ($dsn) = @_;
  439. $dsn =~ s/^(dbi):(\w*?)(?:\((.*?)\))?://i or return;
  440. my ($scheme, $driver, $attr, $attr_hash) = (lc($1), $2, $3);
  441. $driver ||= $ENV{DBI_DRIVER} || '';
  442. $attr_hash = { split /\s*=>?\s*|\s*,\s*/, $attr, -1 } if $attr;
  443. return ($scheme, $driver, $attr, $attr_hash, $dsn);
  444. }
  445. =head1 NAME
  446. tr_expot_species_tree.pl - Export species tree given tree name
  447. =head1 VERSION
  448. This documentation refers to program version 0.1
  449. =head1 SYNOPSIS
  450. =head2 Usage
  451. tr_export_species_tree.pl -o species_tree.nwk -t tree_name
  452. =head2 Required Arguments
  453. --outfile, -o # Path to the species tree file produced
  454. --tree-naame, -t # Name of the tree to fetch from the database
  455. =head1 DESCRIPTION
  456. Exports a species tree with internal nodes labeled with IDs as
  457. used in the database. This allows for reconciliciation against
  458. a species tree with labeled internal nodes that can be more
  459. easily loaded into the database.
  460. =head1 REQUIRED ARGUMENTS
  461. =over 2
  462. =item -o, --outfile
  463. Path of the species tree file to be created.
  464. =item -t, --tree-name
  465. Name of the tree to fetch from the database. This is the name for
  466. the tree that is listed in the 'species_tree_name' column of the
  467. 'species_tree' table.
  468. =back
  469. =head1 OPTIONS
  470. =over 2
  471. =item --format
  472. Format of the species tree to be created. Valid options include
  473. =item --usage
  474. Short overview of how to use program from command line.
  475. =item --help
  476. Show program usage with summary of options.
  477. =item --version
  478. Show program version.
  479. =item --man
  480. Show the full program manual. This uses the perldoc command to print the
  481. POD documentation for the program.
  482. =item -q,--quiet
  483. Run the program with minimal output.
  484. =back
  485. =head1 EXAMPLES
  486. The following are examples of how to use this script
  487. =head2 Typical Use
  488. This is a typcial use case.
  489. =head1 DIAGNOSTICS
  490. =over 2
  491. =item * Expecting input from STDIN
  492. If you see this message, it may indicate that you did not properly specify
  493. the input sequence with -i or --infile flag.
  494. =back
  495. =head1 CONFIGURATION AND ENVIRONMENT
  496. =head2 Environment
  497. The following options can be set in the user environment.
  498. =over 2
  499. =item TR_USER
  500. User name to connect to the tree reconciliation database.
  501. =item TR_PASSWORD
  502. Password for the tree reconciliation database connection
  503. =item TR_DSN
  504. DSN for the tree reconciliation database connection.
  505. =back
  506. For example in the bash shell this would be done be editing your .bashrc file
  507. to contain:
  508. export TR_USERNAME=yourname
  509. export TR_PASS=yourpassword
  510. export TR_DSN='DBI:mysql:database=iplant_tr;host-localhost'
  511. =head1 DEPENDENCIES
  512. The program is dependent on the following:
  513. * BioPerl
  514. Specifically the TreeIO module is required for this program to work.
  515. * DBI
  516. Module required For connecting to the database.
  517. * DBD::mysql
  518. The driver for connecting to a mysql database
  519. =head1 BUGS AND LIMITATIONS
  520. Any known bugs and limitations will be listed here.
  521. =head1 REFERENCE
  522. No current manuscript or web site reference for use of this script.
  523. =head1 LICENSE
  524. Copyright (c) 2012, The Arizona Board of Regents on behalf of
  525. The University of Arizona.
  526. All rights reserved.
  527. Developed by: iPlant Collaborative as a collaboration between
  528. participants at BIO5 at The University of Arizona (the primary hosting
  529. institution), Cold Spring Harbor Laboratory, The University of Texas at
  530. Austin, and individual contributors. Find out more at
  531. http://www.iplantcollaborative.org/.
  532. Redistribution and use in source and binary forms, with or without
  533. modification, are permitted provided that the following conditions are
  534. met:
  535. * Redistributions of source code must retain the above copyright
  536. notice, this list of conditions and the following disclaimer.
  537. * Redistributions in binary form must reproduce the above copyright
  538. notice, this list of conditions and the following disclaimer in the
  539. documentation and/or other materials provided with the distribution.
  540. * Neither the name of the iPlant Collaborative, BIO5, The University
  541. of Arizona, Cold Spring Harbor Laboratory, The University of Texas at
  542. Austin, nor the names of other contributors may be used to endorse or
  543. promote products derived from this software without specific prior
  544. written permission.
  545. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  546. IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  547. TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  548. PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  549. HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  550. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  551. TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  552. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  553. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  554. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  555. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  556. =head1 AUTHOR
  557. James C. Estill E<lt>JamesEstill at gmail.comE<gt>
  558. =head1 HISTORY
  559. STARTED: 04/11/2012
  560. UPDATED: 04/12/2012
  561. VERSION: 0.1
  562. =cut