20 #define tree_assert(cond) arb_assert(cond)
26 #define MAX_DROPPED_GROUP_WARN 100
32 enum tr_lfmode { LF_UNKNOWN, LF_N, LF_R, LF_NR, LF_RN, };
42 double max_found_bootstrap;
50 int dropped_leaf_groups;
51 int dropped_duplicated_groups;
52 Count() : dropped_leaf_groups(0), dropped_duplicated_groups(0) {}
55 void setError(
const char *
message);
56 void setErrorAt(
const char *message);
57 void setExpectedError(
const char *expected);
63 char *content_ahead(
size_t how_many,
bool show_eof);
65 void drop_tree_char(
char expected);
67 void setBranchName_acceptingBootstrap(
TreeNode *
node,
char*& name);
73 char *eat_quoted_string();
92 add_warning(
"Length specified for root-node has been ignored");
96 if (last_character ==
';') read_tree_char();
97 if (last_character != EOF) {
98 char *unused_input = content_ahead(30,
false);
99 add_warningf(
"Unexpected input-data after tree: '%s'", unused_input);
127 : unnamed_counter(0),
128 tree_file_name(strdup(file_name)),
133 max_found_branchlen(-1),
134 max_found_bootstrap(-1),
145 free(tree_file_name);
148 void TreeReader::setError(
const char *
message) {
151 tree_file_name, line_cnt, message);
153 char *TreeReader::content_ahead(
size_t how_many,
bool show_eof) {
154 char show[how_many+1+4];
156 for (i = 0; i<how_many; ++i) {
157 show[i] = last_character;
158 if (show[i] == EOF) {
160 strcpy(show+i,
"<EOF>");
171 void TreeReader::setErrorAt(
const char *
message) {
172 if (last_character == EOF) {
176 char *show = content_ahead(30,
true);
182 void TreeReader::setExpectedError(
const char *expected) {
186 int TreeReader::get_char() {
196 case LF_UNKNOWN: lfmode = LF_N; inc = 1;
break;
197 case LF_N: inc = 1;
break;
198 case LF_R: lfmode = LF_RN; c = get_char();
break;
199 case LF_NR: c = get_char();
break;
200 case LF_RN: inc = 1;
break;
203 else if (c ==
'\r') {
205 case LF_UNKNOWN: lfmode = LF_R; inc = 1;
break;
206 case LF_R: inc = 1;
break;
207 case LF_N: lfmode = LF_NR; c = get_char();
break;
208 case LF_RN: c = get_char();
break;
209 case LF_NR: inc = 1;
break;
211 if (c ==
'\r') c =
'\n';
218 int TreeReader::read_tree_char() {
225 while (!done && !
error) {
227 if (c ==
' ' || c ==
'\t' || c ==
'\n') ;
229 int openBrackets = 1;
231 tree_comment.
put(
'\n');
234 while (openBrackets && !
error) {
238 setError(
"Reached end of file while reading comment");
242 if (openBrackets) tree_comment.
put(c);
260 int TreeReader::read_char() {
266 void TreeReader::eat_white() {
267 int c = last_character;
268 while ((c ==
' ') || (c ==
'\n') || (c ==
'\r') || (c ==
'\t')) {
276 int c = last_character;
278 while (((c<=
'9') && (c>=
'0')) || (c==
'.') || (c==
'-') || (c==
'+') || (c==
'e') || (c==
'E')) {
286 bool consumed_some_length = strng[0];
287 return consumed_some_length;
290 char *TreeReader::eat_quoted_string() {
302 char buffer[MAX_NAME_LEN+2];
304 int c = last_character;
306 #define NAME_TOO_LONG ((s-buffer)>MAX_NAME_LEN)
308 if (c ==
'\'' || c ==
'"') {
309 char found_quote = c;
312 while (c!=EOF && c!=found_quote) {
317 if (c == found_quote) c = read_tree_char();
325 while (c ==
'_') c = read_tree_char();
326 while (c ==
' ') c = read_tree_char();
328 while (c!=
':' && c!=EOF && c!=
',' && c!=
';' && c !=
')') {
331 c = read_tree_char();
336 setError(
GBS_global_string(
"Name '%s' is longer than %i bytes", buffer, MAX_NAME_LEN));
339 return strdup(buffer);
342 void TreeReader::setBranchName_acceptingBootstrap(
TreeNode *
node,
char*& name) {
360 char *new_name =
NULp;
363 const char *
label = name;
367 bootstrap = bootstrap*100.0;
368 if (bootstrap > max_found_bootstrap) { max_found_bootstrap = bootstrap; }
371 error =
"Invalid duplicated bootstrap specification detected";
377 if (label) new_name = strdup(label);
381 reassign(new_name, name);
389 add_warningf(
"Dropped group name specified for a single-node-subtree ('%s')", new_name);
391 add_warning(
"[Note: further warnings of this type will be suppressed]");
398 add_warningf(
"Duplicated group name specification detected: dropped inner ('%s'), kept outer group name ('%s')",
399 node->
name, new_name);
401 add_warning(
"[Note: further warnings of this type will be suppressed]");
404 freeset(node->
name, new_name);
408 node->
name = new_name;
413 void TreeReader::drop_tree_char(
char expected) {
414 if (last_character != expected) {
420 bool TreeReader::eat_and_set_name_and_length(
TreeNode *node,
GBT_LEN& nodeLen) {
431 bool length_consumed =
false;
433 while (!done && !
error) {
434 switch (last_character) {
441 if (!
error && length_consumed) setErrorAt(
"Unexpected ':' (already read a branchlength)");
442 if (!
error) drop_tree_char(
':');
445 if (eat_number(foundlen)) {
453 max_found_branchlen =
std::max(max_found_branchlen, nodeLen);
456 setExpectedError(
"valid length");
459 length_consumed =
true;
467 char *branchName = eat_quoted_string();
469 if (branchName[0]) setBranchName_acceptingBootstrap(node, branchName);
473 setExpectedError(
"branch-name or one of ':;,)'");
503 if (last_character ==
'(') {
504 node = load_subtree(nodeLen);
508 char *name = eat_quoted_string();
510 if (!name[0]) freeset(name, unnamedNodeName());
518 setExpectedError(
"(quoted) string");
521 if (node && !
error) {
522 if (!eat_and_set_name_and_length(node, nodeLen)) {
550 TreeNode *left = load_named_node(leftLen);
553 switch (last_character) {
564 while (last_character ==
',' && !
error) {
568 left = pair; leftLen = 0;
574 right = load_named_node(rightLen);
579 if (last_character ==
')') {
587 setExpectedError(
"one of ',)'");
605 setExpectedError(
"one of ',)'");
614 if (!
error) drop_tree_char(
')');
627 FILE *input = fopen(path,
"rt");
629 bool own_root =
true;
635 const char *
name_only = strrchr(path,
'/');
637 else name_only = path;
641 tree = reader.
load();
642 if (tree) own_root =
false;
647 else if (tree && tree->
is_leaf()) error =
"tree is too small (need at least 2 species)";
655 double bootstrap_scale = 1.0;
656 double branchlen_scale = 1.0;
659 bootstrap_scale = 0.01;
660 reader.add_warningf(
"Auto-scaling bootstrap values by factor %.2f (max. found bootstrap was %5.2f)",
664 if (allow_length_scaling) {
665 branchlen_scale = 0.01;
666 reader.add_warningf(
"Auto-scaling branchlengths by factor %.2f (max. found branchlength = %.2f)\n"
667 "(use ARB_NT/Tree/Modify branches/Scale branchlengths with factor %.2f to undo auto-scaling)",
672 TREE_scale(tree, branchlen_scale, bootstrap_scale);
676 if (wmsg) *warningPtr = strdup(wmsg);
686 *commentPtr = comment;
705 char *tree_comment =
NULp;
716 error = ta.
close(error);
735 static TreeNode *loadFromFileContaining(
const char *treeString,
char **warningsPtr) {
736 const char *filename =
"trees/tmp.tree";
737 FILE *out = fopen(filename,
"wt");
741 fputs(treeString, out);
761 return all().ofgroup(expected);
775 return all().ofgroup(expected);
778 #define TEST_EXPECT_TREELOAD_FAILED_WITH(tree,errpart) TEST_EXPECTATION(loading_tree_failed_with(tree, errpart))
779 #define TEST_EXPECT_TREELOAD_FAILED_WITH__BROKEN(tree,errpart) TEST_EXPECTATION__BROKEN(loading_tree_failed_with(tree, errpart))
781 #define TEST_EXPECT_TREELOAD(tree,newick) TEST_EXPECTATION(loading_tree_succeeds(tree,newick))
782 #define TEST_EXPECT_TREELOAD__BROKEN(tree,newick) TEST_EXPECTATION__BROKEN(loading_tree_succeeds(tree,newick))
784 #define TEST_EXPECT_TREEFILE_FAILS_WITH(name,errpart) do { \
785 TreeNode *tree = TREE_load(name, new SimpleRoot, NULp, false, NULp); \
786 TEST_EXPECT_TREELOAD_FAILED_WITH(tree, errpart); \
789 #define TEST_EXPECT_TREESTRING_FAILS_WITH(treeString,errpart) do { \
790 TreeNode *tree = loadFromFileContaining(treeString, NULp); \
791 TEST_EXPECT_TREELOAD_FAILED_WITH(tree, errpart); \
795 #define TEST_EXPECT_TREESTRING_FAILS_WITH__BROKEN(treeString,errpart,newick) do { \
796 char *warnings = NULp; \
797 TreeNode *tree = loadFromFileContaining(treeString, &warnings); \
798 TEST_EXPECT_TREELOAD_FAILED_WITH__BROKEN(tree, errpart); \
799 TEST_EXPECT_TREELOAD(tree, newick); \
800 TEST_EXPECT_NULL(warnings); \
805 #define TEST_EXPECT_TREESTRING_OK(treeString,newick) do { \
806 char *warnings = NULp; \
807 TreeNode *tree = loadFromFileContaining(treeString, &warnings); \
808 TEST_EXPECT_TREELOAD(tree, newick); \
809 TEST_EXPECT_NULL(warnings); \
814 #define TEST_EXPECT_TREESTRING_OK_WITH_WARNING(treeString,newick,warnPart) do { \
815 char *warnings = NULp; \
816 TreeNode *tree = loadFromFileContaining(treeString, &warnings); \
817 TEST_EXPECT_TREELOAD(tree, newick); \
818 TEST_REJECT_NULL(warnings); \
819 TEST_EXPECT_CONTAINS(warnings, warnPart); \
824 #define TEST_EXPECT_TREESTRING_OK__BROKEN(treeString,newick) do { \
825 TreeNode *tree = loadFromFileContaining(treeString, NULp); \
826 TEST_EXPECT_TREELOAD__BROKEN(tree, newick); \
829 void TEST_load_tree() {
835 char *comment =
NULp;
839 TEST_EXPECT_TREELOAD(tree,
"(((s1,s2),(s3,s 4)),(s5,s-6));");
844 "tree covering most of tree reader code\n"
845 "comment contains [extra brackets] inside comment\n");
848 ": Loaded from trees/test.tree\n");
856 const char *treestring[] = {
857 "(node1,node2)rootgroup;",
858 "(node1:0.00,(node2, node3:0.57)):0;",
859 "(((((a))single)), ((b, c)17%:0.2));",
861 "((a,b)17,(c,d)33.3,(e,f)12.5:0.2);",
862 "((a,b)G,(c,d)H,(e,f)I:0.2);",
863 "((a,b)'17:G',(c,d)'33.3:H',(e,f)'12.5:I':0.2);",
864 "((a,b)17G,(c,d)33.3H,(e,f)12.5I:0.2)",
866 "((a,b)'17%:G',(c,d)'33.3%:H',(e,f)'12.5%:I':0.2);",
867 "((a,b)'0.17:G',(c,d)'0.333:H',(e,f)'0.125:I':0.2);",
870 const char *expected_newick[] = {
872 "(node1,(node2,node3));",
875 "(((a,b),(c,d)),(e,f));",
876 "(((a,b),(c,d)),(e,f));",
877 "(((a,b),(c,d)),(e,f));",
878 "(((a,b),(c,d)),(e,f));",
880 "(((a,b),(c,d)),(e,f));",
881 "(((a,b),(c,d)),(e,f));",
883 const char *expected_warnings[] = {
886 "Dropped group name specified for a single-node-subtree",
888 "Auto-scaling bootstrap values by factor 0.01",
890 "Auto-scaling bootstrap values by factor 0.01",
899 for (
size_t i = 0; i<
ARRAY_ELEMS(treestring); ++i) {
902 TreeNode *tree = loadFromFileContaining(treestring[i], &warnings);
903 TEST_EXPECT_TREELOAD(tree, expected_newick[i]);
988 if (expected_warnings[i]) {
1003 TEST_EXPECT_TREESTRING_OK(
"(,);",
"(unnamed1,unnamed2);");
1004 TEST_EXPECT_TREESTRING_OK(
"( a, (b,(c),d), (e,(f)) );",
"((a,((b,c),d)),(e,f));");
1005 TEST_EXPECT_TREESTRING_OK(
"(((((a)))), ((b, c)));",
"(a,(b,c));");
1007 TEST_EXPECT_TREESTRING_OK_WITH_WARNING(
"( (a), (((b),(c),(d))group)dupgroup, ((e),(f)) );",
1008 "((a,((b,c),d)),(e,f));",
1009 "Duplicated group name specification detected");
1013 const char *tooSmallTree[] = {
1023 for (
size_t i = 0; i<
ARRAY_ELEMS(tooSmallTree); ++i) {
1025 TreeNode *tree = loadFromFileContaining(tooSmallTree[i],
NULp);
1026 TEST_EXPECT_TREELOAD_FAILED_WITH(tree,
"tree is too small");
1028 TEST_ANNOTATE(
NULp);
1031 TreeNode *tree = loadFromFileContaining(
"((a, b)25)20;",
NULp);
1032 TEST_EXPECT_TREELOAD_FAILED_WITH(tree,
"Invalid duplicated bootstrap specification detected");
1036 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(;);",
"Expected one of ',)'");
1038 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(17",
"Expected one of ',)' while end-of-file was reached");
1039 TEST_EXPECT_TREESTRING_FAILS_WITH(
"((((",
"Expected one of ',)' while end-of-file was reached");
1040 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, 'b",
"Expected one of ',)' while end-of-file was reached");
1042 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:5::::",
"Unexpected ':' (already read a branchlength) while looking at '::::<EOF>'");
1043 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:5:c:d",
"Unexpected ':' (already read a branchlength) while looking at ':c:d<EOF>'");
1044 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:5:c:d)",
"Unexpected ':' (already read a branchlength) while looking at ':c:d)<EOF>'");
1046 TEST_EXPECT_TREESTRING_FAILS_WITH(
"[unclosed\ncomment",
"while reading comment");
1047 TEST_EXPECT_TREESTRING_FAILS_WITH(
"[unclosed\ncomment [ bla ]",
"while reading comment");
1049 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:d)",
"Expected valid length while looking at 'd)<EOF>'");
1052 TEST_EXPECT_TREESTRING_OK_WITH_WARNING(
"(a,b):0.5",
"(a,b);",
"Length specified for root-node has been ignored");
1053 TEST_EXPECT_TREESTRING_OK_WITH_WARNING(
"(a, b))",
"(a,b);",
"Unexpected input-data after tree: ')'");
1055 TEST_EXPECT_TREESTRING_OK(
"(a*,b%);",
"(a*,b%);");
1056 TEST_EXPECT_TREESTRING_OK(
"(a, b:5)",
"(a,b);");
1059 TEST_EXPECT_TREEFILE_FAILS_WITH(
"trees/nosuch.tree",
"No such file");
1060 TEST_EXPECT_TREEFILE_FAILS_WITH(
"trees/corrupted.tree",
"Error reading");
1065 #endif // UNIT_TESTS
GB_ERROR get_warnings() const
void set_bootstrap(double bootstrap)
AliDataPtr format(AliDataPtr data, const size_t wanted_len, GB_ERROR &error)
GB_ERROR GBT_write_tree_with_remark(GBDATA *gb_main, const char *tree_name, TreeNode *tree, const char *remark)
void TREE_scale(TreeNode *tree, double length_scale, double bootstrap_scale)
char * GBT_tree_2_newick(const TreeNode *tree, NewickFormat format, bool compact)
bool is_marked_as_default_len(GBT_LEN len)
GB_ERROR TREE_load_to_db(GBDATA *gb_main, const char *treefile, const char *tree_name)
GB_ERROR GB_export_IO_error(const char *action, const char *filename)
#define DEFAULT_BRANCH_LENGTH
const char * GBS_global_string(const char *templat,...)
#define FORWARD_FORMATTED(receiver, format)
int GB_unlink(const char *path)
#define ARRAY_ELEMS(array)
char buffer[MESSAGE_BUFFERSIZE]
double get_max_found_bootstrap() const
__ATTR__FORMAT(2) void add_warningf(const char *format
#define DEFAULT_BRANCH_LENGTH_MARKER
#define TEST_EXPECT_CONTAINS(str, part)
GB_ERROR GB_await_error()
#define tree_assert(cond)
#define is_equal_to_NULL()
virtual TreeNode * makeNode() const =0
bool parse_treelabel(const char *&label, double &bootstrap)
TreeReader(FILE *input, const char *file_name, TreeRoot *troot_)
void message(char *errortext)
#define TEST_REJECT(cond)
#define TEST_REJECT_NULL(n)
static void error(const char *msg)
TreeNode * TREE_load(const char *path, TreeRoot *troot, char **commentPtr, bool allow_length_scaling, char **warningPtr)
expectation_group & add(const expectation &e)
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
static SearchTree * tree[SEARCH_PATTERNS]
char * GBS_log_action_to(const char *comment, const char *action, bool stamp)
#define does_differ_from_NULL()
GBT_LEN get_max_found_branchlen() const
#define does_contain(val)
fputs(TRACE_PREFIX, stderr)
GB_ERROR GB_export_errorf(const char *templat,...)
#define TEST_EXPECT_NULL(n)
static list< LineAttachedMessage > warnings
GB_ERROR close(GB_ERROR error)
const char * name_only(const char *fullpath)
#define __ATTR__USERESULT
void announce_tree_constructed()
static TreeNode * createLinkedTreeNode(const TreeRoot &nodeMaker, TreeNode *left, GBT_LEN leftlen, TreeNode *right, GBT_LEN rightlen)
void GBT_message(GBDATA *gb_main, const char *msg)
float GB_atof(const char *str)
void add_warning(const char *msg)
GB_transaction ta(gb_var)
void destroy(TreeNode *that)
const char * get_remark() const
#define STATIC_ASSERT(const_expression)
#define TEST_EXPECT_EQUAL(expr, want)
size_t get_position() const
char * GBS_global_string_copy(const char *templat,...)
#define MAX_DROPPED_GROUP_WARN
GB_write_int const char s