ARB
nds.cxx
Go to the documentation of this file.
1 // ================================================================ //
2 // //
3 // File : AWT_nds.cxx //
4 // Purpose : //
5 // //
6 // Institute of Microbiology (Technical University Munich) //
7 // http://www.arb-home.de/ //
8 // //
9 // ================================================================ //
10 
11 #include "nds.h"
12 #include <awt_config_manager.hxx>
13 #include <awt_sel_boxes.hxx>
14 
15 #include <aw_awar.hxx>
16 #include <aw_file.hxx>
17 #include <aw_msg.hxx>
18 #include <aw_root.hxx>
19 #include <aw_select.hxx>
20 
21 #include <TreeNode.h>
22 #include <items.h>
23 #include <item_sel_list.h>
24 #include <gb_aci.h>
25 
26 #include <arb_msg_fwd.h>
27 #include <arb_global_defs.h>
28 
29 #define nds_assert(cond) arb_assert(cond)
30 
31 #define NDS_PER_PAGE 10 // number of NDS definitions on each config-page
32 #define NDS_PAGES 6 // how many config-pages (each has NDS_PER_PAGE definitions)
33 
34 #define NDS_COUNT (NDS_PER_PAGE*NDS_PAGES) // overall number of NDS definitions
35 
36 #define AWAR_NDS_USE_ALL "arb_presets/all"
37 #define AWAR_NDS_PAGE "arb_presets/page"
38 
39 #if defined(DEBUG)
40 #define NDS_STRING_SIZE 200
41 #else
42 #define NDS_STRING_SIZE 4000
43 #endif // DEBUG
44 
46  char buf[NDS_STRING_SIZE]; // buffer used to generate output
47  char *bp;
48  int space_left;
49 
50  long count;
51  int show_errors; // how many errors to show
52 
53  long lengths[NDS_COUNT]; // length of generated string
54  char *fieldname[NDS_COUNT]; // database field name (if empty -> no field; may be hierarchical, see 'rek')
55  bool rek[NDS_COUNT]; // 1->key is hierarchical (e.g. 'ali_16s/data')
56  char *parsing[NDS_COUNT]; // ACI/SRT program
57  bool at_group[NDS_COUNT]; // whether string shall appear at group NDS entries
58  bool at_leaf[NDS_COUNT]; // whether string shall appear at leaf NDS entries
59 
60  void init_buffer() {
61  bp = buf;
62  space_left = NDS_STRING_SIZE-1;
63  }
64  char *get_buffer() {
65  bp[0] = 0;
66  return buf;
67  }
68 
69  void insert_overflow_warning() {
70  nds_assert(space_left >= 0); // <0 means 'already warned'
71  while (space_left) {
72  *bp++ = ' ';
73  space_left--;
74  }
75 
76  static const char *warning = "..<truncated>";
77  static int warn_len = 0;
78  if (!warn_len) warn_len = strlen(warning);
79 
80  strcpy(buf+(NDS_STRING_SIZE-warn_len-1), warning);
81  space_left = -1;
82  }
83 
84  void append(char c) {
85  if (space_left >= 1) {
86  *bp++ = c;
87  space_left--;
88  }
89  else if (!space_left) {
90  insert_overflow_warning();
91  }
92  }
93  void append(const char *str, int length = -1) {
94  nds_assert(str);
95  if (length == -1) length = strlen(str);
96  nds_assert(int(strlen(str)) == length);
97 
98  if (space_left >= length) {
99  strcpy(bp, str);
100  bp += length;
101  space_left -= length;
102  }
103  else { // space_left < length
104  if (space_left >= 0) {
105  if (space_left>0) {
106  memcpy(bp, str, space_left);
107  bp += space_left;
108  space_left = 0;
109  }
110  insert_overflow_warning();
111  }
112  }
113  }
114 
115  __ATTR__FORMAT(2) void appendf(const char *format, ...) { FORWARD_FORMATTED(append, format); }
116 
117 public:
118 
120  : count(-1)
121  {
122  for (int i = 0; i<NDS_COUNT; ++i) {
123  fieldname[i] = NULp;
124  parsing[i] = NULp;
125  }
126  init_buffer();
127  }
128 
129  void init(GBDATA *gb_main);
130  const char *work(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name, bool forceGroup);
131 };
132 
133 inline const char *viewkeyAwarName(int i, const char *name) {
134  nds_assert(i >= 0 && i < NDS_PER_PAGE);
135  return GBS_global_string("tmp/viewkeys/viewkey_%i/%s", i, name);
136 }
137 
138 inline AW_awar *viewkeyAwar(AW_root *aw_root, AW_default awdef, int i, const char *name, bool string_awar) {
139  const char *awar_name = viewkeyAwarName(i, name);
140  AW_awar *awar = NULp;
141  if (string_awar) awar = aw_root->awar_string(awar_name, "", awdef);
142  else awar = aw_root->awar_int(awar_name, 0, awdef);
143  return awar;
144 }
145 
146 static void map_viewkey(AW_root *aw_root, AW_default awdef, int i, GBDATA *gb_viewkey) {
147  // maps one NDS key data to one line of the config window
148 
149  GBDATA *gb_key_text = GB_entry(gb_viewkey, "key_text");
150  GBDATA *gb_pars = GB_entry(gb_viewkey, "pars");
151  GBDATA *gb_len1 = GB_entry(gb_viewkey, "len1");
152  GBDATA *gb_group = GB_entry(gb_viewkey, "group");
153  GBDATA *gb_leaf = GB_entry(gb_viewkey, "leaf");
154 
155  nds_assert(gb_key_text);
156  nds_assert(gb_pars);
157  nds_assert(gb_len1);
158  nds_assert(gb_group);
159  nds_assert(gb_leaf);
160 
161  AW_awar *Awar;
162  Awar = viewkeyAwar(aw_root, awdef, i, "key_text", true); Awar->map(gb_key_text);
163  Awar = viewkeyAwar(aw_root, awdef, i, "pars", true); Awar->map(gb_pars);
164  Awar = viewkeyAwar(aw_root, awdef, i, "len1", false); Awar->map(gb_len1); Awar->set_minmax(0, 1000000);
165  Awar = viewkeyAwar(aw_root, awdef, i, "group", false); Awar->map(gb_group);
166  Awar = viewkeyAwar(aw_root, awdef, i, "leaf", false); Awar->map(gb_leaf);
167 }
168 
169 static void map_viewkeys(AW_root *aw_root, /*AW_default*/ GBDATA *awdef, GBDATA *gb_main) {
170  // map visible viewkeys to internal db entries
171  static bool initialized = false;
172  AW_awar *awar_selected_page = NULp;
173 
174  if (!initialized) {
175  awar_selected_page = aw_root->awar_int(AWAR_NDS_PAGE, 0, gb_main);
176  awar_selected_page->add_callback(makeRootCallback(map_viewkeys, awdef, gb_main)); // bind to self
177  initialized = true;
178  }
179  else {
180  awar_selected_page = aw_root->awar(AWAR_NDS_PAGE);
181  }
182 
183  int page = awar_selected_page->read_int();
184 #if defined(DEBUG)
185  printf("map_viewkeys to page %i\n", page);
186 #endif // DEBUG
187  if (page<NDS_PAGES) {
188  GB_transaction ta(gb_main);
189 
190  GBDATA *gb_arb_presets = GB_search(gb_main, "arb_presets", GB_CREATE_CONTAINER);
191  GBDATA *gb_viewkey = NULp;
192 
193  int i1 = page*NDS_PER_PAGE;
194  int i2 = i1+NDS_PER_PAGE-1;
195 
196  for (int i = 0; i <= i2; i++) {
197  gb_viewkey = gb_viewkey ? GB_nextEntry(gb_viewkey) : GB_entry(gb_arb_presets, "viewkey");
199  nds_assert(gb_viewkey);
200  if (i >= i1) map_viewkey(aw_root, awdef, i-i1, gb_viewkey);
201  }
202  }
203 }
204 
205 void create_nds_vars(AW_root *aw_root, AW_default awdef, GBDATA *gb_main, bool force_reinit) {
206  GB_push_transaction(gb_main);
207 
208  GBDATA *gb_viewkey = NULp;
209  GBDATA *gb_arb_presets = GB_search(gb_main, "arb_presets", GB_CREATE_CONTAINER);
210 
211  for (int i = 0; i<NDS_COUNT; ++i) {
212  gb_viewkey = gb_viewkey ? GB_nextEntry(gb_viewkey) : GB_entry(gb_arb_presets, "viewkey");
213 
214  if (!gb_viewkey) {
215  gb_viewkey = GB_create_container(gb_arb_presets, "viewkey");
216  }
217  else if (force_reinit) {
218  for (GBDATA *gb_child = GB_child(gb_viewkey); gb_child; ) {
219  GBDATA *gb_next_child = GB_nextChild(gb_child);
220  GB_delete(gb_child);
221  gb_child = gb_next_child;
222  }
223  }
224 
225  {
226  int group = 0;
227  int leaf = 0;
228  bool was_group_name = false;
229  int default_len = 30;
230 
231  GBDATA *gb_key_text = GB_entry(gb_viewkey, "key_text");
232  if (!gb_key_text) {
233  gb_key_text = GB_create(gb_viewkey, "key_text", GB_STRING);
234  const char *wanted = "";
235  switch (i) {
236  case 0: wanted = "name"; default_len = 12; leaf = 1; break;
237  case 1: wanted = "full_name"; leaf = 1; break;
238  case 2: wanted = ""; was_group_name = true; break;
239  case 3: wanted = "acc"; default_len = 20; leaf = 1; break;
240  case 4: wanted = "date"; break;
241  }
242  GB_write_string(gb_key_text, wanted);
243  }
244 
245  if (strcmp(GB_read_char_pntr(gb_key_text), "group_name") == 0) {
246  GB_write_string(gb_key_text, "");
247  was_group_name = true; // means: change group/leaf + add 'taxonomy(1)' to ACI
248  }
249 
250  GB_searchOrCreate_int(gb_viewkey, "len1", default_len);
251  GBDATA *gb_pars = GB_searchOrCreate_string(gb_viewkey, "pars", "");
252 
253  if (was_group_name) {
254  group = 1;
255  leaf = 0;
256 
257  const char *pars = GB_read_char_pntr(gb_pars);
258 
259  if (pars[0] == 0) pars = "taxonomy(1)"; // empty ACI/SRT
260  else if (pars[0] == ':') pars = GBS_global_string("taxonomy(1)|%s", pars); // was an SRT -> unsure what to do
261  else if (pars[0] == '|') pars = GBS_global_string("taxonomy(1)%s", pars); // was an ACI -> prefix taxonomy
262  else pars = GBS_global_string("taxonomy(1)|%s", pars); // other ACIs -> same
263 
264  GB_write_string(gb_pars, pars);
265  }
266 
267  {
268  GBDATA *gb_flag1 = GB_entry(gb_viewkey, "flag1");
269  if (gb_flag1) {
270  if (GB_read_int(gb_flag1)) { // obsolete
271  leaf = 1;
272  }
273  GB_ERROR error = GB_delete(gb_flag1);
274  if (error) aw_message(error);
275  }
276  }
277 
278  {
279  GBDATA *gb_inherit = GB_entry(gb_viewkey, "inherit");
280  if (gb_inherit) { // 'inherit' is old NDS style -> convert & delete
281  if (was_group_name && GB_read_int(gb_inherit)) leaf = 1;
282  GB_ERROR error = GB_delete(gb_inherit);
283  if (error) aw_message(error);
284  }
285  }
286 
287  GB_searchOrCreate_int(gb_viewkey, "group", group);
288  GB_searchOrCreate_int(gb_viewkey, "leaf", leaf);
289  }
290  }
291 
292  aw_root->awar_int(AWAR_NDS_USE_ALL, 1, gb_main);
293 
294  GB_pop_transaction(gb_main);
295 
296  map_viewkeys(aw_root, awdef, gb_main); // call once
297 }
298 
299 static const char *script_part_of(const char *predef_entry) {
300  const char *numsign = strchr(predef_entry, '#');
301  return numsign ? numsign+1 : predef_entry;
302 }
303 
304 static bool in_pre_update = false;
305 
306 static void awt_pre_to_view(AW_root *aw_root) {
307  if (!in_pre_update) {
308  LocallyModify<bool> dontRecurse(in_pre_update, true);
309 
310  const char *sel_predef_entry = aw_root->awar(AWAR_SELECT_ACISRT_PRE)->read_char_pntr();
311  aw_root->awar(AWAR_SELECT_ACISRT)->write_string(script_part_of(sel_predef_entry));
312  }
313 }
314 static void awt_select_pre_from_view(AW_root *aw_root, AW_selection_list *programs) {
315  if (!in_pre_update) {
316  LocallyModify<bool> dontRecurse(in_pre_update, true);
317 
318  const char *currScript = aw_root->awar(AWAR_SELECT_ACISRT)->read_char_pntr();
319  const char *foundPredef = NULL;
320 
321  for (AW_selection_list_iterator piter(programs); piter && !foundPredef; ++piter) { // search entry pre-defining current script
322  const char *predef = piter.get_value()->get_string();
323  const char *script = script_part_of(predef);
324 
325  if (strcmp(script, currScript) == 0) {
326  foundPredef = predef;
327  }
328  }
329 
330  // select script pre-defining current ACI (or select default if modified)
331  aw_root->awar(AWAR_SELECT_ACISRT_PRE)->write_string(foundPredef ? foundPredef : currScript);
332  }
333 }
334 
335 void AWT_popup_select_srtaci_window(AW_window *aww, const char *acisrt_awarname) {
336  static AW_window *win = NULp;
337 
338  AW_root *aw_root = aww->get_root();
339 
340  if (!win) {
341  AW_awar *awar_curr_aci = aw_root->awar_string(AWAR_SELECT_ACISRT);
342  AW_awar *awar_sel_aci = aw_root->awar_string(AWAR_SELECT_ACISRT_PRE);
343 
344  AW_window_simple *aws = new AW_window_simple;
345  aws->init(aw_root, "SRT_ACI_SELECT", "SRT_ACI_SELECT");
346  aws->load_xfig("awt/srt_select.fig");
347 
348  aws->button_length(13);
349  aws->callback(AW_POPDOWN);
350  aws->at("close");
351  aws->create_button("CLOSE", "CLOSE", "C");
352 
353  aws->callback(makeHelpCallback("acisrt.hlp"));
354  aws->at("help");
355  aws->create_button("HELP", "HELP", "H");
356 
357  aws->at("box");
358  AW_selection_list *programs = aws->create_selection_list(AWAR_SELECT_ACISRT_PRE, true);
359  GB_ERROR error;
360  {
361  StorableSelectionList storable_sellist(TypedSelectionList("sellst", programs, "SRT/ACI scripts", "srt_aci"));
362  error = storable_sellist.load(GB_path_in_ARBLIB("sellists/srt_aci*.sellst"), false);
363  }
364  if (error) aw_message(error);
365 
366  aws->at("field");
367  aws->create_text_field(AWAR_SELECT_ACISRT);
368 
369  awar_sel_aci->add_callback(awt_pre_to_view);
370  awar_curr_aci->add_callback(makeRootCallback(awt_select_pre_from_view, programs));
371 
372  win = aws;
373  }
374 
375  aw_root->awar(AWAR_SELECT_ACISRT)->map(acisrt_awarname);
376  win->activate();
377 }
378 
380  for (int i = 0; i<NDS_PER_PAGE; ++i) {
381  cdef.add(viewkeyAwarName(i, "leaf"), "leaf", i);
382  cdef.add(viewkeyAwarName(i, "group"), "group", i);
383  cdef.add(viewkeyAwarName(i, "key_text"), "key_text", i);
384  cdef.add(viewkeyAwarName(i, "len1"), "len1", i);
385  cdef.add(viewkeyAwarName(i, "pars"), "pars", i);
386  }
387 }
388 
389 static char *nds_store_config() {
391  nds_init_config(cdef);
392  return cdef.read();
393 }
394 
395 static void nds_restore_config(const char *stored, GBDATA *gb_main) {
396  // if stored == NULp -> reset
397 
399  nds_init_config(cdef);
400 
401  if (stored) { // restore
402  AWT_config parsedCfg(stored);
403  if (parsedCfg.has_entry("inherit0")) {
404  aw_message("Converting stored config to new NDS format -- consider saving it again.");
405  // Note: The conversion applied here is also done in create_nds_vars()
406 
407  GB_ERROR error = NULp;
408 
409  for (int i = 0; !error && i<NDS_COUNT; ++i) {
410  bool was_group_name = false;
411  {
412  const char *key_text_key = GBS_global_string("key_text%i", i);
413  const char *key_text = parsedCfg.get_entry(key_text_key);
414  if (strcmp(key_text, "group_name") == 0) {
415  was_group_name = true;
416  parsedCfg.set_entry(key_text_key, "");
417  }
418  }
419 
420  bool leaf = false;
421  bool group = false;
422  int inherit = 0;
423 
424  {
425  const char *inherit_key = GBS_global_string("inherit%i", i);
426  const char *inherit_value = parsedCfg.get_entry(inherit_key);
427 
428  if (inherit_value) {
429  inherit = atoi(inherit_value);
430  parsedCfg.delete_entry(inherit_key);
431  }
432  else {
433  error = GB_export_errorf("Expected entry '%s' in saved config", inherit_key);
434  }
435  }
436 
437  if (was_group_name) {
438  if (!error) {
439  leaf = inherit;
440  group = true;
441 
442  char *aci_key = GBS_global_string_copy("pars%i", i);
443  const char *aci = parsedCfg.get_entry(aci_key);
444  char *new_aci = NULp;
445 
446  if (aci[0] == 0) { new_aci = strdup("taxonomy(1)"); }
447  else if (aci[0] == '|') { new_aci = GBS_global_string_copy("taxonomy(1)%s", aci); }
448  else { new_aci = GBS_global_string_copy("taxonomy(1)|%s", aci); }
449 
450  parsedCfg.set_entry(aci_key, new_aci);
451 
452  free(new_aci);
453  free(aci_key);
454  }
455  }
456  else {
457  leaf = true;
458  }
459 
460  if (!error) {
461  const char *flag1_key = GBS_global_string("active%i", i);
462  const char *flag1_value = parsedCfg.get_entry(flag1_key);
463  if (flag1_value) {
464  int flag1 = atoi(flag1_value);
465  if (flag1 == 0) { leaf = group = false; }
466  parsedCfg.delete_entry(flag1_key);
467  }
468  else {
469  error = GB_export_errorf("Expected entry '%s' in saved config", flag1_key);
470  }
471  }
472 
473  if (!error) {
474  const char *leaf_key = GBS_global_string("leaf%i", i);
475  parsedCfg.set_entry(leaf_key, GBS_global_string("%i", int(leaf)));
476  const char *group_key = GBS_global_string("group%i", i);
477  parsedCfg.set_entry(group_key, GBS_global_string("%i", int(group)));
478  }
479  }
480 
481  if (!error) {
482  char *converted_cfg_str = parsedCfg.config_string();
483  cdef.write(converted_cfg_str);
484  free(converted_cfg_str);
485  }
486  else {
487  aw_message(error);
488  }
489  }
490  else {
491  cdef.write(stored);
492  }
493  }
494  else { // reset to factory defaults
495  cdef.reset(); // AWAR values are just empty here
496  create_nds_vars(AW_root::SINGLETON, AW_ROOT_DEFAULT, gb_main, true); // => reinit NDS
497  }
498 }
499 
501  static AW_window_simple *aws = NULp;
502  if (!aws) {
503  aws = new AW_window_simple;
504  aws->init(aw_root, "NDS_PROPS", "NDS");
505  aws->load_xfig("awt/nds.fig");
506  aws->auto_space(10, 5);
507 
508  aws->callback(AW_POPDOWN);
509  aws->at("close");
510  aws->create_button("CLOSE", "CLOSE", "C");
511 
512  aws->at("help");
513  aws->callback(makeHelpCallback("props_nds.hlp"));
514  aws->create_button("HELP", "HELP", "H");
515 
516  aws->at("page");
517  aws->create_option_menu(AWAR_NDS_PAGE, true);
518  for (int p = 0; p < NDS_PAGES; p++) {
519  const char *text = GBS_global_string("Entries %i - %i", p*NDS_PER_PAGE+1, (p+1)*NDS_PER_PAGE);
520  aws->insert_option(text, "", p);
521  }
522  aws->update_option_menu();
523 
524  aws->at("use");
525  aws->create_option_menu(AWAR_NDS_USE_ALL, true);
526  aws->insert_default_option("Use all entries", "", 1);
527  aws->insert_option ("Only use visible entries", "", 0);
528  aws->update_option_menu();
529 
530  aws->at("config");
531  AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "nds", makeStoreConfigCallback(nds_store_config), makeRestoreConfigCallback(nds_restore_config, gb_main));
532 
533  // --------------------
534 
535  aws->button_length(13);
536  int dummy, closey;
537  aws->at_newline();
538  aws->get_at_position(&dummy, &closey);
539 
540  aws->create_button(NULp, "K");
541 
542  aws->at_newline();
543 
544  int leafx, groupx, fieldx, columnx, srtx, srtux;
545 
546  aws->auto_space(10, 0);
547 
548  int i;
549  for (i=0; i<NDS_PER_PAGE; i++) {
550  aws->get_at_position(&leafx, &dummy);
551  aws->create_toggle(viewkeyAwarName(i, "leaf"));
552 
553  aws->get_at_position(&groupx, &dummy);
554  aws->create_toggle(viewkeyAwarName(i, "group"));
555 
556  aws->get_at_position(&fieldx, &dummy);
557  {
558  const char *awar_name = viewkeyAwarName(i, "key_text");
559  create_itemfield_selection_button(aws, FieldSelDef(awar_name, gb_main, SPECIES_get_selector(), FIELD_FILTER_NDS, "display-field"), NULp);
560  }
561 
562  aws->get_at_position(&columnx, &dummy);
563  aws->create_input_field(viewkeyAwarName(i, "len1"), 4);
564 
565  aws->get_at_position(&srtx, &dummy);
566  {
567  char *awar_name = strdup(viewkeyAwarName(i, "pars"));
568 
569  aws->button_length(0);
570  aws->callback(makeWindowCallback(AWT_popup_select_srtaci_window, awar_name)); // awar_name belongs to cbs now
571  {
572  char *button_id = GBS_global_string_copy("SELECT_SRTACI_%i", i+1);
573  aws->create_button(button_id, "S");
574  free(button_id);
575  }
576 
577  aws->get_at_position(&srtux, &dummy);
578  aws->at_attach_to(true, false, -7, 30);
579  aws->create_input_field(awar_name, 40);
580  }
581 
582  aws->at_unattach();
583  aws->at_newline();
584  }
585 
586  aws->at(leafx, closey);
587 
588  aws->at_x(leafx);
589  aws->create_button(NULp, "LEAF");
590  aws->at_x(groupx);
591  aws->create_button(NULp, "GRP.");
592 
593  aws->at_x(fieldx);
594  aws->create_button(NULp, "FIELD");
595 
596  aws->at_x(columnx);
597  aws->create_button(NULp, "WIDTH");
598 
599  aws->at_x(srtx);
600  aws->create_button(NULp, "SRT");
601 
602  aws->at_x(srtux);
603  aws->create_button(NULp, "ACI/SRT PROGRAM");
604  }
605 
606  return aws;
607 }
608 
609 
610 
612  GBDATA *gb_arb_presets = GB_search(gb_main, "arb_presets", GB_CREATE_CONTAINER);
613  bool only_visible_page = false;
614  int page = 0;
615  {
616  int all = *GBT_readOrCreate_int(gb_arb_presets, "all", 1);
617  if (!all) {
618  page = *GBT_readOrCreate_int(gb_arb_presets, "page", 0);
619  only_visible_page = true;
620  }
621  }
622 
623  count = 0;
624  int idx = 0;
625  for (GBDATA *gbz = GB_entry(gb_arb_presets, "viewkey"); gbz; gbz = GB_nextEntry(gbz), ++idx) {
626  bool use = !only_visible_page || (idx/NDS_PER_PAGE) == page;
627  if (use) {
628  // wanted NDS entry?
629  bool atLeaf = *GBT_read_int(gbz, "leaf");
630  bool atGroup = *GBT_read_int(gbz, "group");
631 
632  if (atLeaf || atGroup) {
633  GBDATA *gb_keyname = GB_entry(gbz, "key_text");
634  char *keyname = GB_read_string(gb_keyname);
635 
636  if (keyname[0] && strcmp(keyname, NO_FIELD_SELECTED) == 0) {
637  freeset(keyname, strdup("")); // NDS code interprets empty keyname as "no field"
638  }
639  freeset(fieldname[count], keyname);
640 
641  rek[count] = bool(GB_first_non_key_char(keyname));
642  lengths[count] = *GBT_read_int(gbz, "len1");
643  at_leaf[count] = atLeaf;
644  at_group[count] = atGroup;
645 
646  GBDATA *gbe = GB_entry(gbz, "pars");
647  freenull(parsing[count]);
648  if (gbe && GB_read_string_count(gbe)>1) parsing[count] = GB_read_string(gbe);
649  count++;
650  }
651  }
652  }
653 
654  show_errors = 10;
655 }
656 
657 static char *quoted_if_containing_separator(const char *text, char separator) {
658  bool contains_separator = strchr(text, separator);
659  if (!contains_separator) return NULp;
660  return GBS_global_string_copy("\"%s\"", text);
661 }
662 
663 const char *NodeTextBuilder::work(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name, bool forceGroup) {
664  // @@@ change result into SizedCstr? (to speed up display)
665 
666  nds_assert(count>=0); // initialized?
667  init_buffer();
668 
669  if (!gbd) {
670  if (!species) return "<internal error: no tree-node, no db-entry>";
671  if (!species->name) return "<internal error: species w/o name>";
672  appendf("<%s>", species->name); // zombie
673  }
674  else {
675  bool field_was_printed = false;
676  bool is_leaf = !forceGroup && (species ? species->is_leaf() : true);
677 
678  for (int i = 0; i < count; i++) {
679  if (is_leaf) { if (!at_leaf[i]) continue; }
680  else { if (!at_group[i]) continue; }
681 
682  char *str = NULp; // the generated string
683  bool apply_aci = false; // whether aci shall be applied
684  bool align_left = true; // otherwise align right
685 
686  {
687  const char *field_output = "";
688  const char *field_name = fieldname[i];
689 
690  if (field_name[0] == 0) { // empty field_name (or NO_FIELD_SELECTED) -> only do ACI/SRT
691  apply_aci = true;
692  }
693  else { // non-empty field_name
694  GBDATA *gbe;
695  if (rek[i]) { // hierarchical key
696  gbe = GB_search(gbd, field_name, GB_FIND);
697  }
698  else { // flat entry
699  gbe = GB_entry(gbd, field_name);
700  }
701 
702  // silently ignore missing fields (and leave apply_aci false!)
703  if (gbe) {
704  apply_aci = true;
705  switch (GB_read_type(gbe)) {
706  case GB_INT: field_output = GBS_global_string("%li", GB_read_int(gbe)); align_left = false; break;
707  case GB_BYTE: field_output = GBS_global_string("%i", GB_read_byte(gbe)); align_left = false; break;
708 
709  case GB_FLOAT: {
710  const char *format = "%5.4f";
711  if (mode == NDS_OUTPUT_TAB_SEPARATED) { // '.' -> ','
712  char *dotted = GBS_global_string_copy(format, GB_read_float(gbe));
713  char *dot = strchr(dotted, '.');
714  if (dot) *dot = ',';
715  field_output = GBS_static_string(dotted);
716  free(dotted);
717  }
718  else {
719  field_output = GBS_global_string(format, GB_read_float(gbe));
720  }
721  align_left = false;
722  break;
723  }
724  case GB_STRING:
725  field_output = GB_read_char_pntr(gbe);
726  if (!field_output) field_output="<read error>";
727  break;
728 
729  default: {
730  char *as_string = GB_read_as_string(gbe);
731  field_output = GBS_static_string(as_string);
732  free(as_string);
733  }
734  }
735  }
736  else {
737  if (GB_have_error()) {
738  field_output = GB_await_error();
739  }
740  }
741  }
742  str = strdup(field_output);
743  }
744 
745  // apply ACI/SRT program
746 
747  GB_ERROR error = NULp;
748  if (apply_aci) {
749  const char *aci = parsing[i];
750  if (aci) {
751  GBL_env env(gb_main, tree_name); // @@@ pass from caller?
752  GBL_call_env callEnv(gbd, env);
753 
754  char *aci_result = GB_command_interpreter_in_env(str, aci, callEnv);
755  if (!aci_result) aci_result = GBS_global_string_copy("<error: %s>", GB_await_error());
756  freeset(str, aci_result);
757  }
758  }
759 
761 
762  // quote string, if it contains separator
763  {
764  char *quoted = NULp;
765  switch (mode) {
767  quoted = quoted_if_containing_separator(str, ',');
768  break;
769 
771  quoted = quoted_if_containing_separator(str, '\t');
772  break;
773 
774  case NDS_OUTPUT_LEAFTEXT:
776  break;
777  }
778 
779  if (quoted) freeset(str, quoted);
780  }
781 
782 
783  bool skip_display = (mode == NDS_OUTPUT_LEAFTEXT && str[0] == 0);
784  if (!skip_display) {
785  switch (mode) {
786  case NDS_OUTPUT_LEAFTEXT:
787  if (!field_was_printed) break; // no comma no space if nothing printed yet
788  append(','); // separate single fields by comma in compressed mode
789  // fall-through
790  case NDS_OUTPUT_SPACE_PADDED: // fixed column width output (for office calc)
791  if (!field_was_printed) break; // no space if nothing printed yet
792  append(' '); // print at least one space if not using tabs
793  break;
794 
796  if (i != 0) append(','); // CSV output (for office calc)
797  break;
798 
800  if (i != 0) append('\t'); // tabbed output (for office calc)
801  break;
802  }
803 
804  field_was_printed = true;
805 
806  int str_len = strlen(str);
807  switch (mode) {
810  append(str, str_len);
811  break;
812 
813  case NDS_OUTPUT_LEAFTEXT:
815  int nds_len = lengths[i];
816  if (str_len>nds_len) { // string is too long -> shorten
817  str[nds_len] = 0;
818  str_len = nds_len;
819  }
820 
821  if (mode == NDS_OUTPUT_SPACE_PADDED) { // may need alignment
822  const char *spaced = GBS_global_string((align_left ? "%-*s" : "%*s"), nds_len, str);
823  append(spaced, nds_len);
824  }
825  else {
827  append(str, str_len);
828  }
829  }
830  }
831  }
832 
833  // show first XXX errors
834  if (error && show_errors>0) {
835  show_errors--;
836  aw_message(error);
837  }
838 
839  free(str);
840  }
841  }
842 
843  return get_buffer();
844 }
845 
847  static NodeTextBuilder ntb;
848  return ntb;
849 }
850 
852  theBuilder().init(gb_main);
853 }
854 
855 const char *make_node_text_nds(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name) {
856  return theBuilder().work(gb_main, gbd, mode, species, tree_name, false);
857 }
858 const char *make_group_text_nds(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name) {
859  // forces group nodes (even if 'species' is a leaf)
860  return theBuilder().work(gb_main, gbd, mode, species, tree_name, true);
861 }
862 
863 static const char *createReplaceTable() {
864  static char replaceTable[256];
865 
866  for (int i = 0; i<32; ++i) replaceTable[i] = '?';
867  for (int i = 32; i<256; ++i) replaceTable[i] = i;
868 
869  const char LFREP = '#';
870 
871  replaceTable['\n'] = LFREP;
872  replaceTable['\r'] = LFREP;
873  replaceTable['\t'] = ' ';
874 
875  return replaceTable;
876 }
877 
878 char *NDS_mask_nonprintable_chars(char * const str) {
879  // mask nonprintable characters in result of NDS.
880  //
881  // modifies and returns 'str'
882  //
883  // background: gtk renders LFs as such (i.e. renders multiple lines),
884  // motif printed a small box (i.e. rendered all text in one line)
885 
886  static const char *replaceTable = createReplaceTable();
887  for (int i = 0; str[i]; ++i) {
888  str[i] = replaceTable[safeCharIndex(str[i])];
889  }
890  return str;
891 }
892 
893 // --------------------------------------------------------------------------------
894 
895 #ifdef UNIT_TESTS
896 #ifndef TEST_UNIT_H
897 #include <test_unit.h>
898 #endif
899 
900 #define TEST_EXPECT_MASK_NONPRINTABLE(i,o) do { \
901  char *masked = strdup(i); \
902  NDS_mask_nonprintable_chars(masked); \
903  TEST_EXPECT_EQUAL(masked,o); \
904  free(masked); \
905  } while (0)
906 
907 void TEST_mask_nds() {
908  TEST_EXPECT_MASK_NONPRINTABLE("plain text", "plain text");
909  TEST_EXPECT_MASK_NONPRINTABLE("with\nLF", "with#LF");
910  TEST_EXPECT_MASK_NONPRINTABLE("with\rLF", "with#LF");
911  TEST_EXPECT_MASK_NONPRINTABLE("with\r\nLF", "with##LF");
912  TEST_EXPECT_MASK_NONPRINTABLE("tab\tseparated", "tab separated");
913  TEST_EXPECT_MASK_NONPRINTABLE("\t\n\t\n", " # #");
914 }
915 
916 #define TEST_EXPECT_NDS_EQUALS(specName,format,expected_NDS) do { \
917  GBDATA *gb_species = GBT_find_species(gb_main, specName); \
918  TEST_REJECT_NULL(gb_species); \
919  \
920  const char *nds = make_node_text_nds(gb_main, gb_species, format, NULp, NULp); \
921  TEST_EXPECT_EQUAL(nds, expected_NDS); \
922  } while(0)
923 
924 #define TEST_EXPECT_NDS_EQUALS__BROKEN(specName,format,expected_NDS) do { \
925  GBDATA *gb_species = GBT_find_species(gb_main, specName); \
926  TEST_REJECT_NULL(gb_species); \
927  \
928  const char *nds = make_node_text_nds(gb_main, gb_species, format, NULp, NULp); \
929  TEST_EXPECT_EQUAL__BROKEN(nds, expected_NDS); \
930  } while(0)
931 
932 void TEST_nds() {
933  GB_shell shell;
934  const char *testDB = "display/nds.arb"; // NDS definitions are in ../../UNIT_TESTER/run/display/nds.arb@arb_presets
935  GBDATA *gb_main = GB_open(testDB, "r");
936 
937  TEST_REJECT_NULL(gb_main);
938 
939  {
940  GB_transaction ta(gb_main);
941  make_node_text_init(gb_main);
942 
943  TEST_EXPECT_NDS_EQUALS("MycChlor", NDS_OUTPUT_LEAFTEXT, "'MycChlor', Mycobacterium #phenolicus, acc=X79094");
944  TEST_EXPECT_NDS_EQUALS("MycChlor", NDS_OUTPUT_SPACE_PADDED, "'MycChlor' Mycobacterium #phenolicus acc=X79094 ");
945  TEST_EXPECT_NDS_EQUALS("MycChlor", NDS_OUTPUT_TAB_SEPARATED, "'MycChlor'\tMycobacterium #phenolicus\tacc=X79094");
946  TEST_EXPECT_NDS_EQUALS("MycChlor", NDS_OUTPUT_COMMA_SEPARATED, "'MycChlor',Mycobacterium #phenolicus,acc=X79094");
947 
948  TEST_EXPECT_NDS_EQUALS("ActUtahe", NDS_OUTPUT_LEAFTEXT, "'ActUtahe', Act;ino planes uta,hen.sis#, acc=X80823");
949  TEST_EXPECT_NDS_EQUALS("ActUtahe", NDS_OUTPUT_SPACE_PADDED, "'ActUtahe' Act;ino planes uta,hen.sis# acc=X80823 ");
950  TEST_EXPECT_NDS_EQUALS("ActUtahe", NDS_OUTPUT_TAB_SEPARATED, "'ActUtahe'\tAct;ino planes uta,hen.sis#\tacc=X80823");
951  TEST_EXPECT_NDS_EQUALS("ActUtahe", NDS_OUTPUT_COMMA_SEPARATED, "'ActUtahe',\"Act;ino planes uta,hen.sis#\",acc=X80823"); // quote 2nd value (cause it contains a comma)
952 
953  TEST_EXPECT_NDS_EQUALS("StpGrise", NDS_OUTPUT_LEAFTEXT, "'StpGrise', Strepto s griseus, acc=M76388 X55435 X6"); // acc truncated!
954  TEST_EXPECT_NDS_EQUALS("StpGrise", NDS_OUTPUT_SPACE_PADDED, "'StpGrise' Strepto s griseus acc=M76388 X55435 X6");
955  TEST_EXPECT_NDS_EQUALS("StpGrise", NDS_OUTPUT_TAB_SEPARATED, "'StpGrise'\tStrepto s griseus\tacc=M76388 X55435 X61478"); // acc not truncated here
956  TEST_EXPECT_NDS_EQUALS("StpGrise", NDS_OUTPUT_COMMA_SEPARATED, "'StpGrise',Strepto s griseus,acc=M76388 X55435 X61478");
957  }
958 
959  GB_close(gb_main);
960 }
961 
962 #endif // UNIT_TESTS
963 
964 // --------------------------------------------------------------------------------
const char * GB_ERROR
Definition: arb_core.h:25
GBDATA * GB_open(const char *path, const char *opent)
Definition: ad_load.cxx:1363
void delete_entry(const char *entry)
group_matcher all()
Definition: test_unit.h:1000
void add(const char *awar_name, const char *config_name)
bool has_entry(const char *entry) const
AliDataPtr format(AliDataPtr data, const size_t wanted_len, GB_ERROR &error)
Definition: insdel.cxx:615
Definition: arbdb.h:65
long GB_read_int(GBDATA *gbd)
Definition: arbdb.cxx:723
GBDATA * GB_child(GBDATA *father)
Definition: adquery.cxx:322
static void dot(double **i, double **j, double **k)
Definition: trnsprob.cxx:59
CONSTEXPR_INLINE unsigned char safeCharIndex(char c)
Definition: dupstr.h:73
GB_ERROR GB_write_string(GBDATA *gbd, const char *s)
Definition: arbdb.cxx:1385
GBDATA * GB_searchOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value)
Definition: adquery.cxx:546
GBDATA * GB_nextEntry(GBDATA *entry)
Definition: adquery.cxx:339
void AWT_insert_config_manager(AW_window *aww, AW_default default_file_, const char *id, const StoreConfigCallback &store_cb, const RestoreConfigCallback &load_or_reset_cb, const char *macro_id, const AWT_predefined_config *predef)
char * GB_read_as_string(GBDATA *gbd)
Definition: arbdb.cxx:1054
const char * viewkeyAwarName(int i, const char *name)
Definition: nds.cxx:133
long read_int() const
Definition: AW_awar.cxx:187
AW_awar * set_minmax(float min, float max)
Definition: AW_awar.cxx:532
NOT4PERL long * GBT_readOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value)
Definition: adtools.cxx:402
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:204
void warning(int warning_num, const char *warning_message)
Definition: util.cxx:61
#define FORWARD_FORMATTED(receiver, format)
Definition: arb_msg_fwd.h:19
void make_node_text_init(GBDATA *gb_main)
Definition: nds.cxx:851
bool GB_have_error()
Definition: arb_msg.cxx:349
void AW_POPDOWN(AW_window *window)
Definition: AW_window.cxx:52
void AWT_popup_select_srtaci_window(AW_window *aww, const char *acisrt_awarname)
Definition: nds.cxx:335
GB_ERROR GB_push_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2482
void activate()
Definition: aw_window.hxx:354
static const char * script_part_of(const char *predef_entry)
Definition: nds.cxx:299
CONSTEXPR long FIELD_FILTER_NDS
Definition: item_sel_list.h:52
#define NO_FIELD_SELECTED
AW_awar * add_callback(const RootCallback &cb)
Definition: AW_awar.cxx:234
GB_ERROR GB_delete(GBDATA *&source)
Definition: arbdb.cxx:1904
void create_itemfield_selection_button(AW_window *aws, const FieldSelDef &selDef, const char *at)
static bool initialized
Definition: AW_advice.cxx:36
#define NDS_PER_PAGE
Definition: nds.cxx:31
const char * work(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name, bool forceGroup)
Definition: nds.cxx:663
const char * read_char_pntr() const
Definition: AW_awar.cxx:171
size_t GB_read_string_count(GBDATA *gbd)
Definition: arbdb.cxx:910
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:353
static AW_root * SINGLETON
Definition: aw_root.hxx:102
NOT4PERL long * GBT_read_int(GBDATA *gb_container, const char *fieldpath)
Definition: adtools.cxx:327
GBDATA * GB_create_container(GBDATA *father, const char *key)
Definition: arbdb.cxx:1827
WindowCallback makeHelpCallback(const char *helpfile)
Definition: aw_window.hxx:106
AW_awar * viewkeyAwar(AW_root *aw_root, AW_default awdef, int i, const char *name, bool string_awar)
Definition: nds.cxx:138
Definition: arbdb.h:67
#define NDS_COUNT
Definition: nds.cxx:34
GB_TYPES GB_read_type(GBDATA *gbd)
Definition: arbdb.cxx:1641
const char * make_node_text_nds(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name)
Definition: nds.cxx:855
static void nds_restore_config(const char *stored, GBDATA *gb_main)
Definition: nds.cxx:395
GBDATA * GB_create(GBDATA *father, const char *key, GB_TYPES type)
Definition: arbdb.cxx:1779
const char * GB_first_non_key_char(const char *str)
Definition: adquery.cxx:416
static const char * createReplaceTable()
Definition: nds.cxx:863
static void awt_select_pre_from_view(AW_root *aw_root, AW_selection_list *programs)
Definition: nds.cxx:314
static int group[MAXN+1]
Definition: ClustalV.cxx:65
static NodeTextBuilder & theBuilder()
Definition: nds.cxx:846
#define TEST_REJECT_NULL(n)
Definition: test_unit.h:1310
#define AWAR_SELECT_ACISRT
Definition: nds.h:21
static void error(const char *msg)
Definition: mkptypes.cxx:96
NDS_Type
Definition: nds.h:24
static void map_viewkey(AW_root *aw_root, AW_default awdef, int i, GBDATA *gb_viewkey)
Definition: nds.cxx:146
GBDATA * GB_searchOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value)
Definition: adquery.cxx:569
char * str
Definition: defines.h:20
float GB_read_float(GBDATA *gbd)
Definition: arbdb.cxx:738
#define AWAR_NDS_USE_ALL
Definition: nds.cxx:36
const char * make_group_text_nds(GBDATA *gb_main, GBDATA *gbd, NDS_Type mode, TreeNode *species, const char *tree_name)
Definition: nds.cxx:858
GB_CSTR GB_path_in_ARBLIB(const char *relative_path)
Definition: adsocket.cxx:1124
AW_awar * awar(const char *awar)
Definition: AW_root.cxx:554
GB_ERROR GB_pop_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2512
Definition: arbdb.h:86
void create_nds_vars(AW_root *aw_root, AW_default awdef, GBDATA *gb_main, bool force_reinit)
Definition: nds.cxx:205
char * NDS_mask_nonprintable_chars(char *const str)
Definition: nds.cxx:878
static char * quoted_if_containing_separator(const char *text, char separator)
Definition: nds.cxx:657
static void map_viewkeys(AW_root *aw_root, GBDATA *awdef, GBDATA *gb_main)
Definition: nds.cxx:169
#define AWAR_NDS_PAGE
Definition: nds.cxx:37
AW_window * AWT_create_nds_window(AW_root *aw_root, GBDATA *gb_main)
Definition: nds.cxx:500
void spaced(const char *word)
Definition: test_unit.h:408
GB_ERROR GB_export_errorf(const char *templat,...)
Definition: arb_msg.cxx:264
bool is_leaf() const
Definition: TreeNode.h:171
AW_awar * awar_int(const char *var_name, long default_value=0, AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:580
static char * nds_store_config()
Definition: nds.cxx:389
char * name
Definition: TreeNode.h:134
int GB_read_byte(GBDATA *gbd)
Definition: arbdb.cxx:728
AW_awar * map(const char *awarn)
Definition: AW_awar.cxx:523
char * GB_read_string(GBDATA *gbd)
Definition: arbdb.cxx:903
ItemSelector & SPECIES_get_selector()
Definition: species.cxx:139
void set_entry(const char *entry, const char *value)
const char * GBS_static_string(const char *str)
Definition: arb_msg.cxx:213
NodeTextBuilder()
Definition: nds.cxx:119
void aw_message(const char *msg)
Definition: AW_status.cxx:932
AW_root * get_root()
Definition: aw_window.hxx:348
#define NDS_STRING_SIZE
Definition: nds.cxx:42
#define nds_assert(cond)
Definition: nds.cxx:29
AW_window_simple * win
#define NULp
Definition: cxxforward.h:97
bool is_leaf() const
Definition: probe_tree.h:67
static bool in_pre_update
Definition: nds.cxx:304
GB_ERROR write_string(const char *aw_string)
static void nds_init_config(AWT_config_definition &cdef)
Definition: nds.cxx:379
NOT4PERL char * GB_command_interpreter_in_env(const char *str, const char *commands, const GBL_call_env &callEnv)
Definition: gb_aci.cxx:361
GBDATA * GB_nextChild(GBDATA *child)
Definition: adquery.cxx:326
GB_ERROR load(const char *filemask, bool append) const
GB_transaction ta(gb_var)
const char * get_entry(const char *entry) const
GB_CSTR GB_read_char_pntr(GBDATA *gbd)
Definition: arbdb.cxx:898
GBDATA * gb_main
Definition: adname.cxx:33
AW_awar * awar_string(const char *var_name, const char *default_value="", AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:570
GBDATA * GB_search(GBDATA *gbd, const char *fieldpath, GB_TYPES create)
Definition: adquery.cxx:531
static void awt_pre_to_view(AW_root *aw_root)
Definition: nds.cxx:306
void init(GBDATA *gb_main)
Definition: nds.cxx:611
size_t length
#define AWAR_SELECT_ACISRT_PRE
Definition: nds.h:22
char * config_string() const
#define AW_ROOT_DEFAULT
Definition: aw_base.hxx:106
#define NDS_PAGES
Definition: nds.cxx:32
GBDATA * GB_entry(GBDATA *father, const char *key)
Definition: adquery.cxx:334
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:195
void GB_close(GBDATA *gbd)
Definition: arbdb.cxx:649
void write(const char *cfgStr) const
Definition: arbdb.h:66