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