Line data Source code
1 : /* File: gui_search_runner.c; Copyright and License: see below */
2 :
3 : #include "gui_search_runner.h"
4 : #include "set/data_search_result_list.h"
5 : #include "utf8stream/utf8stream_writer.h"
6 : #include "u8/u8_trace.h"
7 : #include "u8/u8_log.h"
8 : #include "u8/u8_i32.h"
9 : #include <assert.h>
10 :
11 0 : void gui_search_runner_init ( gui_search_runner_t *this_,
12 : gui_simple_message_to_user_t *message_to_user,
13 : data_database_reader_t *db_reader,
14 : data_database_t *database,
15 : observer_t result_consumer )
16 : {
17 0 : U8_TRACE_BEGIN();
18 0 : assert ( message_to_user != NULL );
19 0 : assert ( db_reader != NULL );
20 0 : assert ( database != NULL );
21 :
22 : /* external entities */
23 0 : (*this_).message_to_user = message_to_user;
24 0 : (*this_).db_reader = db_reader;
25 0 : const u8_error_t d_err = data_database_text_search_init ( &((*this_).db_searcher), database );
26 0 : if ( U8_ERROR_NONE != d_err )
27 : {
28 0 : U8_LOG_WARNING_HEX( "data_database_text_search_t could not be constructed.", d_err );
29 : }
30 0 : (*this_).result_consumer = result_consumer;
31 :
32 : /* request data */
33 0 : utf8stream_writemem_init( &((*this_).search_string_writer),
34 0 : &((*this_).search_string_buf),
35 : sizeof( (*this_).search_string_buf )
36 : );
37 0 : (*this_).page_request = pos_scroll_page_new( 0, false /* backwards */ );
38 :
39 : /* result data */
40 0 : (*this_).result_buffer_start = 0;
41 0 : DATA_SEARCH_RESULT_LIST_INIT( &((*this_).result_list), (*this_).result_buffer );
42 0 : (*this_).result_buffer_more_after = false;
43 :
44 0 : U8_TRACE_END();
45 0 : }
46 :
47 0 : void gui_search_runner_destroy ( gui_search_runner_t *this_ )
48 : {
49 0 : U8_TRACE_BEGIN();
50 :
51 : /* external entities */
52 0 : (*this_).message_to_user = NULL;
53 0 : (*this_).db_reader = NULL;
54 0 : const u8_error_t d_err = data_database_text_search_destroy ( &((*this_).db_searcher) );
55 0 : if ( U8_ERROR_NONE != d_err )
56 : {
57 0 : U8_LOG_WARNING_HEX( "data_database_text_search_t could not be destroyed.", d_err );
58 : }
59 0 : observer_destroy( &((*this_).result_consumer) );
60 :
61 : /* request data */
62 0 : const u8_error_t str_err = utf8stream_writemem_destroy( &((*this_).search_string_writer) );
63 0 : if ( U8_ERROR_NONE != str_err )
64 : {
65 0 : U8_LOG_WARNING_HEX( "utf8stream_writemem_t could not be destroyed cleanly.", d_err );
66 : }
67 :
68 : /* result data */
69 0 : (*this_).result_buffer_start = 0;
70 0 : data_search_result_list_destroy( &((*this_).result_list) );
71 0 : (*this_).result_buffer_more_after = false;
72 :
73 0 : U8_TRACE_END();
74 0 : }
75 :
76 0 : void gui_search_runner_run ( gui_search_runner_t *this_, const char* search_string, pos_scroll_page_t page )
77 : {
78 0 : U8_TRACE_BEGIN();
79 :
80 0 : if ( search_string != NULL )
81 : {
82 : /* store parameters internally */
83 0 : utf8stream_writemem_reset( &((*this_).search_string_writer) );
84 0 : utf8stream_writer_t *write = utf8stream_writemem_get_writer( &((*this_).search_string_writer) );
85 0 : u8_error_t u_err = utf8stream_writer_write_str( write, search_string );
86 0 : if ( u_err != U8_ERROR_NONE )
87 : {
88 0 : U8_TRACE_INFO( "search string too long, was truncated." );
89 : }
90 :
91 0 : gui_search_runner_rerun( this_, page );
92 : }
93 : else
94 : {
95 0 : assert(false);
96 : }
97 :
98 0 : U8_TRACE_END();
99 0 : }
100 :
101 0 : void gui_search_runner_rerun ( gui_search_runner_t *this_, pos_scroll_page_t page )
102 : {
103 0 : U8_TRACE_BEGIN();
104 :
105 : /* store parameters internally */
106 0 : (*this_).page_request = page;
107 0 : pos_scroll_page_trace( &page );
108 0 : const char *const search_string = utf8stream_writemem_get_string( &((*this_).search_string_writer) );
109 0 : uint_fast32_t skip_results
110 0 : = ( pos_scroll_page_get_backwards( &page ) )
111 0 : ? u8_i32_max2( 0, ((signed)( pos_scroll_page_get_anchor_index( &page ) - GUI_SEARCH_RUNNER_MAX_RESULTS + 1 )) )
112 0 : : pos_scroll_page_get_anchor_index( &page );
113 0 : U8_TRACE_INFO_INT( "skipping", skip_results );
114 :
115 : /* reset previous errors/warnings/infos */
116 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
117 0 : u8_error_t d_err = U8_ERROR_NONE; /* a data read or data store error */
118 :
119 : /* prepare search result */
120 0 : (*this_).result_buffer_start = 0;
121 0 : data_search_result_list_clear( &((*this_).result_list) );
122 0 : (*this_).result_buffer_more_after = false;
123 :
124 : /* check if an id is being searched */
125 : data_id_t search_id;
126 0 : data_id_init_by_string ( &search_id, search_string );
127 0 : data_id_trace ( &search_id );
128 0 : const data_row_t search_row = data_id_get_row(&search_id);
129 :
130 0 : if ( data_id_is_valid( &search_id ))
131 : {
132 0 : switch ( data_id_get_table(&search_id) )
133 : {
134 0 : case DATA_TABLE_CLASSIFIER:
135 : {
136 0 : d_err = data_database_reader_get_classifier_by_id( (*this_).db_reader,
137 : search_row,
138 : &((*this_).temp_classifier)
139 : );
140 0 : if ( d_err == U8_ERROR_NONE )
141 : {
142 : data_search_result_t half_initialized;
143 0 : data_search_result_init_classifier( &half_initialized,
144 : search_row,
145 0 : data_classifier_get_main_type( &((*this_).temp_classifier) ),
146 0 : data_classifier_get_name_const( &((*this_).temp_classifier) ),
147 : DATA_ROW_VOID /* diagram_id */
148 : );
149 0 : d_err |= gui_search_runner_private_add_diagrams_of_object( this_,
150 : &half_initialized,
151 : &skip_results
152 : );
153 :
154 0 : data_classifier_destroy( &((*this_).temp_classifier) );
155 0 : data_search_result_destroy( &half_initialized );
156 : }
157 : else
158 : {
159 0 : U8_TRACE_INFO( "classifier does not exist or database not open." );
160 : }
161 : }
162 0 : break;
163 :
164 0 : case DATA_TABLE_FEATURE:
165 : {
166 0 : d_err = data_database_reader_get_feature_by_id( (*this_).db_reader,
167 : search_row,
168 : &((*this_).temp_feature)
169 : );
170 0 : if ( d_err == U8_ERROR_NONE )
171 : {
172 0 : data_row_t classifier_id = data_feature_get_classifier_row( &((*this_).temp_feature) );
173 : data_search_result_t half_initialized;
174 0 : data_search_result_init_feature( &half_initialized,
175 0 : data_feature_get_row( &((*this_).temp_feature) ),
176 0 : data_feature_get_main_type( &((*this_).temp_feature) ),
177 0 : data_feature_get_key_const( &((*this_).temp_feature) ),
178 : classifier_id,
179 : DATA_ROW_VOID /* diagram_id */
180 : );
181 0 : d_err |= gui_search_runner_private_add_diagrams_of_object( this_,
182 : &half_initialized,
183 : &skip_results
184 : );
185 :
186 0 : data_feature_destroy( &((*this_).temp_feature) );
187 0 : data_search_result_destroy( &half_initialized );
188 : }
189 : else
190 : {
191 0 : U8_TRACE_INFO( "feature does not exist or database not open." );
192 : }
193 : }
194 0 : break;
195 :
196 0 : case DATA_TABLE_RELATIONSHIP:
197 : {
198 0 : d_err = data_database_reader_get_relationship_by_id( (*this_).db_reader,
199 : search_row,
200 : &((*this_).temp_relationship)
201 : );
202 0 : if ( d_err == U8_ERROR_NONE )
203 : {
204 0 : data_row_t classifier_id = data_relationship_get_from_classifier_row( &((*this_).temp_relationship) );
205 : data_search_result_t half_initialized;
206 0 : data_search_result_init_relationship( &half_initialized,
207 0 : data_relationship_get_row( &((*this_).temp_relationship) ),
208 0 : data_relationship_get_main_type( &((*this_).temp_relationship) ),
209 0 : data_relationship_get_name_const( &((*this_).temp_relationship) ),
210 : classifier_id,
211 0 : data_relationship_get_to_classifier_row( &((*this_).temp_relationship) ),
212 : DATA_ROW_VOID /* diagram_id */
213 : );
214 0 : d_err |= gui_search_runner_private_add_diagrams_of_object( this_,
215 : &half_initialized,
216 : &skip_results
217 : );
218 :
219 0 : data_relationship_destroy( &((*this_).temp_relationship) );
220 0 : data_search_result_destroy( &half_initialized );
221 : }
222 : else
223 : {
224 0 : U8_TRACE_INFO( "relationship does not exist or database not open." );
225 : }
226 : }
227 0 : break;
228 :
229 0 : case DATA_TABLE_DIAGRAMELEMENT:
230 : {
231 0 : d_err = data_database_reader_get_diagramelement_by_id( (*this_).db_reader,
232 : search_row,
233 : &((*this_).temp_diagramelement)
234 : );
235 0 : if ( d_err == U8_ERROR_NONE )
236 : {
237 0 : if ( skip_results == 0 )
238 : {
239 : data_search_result_t half_initialized;
240 0 : data_search_result_init_classifier( &half_initialized,
241 0 : data_diagramelement_get_classifier_row(&((*this_).temp_diagramelement)),
242 : 0 /* match_type is unknown */,
243 : "" /* match_name */,
244 0 : data_diagramelement_get_diagram_row(&((*this_).temp_diagramelement))
245 : );
246 0 : const u8_error_t err = data_search_result_list_add( &((*this_).result_list), &half_initialized );
247 0 : if ( err != U8_ERROR_NONE )
248 : {
249 0 : d_err |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
250 0 : U8_LOG_ANOMALY( "U8_ERROR_ARRAY_BUFFER_EXCEEDED at inserting search result to list" );
251 0 : (*this_).result_buffer_more_after = true; /* there are more results that cannot be stored in &((*this_).result_list) */
252 : }
253 0 : data_search_result_destroy( &half_initialized );
254 : }
255 : else
256 : {
257 : /* to advance to the requested search result page, skip this entry */
258 0 : skip_results --;
259 0 : (*this_).result_buffer_start ++;
260 : }
261 :
262 0 : data_diagramelement_destroy( &((*this_).temp_diagramelement) );
263 : }
264 : else
265 : {
266 0 : U8_TRACE_INFO( "diagramelement does not exist or database not open." );
267 : }
268 : }
269 0 : break;
270 :
271 0 : case DATA_TABLE_DIAGRAM:
272 : {
273 0 : d_err = data_database_reader_get_diagram_by_id ( (*this_).db_reader, search_row, &((*this_).temp_diagram) );
274 0 : if ( d_err == U8_ERROR_NONE )
275 : {
276 0 : if ( skip_results == 0 )
277 : {
278 : data_search_result_t half_initialized;
279 0 : data_search_result_init_diagram( &half_initialized,
280 : search_row,
281 0 : data_diagram_get_diagram_type( &((*this_).temp_diagram) ),
282 0 : data_diagram_get_name_const( &((*this_).temp_diagram) )
283 : );
284 0 : const u8_error_t err = data_search_result_list_add( &((*this_).result_list), &half_initialized );
285 0 : if ( err != U8_ERROR_NONE )
286 : {
287 0 : d_err |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
288 0 : U8_LOG_ANOMALY( "U8_ERROR_ARRAY_BUFFER_EXCEEDED at inserting search result to list" );
289 0 : (*this_).result_buffer_more_after = true; /* there are more results that cannot be stored in &((*this_).result_list) */
290 : }
291 0 : data_search_result_destroy( &half_initialized );
292 : }
293 : else
294 : {
295 : /* to advance to the requested search result page, skip this entry */
296 0 : skip_results --;
297 0 : (*this_).result_buffer_start ++;
298 : }
299 :
300 0 : data_diagram_destroy( &((*this_).temp_diagram) );
301 : }
302 : else
303 : {
304 0 : U8_TRACE_INFO( "diagram does not exist or database not open." );
305 : }
306 : }
307 0 : break;
308 :
309 0 : default:
310 : {
311 0 : assert(false); /* data_id_is_valid should have been false already */
312 : }
313 : break;
314 : }
315 : }
316 : else
317 : {
318 0 : U8_LOG_EVENT_STR( "User search input is not an id", search_string );
319 : }
320 :
321 : /* free text search */
322 0 : if ( d_err == U8_ERROR_NONE )
323 : {
324 : data_search_result_iterator_t data_search_result_iterator;
325 0 : data_search_result_iterator_init_empty( &data_search_result_iterator );
326 0 : d_err = data_database_text_search_get_objects_by_text_fragment( &((*this_).db_searcher),
327 : search_string,
328 : &data_search_result_iterator
329 : );
330 0 : while (( data_search_result_iterator_has_next( &data_search_result_iterator ) )&&( d_err == U8_ERROR_NONE ))
331 : {
332 : data_search_result_t current_search_result;
333 0 : d_err |= data_search_result_iterator_next( &data_search_result_iterator,
334 : ¤t_search_result
335 : );
336 0 : if ( skip_results == 0 )
337 : {
338 0 : const u8_error_t err = data_search_result_list_add( &((*this_).result_list), ¤t_search_result );
339 0 : if ( err != U8_ERROR_NONE )
340 : {
341 0 : d_err |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
342 0 : U8_LOG_ANOMALY( "U8_ERROR_ARRAY_BUFFER_EXCEEDED at inserting search result to list" );
343 0 : (*this_).result_buffer_more_after = true; /* there are more results that cannot be stored in &((*this_).result_list) */
344 : }
345 : }
346 : else
347 : {
348 : /* to advance to the requested search result page, skip this entry */
349 0 : skip_results --;
350 0 : (*this_).result_buffer_start ++;
351 : }
352 : }
353 0 : d_err |= data_search_result_iterator_destroy( &data_search_result_iterator );
354 : }
355 :
356 0 : if ( d_err == U8_ERROR_ARRAY_BUFFER_EXCEEDED )
357 : {
358 : /* it is rather expected than a real error that the result list gets full */
359 0 : U8_TRACE_INFO( "U8_ERROR_ARRAY_BUFFER_EXCEEDED at inserting search result to list" );
360 : }
361 0 : else if ( U8_ERROR_NONE != d_err )
362 : {
363 0 : U8_LOG_ERROR_HEX( "data_database_text_search_t could not search.", d_err );
364 : }
365 :
366 : /* present the result */
367 0 : observer_notify( &((*this_).result_consumer), this_ );
368 :
369 : /* clear the result (the notification above is synchronous, the search results are already processed.) */
370 0 : data_search_result_list_clear( &((*this_).result_list) );
371 :
372 0 : U8_TRACE_END();
373 0 : }
374 :
375 0 : u8_error_t gui_search_runner_private_add_diagrams_of_object( gui_search_runner_t *this_,
376 : data_search_result_t *result_template,
377 : uint_fast32_t *io_skip_results
378 : )
379 : {
380 0 : U8_TRACE_BEGIN();
381 0 : assert( result_template != NULL );
382 0 : u8_error_t d_err = U8_ERROR_NONE; /* a data read or data store error */
383 :
384 : /* initialize an iterator to fetch all diagrams where result_template occurs: */
385 : data_diagram_iterator_t diagram_iterator;
386 0 : data_diagram_iterator_init_empty( &diagram_iterator );
387 0 : const data_table_t object_type = data_id_get_table( data_search_result_get_match_id_const( result_template ));
388 0 : switch ( object_type )
389 : {
390 0 : case DATA_TABLE_CLASSIFIER:
391 : {
392 : const data_row_t classifier_row
393 0 : = data_id_get_row( data_search_result_get_match_id_const( result_template ));
394 0 : d_err |= data_database_reader_get_diagrams_by_classifier_id( (*this_).db_reader,
395 : classifier_row,
396 : &diagram_iterator
397 : );
398 : }
399 0 : break;
400 :
401 0 : case DATA_TABLE_FEATURE:
402 : {
403 : const data_row_t classifier_row
404 0 : = data_id_get_row( data_search_result_get_src_classifier_id_const( result_template ));
405 0 : d_err |= data_database_reader_get_diagrams_by_classifier_id( (*this_).db_reader,
406 : classifier_row,
407 : &diagram_iterator
408 : );
409 : }
410 0 : break;
411 :
412 0 : case DATA_TABLE_RELATIONSHIP:
413 : {
414 : const data_row_t relationship_row
415 0 : = data_id_get_row( data_search_result_get_match_id_const( result_template ));
416 0 : d_err |= data_database_reader_get_diagrams_by_relationship_id( (*this_).db_reader,
417 : relationship_row,
418 : &diagram_iterator
419 : );
420 : }
421 0 : break;
422 :
423 0 : default:
424 : {
425 0 : U8_LOG_ERROR_INT( "gui_search_runner_private_add_diagrams_of_object called with search result of wrong type:",
426 : object_type
427 : );
428 0 : assert(false);
429 : }
430 : break;
431 : }
432 :
433 0 : while (( data_diagram_iterator_has_next( &diagram_iterator ) )&&( d_err == U8_ERROR_NONE ))
434 : {
435 : /* fetch diagram from iterator */
436 0 : d_err |= data_diagram_iterator_next( &diagram_iterator, &((*this_).temp_diagram) );
437 0 : const data_row_t diagram_row = data_diagram_get_row( &((*this_).temp_diagram) );
438 :
439 0 : if ( (*io_skip_results) == 0 )
440 : {
441 : /* complete the half initialized search result template */
442 0 : data_id_reinit( data_search_result_get_diagram_id_ptr( result_template ), DATA_TABLE_DIAGRAM, diagram_row );
443 :
444 0 : const u8_error_t err = data_search_result_list_add( &((*this_).result_list), result_template );
445 0 : if ( err != U8_ERROR_NONE )
446 : {
447 0 : d_err |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
448 0 : U8_LOG_ANOMALY( "U8_ERROR_ARRAY_BUFFER_EXCEEDED at inserting search result to list" );
449 0 : (*this_).result_buffer_more_after = true; /* there are more results that cannot be stored in &((*this_).result_list) */
450 : }
451 : }
452 : else
453 : {
454 : /* to advance to the requested search result page, skip this entry */
455 0 : *io_skip_results = (*io_skip_results) - 1;
456 0 : (*this_).result_buffer_start ++;
457 : }
458 :
459 0 : data_diagram_destroy( &((*this_).temp_diagram) );
460 : }
461 :
462 0 : d_err |= data_diagram_iterator_destroy( &diagram_iterator );
463 :
464 0 : U8_TRACE_END_ERR( d_err );
465 0 : return d_err;
466 : }
467 :
468 :
469 : /*
470 : Copyright 2020-2026 Andreas Warnke
471 :
472 : Licensed under the Apache License, Version 2.0 (the "License");
473 : you may not use this file except in compliance with the License.
474 : You may obtain a copy of the License at
475 :
476 : http://www.apache.org/licenses/LICENSE-2.0
477 :
478 : Unless required by applicable law or agreed to in writing, software
479 : distributed under the License is distributed on an "AS IS" BASIS,
480 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
481 : See the License for the specific language governing permissions and
482 : limitations under the License.
483 : */
|