source: trunk/SL/REFENTRIES/refentries.cxx

Last change on this file was 19365, checked in by westram, 18 months ago
  • string splitters:
    • unittests for GBT_split_string:
      • add tests for dropEmptyTokens.
      • document special behavior for empty string.
    • define enum SplitMode. use enum instead of bool param for GBT_splitNdestroy_string + GBT_split_string.
File size: 13.7 KB
Line 
1// ============================================================ //
2//                                                              //
3//   File      : refentries.cxx                                 //
4//   Purpose   :                                                //
5//                                                              //
6//   Coded by Ralf Westram (coder@reallysoft.de) in June 2011   //
7//   Institute of Microbiology (Technical University Munich)    //
8//   http://www.arb-home.de/                                    //
9//                                                              //
10// ============================================================ //
11
12#include "refentries.h"
13
14#include <awt_config_manager.hxx>
15#include <item_sel_list.h>
16
17#include <aw_root.hxx>
18#include <aw_awars.hxx>
19#include <aw_msg.hxx>
20
21#include <arbdbt.h>
22#include <gb_aci.h>
23
24#include <cctype>
25
26namespace RefEntries {
27
28    static ARB_ERROR generate_item_error(const char *format, ItemSelector& itemtype, GBDATA *gb_item) {
29        GBDATA *gb_main          = GB_get_root(gb_item);
30        char   *item_id          = itemtype.generate_item_id(gb_main, gb_item);
31        char   *item_description = GBS_global_string_copy("%s '%s'", itemtype.item_name, item_id);
32
33        re_assert(strstr(format, "%s"));
34        ARB_ERROR error = GBS_global_string(format, item_description);
35
36        free(item_description);
37        free(item_id);
38
39        return error;
40    }
41
42    const char *RefSelector::get_refs(ItemSelector& itemtype, GBDATA *gb_item) const {
43        const char *refs    = NULp;
44        GBDATA     *gb_refs = GB_entry(gb_item, field);
45        if (gb_refs) {
46            refs = GB_read_char_pntr(gb_refs);
47        }
48        else if (error_if_field_missing) {
49            char *err_format = GBS_global_string_copy("%%s has no field '%s'", field);
50            GB_export_error(generate_item_error(err_format, itemtype, gb_item).deliver());
51            free(err_format);
52        }
53        return refs;
54    }
55
56    char *RefSelector::filter_refs(const char *refs, GBDATA *gb_item) const {
57        return refs ? GB_command_interpreter_in_env(refs, aci, GBL_simple_call_env(gb_item)) : NULp;
58    }
59
60    static ARB_ERROR addRefsTo(DBItemSet& referred, ItemSelector& itemtype, GBDATA *gb_item, const RefSelector& ref) {
61        const char *refs     = ref.get_refs(itemtype, gb_item);
62        char       *filtered = ref.filter_refs(refs, gb_item);
63        ARB_ERROR   error    = GB_incur_error_if(!filtered);
64
65        if (filtered) {
66            re_assert(!error);
67            ConstStrArray refNames;
68            GBT_split_string(refNames, filtered, ";, ", SPLIT_DROPEMPTY);
69            size_t   refCount = refNames.size();
70
71            for (size_t r = 0; r<refCount && !error; ++r) {
72                GBDATA *gb_main     = GB_get_root(gb_item);
73                GBDATA *gb_referred = itemtype.find_item_by_id(gb_main, refNames[r]);
74                if (gb_referred) {
75                    referred.insert(gb_referred);
76                }
77                else if (!ref.ignore_unknown_refs()) {
78                    error = generate_item_error(GBS_global_string("%%s refers to unknown %s '%s'\n"
79                                                                  "in content of field '%s'\n"
80                                                                  "(content ='%s',\n"
81                                                                  " filtered='%s')\n",
82                                                                  itemtype.item_name, refNames[r],
83                                                                  ref.get_field(),
84                                                                  refs,
85                                                                  filtered),
86                                                itemtype, gb_item);
87                }
88            }
89
90            free(filtered);
91        }
92
93        return error;
94    }
95
96    ARB_ERROR ReferringEntriesHandler::with_all_referred_items(QUERY_RANGE range, const RefSelector& refsel, referred_item_handler callback) {
97        re_assert(range != QUERY_CURRENT_ITEM); // would need AW_root
98
99        ARB_ERROR  error;
100        DBItemSet referred;
101
102        {
103            GB_transaction ta(gb_main);
104
105            for (GBDATA *gb_container = itemtype.get_first_item_container(gb_main, NULp, QUERY_ALL_ITEMS);
106                 gb_container && !error;
107                 gb_container = itemtype.get_next_item_container(gb_container, QUERY_ALL_ITEMS))
108            {
109                for (GBDATA *gb_item = itemtype.get_first_item(gb_container, range);
110                     gb_item && !error;
111                     gb_item = itemtype.get_next_item(gb_item, range))
112                {
113                    error = addRefsTo(referred, itemtype, gb_item, refsel);
114                }
115            }
116        }
117
118        if (!error) error = callback(gb_main, referred);
119        return error;
120    }
121
122    ARB_ERROR ReferringEntriesHandler::with_all_referred_items(GBDATA *gb_item, const RefSelector& refsel, referred_item_handler callback) {
123        DBItemSet referred;
124        ARB_ERROR  error  = addRefsTo(referred, itemtype, gb_item, refsel);
125        if (!error) error = callback(gb_main, referred);
126        return error;
127    }
128
129
130
131    // ------------------------
132    //      GUI related below
133
134
135#define AWAR_MARKBYREF                "awt/refs/"
136#define AWAR_MARKBYREF_ALL            AWAR_MARKBYREF "which"
137#define AWAR_MARKBYREF_FIELD          AWAR_MARKBYREF "field"
138#define AWAR_MARKBYREF_IGNORE_MISSING AWAR_MARKBYREF "ignore_missing"
139#define AWAR_MARKBYREF_IGNORE_UNKNOWN AWAR_MARKBYREF "ignore_unknown"
140#define AWAR_MARKBYREF_FILTER         AWAR_MARKBYREF "filter"
141
142#define AWAR_MARKBYREF_TEMP "tmp/awt/refs/"
143#define AWAR_MARKBYREF_SELECTED AWAR_MARKBYREF_TEMP "selected"
144#define AWAR_MARKBYREF_CONTENT  AWAR_MARKBYREF_TEMP "content"
145#define AWAR_MARKBYREF_RESULT   AWAR_MARKBYREF_TEMP "result"
146
147    static void perform_refentries(AW_window *aww, ReferringEntriesHandler *reh, referred_item_handler ricb) {
148        AW_root     *aw_root = aww->get_root();
149        QUERY_RANGE  range   = aw_root->awar(AWAR_MARKBYREF_ALL)->read_int() ? QUERY_ALL_ITEMS : QUERY_MARKED_ITEMS;
150
151        RefSelector refsel(aw_root->awar(AWAR_MARKBYREF_FIELD)->read_char_pntr(),
152                           aw_root->awar(AWAR_MARKBYREF_FILTER)->read_char_pntr(),
153                           !aw_root->awar(AWAR_MARKBYREF_IGNORE_MISSING)->read_int(),
154                           !aw_root->awar(AWAR_MARKBYREF_IGNORE_UNKNOWN)->read_int());
155
156        ARB_ERROR error = reh->with_all_referred_items(range, refsel, ricb);
157        aw_message_if(error);
158    }
159
160    static void refresh_result_cb(AW_root *aw_root, ReferringEntriesHandler *reh) {
161        ItemSelector&   itemtype = reh->get_referring_item();
162        GBDATA         *gb_main  = reh->get_gbmain();
163        GB_transaction  ta(gb_main);
164
165        GBDATA  *gb_item       = itemtype.get_selected_item(gb_main, aw_root);
166        AW_awar *awar_selected = aw_root->awar(AWAR_MARKBYREF_SELECTED);
167        AW_awar *awar_result   = aw_root->awar(AWAR_MARKBYREF_RESULT);
168
169        awar_result->write_string("<no result>");
170        if (!gb_item) {
171            awar_selected->write_string(GBS_global_string("<no %s selected>", itemtype.item_name));
172        }
173        else {
174            char *id = itemtype.generate_item_id(gb_main, gb_item);
175            if (!id) {
176                awar_selected->write_string(GB_await_error());
177            }
178            else {
179                awar_selected->write_string(id);
180
181                AW_awar *awar_content = aw_root->awar(AWAR_MARKBYREF_CONTENT);
182
183                RefSelector refsel(aw_root->awar(AWAR_MARKBYREF_FIELD)->read_char_pntr(),
184                                   aw_root->awar(AWAR_MARKBYREF_FILTER)->read_char_pntr(),
185                                   true, true);
186
187                const char *refs = refsel.get_refs(itemtype, gb_item);
188
189                if (!refs) {
190                    char *err = GBS_string_eval(GB_await_error(), "\n=; ");
191                    awar_content->write_string(err);
192                    free(err);
193                }
194                else {
195                    awar_content->write_string(refs);
196
197                    char *result = refsel.filter_refs(refs, gb_item);
198
199                    if (result) {
200                        awar_result->write_string(result);
201                        free(result);
202                    }
203                    else {
204                        char *err = GBS_string_eval(GB_await_error(), "\n=;");
205                        awar_result->write_string(err);
206                        free(err);
207                    }
208                }
209
210                free(id);
211            }
212        }
213    }
214
215    static void bind_result_refresh_cbs(AW_root *aw_root, ReferringEntriesHandler *reh) {
216        RootCallback refreshCb = makeRootCallback(refresh_result_cb, reh);
217
218        aw_root->awar(AWAR_MARKBYREF_FIELD) ->add_callback(refreshCb);
219        aw_root->awar(AWAR_MARKBYREF_FILTER)->add_callback(refreshCb);
220        aw_root->awar(AWAR_SPECIES_NAME)    ->add_callback(refreshCb);    // @@@ hack
221    }
222
223    void create_refentries_awars(AW_root *aw_root, AW_default aw_def) {
224        aw_root->awar_string(AWAR_MARKBYREF_FIELD, "used_rels",       aw_def);
225        aw_root->awar_string(AWAR_MARKBYREF_FILTER,   "/[0-9.]+[%]*://", aw_def);
226
227        aw_root->awar_string(AWAR_MARKBYREF_SELECTED, "<no item>",    aw_def);
228        aw_root->awar_string(AWAR_MARKBYREF_CONTENT,  "<no content>", aw_def);
229        aw_root->awar_string(AWAR_MARKBYREF_RESULT,   "<no result>",  aw_def);
230
231        aw_root->awar_int(AWAR_MARKBYREF_ALL,            0, aw_def);
232        aw_root->awar_int(AWAR_MARKBYREF_IGNORE_MISSING, 0, aw_def);
233        aw_root->awar_int(AWAR_MARKBYREF_IGNORE_UNKNOWN, 0, aw_def);
234    }
235
236    static AWT_config_mapping_def markByRef_config_mapping[] = {
237        { AWAR_MARKBYREF_ALL,            "examine" },
238        { AWAR_MARKBYREF_FIELD,          "field" },
239        { AWAR_MARKBYREF_IGNORE_MISSING, "ignore_missing" },
240        { AWAR_MARKBYREF_FILTER,         "filter" },
241        { AWAR_MARKBYREF_IGNORE_UNKNOWN, "ignore_unknown" },
242
243        { NULp, NULp }
244    };
245
246    static AWT_predefined_config markByRef_predefined_config[] = {
247        { "*relatives_used_by_aligner", "For use with 'used_rels' entry as\ngenerated by fast-aligner.",         "field='used_rels';filter='/:[0-9]+//'" },
248        { "*next_relatives_of_listed",  "For use with entries generated by\n'Search next relatives of listed'.", "field='tmp';filter='/[0-9.]+[%]://'" },
249
250        { NULp, NULp, NULp }
251    };
252
253    AW_window *create_refentries_window(AW_root *aw_root, ReferringEntriesHandler *reh, const char *window_id, const char *title, const char *help, client_area_builder build_client_area, const char *action, referred_item_handler action_cb) {
254        AW_window_simple *aws = new AW_window_simple;
255
256        const int PAD = 10;
257
258        aws->init(aw_root, window_id, title);
259        aws->at(PAD, PAD);
260        aws->auto_space(PAD, PAD);
261
262        bind_result_refresh_cbs(aw_root, reh);
263
264        aws->callback(AW_POPDOWN);
265        aws->create_button("CLOSE", "CLOSE", "C");
266
267        aws->callback(makeHelpCallback(help));
268        aws->create_button("HELP", "HELP", "H");
269
270        const int LABEL_LENGTH = 27;
271        const int LABEL_SHORTL = 10;
272        aws->label_length(LABEL_LENGTH);
273
274        const int HEIGHT_IF = 32; // lineheight of attached input field
275        const int HEIGHT_SB = 25; // lineheight of attached selection butoon
276        const int HEIGHT_DF = 18; // lineheight of attached display field
277
278        ItemSelector& itemtype = reh->get_referring_item();
279
280        char *items_name = strdup(itemtype.items_name);
281        items_name[0]    = toupper(items_name[0]);
282
283        aws->at_newline();
284        aws->label(GBS_global_string("%s to examine", items_name));
285        aws->create_option_menu(AWAR_MARKBYREF_ALL);
286        aws->insert_option("Marked", "M", 0);
287        aws->insert_option("All",    "A", 1);
288        aws->update_option_menu();
289
290        aws->at_newline();
291        aws->at_attach_to(true, false, -PAD, HEIGHT_SB);
292        aws->label("Field containing references");
293        create_itemfield_selection_button(aws, FieldSelDef(AWAR_MARKBYREF_FIELD, reh->get_gbmain(), SPECIES_get_selector(), FIELD_FILTER_STRING, "reference field"), NULp);
294
295        aws->at_newline();
296        aws->label("Ignore if field missing?");
297        aws->create_toggle(AWAR_MARKBYREF_IGNORE_MISSING);
298
299        aws->at_newline();
300        aws->at_attach_to(true, false, -PAD, HEIGHT_IF);
301        aws->label("Filter content by ACI");
302        aws->create_input_field(AWAR_MARKBYREF_FILTER, 30);
303
304        aws->label_length(LABEL_SHORTL);
305
306        aws->at_newline();
307        aws->at_attach_to(true, false, -PAD, HEIGHT_DF);
308        aws->label("Selected");
309        aws->create_button(NULp, AWAR_MARKBYREF_SELECTED, NULp, "+");
310
311        aws->at_newline();
312        aws->at_attach_to(true, false, -PAD, HEIGHT_DF);
313        aws->label("Content");
314        aws->create_button(NULp, AWAR_MARKBYREF_CONTENT,  NULp, "+");
315
316        aws->at_newline();
317        aws->at_attach_to(true, false, -PAD, HEIGHT_DF);
318        aws->label("Result");
319        aws->create_button(NULp, AWAR_MARKBYREF_RESULT,   NULp, "+");
320
321        aws->label_length(LABEL_LENGTH);
322
323        aws->at_newline();
324        aws->label("Ignore unknown references?");
325        aws->create_toggle(AWAR_MARKBYREF_IGNORE_UNKNOWN);
326
327        if (build_client_area) build_client_area(aws);
328
329        aws->at_newline();
330        aws->callback(makeWindowCallback(perform_refentries, reh, action_cb));
331        aws->create_autosize_button("ACTION", action, "");
332
333        aws->at_attach(-PAD, PAD); // attach icon to NE corner
334        AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "mark_by_ref", markByRef_config_mapping, NULp, markByRef_predefined_config);
335        aws->at_unattach();
336
337        aws->window_fit();
338
339        free(items_name);
340
341        refresh_result_cb(aw_root, reh);
342
343        return aws;
344    }
345
346};
347
Note: See TracBrowser for help on using the repository browser.