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