1 /* valapi.c
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information (NCBI)
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government do not place any restriction on its use or reproduction.
13 * We would, however, appreciate having the NCBI and the author cited in
14 * any work or product based on this material
15 *
16 * Although all reasonable efforts have been taken to ensure the accuracy
17 * and reliability of the software and data, the NLM and the U.S.
18 * Government do not and cannot warrant the performance or results that
19 * may be obtained by using this software or data. The NLM and the U.S.
20 * Government disclaim all warranties, express or implied, including
21 * warranties of performance, merchantability or fitness for any particular
22 * purpose.
23 *
24 * ===========================================================================
25 *
26 * File Name: valapi.c
27 *
28 * Author: Colleen Bollin
29 *
30 * Version Creation Date: 4/7/2009
31 *
32 * $Revision: 1.33 $
33 *
34 * File Description:
35 *
36 * Modifications:
37 * --------------------------------------------------------------------------
38 * Date Name Description of modification
39 * ------- ---------- -----------------------------------------------------
40 *
41 *
42 * ==========================================================================
43 */
44
45
46 /* TODO - create a superstructure for comment rules, so that they can be stored in a searchable list
47 * and the match expressions can be precompiled.
48 */
49
50 #include <asn.h>
51 #include <objfeat.h>
52 #include <subutil.h>
53 #include <objmgr.h>
54 #include <objfdef.h>
55 #include <gbftdef.h>
56 #include <sqnutils.h>
57 #include <edutil.h>
58 #include <gather.h>
59 #include <ffprint.h>
60 #include <asn2gnbi.h>
61 #include <findrepl.h>
62 #include <utilpub.h>
63 #include <regex.h>
64 #define NLM_GENERATED_CODE_PROTO
65 #include <objvalid.h>
66 #include <valapi.h>
67 #include "validrules.inc"
68
69 static CommentRulePtr CommentRules = NULL;
70
71
CompareFieldRules(FieldRulePtr r1,FieldRulePtr r2)72 static int CompareFieldRules (FieldRulePtr r1, FieldRulePtr r2)
73 {
74 int rval = 0;
75
76 if (r1 == NULL && r2 == NULL) {
77 rval = 0;
78 } else if (r1 == NULL) {
79 rval = -1;
80 } else if (r2 == NULL) {
81 rval = 1;
82 } else {
83 rval = StringCmp (r1->field_name, r2->field_name);
84 }
85 return rval;
86 }
87
88
SortVnpByFieldRule(VoidPtr ptr1,VoidPtr ptr2)89 NLM_EXTERN int LIBCALLBACK SortVnpByFieldRule (VoidPtr ptr1, VoidPtr ptr2)
90
91 {
92 ValNodePtr vnp1;
93 ValNodePtr vnp2;
94
95 if (ptr1 != NULL && ptr2 != NULL) {
96 vnp1 = *((ValNodePtr PNTR) ptr1);
97 vnp2 = *((ValNodePtr PNTR) ptr2);
98 if (vnp1 != NULL && vnp2 != NULL) {
99 return CompareFieldRules(vnp1->data.ptrvalue, vnp2->data.ptrvalue);
100 }
101 }
102 return 0;
103 }
104
105
SortFieldsInCommentRule(CommentRulePtr cr)106 static void SortFieldsInCommentRule (CommentRulePtr cr)
107 {
108 FieldRulePtr r, prev = NULL;
109 ValNodePtr list = NULL, vnp;
110
111 if (cr == NULL || cr->fields == NULL) {
112 //LCOV_EXCL_START
113 //not valid, not testable
114 return;
115 //LCOV_EXCL_STOP
116 }
117
118 for (r = cr->fields; r != NULL; r = r->next) {
119 ValNodeAddPointer (&list, 0, r);
120 }
121
122 list = ValNodeSort (list, SortVnpByFieldRule);
123
124 prev = list->data.ptrvalue;
125 cr->fields = prev;
126 for (vnp = list->next; vnp != NULL; vnp = vnp->next) {
127 prev->next = vnp->data.ptrvalue;
128 prev = prev->next;
129 }
130 prev->next = NULL;
131 list = ValNodeFree (list);
132 }
133
134
SortCommentRuleFields(CommentSetPtr cr_set)135 static void SortCommentRuleFields (CommentSetPtr cr_set)
136 {
137 while (cr_set != NULL) {
138 if (!cr_set->require_order) {
139 SortFieldsInCommentRule(cr_set);
140 }
141 cr_set = cr_set->next;
142 }
143 }
144
145
146 //LCOV_EXCL_START
147 //not testable in regression
LoadCommentRulesFromLocalString(void)148 static Boolean LoadCommentRulesFromLocalString (void)
149
150 {
151 #ifndef WIN16
152 AsnIoMemPtr aimp;
153 CharPtr ptr;
154
155 ptr = MergeStringArray ((CharPtr PNTR) s_Defaultvalidrules, sizeof (s_Defaultvalidrules) / sizeof (char*));
156 if (ptr == NULL) return FALSE;
157
158 aimp = AsnIoMemOpen ("r", (BytePtr) ptr, (Int4) StringLen (ptr));
159 if (aimp == NULL || aimp->aip == NULL) return FALSE;
160
161 CommentRules = CommentSetAsnRead (aimp->aip, NULL);
162 SortCommentRuleFields(CommentRules);
163 AsnIoMemClose (aimp);
164 MemFree (ptr);
165 #endif
166 return (Boolean) (CommentRules != NULL);
167 }
168 //LCOV_EXCL_STOP
169
170
LoadCommentRuleSet(void)171 NLM_EXTERN CommentRulePtr LoadCommentRuleSet (void)
172 {
173 Char buf[PATH_MAX];
174 AsnIoPtr aip;
175
176 if (CommentRules != NULL)
177 return CommentRules;
178
179 if (! FindPath("ncbi", "ncbi", "data", buf, sizeof (buf)))
180 {
181 //LCOV_EXCL_START
182 //not testable in regression
183 if (LoadCommentRulesFromLocalString ()) {
184 return CommentRules;
185 }
186
187 ErrPostEx(SEV_WARNING, 0, 0, "FindPath failed in LoadCommentRuleSet - ncbi configuration file missing or incorrect");
188 return CommentRules;
189 //LCOV_EXCL_STOP
190 }
191
192 StringCat(buf, "validrules.prt");
193 if ((aip = AsnIoOpen(buf, "r")) == NULL)
194 {
195 //LCOV_EXCL_START
196 //not testable in regression
197 if (LoadCommentRulesFromLocalString ()) {
198 return CommentRules;
199 }
200
201 ErrPostEx(SEV_WARNING, 0, 0, "Couldn't open [%s]", buf);
202 return CommentRules;
203 //LCOV_EXCL_STOP
204 }
205
206 CommentRules = CommentSetAsnRead(aip, NULL);
207 AsnIoClose(aip);
208 SortCommentRuleFields (CommentRules);
209 return CommentRules;
210 }
211
212
GetCommentRuleFromRuleSet(CharPtr prefix)213 NLM_EXTERN CommentRulePtr GetCommentRuleFromRuleSet (CharPtr prefix)
214 {
215 CommentRulePtr cr;
216 Boolean found = FALSE;
217
218 if (CommentRules == NULL) {
219 cr = LoadCommentRuleSet ();
220 } else {
221 cr = CommentRules;
222 }
223
224 while (cr != NULL && !found) {
225 if (StringICmp (cr->prefix, prefix) == 0) {
226 found = TRUE;
227 } else {
228 cr = cr->next;
229 }
230 }
231 return cr;
232 }
233
234
DoesFieldNameMatchRuleName(UserFieldPtr ufp,CharPtr rule_field_name)235 static Boolean DoesFieldNameMatchRuleName (UserFieldPtr ufp, CharPtr rule_field_name)
236 {
237 Char buf[15];
238 Boolean rval = FALSE;
239
240 if (ufp == NULL) {
241 return FALSE;
242 }
243
244 /* Does this match the rule field name? */
245 if (ufp->label == NULL) {
246 rval = FALSE;
247 } else if (ufp->label->id > 0) {
248 sprintf (buf, "%d", ufp->label->id);
249 if (StringCmp (rule_field_name, buf) == 0) {
250 rval = TRUE;
251 }
252 } else if (StringCmp (rule_field_name, ufp->label->str) == 0) {
253 rval = TRUE;
254 }
255 return rval;
256 }
257
258
DoesStringMatchExpression(CharPtr str,CharPtr expression)259 static Boolean DoesStringMatchExpression (CharPtr str, CharPtr expression)
260 {
261 Boolean rval;
262 regex_t buffer;
263 const char *errstr;
264 Int4 len;
265
266 MemSet (&buffer, 0, sizeof (regex_t));
267 errstr = re_compile_pattern (expression, StringLen (expression), &buffer);
268
269 len = StringLen (str);
270 if (re_match (&buffer, str, len, 0, NULL) > -1) {
271 rval = TRUE;
272 } else {
273 rval = FALSE;
274 }
275
276 return rval;
277 }
278
279
DoesFieldValueMatchExpression(UserFieldPtr ufp,CharPtr match_expression)280 static Boolean DoesFieldValueMatchExpression (UserFieldPtr ufp, CharPtr match_expression)
281 {
282 Char buf[15];
283 Boolean rval = FALSE;
284
285 if (ufp->choice == 1) {
286 /* compare string value */
287 if (DoesStringMatchExpression(ufp->data.ptrvalue, match_expression)) {
288 rval = TRUE;
289 }
290 } else if (ufp->choice == 2) {
291 /* compare int value */
292 sprintf (buf, "%d", ufp->data.intvalue);
293 if (DoesStringMatchExpression(buf, match_expression)) {
294 rval = TRUE;
295 }
296 } else {
297 /* for now, only allowing matches for int or string fields */
298 rval = FALSE;
299 }
300 return rval;
301 }
302
303
DoesFieldValueMatchRule(UserFieldPtr ufp,FieldRulePtr rule)304 static Boolean DoesFieldValueMatchRule (UserFieldPtr ufp, FieldRulePtr rule)
305 {
306 Boolean rval = FALSE;
307
308 if (ufp == NULL || rule == NULL) {
309 return FALSE;
310 }
311 /* now validate field value */
312 if (rule->match_expression == NULL) {
313 rval = TRUE;
314 } else {
315 rval = DoesFieldValueMatchExpression (ufp, rule->match_expression);
316 }
317 return rval;
318 }
319
320
DoesStructuredCommentHavePrefix(UserObjectPtr uop,CharPtr prefix)321 static Boolean DoesStructuredCommentHavePrefix (UserObjectPtr uop, CharPtr prefix)
322 {
323 UserFieldPtr ufp;
324 Int4 prefix_len;
325 Boolean rval = FALSE;
326
327 if (uop == NULL || uop->type == NULL || StringICmp (uop->type->str, "StructuredComment") != 0) {
328 return FALSE;
329 } else if (StringHasNoText (prefix)) {
330 return TRUE;
331 }
332
333 prefix_len = StringLen (prefix);
334
335 for (ufp = uop->data; ufp != NULL && !rval; ufp = ufp->next) {
336 if (ufp->label != NULL
337 && (StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0
338 || StringICmp (ufp->label->str, "StructuredCommentSuffix") == 0)
339 && ufp->choice == 1
340 && StringNCmp (ufp->data.ptrvalue, prefix, prefix_len) == 0) {
341 rval = TRUE;
342 }
343 }
344 return rval;
345 }
346
347
FindRuleForFieldName(UserFieldPtr ufp,FieldRulePtr field_rule)348 static FieldRulePtr FindRuleForFieldName (UserFieldPtr ufp, FieldRulePtr field_rule)
349 {
350 if (ufp == NULL) {
351 return NULL;
352 }
353
354 while (field_rule != NULL && ! DoesFieldNameMatchRuleName(ufp, field_rule->field_name)) {
355 field_rule = field_rule->next;
356 }
357 return field_rule;
358 }
359
360
FindFieldForRuleName(UserFieldPtr ufp,CharPtr field_rule)361 static UserFieldPtr FindFieldForRuleName (UserFieldPtr ufp, CharPtr field_rule)
362 {
363 if (StringHasNoText (field_rule)) {
364 return NULL;
365 }
366
367 while (ufp != NULL && !DoesFieldNameMatchRuleName(ufp, field_rule)) {
368 ufp = ufp->next;
369 }
370 return ufp;
371 }
372
373
VACompareUserFields(UserFieldPtr ufp1,UserFieldPtr ufp2)374 static int VACompareUserFields (UserFieldPtr ufp1, UserFieldPtr ufp2)
375 {
376 int rval = 0;
377 CharPtr cp1, cp2;
378 Char buf1[15];
379 Char buf2[15];
380
381 if (ufp1 == NULL && ufp2 == NULL) {
382 rval = 0;
383 } else if (ufp1 == NULL) {
384 rval = -1;
385 } else if (ufp2 == NULL) {
386 rval = 1;
387 } else if (ufp1->label == NULL) {
388 rval = -1;
389 } else if (ufp2->label == NULL) {
390 rval = 1;
391 } else if (ufp1->label->id > 0 && ufp2->label->id > 0) {
392 if (ufp1->label->id > ufp2->label->id) {
393 rval = 1;
394 } else if (ufp1->label->id < ufp2->label->id) {
395 rval = -1;
396 } else {
397 rval = 0;
398 }
399 } else {
400 if (ufp1->label->id > 0) {
401 sprintf (buf1, "%d", ufp1->label->id);
402 cp1 = buf1;
403 } else {
404 cp1 = ufp1->label->str;
405 }
406 if (ufp2->label->id > 0) {
407 sprintf (buf2, "%d", ufp2->label->id);
408 cp2 = buf2;
409 } else {
410 cp2 = ufp2->label->str;
411 }
412 rval = StringCmp (cp1, cp2);
413 }
414 return rval;
415 }
416
417
SortVnpByUserField(VoidPtr ptr1,VoidPtr ptr2)418 NLM_EXTERN int LIBCALLBACK SortVnpByUserField (VoidPtr ptr1, VoidPtr ptr2)
419
420 {
421 ValNodePtr vnp1;
422 ValNodePtr vnp2;
423
424 if (ptr1 != NULL && ptr2 != NULL) {
425 vnp1 = *((ValNodePtr PNTR) ptr1);
426 vnp2 = *((ValNodePtr PNTR) ptr2);
427 if (vnp1 != NULL && vnp2 != NULL) {
428 return VACompareUserFields(vnp1->data.ptrvalue, vnp2->data.ptrvalue);
429 }
430 }
431 return 0;
432 }
433
434
SortFieldsInUserObject(UserObjectPtr uop)435 static void SortFieldsInUserObject (UserObjectPtr uop)
436 {
437 UserFieldPtr ufp, prev = NULL;
438 ValNodePtr list = NULL, vnp;
439
440 if (uop == NULL || uop->data == NULL) {
441 //LCOV_EXCL_START
442 //invalid
443 return;
444 //LCOV_EXCL_STOP
445 }
446
447 for (ufp = uop->data; ufp != NULL; ufp = ufp->next) {
448 ValNodeAddPointer (&list, 0, ufp);
449 }
450
451 list = ValNodeSort (list, SortVnpByUserField);
452
453 prev = list->data.ptrvalue;
454 uop->data = prev;
455 for (vnp = list->next; vnp != NULL; vnp = vnp->next) {
456 prev->next = vnp->data.ptrvalue;
457 prev = prev->next;
458 }
459 prev->next = NULL;
460 list = ValNodeFree (list);
461 }
462
PrefixOrSuffixInList(CharPtr val,CharPtr before,CharPtr after)463 static Boolean PrefixOrSuffixInList (CharPtr val, CharPtr before, CharPtr after)
464
465 {
466 Char buf [1024];
467 size_t len, l_before, l_after;
468 ValNodePtr list, vnp;
469 Boolean rsult = FALSE;
470 CharPtr str;
471
472 if (val == NULL) return FALSE;
473 len = StringLen (val);
474 if (len < 10) return FALSE;
475 l_before = StringLen (before);
476 l_after = StringLen (after);
477 if (StringNCmp (val, before, l_before) != 0) return FALSE;
478 if (StringNCmp (val + len - l_after, after, l_after) != 0) return FALSE;
479
480 if (len > sizeof (buf)) return FALSE;
481 StringNCpy_0 (buf, val + l_before, sizeof (buf));
482 buf [len - l_before - l_after] = '\0';
483
484 list = GetStructuredCommentPrefixList ();
485 if (list == NULL) return FALSE;
486
487 for (vnp = list; vnp != NULL; vnp = vnp->next) {
488 str = (CharPtr) vnp->data.ptrvalue;
489 if (StringHasNoText (str)) continue;
490 if (StringCmp (buf, str) == 0) rsult = TRUE;
491 }
492
493 ValNodeFreeData (list);
494
495 return rsult;
496 }
497
498
FindForbiddenPhrases(UserObjectPtr uop,CommentRulePtr comment_rule,StructuredCommentCallback s_callback,Pointer s_callback_data)499 static EFieldValid FindForbiddenPhrases
500 (UserObjectPtr uop,
501 CommentRulePtr comment_rule,
502 StructuredCommentCallback s_callback,
503 Pointer s_callback_data)
504 {
505 UserFieldPtr ufp;
506 ValNodePtr vnp;
507 EFieldValid rval = eFieldValid_Valid;
508
509 if (uop == NULL || comment_rule == NULL || comment_rule->forbidden_phrases == NULL) {
510 return eFieldValid_Valid;
511 }
512
513 /* examine fields for forbidden phrases */
514 for (ufp = uop->data; ufp != NULL; ufp = ufp->next) {
515 if (ufp->label != NULL
516 && (StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0
517 || StringICmp (ufp->label->str, "StructuredCommentSuffix") == 0)) {
518 /* skip suffix and prefix */
519 continue;
520 } else {
521 if (ufp->choice == 1) {
522 /* compare string value */
523 for (vnp = comment_rule->forbidden_phrases; vnp != NULL; vnp = vnp->next) {
524 if (StringISearch(ufp->data.ptrvalue, vnp->data.ptrvalue) != NULL) {
525 rval = eFieldValid_Inappropriate;
526 if (s_callback == NULL) {
527 break;
528 } else {
529 s_callback (eFieldValid_Inappropriate, NULL, ufp, NULL, s_callback_data, uop);
530 }
531 }
532 }
533 }
534 }
535 }
536 return rval;
537 }
538
539
AreStructuredCommentContentsValidForRule(UserObjectPtr uop,CommentRulePtr comment_rule,StructuredCommentCallback s_callback,Pointer s_callback_data)540 static EFieldValid AreStructuredCommentContentsValidForRule
541 (UserObjectPtr uop,
542 CommentRulePtr comment_rule,
543 StructuredCommentCallback s_callback,
544 Pointer s_callback_data)
545 {
546 UserFieldPtr ufp, ufp_tmp, depend_ufp;
547 FieldRulePtr field_rule, rule_tmp;
548 DependentFieldRulePtr depend_rule;
549 EFieldValid rval = eFieldValid_Valid, tmp_val;
550 Boolean free_uop = FALSE;
551
552 if (uop == NULL || uop->type == NULL || StringICmp (uop->type->str, "StructuredComment") != 0) {
553 return eFieldValid_Invalid;
554 }
555
556 if (!comment_rule->require_order) {
557 free_uop = TRUE;
558 uop = (UserObjectPtr) AsnIoMemCopy (uop, (AsnReadFunc) UserObjectAsnRead, (AsnWriteFunc) UserObjectAsnWrite);
559 SortFieldsInUserObject(uop);
560 }
561
562 /* now check individual fields */
563 ufp = uop->data;
564 field_rule = comment_rule->fields;
565
566 while (field_rule != NULL && ufp != NULL) {
567 if (ufp->label != NULL
568 && (StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0
569 || StringICmp (ufp->label->str, "StructuredCommentSuffix") == 0)) {
570 /* skip suffix and prefix */
571 ufp = ufp->next;
572 } else if (DoesFieldNameMatchRuleName (ufp, field_rule->field_name)) {
573 if (DoesFieldValueMatchRule (ufp, field_rule)) {
574 /* all good */
575 } else {
576 if (s_callback == NULL) {
577 rval = eFieldValid_Invalid;
578 goto IsStructuredCommentValidForRule_exit;
579 } else {
580 if (rval == eFieldValid_Valid) {
581 rval = eFieldValid_Invalid;
582 }
583 s_callback (eFieldValid_Invalid, field_rule, ufp, NULL, s_callback_data, uop);
584 }
585 }
586 ufp = ufp->next;
587 field_rule = field_rule->next;
588 } else if ((rule_tmp = FindRuleForFieldName(ufp, comment_rule->fields)) == NULL) {
589 if (!comment_rule->allow_unlisted) {
590 if (s_callback == NULL) {
591 rval = eFieldValid_Invalid;
592 goto IsStructuredCommentValidForRule_exit;
593 } else {
594 if (rval == eFieldValid_Valid) {
595 rval = eFieldValid_Invalid;
596 }
597 s_callback (eFieldValid_Invalid, NULL, ufp, NULL, s_callback_data, uop);
598 }
599 }
600 ufp = ufp->next;
601 } else {
602 ufp_tmp = FindFieldForRuleName (ufp, field_rule->field_name);
603 if (ufp_tmp == NULL) {
604 if (field_rule->required) {
605 if (s_callback == NULL) {
606 rval = eFieldValid_MissingRequiredField;
607 goto IsStructuredCommentValidForRule_exit;
608 } else {
609 if (rval == eFieldValid_Valid) {
610 rval = eFieldValid_MissingRequiredField;
611 }
612 s_callback (eFieldValid_MissingRequiredField, field_rule, NULL, NULL, s_callback_data, uop);
613 }
614 } else {
615 /* field wasn't required, it's ok */
616 }
617 } else if (comment_rule->require_order) {
618 /* field is out of order */
619 if (s_callback == NULL) {
620 rval = eFieldValid_FieldOutOfOrder;
621 goto IsStructuredCommentValidForRule_exit;
622 } else {
623 if (rval == eFieldValid_Valid) {
624 rval = eFieldValid_FieldOutOfOrder;
625 }
626 s_callback (eFieldValid_FieldOutOfOrder, field_rule, ufp_tmp, NULL, s_callback_data, uop);
627 if (!DoesFieldValueMatchRule (ufp_tmp, field_rule)) {
628 s_callback (eFieldValid_Invalid, field_rule, ufp_tmp, NULL, s_callback_data, uop);
629 }
630 }
631 } else {
632 if (!DoesFieldValueMatchRule(ufp_tmp, field_rule)) {
633 if (s_callback == NULL) {
634 rval = eFieldValid_Invalid;
635 goto IsStructuredCommentValidForRule_exit;
636 } else {
637 s_callback(eFieldValid_Invalid, field_rule, ufp_tmp, NULL, s_callback_data, uop);
638 }
639 }
640 }
641 field_rule = field_rule->next;
642 }
643 }
644
645 /* validate remaining rules */
646 while (field_rule != NULL) {
647 if (field_rule->required) {
648 if (s_callback == NULL) {
649 rval = eFieldValid_MissingRequiredField;
650 goto IsStructuredCommentValidForRule_exit;
651 } else {
652 if (rval == eFieldValid_Valid) {
653 rval = eFieldValid_MissingRequiredField;
654 }
655 s_callback (eFieldValid_MissingRequiredField, field_rule, NULL, NULL, s_callback_data, uop);
656 }
657 }
658 field_rule = field_rule->next;
659 }
660
661 while (ufp != NULL) {
662 if (ufp->label != NULL
663 && (StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0
664 || StringICmp (ufp->label->str, "StructuredCommentSuffix") == 0)) {
665 /* skip suffix and prefix */
666 ufp = ufp->next;
667 continue;
668 }
669 field_rule = FindRuleForFieldName (ufp, comment_rule->fields);
670 if (field_rule == NULL && !comment_rule->allow_unlisted) {
671 if (s_callback == NULL) {
672 rval = eFieldValid_Invalid;
673 goto IsStructuredCommentValidForRule_exit;
674 } else {
675 if (rval == eFieldValid_Valid) {
676 rval = eFieldValid_Invalid;
677 }
678 s_callback (eFieldValid_Invalid, NULL, ufp, NULL, s_callback_data, uop);
679 }
680 } else if (field_rule != NULL && FindFieldForRuleName(uop->data, field_rule->field_name) != ufp) {
681 if (s_callback == NULL) {
682 rval = eFieldValid_DuplicateField;
683 goto IsStructuredCommentValidForRule_exit;
684 } else {
685 if (rval == eFieldValid_Valid) {
686 rval = eFieldValid_DuplicateField;
687 }
688 s_callback (eFieldValid_DuplicateField, field_rule, ufp, NULL, s_callback_data, uop);
689 }
690 }
691 ufp = ufp->next;
692 }
693
694 /* now look at fields in context of other fields */
695 for (depend_rule = comment_rule->dependent_rules; depend_rule != NULL; depend_rule = depend_rule->next) {
696 depend_ufp = FindFieldForRuleName(uop->data, depend_rule->match_name);
697 if (depend_ufp != NULL &&
698 ((!depend_rule->invert_match && DoesFieldValueMatchExpression(depend_ufp, depend_rule->value_constraint))
699 || (depend_rule->invert_match && !DoesFieldValueMatchExpression(depend_ufp, depend_rule->value_constraint)))) {
700 for (field_rule = depend_rule->other_fields; field_rule != NULL; field_rule = field_rule->next) {
701 ufp = FindFieldForRuleName (uop->data, field_rule->field_name);
702 if (ufp == NULL) {
703 if (s_callback == NULL) {
704 rval = eFieldValid_MissingRequiredField;
705 goto IsStructuredCommentValidForRule_exit;
706 } else {
707 if (rval == eFieldValid_Valid) {
708 rval = eFieldValid_MissingRequiredField;
709 }
710 s_callback (eFieldValid_MissingRequiredField, field_rule, ufp, depend_ufp, s_callback_data, uop);
711 }
712 } else if (!DoesFieldValueMatchRule (ufp, field_rule)) {
713 if (s_callback == NULL) {
714 rval = eFieldValid_Invalid;
715 goto IsStructuredCommentValidForRule_exit;
716 } else {
717 if (rval == eFieldValid_Valid) {
718 rval = eFieldValid_MissingRequiredField;
719 }
720 s_callback (eFieldValid_Invalid, field_rule, ufp, depend_ufp, s_callback_data, uop);
721 }
722 }
723 }
724 for (field_rule = depend_rule->disallowed_fields; field_rule != NULL; field_rule = field_rule->next) {
725 //LCOV_EXCL_START
726 //no rules currently have disallowed fields
727 ufp = FindFieldForRuleName (uop->data, field_rule->field_name);
728 if (ufp != NULL && DoesFieldValueMatchRule (ufp, field_rule)) {
729 if (s_callback == NULL) {
730 rval = eFieldValid_Disallowed;
731 goto IsStructuredCommentValidForRule_exit;
732 } else {
733 if (rval == eFieldValid_Valid) {
734 rval = eFieldValid_Disallowed;
735 }
736 s_callback (eFieldValid_Disallowed, field_rule, ufp, depend_ufp, s_callback_data, uop);
737 }
738 }
739 //LCOV_EXCL_STOP
740 }
741 }
742 }
743
744 tmp_val = FindForbiddenPhrases(uop, comment_rule, s_callback, s_callback_data);
745 if (rval == eFieldValid_Valid) {
746 rval = tmp_val;
747 }
748
749 IsStructuredCommentValidForRule_exit:
750 if (free_uop) {
751 uop = UserObjectFree (uop);
752 }
753 return rval;
754 }
755
756
757 NLM_EXTERN EFieldValid
IsStructuredCommentValidForRule(UserObjectPtr uop,CommentRulePtr comment_rule,StructuredCommentCallback s_callback,Pointer s_callback_data)758 IsStructuredCommentValidForRule
759 (UserObjectPtr uop,
760 CommentRulePtr comment_rule,
761 StructuredCommentCallback s_callback,
762 Pointer s_callback_data)
763 {
764 UserFieldPtr ufp;
765 EFieldValid rval = eFieldValid_Valid;
766
767 if (uop == NULL || uop->type == NULL || StringICmp (uop->type->str, "StructuredComment") != 0) {
768 return eFieldValid_Invalid;
769 }
770
771 for (ufp = uop->data; ufp != NULL; ufp = ufp->next) {
772 if (ufp->label != NULL && StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0) {
773 /* check prefix */
774 if (! PrefixOrSuffixInList ((CharPtr) ufp->data.ptrvalue, "##", "-START##")) {
775 if (s_callback == NULL) {
776 return eFieldValid_Invalid;
777 } else {
778 if (rval == eFieldValid_Valid) {
779 rval = eFieldValid_Invalid;
780 }
781 s_callback (eFieldValid_Invalid, NULL, ufp, NULL, s_callback_data, uop);
782 }
783 }
784 } else if (ufp->label != NULL && StringICmp (ufp->label->str, "StructuredCommentSuffix") == 0) {
785 /* check suffix */
786 if (! PrefixOrSuffixInList ((CharPtr) ufp->data.ptrvalue, "##", "-END##")) {
787 if (s_callback == NULL) {
788 return eFieldValid_Invalid;
789 } else {
790 if (rval == eFieldValid_Valid) {
791 rval = eFieldValid_Invalid;
792 }
793 s_callback (eFieldValid_Invalid, NULL, ufp, NULL, s_callback_data, uop);
794 }
795 }
796 }
797 }
798
799 if (comment_rule == NULL) {
800 return rval;
801 }
802
803 /* first, make sure comment rule prefix matches comment */
804 if (!DoesStructuredCommentHavePrefix (uop, comment_rule->prefix)) {
805 return eFieldValid_Invalid;
806 }
807
808 return AreStructuredCommentContentsValidForRule (uop, comment_rule, s_callback, s_callback_data);
809 }
810
811
IsStructuredCommentValid(UserObjectPtr uop,StructuredCommentCallback s_callback,Pointer s_callback_data)812 NLM_EXTERN EFieldValid IsStructuredCommentValid (UserObjectPtr uop, StructuredCommentCallback s_callback, Pointer s_callback_data)
813 {
814 CommentRulePtr cr;
815 UserFieldPtr ufp;
816 CharPtr prefix = NULL;
817
818 for (ufp = uop->data; ufp != NULL && prefix == NULL; ufp = ufp->next) {
819 if (ufp->label != NULL
820 && StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0
821 && ufp->choice == 1) {
822 prefix = ufp->data.ptrvalue;
823 }
824 }
825
826 if (prefix == NULL) {
827 return TRUE;
828 }
829 cr = GetCommentRuleFromRuleSet (prefix);
830 return IsStructuredCommentValidForRule (uop, cr, s_callback, s_callback_data);
831 }
832
833
MovePrefixAndSuffixFieldsToFlank(UserObjectPtr uop)834 static Boolean MovePrefixAndSuffixFieldsToFlank (UserObjectPtr uop)
835 {
836 UserFieldPtr ufp, ufp_prev = NULL, ufp_next, ufp_last = NULL;
837 Boolean changed = FALSE;
838
839 if (uop == NULL) {
840 return FALSE;
841 }
842
843 for (ufp = uop->data; ufp != NULL; ufp = ufp_next) {
844 ufp_next = ufp->next;
845 if (IsStructuredCommentPrefix(ufp)) {
846 /* move prefix to first position */
847 if (ufp_prev != NULL) {
848 ufp_prev->next = ufp_next;
849 ufp->next = uop->data;
850 uop->data = ufp;
851 changed = TRUE;
852 } else {
853 ufp_prev = ufp;
854 }
855 } else if (IsStructuredCommentSuffix(ufp) && ufp->next != NULL) {
856 changed = TRUE;
857 ufp_last = ufp;
858 if (ufp_prev == NULL) {
859 uop->data = ufp_next;
860 } else {
861 ufp_prev->next = ufp_next;
862 }
863 ufp->next = NULL;
864 } else {
865 ufp_prev = ufp;
866 }
867 }
868 if (ufp_last != NULL) {
869 if (ufp_prev == NULL) {
870 uop->data = ufp_last;
871 } else {
872 ufp_prev->next = ufp_last;
873 }
874 }
875 return changed;
876 }
877
878
879 //LCOV_EXCL_START
880 //not used for validation
ReorderStructuredCommentFields(UserObjectPtr uop)881 NLM_EXTERN Boolean ReorderStructuredCommentFields (UserObjectPtr uop)
882 {
883 CommentRulePtr cr;
884 FieldRulePtr rule;
885 UserFieldPtr ufp, ufp_prev = NULL, new_list = NULL, new_prev = NULL;
886 CharPtr prefix = NULL;
887 UserObjectPtr uop_orig;
888 Boolean changed = FALSE;
889
890 if (uop == NULL || uop->type == NULL || StringICmp (uop->type->str, "StructuredComment") != 0) return FALSE;
891
892 uop_orig = AsnIoMemCopy (uop, (AsnReadFunc) UserObjectAsnRead, (AsnWriteFunc) UserObjectAsnWrite);
893 prefix = GetStructuredCommentPrefix (uop);
894 if (prefix != NULL
895 && (cr = GetCommentRuleFromRuleSet (prefix)) != NULL) {
896 ufp = uop->data;
897 for (rule = cr->fields; rule != NULL; rule = rule->next) {
898 ufp_prev = NULL;
899 for (ufp = uop->data; ufp != NULL && StringCmp (ufp->label->str, rule->field_name) != 0; ufp = ufp->next) {
900 ufp_prev = ufp;
901 }
902 if (ufp != NULL) {
903 if (ufp_prev == NULL) {
904 uop->data = ufp->next;
905 } else {
906 ufp_prev->next = ufp->next;
907 }
908 ufp->next = NULL;
909 if (new_prev == NULL) {
910 new_list = ufp;
911 } else {
912 new_prev->next = ufp;
913 }
914 new_prev = ufp;
915 }
916 }
917 if (new_prev != NULL) {
918 new_prev->next = uop->data;
919 uop->data = new_list;
920 }
921 changed = TRUE;
922 }
923 changed |= MovePrefixAndSuffixFieldsToFlank (uop);
924
925 changed = !AsnIoMemComp (uop, uop_orig, (AsnWriteFunc) UserObjectAsnWrite);
926 uop_orig = UserObjectFree (uop_orig);
927
928 return changed;
929 }
930
931 //not used for validation
ReorderStructuredCommentFieldsCallback(SeqDescrPtr sdp,Pointer userdata)932 static void ReorderStructuredCommentFieldsCallback (SeqDescrPtr sdp, Pointer userdata)
933 {
934 BoolPtr r = (BoolPtr) userdata;
935 if (sdp->choice == Seq_descr_user) {
936 if (ReorderStructuredCommentFields ((UserObjectPtr) sdp->data.ptrvalue)) {
937 if (r != NULL) {
938 *r = TRUE;
939 }
940 }
941 }
942 }
943
944
945 //not used for validation
ReorderStructuredCommentsInSeqEntry(SeqEntryPtr sep)946 NLM_EXTERN Boolean ReorderStructuredCommentsInSeqEntry (SeqEntryPtr sep)
947 {
948 Boolean rval = FALSE;
949
950 VisitDescriptorsInSep (sep, &rval, ReorderStructuredCommentFieldsCallback);
951 return rval;
952 }
953
954
955 //not used for validation
DoesStructuredCommentHaveAnyPrefixOrSuffix(UserObjectPtr uop)956 static Boolean DoesStructuredCommentHaveAnyPrefixOrSuffix (UserObjectPtr uop)
957 {
958 UserFieldPtr ufp;
959 Boolean rval = FALSE;
960
961 if (uop == NULL || uop->type == NULL || StringICmp (uop->type->str, "StructuredComment") != 0) {
962 return FALSE;
963 }
964
965 for (ufp = uop->data; ufp != NULL && !rval; ufp = ufp->next) {
966 if (ufp->label != NULL
967 && (StringICmp (ufp->label->str, "StructuredCommentPrefix") == 0
968 || StringICmp (ufp->label->str, "StructuredCommentSuffix") == 0)) {
969 rval = TRUE;
970 }
971 }
972 return rval;
973 }
974
975
976 //LCOV_EXCL_START
977 //not used in validation
IsRuleOkForStructuredComment(UserObjectPtr uop,CommentRulePtr cr)978 static Boolean IsRuleOkForStructuredComment (UserObjectPtr uop, CommentRulePtr cr)
979 {
980 UserFieldPtr ufp;
981 FieldRulePtr field_rule;
982 Boolean rval = FALSE;
983 CommentRulePtr cr_tmp = NULL;
984
985 if (uop == NULL || uop->data == NULL || cr == NULL) {
986 return FALSE;
987 }
988 ufp = uop->data;
989
990 /* all field names must be recognized */
991 while (ufp != NULL) {
992 if (!IsStructuredCommentPrefix(ufp) && !IsStructuredCommentSuffix(ufp)) {
993 /* ignore prefix and suffix if present */
994 field_rule = FindRuleForFieldName(ufp, cr->fields);
995 if (field_rule == NULL) {
996 return FALSE;
997 }
998 }
999 ufp = ufp->next;
1000 }
1001
1002 /* don't be picky about order when assigning prefix */
1003 if (cr->require_order) {
1004 cr_tmp = (CommentRulePtr) AsnIoMemCopy (cr, (AsnReadFunc) CommentRuleAsnRead, (AsnWriteFunc) CommentRuleAsnWrite);
1005 cr_tmp->require_order = FALSE;
1006 cr = cr_tmp;
1007 }
1008 if (AreStructuredCommentContentsValidForRule (uop, cr, NULL, NULL) == eFieldValid_Valid) {
1009 rval = TRUE;
1010 }
1011 cr_tmp = CommentRuleFree (cr_tmp);
1012 return rval;
1013 }
1014
1015
1016 //not used for validation
AutoapplyStructuredCommentPrefix(UserObjectPtr uop)1017 NLM_EXTERN CharPtr AutoapplyStructuredCommentPrefix (UserObjectPtr uop)
1018 {
1019 CommentRulePtr cr;
1020 CharPtr prefix = NULL;
1021
1022 if (uop == NULL || DoesStructuredCommentHaveAnyPrefixOrSuffix(uop)) {
1023 return NULL;
1024 }
1025
1026 if (CommentRules == NULL) {
1027 cr = LoadCommentRuleSet ();
1028 } else {
1029 cr = CommentRules;
1030 }
1031
1032 while (cr != NULL) {
1033 if (IsRuleOkForStructuredComment(uop, cr)) {
1034 if (prefix == NULL) {
1035 prefix = cr->prefix;
1036 } else {
1037 return NULL;
1038 }
1039 }
1040 cr = cr->next;
1041 }
1042
1043 if (prefix != NULL) {
1044 AddItemStructuredCommentUserObject (uop, "StructuredCommentPrefix", prefix);
1045 }
1046 return prefix;
1047 }
1048
1049
1050 //not used for validation
NewRuleForStructuredComment(UserObjectPtr uop)1051 NLM_EXTERN CommentRulePtr NewRuleForStructuredComment (UserObjectPtr uop)
1052 {
1053 CommentRulePtr cr;
1054 CommentRulePtr new_cr = NULL;
1055
1056 if (uop == NULL || uop->type == NULL
1057 || StringICmp (uop->type->str, "StructuredComment") != 0
1058 || !DoesStructuredCommentHaveAnyPrefixOrSuffix (uop)
1059 || IsStructuredCommentValid (uop, NULL, NULL) == eFieldValid_Valid) {
1060 return NULL;
1061 }
1062
1063 if (CommentRules == NULL) {
1064 cr = LoadCommentRuleSet ();
1065 } else {
1066 cr = CommentRules;
1067 }
1068
1069 while (cr != NULL) {
1070 if (IsRuleOkForStructuredComment(uop, cr)) {
1071 if (new_cr == NULL) {
1072 new_cr = cr;
1073 } else {
1074 return NULL;
1075 }
1076 }
1077 cr = cr->next;
1078 }
1079
1080 return new_cr;
1081 }
1082 //LCOV_EXCL_STOP
1083
1084