NCBI C++ ToolKit
macro_fn_aecr.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: macro_fn_aecr.cpp 47378 2023-02-27 20:09:44Z asztalos $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
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 have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Andrea Asztalos
27  *
28  */
29 
30 #include <ncbi_pch.hpp>
36 #include <objects/seq/Seqdesc.hpp>
37 
42 #include <util/xregexp/regexp.hpp>
43 #include <objmgr/util/sequence.hpp>
44 
47 
53 
59 
60 /** @addtogroup GUI_MACRO_SCRIPTS_UTIL
61  *
62  * @{
63  */
64 
66 BEGIN_SCOPE(macro)
68 
69 // All DO functions should make changes on the "Edited" object of the BioDataIterator
70 
71 /// class CMacroFunction_SwapRelatedFeaturesQual_Depr
72 /// SwapRelateFeaturesQual(src_field, dest_feat_subtype, dest_field);
73 ///
74 DEFINE_MACRO_FUNCNAME(CMacroFunction_SwapRelatedFeaturesQual_Depr, "SwapRelateFeaturesQual")
75 void CMacroFunction_SwapRelatedFeaturesQual_Depr::TheFunction()
76 {
77  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
78  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
79  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
80  if (!src_feat || !scope)
81  return;
82 
83  CObjectInfo oi = m_DataIter->GetEditedObject();
84 
85  CMQueryNodeValue::TObs src_objs;
86  CMQueryNodeValue::EType src_type = m_Args[0]->GetDataType();
87 
88  // the source field has to be set
89  if (src_type == CMQueryNodeValue::eString) {
90  if (!GetFieldsByName(&src_objs, oi, m_Args[0]->GetString()))
91  return;
92  }
93  else if (src_type == CMQueryNodeValue::eObjects) {
94  src_objs = m_Args[0]->GetObjects();
95  }
96  else if (src_type == CMQueryNodeValue::eRef) {
97  x_GetObjectsFromRef(src_objs, 0);
98  }
99 
100  if (src_objs.empty()) {
101  return;
102  }
103 
104  // the destination field also has to be set and
105  // should be a field from the corresponding feature
106  CSeqFeatData::ESubtype related_feature = NMacroUtil::GetFeatSubtype(m_Args[1]->GetString());
107  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, related_feature, scope);
108  if (feat_list.empty()) {
109  return;
110  }
111 
112  CConstRef<CSeq_feat> orig_feat = *feat_list.begin();
113  CRef<CSeq_feat> new_feat(new CSeq_feat);
114  new_feat->Assign(*orig_feat);
115  CObjectInfo objInfo(new_feat.GetPointer(), new_feat.GetPointer()->GetTypeInfo());
116 
117  CMQueryNodeValue::TObs dest_objs;
118  const string& dest_field = m_Args[2]->GetString();
119  if (!GetFieldsByName(&dest_objs, objInfo, dest_field) || dest_objs.empty()) {
120  return;
121  }
122 
123  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
124  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
125 
126  while (src_it != src_objs.end() && dest_it != dest_objs.end()) {
127  CObjectInfo src_prim = NMacroUtil::GetPrimitiveObjInfo(src_it->field);
128  CObjectInfo dest_prim = NMacroUtil::GetPrimitiveObjInfo(dest_it->field);
129  if (CMacroFunction_SwapQual::s_SwapFields(src_prim, dest_prim)) {
130  m_QualsChangedCount += 2;
131  }
132  ++src_it;
133  ++dest_it;
134  }
135 
136  if (m_QualsChangedCount) {
137  m_DataIter->SetModified();
138 
139  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
140  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(scope->GetSeq_featHandle(*orig_feat), *new_feat)));
141  m_DataIter->RunCommand(cmd, m_CmdComposite);
142 
143  TChangedQuals report;
144  report["swapping qualifiers"] = m_QualsChangedCount;
145  CRef<IFunctionLog> fnc_log(new CGeneralFuncLog(report));
146  x_LogChangedQuals(fnc_log);
147  }
148 }
149 
150 bool CMacroFunction_SwapRelatedFeaturesQual_Depr::x_ValidArguments() const
151 {
152  if (m_Args.size() != 3) {
153  return false;
154  }
155 
156  if (!(m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef()))
157  return false;
158 
159  for (size_t index = 1; index < m_Args.size(); ++index) {
160  if (m_Args[index]->GetDataType() != CMQueryNodeValue::eString) {
161  return false;
162  }
163  }
164 
165  return true;
166 }
167 
168 
169 ///////////////////////////////////////////////////////////////////////////////
170 /// class CMacroFunction_RemoveQual
171 /// RemoveQual("genome");
172 /// or
173 /// obj = PUB_AFFIL("sub");
174 /// RemoveQual(obj);
175 ///
177 
178 void CMacroFunction_RemoveQual::TheFunction()
179 {
180  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
181 
182  CObjectInfo oi = m_DataIter->GetEditedObject();
183  CMQueryNodeValue::TObs res_oi;
185  const string& field_name = m_Args[0]->GetString();
186  if (!GetFieldsByName(&res_oi, oi, field_name))
187  return;
188  }
189  else if (type == CMQueryNodeValue::eObjects) {
190  res_oi = m_Args[0]->GetObjects();
191  }
192  else if (type == CMQueryNodeValue::eRef) {
193  x_GetObjectsFromRef(res_oi, 0);
194  }
195 
196  if (res_oi.empty()) {
197  return;
198  }
199 
200  m_QualsChangedCount = s_RemoveFields(m_DataIter, res_oi);
201 
202  if (m_QualsChangedCount) {
203  TChangedQuals report;
204  report["removal of qualifiers"] = m_QualsChangedCount;
205  CRef<IFunctionLog> fnc_log(new CGeneralFuncLog(report));
206  x_LogChangedQuals(fnc_log);
207  }
208 }
209 
211 {
212  if (m_Args.empty()) return false;
213  bool first_ok = (m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef());
214  return (m_Args.size() == 1 && first_ok);
215 }
216 
218 {
219  if (objs.empty())
220  return 0;
221 
222  Int4 quals_changed = 0;
223  CObjectInfo oi = dataiter->GetEditedObject();
224 
225  // special attention to gene qualifiers that are removed from gene Xrefs
226  bool is_gene_suppressed_before = false;
227  bool is_gene_suppressed_after = false;
228  CConstRef<CObject> obj = dataiter->GetScopedObject().object;
229  if (dynamic_cast<const CSeq_feat*>(obj.GetPointer())) {
231  auto gene_xref = feat->GetGeneXref();
232  is_gene_suppressed_before = gene_xref && gene_xref->IsSuppressed();
233  }
234 
235  bool is_taxname = NMacroUtil::IsTaxname(objs.front());
237  if (NStr::EqualNocase(it->parent.GetName(), "Name-std")) {
238  CObjectInfoMI mem = it->parent.FindClassMember("first");
239  if (mem.IsSet() && mem.GetMember() == it->field) {
240  CName_std* std_name = CTypeConverter<CName_std>::SafeCast(it->parent.GetObjectPtr());
241  NMacroUtil::RemoveFirstName(*std_name);
242  quals_changed++;
243  }
244  else {
245  if (RemoveFieldByName(*it)) {
246  quals_changed++;
247  }
248  }
249  }
250  else {
251  if (RemoveFieldByName(*it)) {
252  quals_changed++;
253  }
254  }
255  }
256 
257  if (quals_changed) {
258  if (is_taxname) {
260  }
261  CConstRef<CObject> obj = dataiter->GetScopedObject().object;
262  CRef<CScope> scope = dataiter->GetScopedObject().scope;
263 
264  if (dynamic_cast<const CUser_object*>(obj.GetPointer())) {
266  if (userobj->IsSetData() && userobj->GetData().empty()) {
267  dataiter->SetToDelete(true);
268  }
269  else if (userobj->GetType().IsStr() && NStr::EqualCase(userobj->GetType().GetStr(), "DBLink")) {
270  EDIT_EACH_USERFIELD_ON_USEROBJECT(field_it, *userobj) {
271  CUser_field& field = **field_it;
272  if (field.IsSetData()) {
273  if ((field.GetData().IsStrs() && field.GetData().GetStrs().empty()) ||
274  (field.GetData().IsStr() && field.GetData().GetStr().empty())) {
275  ERASE_USERFIELD_ON_USEROBJECT(field_it, *userobj);
276  }
277  }
278  }
279  }
280  }
281  else if (dynamic_cast<const CSeqdesc*>(obj.GetPointer())) {
283  if (desc->IsGenbank() && desc->GetGenbank().IsEmpty()) {
284  dataiter->SetToDelete(true);
285  }
286  }
287  else if (dynamic_cast<const CBioseq*>(obj.GetPointer())) {
290  } else {
292  if (dynamic_cast<const CSeq_feat*>(obj.GetPointer())) {
294  cleanup.BasicCleanup(*seq_feat);
295  if (seq_feat->IsSetXref()) {
296  auto gene_xref = seq_feat->GetGeneXref();
297  if (gene_xref) {
298  is_gene_suppressed_after = gene_xref->IsSuppressed();
299  if (!is_gene_suppressed_before && is_gene_suppressed_after)
300  NMacroUtil::RemoveGeneXref(*seq_feat);
301  }
302  }
303  }
304  else if (dynamic_cast<const CBioSource*>(obj.GetPointer())) {
306  cleanup.BasicCleanup(*bsrc);
307  if (bsrc->IsSetOrg() && bsrc->GetOrg().IsSetDb() && bsrc->GetOrg().GetDb().empty()) {
308  bsrc->SetOrg().ResetDb();
309  }
310  }
311  }
312 
313  dataiter->SetModified();
314  }
315  return quals_changed;
316 }
317 
318 
319 // cleanup for biosource
321 {
322  if (bsrc.IsSetOrgMod()) {
323  if (bsrc.GetOrg().GetOrgname().GetMod().empty()) {
324  bsrc.SetOrg().SetOrgname().ResetMod();
325  }
326  }
327 
328  if (bsrc.IsSetSubtype()) {
329  if (bsrc.GetSubtype().empty()) {
330  bsrc.ResetSubtype();
331  }
332  }
333 }
334 
335 ///////////////////////////////////////////////////////////////////////////////
336 /// class CMacroFunction_RemoveModifier
337 /// o = Resolve(path_to_container) Where o.subtype = subtype;
338 /// RemoveModifier(o) - deleting subtype modifier from the list of modifiers
339 ///
340 DEFINE_MACRO_FUNCNAME(CMacroFunction_RemoveModifier, "RemoveModifier")
341 
342 void CMacroFunction_RemoveModifier::TheFunction()
343 {
344  // objects to be removed:
345  CMQueryNodeValue::TObs res_oi;
346  x_GetObjectsFromRef(res_oi, 0);
347  if (res_oi.empty()) {
348  // nothing to delete
349  return;
350  }
351 
352  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
353  const CBioSource* c_bsrc = dynamic_cast<const CBioSource*>(obj.GetPointer());
354  // the asn selector should be a biosource
355  if (!c_bsrc) {
356  return;
357  }
358 
360  if (RemoveFieldByName(*it)) {
361  m_QualsChangedCount++;
362  }
363  }
364 
365  if (m_QualsChangedCount) {
366  CObjectInfo oi = m_DataIter->GetEditedObject();
368  s_ResetModSubsrcQuals(*bsrc);
369  m_DataIter->SetModified();
370 
371  TChangedQuals report;
372  report["removing source modifiers"] = m_QualsChangedCount;
373  CRef<IFunctionLog> fnc_log(new CGeneralFuncLog(report));
374  x_LogChangedQuals(fnc_log);
375  }
376 }
377 
378 bool CMacroFunction_RemoveModifier::x_ValidArguments() const
379 {
380  return (m_Args.size() == 1 && m_Args[0]->GetDataType() == CMQueryNodeValue::eRef);
381 }
382 
383 
384 ///////////////////////////////////////////////////////////////////////////////
385 // class CMacroFunction_RemoveOutside
386 /// RemoveOutsideStringQual(field_name(str/obj), before_match(b), left_del(str), rmv_left(b),
387 /// after_match(b), right_del(str), rmv_right(b), case_insensitive(b), whole_word(b), update_mrna(b))
388 /// Last parameter is optional
389 ///
390 DEFINE_MACRO_FUNCNAME(CMacroFunction_RemoveOutside, "RemoveOutsideStringQual")
391 
392 void CMacroFunction_RemoveOutside::TheFunction()
393 {
394  CObjectInfo oi = m_DataIter->GetEditedObject();
395  CMQueryNodeValue::TObs res_oi;
396  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
397 
399  if (!GetFieldsByName(&res_oi, oi, m_Args[0]->GetString()))
400  return;
401  }
402  else if (type == CMQueryNodeValue::eObjects) {
403  res_oi = m_Args[0]->GetObjects();
404  }
405  else if (type == CMQueryNodeValue::eRef) {
406  x_GetObjectsFromRef(res_oi, 0);
408  }
409 
410  if (res_oi.empty()) {
411  return;
412  }
413 
414 
415  CRef<CRemoveTextOptions> options = x_GetRemoveTextOptions(1);
416  x_RmvOutsideFields(res_oi, *options);
417 
418  if (m_QualsChangedCount) {
419  NMacroUtil::CleanupForTaxnameChange(res_oi.front(), oi);
420  m_DataIter->SetModified();
421 
423  log << m_DataIter->GetBestDescr() << ": removed text outside string in " << m_QualsChangedCount << " qualifiers";
424 
425  bool update_mrna = (m_Args.size() == 10) ? m_Args[9]->GetBool() : false;
426  CConstRef<CObject> object = m_DataIter->GetScopedObject().object;
427  const CSeq_feat* feat = dynamic_cast<const CSeq_feat*>(object.GetPointer());
428 
429  if (update_mrna && feat && feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
430  string message;
431  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
433  string prot_product = edit_feat->GetData().GetProt().GetName().front();
434  CRef<CCmdComposite> cmd = UpdatemRNAProduct(prot_product, object, *scope);
435  if (cmd) {
436  m_DataIter->RunCommand(cmd, m_CmdComposite);
437  log << ", applied " + prot_product + " to mRNA product name ";
438  }
439  }
440  x_LogFunction(log);
441  }
442 }
443 
445 {
447  string left_del = m_Args[start_index + 1]->GetString();
448  bool remove_before_match = false;
449  if (m_Args[start_index]->GetBool()) {
450  if (NStr::EqualCase(left_del, "eDigits")) {
451  before_match = CRemoveTextOptions::eDigits;
452  left_del = kEmptyStr;
453  }
454  else if (NStr::EqualCase(left_del, "eLetters")) {
455  before_match = CRemoveTextOptions::eLetters;
456  left_del = kEmptyStr;
457  }
458  else {
459  before_match = CRemoveTextOptions::eText;
460  }
461  remove_before_match = m_Args[start_index + 2]->GetBool();
462  }
463 
464 
466  string right_del = m_Args[start_index + 4]->GetString();
467  bool remove_after_match = false;
468  if (m_Args[start_index + 3]->GetBool()) {
469  if (NStr::EqualCase(right_del, "eDigits")) {
470  after_match = CRemoveTextOptions::eDigits;
471  right_del = kEmptyStr;
472  }
473  else if (NStr::EqualCase(right_del, "eLetters")) {
474  after_match = CRemoveTextOptions::eLetters;
475  right_del = kEmptyStr;
476  }
477  else {
478  after_match = CRemoveTextOptions::eText;
479  }
480  remove_after_match = m_Args[start_index + 5]->GetBool();
481  }
482 
483  bool case_insensitive = m_Args[start_index + 6]->GetBool();
484  bool whole_word = m_Args[start_index + 7]->GetBool();
485 
486  CRef<CRemoveTextOptions> options(new CRemoveTextOptions(before_match, left_del, remove_before_match,
487  after_match, right_del, remove_after_match,
488  case_insensitive, whole_word));
489  return options;
490 }
491 
493 {
494  for (auto& it : resolved_objs) {
497  for (auto& iter : objs) {
498  CObjectInfo obj_oi = iter.field;
500  string value = obj_oi.GetPrimitiveValueString();
501  if (options.EditText(value)) {
502  SetQualStringValue(obj_oi, value);
503  }
504  }
505  else if (obj_oi.GetPrimitiveValueType() == ePrimitiveValueEnum) {
506  string value;
507  try {
508  value = obj_oi.GetPrimitiveValueString();
509  }
510  catch (const CException&) {
512  }
513  if (options.EditText(value)) {
514  try {
517  }
518  catch (const CException&) {
519  // if this fails, try to convert the string to int and assign again
520  try {
524  }
525  catch (const CException&) {}
526  }
527  }
528  }
529  }
530  }
531 }
532 
534 {
535  if (m_Args.size() != 9 && m_Args.size() != 10)
536  return false;
537 
538  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
540  if (!first_ok)
541  return false;
542 
543  return x_CheckArguments(1);
544 }
545 
547 {
548  if (m_Args[index]->GetDataType() != CMQueryNodeValue::eBool
549  || m_Args[++index]->GetDataType() != CMQueryNodeValue::eString
550  || m_Args[++index]->GetDataType() != CMQueryNodeValue::eBool) {
551  return false;
552  }
553  if (m_Args[++index]->GetDataType() != CMQueryNodeValue::eBool
554  || m_Args[++index]->GetDataType() != CMQueryNodeValue::eString
555  || m_Args[++index]->GetDataType() != CMQueryNodeValue::eBool) {
556  return false;
557  }
558  ++index;
559  for (size_t i = index; i < m_Args.size(); ++i) {
560  if (m_Args[i]->GetDataType() != CMQueryNodeValue::eBool)
561  return false;
562  }
563  return true;
564 }
565 
566 ///////////////////////////////////////////////////////////////////////////////
567 /// class CMacroFunction_SetStringQual
568 /// Expected Syntax:
569 /// SetStringQual(field_name, newValue, existingtext_option, delimiter, remove_blank)
570 /// The last two parameters are optional.
571 /// Empty new values don't have any effect by default. If remove_blank is True,
572 /// the existing field is removed.
573 ///
575 
576 void CMacroFunction_SetStringQual::TheFunction()
577 {
578  size_t index = 1;
579  string newValue = NMacroUtil::GetStringValue(m_Args[index]);
580  const string& action_type = m_Args[++index]->GetString();
581  string delimiter;
582  bool remove_field = false;
583  x_GetOptionalArgs(delimiter, remove_field, index);
584 
585  CObjectInfo oi = m_DataIter->GetEditedObject();
586  CMQueryNodeValue::TObs res_oi;
587  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
588 
590  if (remove_field && newValue.empty()) {
591  if (!GetFieldsByName(&res_oi, oi, m_Args[0]->GetString()))
592  return;
593  }
594  else if (!newValue.empty() && !SetFieldsByName(&res_oi, oi, m_Args[0]->GetString())) {
595  return;
596  }
597  }
598  else if (type == CMQueryNodeValue::eObjects) {
599  res_oi = m_Args[0]->GetObjects();
600  }
601  else if (type == CMQueryNodeValue::eRef) {
602  x_GetObjectsFromRef(res_oi, 0);
603  }
604 
605  if (res_oi.empty()) {
606  return;
607  }
608 
609  if (!newValue.empty()) {
610  vector<string> new_values;
612  x_SetFields(res_oi, newValue, existing_text, new_values);
613 
614  m_Result->SetBool(false);
615  if (m_QualsChangedCount) {
616  NMacroUtil::CleanupForTaxnameChange(res_oi.front(), oi);
617  m_DataIter->SetModified();
618  m_Result->SetBool(true);
619 
621  for (size_t i = 0; i < new_values.size(); ++i) {
622  log << m_DataIter->GetBestDescr() << ": set '" << new_values[i] << "' as a new value\n";
623  }
624  x_LogFunction(log);
625  }
626  }
627  else if (remove_field) {
628  m_QualsChangedCount = CMacroFunction_RemoveQual::s_RemoveFields(m_DataIter, res_oi);
629  if (m_QualsChangedCount) {
631  log << m_DataIter->GetBestDescr() << ": removed " << m_QualsChangedCount << " qualifiers";
632  x_LogFunction(log);
633  }
634  }
635 }
636 
638 {
639  // can accept as its first parameter: objects, string or reference
640  size_t arg_nr = m_Args.size();
641  if (arg_nr < 3 && arg_nr > 5) {
642  return false;
643  }
644 
645  size_t index = 0;
646  bool first_ok = m_Args[index]->IsString() || m_Args[index]->AreObjects() || m_Args[index]->IsRef();
647  if (!first_ok) return false;
648 
649  ++index;
650  NMacroUtil::GetPrimitiveFromRef(m_Args[index].GetNCObject());
651  bool second_ok = m_Args[index]->IsString() || m_Args[index]->IsInt() || m_Args[index]->IsDouble();
652  if (!second_ok) return false;
653 
654  if (!m_Args[++index]->IsString()) return false;
655 
656  if (arg_nr > 3 && (!m_Args[++index]->IsString() && !m_Args[index]->IsBool())) return false;
657 
658  if (arg_nr > 4 && !m_Args[++index]->IsBool()) return false;
659 
660  return true;
661 }
662 
664  edit::EExistingText existing_text, vector<string>& new_values)
665 {
666  for (auto& it : objs) {
667  CObjectInfo obj = it.field;
668  switch (obj.GetTypeFamily()) {
669  case eTypeFamilyPrimitive: // works when the field exists or does not
670  if (existing_text != edit::eExistingText_add_qual) {
671  x_SetNewPrimitiveValue(obj, newValue, existing_text, new_values);
672  }
673  else if (it.parent.GetTypeFamily() == eTypeFamilyContainer) {
674  // add new element to the container
675  CObjectInfo new_oi(it.parent.AddNewElement());
676  SetQualStringValue(new_oi, newValue);
677  new_values.push_back(newValue);
678  }
679  break;
680  case eTypeFamilyPointer: // works only when the field exists
682  CObjectInfo oi_ptr(obj.GetPointedObject());
683  x_SetNewPrimitiveValue(oi_ptr, newValue, existing_text, new_values);
684  }
685  break;
686  case eTypeFamilyContainer: { // works when the field exists or does not
689 
690  if (objs.empty() || existing_text == edit::eExistingText_add_qual) {
692  CObjectInfo new_oi(obj.AddNewPointedElement());
693  SetQualStringValue(new_oi, newValue);
694  new_values.push_back(newValue);
695  }
696  else {
697  CObjectInfo new_oi(obj.AddNewElement());
698  SetQualStringValue(new_oi, newValue);
699  new_values.push_back(newValue);
700  }
701  }
702  else {
703  if (existing_text == edit::eExistingText_replace_old) {
704  if (objs.size() > 1) {
705  // keep the first element, update it and delete the rest from the container
707  CObjectInfoEI e = first;
708  ++e;
709  while (e) {
710  e.Erase();
711  e = first;
712  ++e;
713  }
714  }
715  SetQualStringValue(objs.front().field, newValue);
716  new_values.push_back(newValue);
717  }
718  else {
719  // there are multiple ones, and all of them needs to be updated
721  x_SetNewPrimitiveValue(iter->field, newValue, existing_text, new_values);
722  }
723  }
724  }
725  break;
726  }
727  default:
728  break;
729  }
730  }
731 }
732 
734  const string& newValue, edit::EExistingText existing_text, vector<string>& new_values)
735 {
737 
740 
741  string orig_value = oi.GetPrimitiveValueString();
742  if (edit::AddValueToString(orig_value, newValue, existing_text)) {
743  if (SetQualStringValue(oi, orig_value)) {
744  new_values.push_back(orig_value);
745  }
746  }
747  }
748 }
749 
750 ///////////////////////////////////////////////////////////////////////////////
751 ///class CMacroFunction_SetRnaProduct
752 /// SetRnaProduct(new_value, existing_text_opt, delimiter, remove_blank)
753 ///
754 DEFINE_MACRO_FUNCNAME(CMacroFunction_SetRnaProduct, "SetRnaProduct")
755 void CMacroFunction_SetRnaProduct::TheFunction()
756 {
757  CConstRef<CObject> object = m_DataIter->GetScopedObject().object;
758  const CSeq_feat* seq_feat = dynamic_cast<const CSeq_feat*>(object.GetPointer());
759  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
760  if (!seq_feat || !seq_feat->GetData().IsRna() || !scope)
761  return;
762 
763  size_t index = 0;
764  string newValue = NMacroUtil::GetStringValue(m_Args[index]);
765  const string& action_type = m_Args[++index]->GetString();
766  string delimiter;
767  bool remove_field = false;
768  x_GetOptionalArgs(delimiter, remove_field, index);
769 
770  CObjectInfo oi = m_DataIter->GetEditedObject();
773 
774  CRNA_ref& rna_ref = edit_feat->SetData().SetRna();
775  string orig_value = rna_ref.GetRnaProductName();
776 
777  if (!newValue.empty()) {
778  string remainder;
779  if (edit::AddValueToString(orig_value, newValue, existing_text)) {
780  rna_ref.SetRnaProductName(orig_value, remainder);
781  m_QualsChangedCount++;
782  if (!remainder.empty()) {
783  if (edit_feat->IsSetComment()) {
784  existing_text = edit::eExistingText_append_semi;
785  edit::AddValueToString(edit_feat->SetComment(), remainder, existing_text);
786  }
787  }
788  }
789  }
790  else if (remove_field) {
791  string remainder;
792  rna_ref.SetRnaProductName(kEmptyStr, remainder);
793  m_QualsChangedCount++;
794  }
795 
796  if (m_QualsChangedCount) {
797  m_DataIter->SetModified();
798 
800  log << m_DataIter->GetBestDescr();
801  if (newValue.empty() && remove_field) {
802  log << ": removed rna product";
803  }
804  else {
805  log << ": set '" << orig_value << "' as new rna product value";
806  }
807  x_LogFunction(log);
808  }
809 }
810 
811 bool CMacroFunction_SetRnaProduct::x_ValidArguments() const
812 {
813  // can accept as its first parameter: objects, string or reference
814  size_t arg_nr = m_Args.size();
815  if (arg_nr < 2 && arg_nr > 4) {
816  return false;
817  }
818 
819  size_t index = 0;
820  NMacroUtil::GetPrimitiveFromRef(m_Args[index].GetNCObject());
821  bool first_ok = m_Args[index]->IsString() || m_Args[index]->IsInt() || m_Args[index]->IsDouble();
822  if (!first_ok) return false;
823 
824  if (!m_Args[++index]->IsString()) return false;
825  if (arg_nr > 2 && (!m_Args[++index]->IsString() && !m_Args[index]->IsBool())) return false;
826  if (arg_nr > 3 && !m_Args[++index]->IsBool()) return false;
827 
828  return true;
829 }
830 
831 
832 ///////////////////////////////////////////////////////////////////////////////
833 /// class CMacroFunction_GetRnaProduct
834 /// GetRnaProduct()
835 ///
836 DEFINE_MACRO_FUNCNAME(CMacroFunction_GetRnaProduct, "GETRNAPRODUCT")
837 
838 void CMacroFunction_GetRnaProduct::TheFunction()
839 {
840  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
841  const CSeq_feat* feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
842  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
843  m_Result->SetNotSet();
844  if (!feat || !scope || !(feat->IsSetData() && feat->GetData().IsRna()))
845  return;
846 
847  const CRNA_ref& rna_ref = feat->GetData().GetRna();
848  string value = rna_ref.GetRnaProductName();
849 
850  if (m_Nested == eNotNested) { // return a string
851  m_Result->SetString(value);
852  }
853  else {
854  // return a reference to a CMQueryNodeValue of type string
856  new_node->SetString(value);
857  m_Result->SetRef(new_node);
858  }
859 }
860 
861 bool CMacroFunction_GetRnaProduct::x_ValidArguments() const
862 {
863  return (m_Args.empty());
864 }
865 ///////////////////////////////////////////////////////////////////////////////
866 /// class CMacroFunction_RemoveRnaProduct
867 /// RemoveRnaProduct()
868 ///
869 DEFINE_MACRO_FUNCNAME(CMacroFunction_RemoveRnaProduct, "RemoveRnaProduct")
870 void CMacroFunction_RemoveRnaProduct::TheFunction()
871 {
872  CObjectInfo oi = m_DataIter->GetEditedObject();
874  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
875  if (!edit_feat || !edit_feat->GetData().IsRna() || !scope)
876  return;
877 
878  CRNA_ref& rna_ref = edit_feat->SetData().SetRna();
879  string remainder;
880  rna_ref.SetRnaProductName(kEmptyStr, remainder);
881 
882  m_DataIter->SetModified();
883  m_QualsChangedCount++;
884 
885  TChangedQuals report;
886  report["removing RNA product"] = m_QualsChangedCount;
887  CRef<IFunctionLog> fnc_log(new CGeneralFuncLog(report));
888  x_LogChangedQuals(fnc_log);
889 }
890 
891 bool CMacroFunction_RemoveRnaProduct::x_ValidArguments() const
892 {
893  return (m_Args.empty());
894 }
895 
896 
897 ///////////////////////////////////////////////////////////////////////////////
898 /// class CMacroFunction_EditStringQual
899 /// Usage: EditStringQual(field_name|rt_obj, find_text, repl_text, location, case_sensitive, is_regex)
900 ///
902 
903 void CMacroFunction_EditStringQual::TheFunction()
904 {
905  size_t index = 1;
906  const string& find_txt = NMacroUtil::GetStringValue(m_Args[index]);
907  string repl_txt = NMacroUtil::GetStringValue(m_Args[++index]);
908  const string& location = m_Args[++index]->GetString();
909  bool case_sensitive = m_Args[++index]->GetBool();
910  bool is_regex = (++index < m_Args.size()) ? m_Args[index]->GetBool() : false;
911 
912  m_Result->SetBool(false); // assume, that no change has been made
913 
914  CObjectInfo oi = m_DataIter->GetEditedObject();
915  CMQueryNodeValue::TObs res_oi;
916  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
917 
919  if (!GetFieldsByName(&res_oi, oi, m_Args[0]->GetString()))
920  return;
921  }
922  else if (type == CMQueryNodeValue::eObjects) {
923  res_oi = m_Args[0]->GetObjects();
924  }
925  else if (type == CMQueryNodeValue::eRef) {
926  x_GetObjectsFromRef(res_oi, 0);
928  }
929 
930  if (res_oi.empty()) {
931  return;
932  }
933 
934  x_EditFields(res_oi, find_txt, repl_txt, s_GetLocFromName(location), case_sensitive, is_regex);
935  if (m_QualsChangedCount) {
936  NMacroUtil::CleanupForTaxnameChange(res_oi.front(), oi);
937 
938  m_DataIter->SetModified();
939  m_Result->SetBool(true); // the field has been modified
940 
942  log << m_DataIter->GetBestDescr() << ": edited " << m_QualsChangedCount << " qualifiers, replaced " << find_txt;
943  if (NStr::IsBlank(repl_txt)) {
944  repl_txt.assign("''");
945  }
946  log << " with " << repl_txt;
947  x_LogFunction(log);
948  }
949 }
950 
951 bool CMacroFunction_EditStringQual::s_EditText(string& str, const string& find,
952  const string& replace, ESearchLoc loc, bool case_sensitive, bool is_regex)
953 {
954  bool rval = false;
955  if (is_regex)
956  {
958  if (case_sensitive)
959  {
960  options = CRegexp::fCompile_default;
961  }
962 
963  CRegexpUtil replacer(str);
964  size_t num = replacer.Replace(find, replace, options, CRegexp::fMatch_default, 0);
965  if (num > 0)
966  {
967  str = replacer.GetResult();
968  rval = true;
969  }
970  }
971  else
972  {
973  SIZE_TYPE pos = NPOS;
974  if (loc == eAnywhere) {
975  pos = NStr::Find(str, find, (case_sensitive) ? NStr::eCase : NStr::eNocase);
976  while (pos != NPOS) {
977  str = str.substr(0, pos) + replace + str.substr(pos + find.length());
978  if (case_sensitive) {
979  pos = NStr::FindCase(str, find, pos + replace.length());
980  }
981  else {
982  pos = NStr::FindNoCase(str, find, pos + replace.length());
983  }
984  rval = true;
985  }
986  }
987  else {
988  pos = NPOS;
989  if (loc == eBeginning) {
990  if (NStr::StartsWith(str, find, (case_sensitive) ? NStr::eCase : NStr::eNocase)) {
991  pos = 0;
992  }
993  }
994  else if (loc == eEnd) {
995  if (NStr::EndsWith(str, find, (case_sensitive) ? NStr::eCase : NStr::eNocase)) {
996  pos = str.length() - find.length();
997  }
998  }
999  if (pos != NPOS) {
1000  str = str.substr(0, pos) + replace + str.substr(pos + find.length());
1001  rval = true;
1002  }
1003  }
1004  }
1006 
1007  return rval;
1008 }
1009 
1011  CMQueryNodeValue::TObs& resolved_objs,
1012  const string& find_txt,
1013  const string& repl_txt,
1014  ESearchLoc loc,
1015  bool case_sensitive,
1016  bool is_regex)
1017 {
1018  for (auto& it : resolved_objs) {
1021  for(auto& iter : objs) {
1022  CObjectInfo oi = iter.field;
1024  string value = oi.GetPrimitiveValueString();
1025  if (s_EditText(value, find_txt, repl_txt, loc, case_sensitive, is_regex)) {
1026  if (NStr::EqualNocase(iter.parent.GetName(), "Name-std")) {
1027  CObjectInfoMI mem = iter.parent.FindClassMember("first");
1028  if (mem.IsSet() && mem.GetMember() == oi) {
1029  CName_std* std_name = CTypeConverter<CName_std>::SafeCast(iter.parent.GetObjectPtr());
1032  }
1034  }
1035  else {
1037  }
1038  }
1039  }
1040  else if (oi.GetPrimitiveValueType() == ePrimitiveValueInteger) {
1042  string orig_val = NStr::Int8ToString(value);
1043  if (s_EditText(orig_val, find_txt, repl_txt, loc, case_sensitive, is_regex)) {
1044  NStr::TruncateSpacesInPlace(orig_val);
1047  }
1048  }
1049 
1050  else if (oi.GetPrimitiveValueType() == ePrimitiveValueEnum) {
1051  string value;
1052  try {
1054  }
1055  catch (const CException&) {
1057  }
1058  if (s_EditText(value, find_txt, repl_txt, loc, case_sensitive, is_regex)) {
1059  try {
1062  }
1063  catch (const CException&) {
1064  // if this fails, try to convert the string to int and assign again
1065  try {
1068  }
1069  catch (const CException&) {}
1070  }
1071  }
1072  }
1073  }
1074  }
1075 }
1076 
1078 {
1079  ESearchLoc loc = eAnywhere; // default search location
1080  if (NStr::EqualNocase(name, "at the beginning")) {
1081  loc = eBeginning;
1082  }
1083  else if (NStr::EqualNocase(name, "at the end")) {
1084  loc = eEnd;
1085  }
1086  return loc;
1087 }
1088 
1090 {
1091  if (m_Args.size() != 5 && m_Args.size() != 6)
1092  return false;
1093 
1094  bool first_ok = m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef();
1095  if (!first_ok) {
1096  return false;
1097  }
1098 
1099  for (size_t index = 1; index < 3; index++) {
1100  if (!m_Args[index]->IsString() && !m_Args[index]->IsInt()) {
1101  return false;
1102  }
1103  }
1104  if (m_Args.size() == 6) {
1105  if (!m_Args.back()->IsBool())
1106  return false;
1107  }
1108  return (m_Args[3]->IsString() && m_Args[4]->IsBool());
1109 }
1110 
1111 
1112 ///////////////////////////////////////////////////////////////////////////////
1113 namespace {
1114 
1115  vector<string> kSatelliteTypes = { "satellite", "microsatellite", "minisatellite" };
1116  vector<string> kMobileETypeTypes = {
1117  "insertion sequence",
1118  "integron",
1119  "LINE",
1120  "MITE",
1121  "non-LTR retrotransposon",
1122  "other",
1123  "P-element",
1124  "retrotransposon",
1125  "SINE",
1126  "superintegron",
1127  "transposable element",
1128  "transposon"
1129  };
1130  bool GetBioSourceDestObjects(CObjectInfo& oi, const string& field_name, CMQueryNodeValue::TObs& dest_objs);
1131 
1132  bool SetFeatDestinationField(CRef<CSeq_feat> feat, const string& field_name, CMQueryNodeValue::TObs& result);
1133 
1134  bool IstRNAProductField(const CSeq_feat& feat, const string& field_name);
1135 
1136  bool GetDestinationObjects(CConstRef<CObject> object, CObjectInfo& oi, const string& field_name, CMQueryNodeValue::TObs& result)
1137  {
1138  const CBioSource* bsrc = dynamic_cast<const CBioSource*>(object.GetPointer());
1139  const CSeq_feat* seq_feat = dynamic_cast<const CSeq_feat*>(object.GetPointer());
1140  if (bsrc) {
1141  return GetBioSourceDestObjects(oi, field_name, result);
1142  }
1143  if (seq_feat) {
1145  return SetFeatDestinationField(Ref(feat), field_name, result);
1146  }
1147 
1148  return SetFieldsByName(&result, oi, field_name);
1149  }
1150 
1151  bool GetBioSourceDestObjects(CObjectInfo& oi, const string& field_name, CMQueryNodeValue::TObs& dest_objs)
1152  {
1153  if (field_name.empty())
1154  return false;
1155 
1156  size_t orig_size = dest_objs.size();
1157  // check if the field denotes an orgmod or a subsource modifier
1158  if (NMacroUtil::IsBiosourceModifier(field_name)) {
1159 
1160  // extract it into a function that adds these modifier fields
1162  if (!bsrc)
1163  return false;
1164  // check if it's a subsource modifier
1167 
1168  EDIT_EACH_SUBSOURCE_ON_BIOSOURCE(subsrc, *bsrc) {
1169  if ((*subsrc)->IsSetSubtype() && (*subsrc)->GetSubtype() == st) {
1170  CObjectInfo subsrc_oi((*subsrc).GetPointer(), (*subsrc)->GetTypeInfo());
1171  CObjectInfo name_oi = subsrc_oi.FindClassMember("name").GetMember();
1172  dest_objs.push_back(CMQueryNodeValue::SResolvedField(subsrc_oi, name_oi));
1173  }
1174  }
1175 
1176  if (dest_objs.empty()) {
1177  CRef<CSubSource> sub_src(new CSubSource());
1178  sub_src->SetSubtype(st);
1179  bsrc->SetSubtype().push_back(sub_src);
1180  CObjectInfo subsrc_oi(sub_src.GetPointer(), sub_src->GetTypeInfo());
1181  CObjectInfo name_oi = subsrc_oi.FindClassMember("name").GetMember();
1182  dest_objs.push_back(CMQueryNodeValue::SResolvedField(subsrc_oi, name_oi));
1183  }
1184  // check if it's an orgmod modifier
1185  }
1188 
1189  EDIT_EACH_ORGMOD_ON_BIOSOURCE(orgmod, *bsrc) {
1190  if ((*orgmod)->IsSetSubtype() && (*orgmod)->GetSubtype() == st) {
1191  CObjectInfo orgmod_oi((*orgmod).GetPointer(), (*orgmod)->GetTypeInfo());
1192  CObjectInfo subname_oi = orgmod_oi.FindClassMember("subname").GetMember();
1193  dest_objs.push_back(CMQueryNodeValue::SResolvedField(orgmod_oi, subname_oi));
1194  }
1195  }
1196 
1197  if (dest_objs.empty()) {
1198  CRef<COrgMod> orgmod(new COrgMod());
1199  orgmod->SetSubtype(st);
1200  orgmod->SetSubname(kEmptyStr);
1201  if (!bsrc->IsSetOrgname()) {
1202  CRef<COrgName> orgname(new COrgName());
1203  orgname->SetMod().push_back(orgmod);
1204  bsrc->SetOrg().SetOrgname(*orgname);
1205  }
1206  else {
1207  bsrc->SetOrg().SetOrgname().SetMod().push_back(orgmod);
1208  }
1209  CObjectInfo orgmod_oi(orgmod.GetPointer(), orgmod->GetTypeInfo());
1210  CObjectInfo subname_oi = orgmod_oi.FindClassMember("subname").GetMember();
1211  dest_objs.push_back(CMQueryNodeValue::SResolvedField(orgmod_oi, subname_oi));
1212  }
1213  }
1214  }
1215  else {
1216  SetFieldsByName(&dest_objs, oi, field_name);
1217  }
1218 
1219  return dest_objs.size() - orig_size > 0;
1220  }
1221 
1222  // feature related functions
1223  bool SetFeatDestinationField(CRef<CSeq_feat> feat, const string& field_name, CMQueryNodeValue::TObs& result)
1224  {
1225  if (feat.IsNull() || field_name.empty())
1226  return false;
1227 
1228  CObjectInfo dest_oi(feat.GetPointer(), feat->GetThisTypeInfo());
1229  if (SetFieldsByName(&result, dest_oi, field_name)) {
1230  return true;
1231  }
1232 
1233  size_t orig_size = result.size();
1234  string field = field_name;
1235  if (NMacroUtil::IsSatelliteSubfield(field_name)) {
1236  field = "satellite";
1237  }
1238  else if (NMacroUtil::StringsAreEquivalent(field_name, "mobile-element-type-type")) {
1239  field = "mobile-element-type";
1240  }
1241 
1242  // field_name is a possible GB qualifier
1243  EDIT_EACH_GBQUAL_ON_SEQFEAT(gbq_it, *feat) {
1244  if ((*gbq_it)->IsSetQual() && NStr::EqualNocase((*gbq_it)->GetQual(), field)) {
1245  CObjectInfo gbqual_oi((*gbq_it).GetPointer(), (*gbq_it)->GetTypeInfo());
1246  CObjectInfo val_oi = gbqual_oi.FindClassMember("val").GetMember();
1247  result.push_back(CMQueryNodeValue::SResolvedField(gbqual_oi, val_oi));
1248  }
1249  }
1250 
1251  if (result.empty()) {
1252  if (NStr::EndsWith(field_name, "::product"))
1253  return true; // returning true but the output argument results is empty!
1254 
1255  CRef<CGb_qual> new_gbqual(new CGb_qual(field, kEmptyStr));
1256  feat->SetQual().push_back(new_gbqual);
1257  CObjectInfo gbqual_oi(new_gbqual.GetPointer(), new_gbqual->GetTypeInfo());
1258  CObjectInfo val_oi = gbqual_oi.FindClassMember("val").GetMember();
1259  result.push_back(CMQueryNodeValue::SResolvedField(gbqual_oi, val_oi));
1260  }
1261 
1262  return result.size() - orig_size > 0;
1263  }
1264 
1265  bool GetFeatDestinationField(CRef<CSeq_feat> feat, const string& field_name, CMQueryNodeValue::TObs& result)
1266  {
1267  if (field_name.empty())
1268  return false;
1269 
1270  CObjectInfo dest_oi(feat.GetPointer(), feat->GetThisTypeInfo());
1271  if (GetFieldsByName(&result, dest_oi, field_name)) {
1272  return true;
1273  }
1274 
1275  string field = field_name;
1276  if (NMacroUtil::IsSatelliteSubfield(field_name)) {
1277  field = "satellite";
1278  }
1279  else if (NMacroUtil::StringsAreEquivalent(field_name, "mobile-element-type-type")) {
1280  field = "mobile-element-type";
1281  }
1282 
1283  // field_name is a possible GB qualifier
1284  EDIT_EACH_GBQUAL_ON_SEQFEAT(gbq_it, *feat) {
1285  if ((*gbq_it)->IsSetQual() && NStr::EqualNocase((*gbq_it)->GetQual(), field)) {
1286  CObjectInfo gbqual_oi((*gbq_it).GetPointer(), (*gbq_it)->GetTypeInfo());
1287  CObjectInfo val_oi = gbqual_oi.FindClassMember("val").GetMember();
1288  result.push_back(CMQueryNodeValue::SResolvedField(gbqual_oi, val_oi));
1289  }
1290  }
1291  return (!result.empty());
1292  }
1293 
1294  bool IstRNAProductField(const CSeq_feat& feat, const string& field_name)
1295  {
1296  return (feat.IsSetData() &&
1298  (NStr::EqualNocase(field_name, "tRNA::product") || NStr::EqualNocase(field_name, "any::product")));
1299  }
1300 
1301  CRef<CSeq_feat> CreateNewGene(const CSeq_feat& src_feat, CScope& scope);
1302 
1303  CRef<CSeq_feat> CreateNewProtein(const CSeq_feat& src_feat, CScope& scope);
1304 
1305  CRef<CSeq_feat> CreateNewRelatedFeature(const CSeq_feat& src_feat, const string& field_name, CScope& scope)
1306  {
1307  if (NStr::EndsWith(field_name, "locus")) {
1308  return CreateNewGene(src_feat, scope);
1309  }
1310  else if (NMacroUtil::StringsAreEquivalent(field_name, "protein name") ||
1311  NMacroUtil::StringsAreEquivalent(field_name, "data.prot.name") ||
1312  NMacroUtil::StringsAreEquivalent(field_name, "data.prot.ec")) {
1313  return CreateNewProtein(src_feat, scope);
1314  }
1315  return CRef<CSeq_feat>(nullptr);
1316  }
1317 
1318  CRef<CSeq_feat> CreateNewGene(const CSeq_feat& src_feat, CScope& scope)
1319  {
1320  if (!src_feat.IsSetData() || !src_feat.IsSetLocation())
1321  return CRef<CSeq_feat>(nullptr);
1322 
1323  CRef<CSeq_feat> new_gene(new CSeq_feat);
1324  CConstRef<CSeq_loc> loc(&src_feat.GetLocation());
1325 
1326  CConstRef<CSeq_feat> cds_feat;
1327  if (src_feat.GetData().IsProt()) {
1328  CBioseq_Handle product = scope.GetBioseqHandle(src_feat.GetLocation());
1329  if (product) {
1330  cds_feat = sequence::GetCDSForProduct(product);
1331  }
1332  }
1333  else if (src_feat.GetData().IsCdregion()) {
1334  cds_feat = ConstRef(&src_feat);
1335  }
1336 
1337  if (cds_feat) {
1339  if (mrna) {
1340  loc = Ref(&mrna.GetLocation());
1341  }
1342  else {
1343  loc = Ref(&cds_feat->GetLocation());
1344  }
1345  }
1346 
1348  new_gene->SetLocation(*gene_loc);
1349  new_gene->SetPartial(new_gene->GetLocation().IsPartialStart(eExtreme_Biological) || new_gene->GetLocation().IsPartialStop(eExtreme_Biological));
1350  return new_gene;
1351  }
1352 
1353  CRef<CSeq_feat> CreateNewProtein(const CSeq_feat& src_feat, CScope& scope)
1354  {
1355  if (!src_feat.IsSetData() || !src_feat.IsSetLocation())
1356  return CRef<CSeq_feat>(nullptr);
1357 
1359  if (src_feat.GetData().IsCdregion()) {
1360  if (src_feat.IsSetProduct()) {
1361  CBioseq_Handle product = scope.GetBioseqHandle(src_feat.GetProduct());
1362  if (product && !CFeat_CI(product, CSeqFeatData::eSubtype_prot)) {
1363  SetProteinFeature(*prot, product, src_feat);
1364  }
1365  }
1366  }
1367  else if (src_feat.GetData().GetSubtype() == CSeqFeatData::eSubtype_mRNA) {
1369  if (cds && cds.IsSetProduct()) {
1370  CBioseq_Handle product = scope.GetBioseqHandle(cds.GetProduct());
1371  if (product && !CFeat_CI(product, CSeqFeatData::eSubtype_prot)) {
1372  SetProteinFeature(*prot, product, *cds.GetOriginalSeq_feat());
1373  }
1374  }
1375  }
1376  else if (src_feat.GetData().IsGene()) {
1377  list<CMappedFeat> cds_feats;
1378  feature::GetCdssForGene(scope.GetSeq_featHandle(src_feat), cds_feats);
1379  for (auto& it : cds_feats) {
1380  if (it.IsSetProduct()) {
1381  CBioseq_Handle product = scope.GetBioseqHandle(it.GetProduct());
1382  if (product && !CFeat_CI(product, CSeqFeatData::eSubtype_prot)) {
1383  SetProteinFeature(*prot, product, src_feat);
1384  continue;
1385  }
1386  }
1387  }
1388  }
1389  else if (src_feat.GetData().GetSubtype() == CSeqFeatData::eSubtype_mat_peptide_aa) {
1390  CBioseq_Handle product = scope.GetBioseqHandle(src_feat.GetLocation());
1391  if (product && !CFeat_CI(product, CSeqFeatData::eSubtype_prot)) {
1393  if (cds_feat) {
1394  SetProteinFeature(*prot, product, *cds_feat);
1395  }
1396  }
1397  }
1398  return prot;
1399  }
1400 
1401 }
1402 
1403 ///////////////////////////////////////////////////////////////////////////////
1404 /// class CMacroFunction_CopyStringQual
1405 /// Expected syntax:
1406 /// CopyStringQual( src_field, dest_field, existingtext_opt, delimiter)
1407 /// the last parameter is optional
1408 /// src_field_name and dest_field_name are subfields of the same object
1409 /// Can not use this function to copy from taxname to protein description
1410 
1412 
1414 {
1415  CObjectInfo oi = m_DataIter->GetEditedObject();
1416  CMQueryNodeValue::TObs src_objs;
1417  size_t index = 0;
1418  if (!x_GetSourceFields(oi, index, src_objs))
1419  return;
1420 
1421  CMQueryNodeValue::TObs dest_objs;
1422  if (!x_GetDestFields(oi, ++index, dest_objs))
1423  return;
1424 
1425  const string& action_type = m_Args[++index]->GetString();
1426  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
1427  m_ExistingText = NMacroUtil::ActionTypeToExistingTextOption(action_type, delimiter);
1428 
1429  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
1430  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
1431  if (src_feat && m_Args[1]->IsString() && NStr::EndsWith(m_Args[1]->GetString(), "::product")) {
1433  for (auto&& it : src_objs) {
1434  CMQueryNodeValue::TObs src_prim_objs;
1435  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
1436 
1437  CObjectInfo src = src_prim_objs.front().field;
1438  string src_val = x_GetSourceString(src);
1439 
1440  string remainder;
1441  edit_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
1442  m_QualsChangedCount++;
1443  }
1444  }
1445  else {
1446  ChangeFields(src_objs, dest_objs);
1447  }
1448 
1449  if (m_QualsChangedCount) {
1450  if (!dest_objs.empty()) {
1451  NMacroUtil::CleanupForTaxnameChange(dest_objs.front(), oi);
1452  }
1453  m_DataIter->SetModified();
1455  log << m_DataIter->GetBestDescr() << ": copied " << m_QualsChangedCount << " qualifiers";
1456  x_LogFunction(log);
1457  }
1458 }
1459 
1461 {
1462  size_t arg_size = m_Args.size();
1463  if (arg_size != 3 && arg_size != 4) {
1464  return false;
1465  }
1466 
1467  for (size_t i = 0; i < 2; ++i) {
1468  CMQueryNodeValue::EType type = m_Args[i]->GetDataType();
1469  bool type_ok = (type == CMQueryNodeValue::eString)
1471  || (type == CMQueryNodeValue::eRef);
1472  if (!type_ok)
1473  return false;
1474  }
1475 
1476  for (size_t i = 2; i < arg_size; ++i) {
1477  if (m_Args[i]->GetDataType() != CMQueryNodeValue::eString)
1478  return false;
1479  }
1480  return true;
1481 }
1482 
1484 {
1485  if (dest.GetTypeFamily() != eTypeFamilyPrimitive) {
1486  return false;
1487  }
1488 
1489  string src_val = x_GetSourceString(src);
1490  string dest_val = dest.GetPrimitiveValueString();
1491  if (edit::AddValueToString(dest_val, src_val, m_ExistingText)) {
1492  return SetQualStringValue(dest, dest_val);
1493  }
1494  return false;
1495 }
1496 
1497 
1498 // class CMacroFunction_CopyFeatQual
1499 /// CopyFeatureQual(src_field, dest_feature_type, dest_feature_field, update_mrna, existing_text_opt, delimiter)
1500 /// The last parameter is optional
1501 /// The related mRNA is only updated if the action is successful and the destination feature is protein
1502 ///
1505 {
1506  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
1507  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
1508  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
1509  if (!src_feat || !scope)
1510  return;
1511 
1512  const string& dest_field = m_Args[2]->GetString();
1513  if (dest_field.empty())
1514  return;
1515 
1516  CObjectInfo oi = m_DataIter->GetEditedObject();
1517  CMQueryNodeValue::TObs src_objs;
1518  if (!x_GetSourceFields(oi, 0, src_objs))
1519  return;
1520 
1521  // the destination field can be set at this point
1522  CSeqFeatData::ESubtype target_feature = NMacroUtil::GetFeatSubtype(m_Args[1]->GetString());
1523  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, target_feature, scope);
1524  CMQueryNodeValue::TObs dest_objs;
1525  bool changed = false, created = false;
1526  CRef<CSeq_feat> dest_feat;
1527  CSeq_feat_Handle fh;
1528  if (!feat_list.empty() && feat_list.size() == 1) {
1529  dest_feat.Reset(new CSeq_feat);
1530  dest_feat->Assign(*feat_list.front());
1531  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
1532  return;
1533  }
1534  fh = scope->GetSeq_featHandle(*feat_list.front());
1535  changed = true;
1536  }
1537  else {
1538  // new feature
1539  dest_feat = CreateNewRelatedFeature(*src_feat, dest_field, *scope);
1540  if (!dest_feat) return;
1541  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
1542  return;
1543  }
1544  created = true;
1545  if (src_feat->GetData().IsCdregion() && src_feat->IsSetXref() && dest_feat->GetData().IsProt()) {
1547  NON_CONST_ITERATE(CSeq_feat::TXref, it, edit_feat->SetXref()) {
1548  if ((*it)->IsSetData() && (*it)->GetData().IsProt()) {
1549  dest_feat->SetData().SetProt().Assign((*it)->GetData().GetProt());
1550  edit_feat->SetXref().erase(it);
1551  break;
1552  }
1553  }
1554  if (edit_feat->GetXref().empty()) {
1555  edit_feat->ResetXref();
1556  }
1557  }
1558  }
1559 
1560  size_t index = 3;
1561  bool update_mrna = m_Args[index]->GetBool();
1562  const string& action_type = m_Args[++index]->GetString();
1563  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
1564 
1566 
1567  if (IstRNAProductField(*dest_feat, dest_field)) {
1568  for (auto&& it : src_objs) {
1569  CMQueryNodeValue::TObs src_prim_objs;
1570  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
1571 
1572  CObjectInfo src = src_prim_objs.front().field;
1573  string src_val = x_GetSourceString(src);
1574 
1575  string remainder;
1576  dest_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
1578  }
1579  }
1580  else {
1581  ChangeFields(src_objs, dest_objs);
1582  }
1583 
1584  if (m_QualsChangedCount) {
1585  m_DataIter->SetModified();
1587  log << m_DataIter->GetBestDescr() << ": copied " << m_QualsChangedCount << " qualifiers";
1588 
1590  if (changed && fh) {
1591  cmd.Reset(new CCmdComposite("Change Related feature"));
1592  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
1593  }
1594  else if (created) {
1595  cmd.Reset(new CCmdComposite("Create feature"));
1596  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
1597  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
1598  }
1599 
1600  if (update_mrna) {
1601  if (dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
1602  string message;
1603  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*dest_feat, *scope, message);
1604  if (upd_cmd) {
1605  cmd->AddCommand(*upd_cmd);
1606  log << ", " << message;
1607  }
1608  }
1609  else if (src_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
1611  string prot_product = edit_feat->GetData().GetProt().GetName().front();
1612  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(prot_product, obj, *scope);
1613  if (upd_cmd) {
1614  cmd->AddCommand(*upd_cmd);
1615  log << ", applied " + prot_product + " to mRNA product name ";
1616  }
1617  }
1618  }
1619  m_DataIter->RunCommand(cmd, m_CmdComposite);
1620  x_LogFunction(log);
1621  }
1622 }
1623 
1625 {
1626  if (m_Args.size() < 5 || m_Args.size() > 6)
1627  return false;
1628 
1629  if (!( m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef()))
1630  return false;
1631 
1632  auto type = m_Args[0]->GetDataType();
1633  for (size_t index = 1; index < m_Args.size(); ++index) {
1635  if (m_Args[index]->GetDataType() != type)
1636  return false;
1637  }
1638  return true;
1639 }
1640 
1641 
1642 ///////////////////////////////////////////////////////////////////////////////
1643 // CMacroFunction_CopyRNARelQual
1644 /// CopyRnaRelQual('rna'/'gene', src_field, 'gene'/'rna', dest_field, existing_text_opt, delimiter)
1645 /// The last parameter is optional
1646 /// Use this function when you need to copy from/to an RNA qualifier and a Gene qualifier
1647 /// Don't use this function to copy between two RNA qualifiers
1650 {
1651  // The iterator should be an RNA feature
1652  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
1653  const CSeq_feat* rna_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
1654 
1655  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
1656  if (!rna_feat || !scope)
1657  return;
1658 
1659  if (rna_feat->IsSetData() && !rna_feat->GetData().IsRna())
1660  return;
1661 
1662  const string& src_feat = m_Args[0]->GetString();
1663  const string& dest_feat = m_Args[2]->GetString();
1664  if (src_feat == "rna" && src_feat == dest_feat)
1665  return;
1666 
1667  if ((src_feat != "rna" && src_feat != "gene") || (dest_feat != "rna" && dest_feat != "gene"))
1668  return;
1669 
1670  size_t index = 4;
1671  const string& action_type = m_Args[index]->GetString();
1672  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
1674 
1675  if (src_feat == "rna" && dest_feat == "gene") {
1676  CObjectInfo oi = m_DataIter->GetEditedObject();
1677  CMQueryNodeValue::TObs src_objs;
1678  if (!x_GetSourceFields(oi, 1, src_objs))
1679  return;
1680 
1681  const string dest_field = m_Args[3]->GetString();
1682  // the destination field can be set at this point
1684  CMQueryNodeValue::TObs dest_objs;
1685  bool changed = false, created = false;
1686  CRef<CSeq_feat> dest_feat;
1687  CSeq_feat_Handle fh;
1688  if (!feat_list.empty() && feat_list.size() == 1) {
1689  dest_feat.Reset(new CSeq_feat);
1690  dest_feat->Assign(*feat_list.front());
1691  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
1692  return;
1693  }
1694  fh = scope->GetSeq_featHandle(*feat_list.front());
1695  changed = true;
1696  }
1697  else {
1698  // new feature
1699  dest_feat = CreateNewRelatedFeature(*rna_feat, dest_field, *scope);
1700  if (!dest_feat) return;
1701  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
1702  return;
1703  }
1704  created = true;
1705  }
1706 
1707  ChangeFields(src_objs, dest_objs);
1708 
1709  if (m_QualsChangedCount) {
1711  if (changed && fh) {
1712  cmd.Reset(new CCmdComposite("Change Related feature"));
1713  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
1714  }
1715  else if (created) {
1716  cmd.Reset(new CCmdComposite("Create feature"));
1717  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
1718  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
1719  }
1720 
1721  m_DataIter->RunCommand(cmd, m_CmdComposite);
1723  log << m_DataIter->GetBestDescr() << ": copied " << m_QualsChangedCount << " qualifiers";
1724  x_LogFunction(log);
1725  }
1726  }
1727  else if (src_feat == "gene" && dest_feat == "gene") {
1729  CMQueryNodeValue::TObs src_objs, dest_objs;
1730 
1731  CRef<CSeq_feat> gene_feat;
1732  CSeq_feat_Handle fh;
1733  if (!feat_list.empty() && feat_list.size() == 1) {
1734  gene_feat.Reset(new CSeq_feat);
1735  gene_feat->Assign(*feat_list.front());
1736 
1737  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
1738  if (!x_GetSourceFields(gene_oi, 1, src_objs))
1739  return;
1740 
1741  if (!x_GetDestFields(gene_oi, 3, dest_objs))
1742  return;
1743 
1744  fh = scope->GetSeq_featHandle(*feat_list.front());
1745  }
1746  else {
1747  // no gene was found
1748  return;
1749  }
1750 
1751  ChangeFields(src_objs, dest_objs);
1752 
1753  if (m_QualsChangedCount) {
1755  if (fh) {
1756  cmd.Reset(new CCmdComposite("Change Related feature"));
1757  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *gene_feat)));
1758  }
1759 
1760  m_DataIter->RunCommand(cmd, m_CmdComposite);
1762  log << m_DataIter->GetBestDescr() << ": copied " << m_QualsChangedCount << " qualifiers";
1763  x_LogFunction(log);
1764  }
1765  }
1766  else if (src_feat == "gene" && dest_feat == "rna") {
1767 
1769  CMQueryNodeValue::TObs src_objs, dest_objs;
1770  CRef<CSeq_feat> gene_feat;
1771  if (!feat_list.empty() && feat_list.size() == 1) {
1772  gene_feat.Reset(new CSeq_feat);
1773  gene_feat->Assign(*feat_list.front());
1774 
1775  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
1776  if (!x_GetSourceFields(gene_oi, 1, src_objs))
1777  return;
1778  }
1779  else {
1780  // no gene was found
1781  return;
1782  }
1783 
1784  CObjectInfo oi = m_DataIter->GetEditedObject();
1785  if (!x_GetDestFields(oi, 3, dest_objs))
1786  return;
1787 
1789  if (m_Args[3]->IsString() && NStr::EndsWith(m_Args[3]->GetString(), "::product")) {
1790  for (auto&& it : src_objs) {
1791  CMQueryNodeValue::TObs src_prim_objs;
1792  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
1793 
1794  CObjectInfo src = src_prim_objs.front().field;
1795  string src_val = x_GetSourceString(src);
1796 
1797  string remainder;
1798  edit_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
1800  }
1801  }
1802  else {
1803  ChangeFields(src_objs, dest_objs);
1804  }
1805 
1806  if (m_QualsChangedCount) {
1807  m_DataIter->SetModified();
1809  log << m_DataIter->GetBestDescr() << ": copied " << m_QualsChangedCount << " qualifiers";
1810  x_LogFunction(log);
1811  }
1812  }
1813 }
1814 
1816 {
1817  if (m_Args.size() < 5 || m_Args.size() > 6)
1818  return false;
1819 
1820  if (!m_Args[0]->IsString() || !m_Args[2]->IsString() || !m_Args[4]->IsString())
1821  return false;
1822 
1823  for (size_t index = 1; index < 4; index += 2) {
1824  if (!(m_Args[index]->IsString() || m_Args[index]->AreObjects() || m_Args[index]->IsRef()))
1825  return false;
1826  }
1827  return (m_Args.size() == 5 || (m_Args.size() == 6 && m_Args[5]->IsString()));
1828 }
1829 
1830 
1831 ///////////////////////////////////////////////////////////////////////////////
1832 // CMacroFunction_SetRelFeatQual
1833 /// SetRelatedFeatureQual(feat_type, field_name, new_value, existing_text_option, delimiter, remove_blank, update_mrna)
1834 /// The last three parameters are optional
1835 ///
1838 {
1839  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
1840  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
1841  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
1842  if (!src_feat || !scope)
1843  return;
1844 
1845  CObjectInfo oi = m_DataIter->GetEditedObject();
1846  const string& field_name = m_Args[1]->GetString();
1847  CSeqFeatData::ESubtype feat_type = NMacroUtil::GetFeatSubtype(m_Args[0]->GetString());
1848  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, feat_type, scope);
1849 
1850  size_t index = 2;
1851  string newValue = NMacroUtil::GetStringValue(m_Args[index]);
1852  const string& action_type = m_Args[++index]->GetString();
1853  string delimiter;
1854  bool remove_field = false;
1855  bool update_mrna = false;
1856  size_t arg_nr = m_Args.size();
1857 
1858  if (++index < arg_nr) {
1859  if (m_Args[index]->IsString()) {
1860  delimiter = m_Args[index]->GetString();
1861  if (++index < arg_nr) {
1862  remove_field = m_Args[index]->GetBool();
1863  if (++index < arg_nr)
1864  update_mrna = m_Args[index]->GetBool();
1865  }
1866  }
1867  else if (m_Args[index]->IsBool()) {
1868  remove_field = m_Args[index]->GetBool();
1869  if (++index < arg_nr)
1870  update_mrna = m_Args[index]->GetBool();
1871  }
1872  }
1874 
1875  if (!newValue.empty()) {
1877  bool changed = false, created = false;
1878  CRef<CSeq_feat> dest_feat;
1879  CSeq_feat_Handle fh;
1880  if (!feat_list.empty() && feat_list.size() == 1) {
1881  dest_feat.Reset(new CSeq_feat);
1882  dest_feat->Assign(*feat_list.front());
1883  if (!SetFeatDestinationField(dest_feat, field_name, objs)) {
1884  return;
1885  }
1886  fh = scope->GetSeq_featHandle(*feat_list.front());
1887  changed = true;
1888  }
1889  else {
1890  // new feature
1891  dest_feat = CreateNewRelatedFeature(*src_feat, field_name, *scope);
1892  if (!dest_feat) return;
1893  if (!SetFeatDestinationField(dest_feat, field_name, objs)) {
1894  return;
1895  }
1896  created = true;
1897  if (src_feat->GetData().IsCdregion() && src_feat->IsSetXref() && dest_feat->GetData().IsProt()) {
1899  NON_CONST_ITERATE(CSeq_feat::TXref, it, edit_feat->SetXref()) {
1900  if ((*it)->IsSetData() && (*it)->GetData().IsProt()) {
1901  dest_feat->SetData().SetProt().Assign((*it)->GetData().GetProt());
1902  edit_feat->SetXref().erase(it);
1904  break;
1905  }
1906  }
1907  if (edit_feat->GetXref().empty()) {
1908  edit_feat->ResetXref();
1909  }
1910  }
1911  }
1912 
1913  vector<string> new_values;
1914  x_SetFields(objs, newValue, existing_text, new_values);
1915 
1916  if (m_QualsChangedCount) {
1917  m_DataIter->SetModified();
1919  log << m_DataIter->GetBestDescr();
1920  for (size_t i = 0; i < new_values.size(); ++i) {
1921  log << ": set '" << new_values[i] << "' as a new value";
1922  }
1923 
1924  if (changed && fh) {
1925  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
1926  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
1927  if (update_mrna && dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
1928  string prot_product = dest_feat->GetData().GetProt().GetName().front();
1929  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(prot_product, feat_list.front(), *scope);
1930  if (upd_cmd) {
1931  cmd->AddCommand(*upd_cmd);
1932  log << ", applied " + prot_product + " to mRNA product name ";
1933  }
1934  }
1935  m_DataIter->RunCommand(cmd, m_CmdComposite);
1936  }
1937  else if (created) {
1938  CRef<CCmdComposite> cmd(new CCmdComposite("Create feature"));
1939  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
1940  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
1941  if (update_mrna && dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
1942  string message;
1943  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*dest_feat, *scope, message);
1944  if (upd_cmd) {
1945  cmd->AddCommand(*upd_cmd);
1946  log << ", " << message;
1947  }
1948  }
1949  m_DataIter->RunCommand(cmd, m_CmdComposite);
1950  }
1951 
1952  x_LogFunction(log);
1953  }
1954  }
1955  else if (remove_field) {
1957  CRef<CSeq_feat> dest_feat;
1958  CSeq_feat_Handle fh;
1959  if (!feat_list.empty() && feat_list.size() == 1) {
1960  dest_feat.Reset(new CSeq_feat);
1961  dest_feat->Assign(*feat_list.front());
1962  if (!GetFeatDestinationField(dest_feat, field_name, objs)) {
1963  return;
1964  }
1965  fh = scope->GetSeq_featHandle(*feat_list.front());
1966  }
1967 
1968  for (auto& it : objs) {
1969  if (RemoveFieldByName(it)) {
1971  }
1972  }
1973 
1974  if (m_QualsChangedCount) {
1976  if (fh) {
1977  CCleanup cleanup(scope);
1978  cleanup.BasicCleanup(*dest_feat);
1979 
1980  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
1981  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
1982  log << m_DataIter->GetBestDescr() << ": removed " << m_QualsChangedCount << " qualifiers";
1983 
1984  if (update_mrna && dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
1985  string message;
1986  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*dest_feat, *scope, message);
1987  if (upd_cmd) {
1988  cmd->AddCommand(*upd_cmd);
1989  log << ", " << message;
1990  }
1991  }
1992  m_DataIter->RunCommand(cmd, m_CmdComposite);
1993  }
1994  x_LogFunction(log);
1995  }
1996  }
1997 }
1998 
2000 {
2001  // can accept as its first parameter: objects, string or reference
2002  size_t arg_nr = m_Args.size();
2003  if (arg_nr < 4 && arg_nr > 7) {
2004  return false;
2005  }
2006 
2007  NMacroUtil::GetPrimitiveFromRef(m_Args[2].GetNCObject());
2008  size_t index = 0;
2009  for (; index < 4; ++index) {
2010  if (!m_Args[index]->IsString())
2011  return false;
2012  }
2013 
2014  if (index < arg_nr && (!m_Args[index]->IsString() && !m_Args[index]->IsBool())) return false;
2015  if (++index < arg_nr && !m_Args[index]->IsBool()) return false;
2016  if (++index < arg_nr && !m_Args[index]->IsBool()) return false;
2017  return true;
2018 }
2019 
2020 
2021 ///////////////////////////////////////////////////////////////////////////////
2022 // CMacroFunction_EditRelFeatQual
2023 /// EditRelatedFeatureQual(feat_type, field_name, find_text, repl_text, location, case_sensitive, is_regex)
2024 ///
2025 const char* CMacroFunction_EditRelFeatQual::sm_FunctionName = "EditRelatedFeatureQual";
2027 {
2028  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2029  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
2030  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
2031  if (!src_feat || !scope)
2032  return;
2033 
2034  size_t index = 2;
2035  const string& find_txt = NMacroUtil::GetStringValue(m_Args[index]);
2036  string repl_txt = NMacroUtil::GetStringValue(m_Args[++index]);
2037  const string& location = m_Args[++index]->GetString();
2038  bool case_sensitive = m_Args[++index]->GetBool();
2039  bool is_regex = (++index < m_Args.size()) ? m_Args[index]->GetBool() : false;
2040 
2041  CObjectInfo oi = m_DataIter->GetEditedObject();
2042  const string& field_name = m_Args[1]->GetString();
2043  CSeqFeatData::ESubtype feat_type = NMacroUtil::GetFeatSubtype(m_Args[0]->GetString());
2044  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, feat_type, scope);
2045 
2047  CRef<CSeq_feat> dest_feat;
2048  CSeq_feat_Handle fh;
2049  if (!feat_list.empty() && feat_list.size() == 1) {
2050  dest_feat.Reset(new CSeq_feat);
2051  dest_feat->Assign(*feat_list.front());
2052  if (!GetFeatDestinationField(dest_feat, field_name, objs)) {
2053  return;
2054  }
2055  fh = scope->GetSeq_featHandle(*feat_list.front());
2056  }
2057 
2058  x_EditFields(objs, find_txt, repl_txt, s_GetLocFromName(location), case_sensitive, is_regex);
2059  if (m_QualsChangedCount) {
2060  if (fh) {
2061  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
2062  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
2063  m_DataIter->RunCommand(cmd, m_CmdComposite);
2064  }
2065 
2067  log << m_DataIter->GetBestDescr() << ": edited " << m_QualsChangedCount << " qualifiers, replaced " << find_txt;
2068  if (NStr::IsBlank(repl_txt)) {
2069  repl_txt.assign("''");
2070  }
2071  log << " with " << repl_txt;
2072  x_LogFunction(log);
2073  }
2074 }
2075 
2077 {
2078  if (m_Args.size() != 6 && m_Args.size() != 7)
2079  return false;
2080 
2081  for (size_t index = 2; index < 4; index++) {
2082  if (m_Args[index]->GetDataType() != CMQueryNodeValue::eString && m_Args[index]->GetDataType() != CMQueryNodeValue::eInt) {
2083  return false;
2084  }
2085  }
2086 
2087  if (m_Args.size() == 7) {
2088  if (m_Args.back()->GetDataType() != CMQueryNodeValue::eBool)
2089  return false;
2090  }
2091  return (m_Args[0]->GetDataType() == CMQueryNodeValue::eString
2092  && m_Args[1]->GetDataType() == CMQueryNodeValue::eString
2093  && m_Args[5]->GetDataType() == CMQueryNodeValue::eBool);
2094 }
2095 
2096 
2097 ///////////////////////////////////////////////////////////////////////////////
2098 // CMacroFunction_RmvRelFeatQual
2099 /// RemoveRelatedFeatureQual(feat_type, field_name)
2100 ///
2101 DEFINE_MACRO_FUNCNAME(CMacroFunction_RmvRelFeatQual, "RemoveRelatedFeatureQual")
2102 void CMacroFunction_RmvRelFeatQual::TheFunction()
2103 {
2104  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2105  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
2106  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
2107  if (!src_feat || !scope)
2108  return;
2109 
2110  CObjectInfo oi = m_DataIter->GetEditedObject();
2111  const string& field_name = m_Args[1]->GetString();
2112  CSeqFeatData::ESubtype feat_type = NMacroUtil::GetFeatSubtype(m_Args[0]->GetString());
2113  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, feat_type, scope);
2114 
2116  CRef<CSeq_feat> dest_feat;
2117  CSeq_feat_Handle fh;
2118  if (!feat_list.empty() && feat_list.size() == 1) {
2119  dest_feat.Reset(new CSeq_feat);
2120  dest_feat->Assign(*feat_list.front());
2121  if (!GetFeatDestinationField(dest_feat, field_name, objs)) {
2122  return;
2123  }
2124  fh = scope->GetSeq_featHandle(*feat_list.front());
2125  }
2126 
2127  for (auto& it : objs) {
2128  if (RemoveFieldByName(it)) {
2129  m_QualsChangedCount++;
2130  }
2131  }
2132 
2133 
2134  if (m_QualsChangedCount) {
2135  if (fh) {
2136  CCleanup cleanup(scope);
2137  cleanup.BasicCleanup(*dest_feat);
2138 
2139  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
2140  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
2141  m_DataIter->RunCommand(cmd, m_CmdComposite);
2142  }
2144  log << m_DataIter->GetBestDescr() << ": removed " << m_QualsChangedCount << " qualifiers";
2145  x_LogFunction(log);
2146  }
2147 }
2148 
2149 bool CMacroFunction_RmvRelFeatQual::x_ValidArguments() const
2150 {
2151  if (m_Args.size() != 2)
2152  return false;
2153 
2154  //TODO - extend it to accept reference, for example: gene synonym with a constraint
2155  return (m_Args[0]->IsString() && m_Args[1]->IsString());
2156 }
2157 
2158 
2159 ///////////////////////////////////////////////////////////////////////////////
2160 // class CMacroFunction_RmvOutsideRelFeatQual
2161 /// RmvOutsideRelatedFeatureQual(feat_type, field_name, before_match(b), left_del(str), rmv_left(b),
2162 /// after_match(b), right_del(str), rmv_right(b), case_insensitive(b), whole_word(b), update_mrna(b))
2163 
2164 DEFINE_MACRO_FUNCNAME(CMacroFunction_RmvOutsideRelFeatQual, "RmvOutsideRelatedFeatureQual")
2165 
2167 {
2168  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2169  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
2170  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
2171  if (!src_feat || !scope)
2172  return;
2173 
2174  CObjectInfo oi = m_DataIter->GetEditedObject();
2175  const string& field_name = m_Args[1]->GetString();
2176  CSeqFeatData::ESubtype feat_type = NMacroUtil::GetFeatSubtype(m_Args[0]->GetString());
2177  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, feat_type, scope);
2178 
2179 
2181  CRef<CSeq_feat> dest_feat;
2182  CSeq_feat_Handle fh;
2183  if (!feat_list.empty() && feat_list.size() == 1) {
2184  dest_feat.Reset(new CSeq_feat);
2185  dest_feat->Assign(*feat_list.front());
2186  if (!GetFeatDestinationField(dest_feat, field_name, objs)) {
2187  return;
2188  }
2189  fh = scope->GetSeq_featHandle(*feat_list.front());
2190  }
2191 
2192  CRef<CRemoveTextOptions> options = x_GetRemoveTextOptions(2);
2193  x_RmvOutsideFields(objs, *options);
2194  if (m_QualsChangedCount) {
2195  if (fh) {
2196  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
2197  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
2198  m_DataIter->RunCommand(cmd, m_CmdComposite);
2199  }
2200 
2202  log << m_DataIter->GetBestDescr() << ": removed text outside string in " << m_QualsChangedCount << " qualifiers";
2203 
2204  bool update_mrna = (m_Args.size() == 11) ? m_Args[10]->GetBool() : false;
2205  if (update_mrna && dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
2206  string message;
2207  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
2208  string prot_product = dest_feat->GetData().GetProt().GetName().front();
2209  CRef<CCmdComposite> cmd = UpdatemRNAProduct(prot_product, feat_list.front(), *scope);
2210  if (cmd) {
2211  m_DataIter->RunCommand(cmd, m_CmdComposite);
2212  log << ", applied " + prot_product + " to mRNA product name ";
2213  }
2214  }
2215 
2216  x_LogFunction(log);
2217  }
2218 }
2219 
2221 {
2222  if (m_Args.size() != 10 && m_Args.size() != 11)
2223  return false;
2224 
2225  if (m_Args[0]->GetDataType() != CMQueryNodeValue::eString || m_Args[1]->GetDataType() != CMQueryNodeValue::eString)
2226  return false;
2227 
2228  return x_CheckArguments(2);
2229 }
2230 
2231 
2232 // IOperateOnTwoQuals interface
2234 {
2235  _ASSERT(!src_objs.empty() && !dest_objs.empty());
2236  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
2237  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
2238 
2239  while (src_it != src_objs.end()) {
2240  CMQueryNodeValue::TObs src_prim_objs;
2241  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, *src_it);
2242  CMQueryNodeValue::TObs dest_prim_objs;
2243  NMacroUtil::GetPrimitiveObjectInfos(dest_prim_objs, *dest_it);
2244  if (dest_prim_objs.empty() && dest_it->field.GetTypeFamily() == eTypeFamilyContainer) {
2245  // add new element when the container is empty
2246  CObjectInfo new_oi(dest_it->field.AddNewElement());
2247  dest_prim_objs.push_back(CMQueryNodeValue::SResolvedField(dest_it->field, new_oi));
2248  }
2249  for (auto& it : src_prim_objs) {
2250  x_ChangeFields(it.field, dest_prim_objs.front().field);
2251  }
2252 
2253  ++src_it;
2254  ++dest_it;
2255  if (dest_it == dest_objs.end()) {
2256  --dest_it;
2257  }
2258  }
2259 }
2260 
2262 {
2263  if (index >= m_Args.size())
2264  return false;
2265 
2266  // the source field has to be set
2267  CMQueryNodeValue::EType src_type = m_Args[index]->GetDataType();
2268 
2269  if (src_type == CMQueryNodeValue::eString) {
2270  if (!GetFieldsByName(&result, oi, m_Args[index]->GetString()))
2271  return false;
2272  }
2273  else if (src_type == CMQueryNodeValue::eObjects) {
2274  result = m_Args[index]->GetObjects();
2275  }
2276  else if (src_type == CMQueryNodeValue::eRef) {
2277  x_GetObjectsFromRef(result, index);
2279  }
2280 
2281  return (!result.empty());
2282 }
2283 
2285 {
2286  // the destination field might be set during this action
2287  CMQueryNodeValue::EType dest_type = m_Args[index]->GetDataType();
2288 
2289  if (dest_type == CMQueryNodeValue::eString) {
2290  const string& dest_field = m_Args[index]->GetString();
2291  CConstRef<CObject> object = m_DataIter->GetScopedObject().object;
2292  bool valid = GetDestinationObjects(object, oi, dest_field, result);
2293  if (valid && result.empty()) // for tRNA product
2294  return valid;
2295  }
2296  else if (dest_type == CMQueryNodeValue::eObjects) {
2297  result = m_Args[index]->GetObjects();
2298  }
2299  else if (dest_type == CMQueryNodeValue::eRef) {
2300  x_GetObjectsFromRef(result, index);
2301  }
2302  return !result.empty();
2303 }
2304 
2306 {
2307  string src_val;
2308  if (src.GetTypeFamily() == eTypeFamilyPrimitive) {
2309  switch (src.GetPrimitiveValueType()) {
2310  case ePrimitiveValueString:
2311  src_val = src.GetPrimitiveValueString();
2312  // strip_name is not implemented yet
2313  break;
2314  case ePrimitiveValueEnum: //e.g., codon-start
2315  src_val = NStr::IntToString(src.GetPrimitiveValueInt());
2316  break;
2317  case ePrimitiveValueInteger: // e.g., local_id
2318  src_val = NStr::IntToString(src.GetPrimitiveValueInt4());
2319  break;
2320  case ePrimitiveValueReal:
2322  break;
2323  case ePrimitiveValueBool:
2324  src_val = NStr::BoolToString(src.GetPrimitiveValueBool());
2325  break;
2326  default:
2327  break;
2328  }
2329  }
2330  else if (src.GetTypeFamily() == eTypeFamilyClass) {
2331  if (src.GetName() == "Dbtag") {
2333  if (tag) {
2334  tag->GetLabel(&src_val);
2335  }
2336  }
2337  }
2338  return src_val;
2339 }
2340 
2341 
2342 ///////////////////////////////////////////////////////////////////////////////
2343 /// class CMacroFunction_ConvertStringQual
2344 /// ConvertStringQual(src_field, dst_field, capitalization, strip_name, existing_text_opt, delimiter)
2345 /// last parameter is optional
2346 /// The function will NOT delete the src_field.
2347 /// If the user wants to delete the src_field, (s)he should use RemoveQual() or RemoveModifier() functions
2348 ///
2350 
2352 {
2353  CObjectInfo oi = m_DataIter->GetEditedObject();
2354  CMQueryNodeValue::TObs src_objs;
2355  size_t index = 0;
2356  if (!x_GetSourceFields(oi, index, src_objs))
2357  return;
2358 
2359  CMQueryNodeValue::TObs dest_objs;
2360  if (!x_GetDestFields(oi, ++index, dest_objs))
2361  return;
2362 
2363  const string& capitalization = m_Args[++index]->GetString();
2364  //bool strip_name = m_Args[++index]->GetBool();
2365  //strip_name = false; // this option is not implemented ( it's false by default)
2366  ++index;
2367  const string& action_type = m_Args[++index]->GetString();
2368  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
2369  m_ExistingText = NMacroUtil::ActionTypeToExistingTextOption(action_type, delimiter);
2370  m_CapChange = NMacroUtil::ConvertStringtoCapitalOption(capitalization);
2371 
2372  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2373  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
2374  if (src_feat && m_Args[1]->IsString() && NStr::EndsWith(m_Args[1]->GetString(), "::product")) {
2376  for (auto&& it : src_objs) {
2377  CMQueryNodeValue::TObs src_prim_objs;
2378  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
2379 
2380  CObjectInfo src = src_prim_objs.front().field;
2381  string src_val = x_GetSourceString(src);
2382 
2383  string remainder;
2384  edit_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
2385  m_QualsChangedCount++;
2386  }
2387  }
2388  else {
2389  ChangeFields(src_objs, dest_objs);
2390  }
2391 
2392  if (m_QualsChangedCount) {
2393  if (!dest_objs.empty()) {
2394  NMacroUtil::CleanupForTaxnameChange(dest_objs.front(), oi);
2395  }
2396  m_DataIter->SetModified();
2398  log << m_DataIter->GetBestDescr() << ": converted " << m_QualsChangedCount << " qualifiers";
2399  x_LogFunction(log);
2400  }
2401 }
2402 
2404 {
2405  if (dest.GetTypeFamily() != eTypeFamilyPrimitive) {
2406  return false;
2407  }
2408 
2409  string src_val = x_GetSourceString(src);
2411  string dest_val = dest.GetPrimitiveValueString();
2412  if (edit::AddValueToString(dest_val, src_val, m_ExistingText)) {
2413  return SetQualStringValue(dest, dest_val);
2414  }
2415  }
2416  return false;
2417 }
2418 
2420 {
2421  string src_val = IOperateOnTwoQuals::x_GetSourceString(src);
2422  CSeq_entry_Handle seh = m_DataIter->GetSEH();
2423  FixCapitalizationInString(seh, src_val, m_CapChange);
2424  return src_val;
2425 }
2426 
2427 
2429 {
2430  size_t as = m_Args.size();
2431  if (!(as == 5 || as == 6)) {
2432  return false;
2433  }
2434 
2435  for (size_t i = 0; i < 2; ++i) {
2436  CMQueryNodeValue::EType type = m_Args[i]->GetDataType();
2437  bool type_ok = (type == CMQueryNodeValue::eString)
2439  || (type == CMQueryNodeValue::eRef);
2440  if (!type_ok)
2441  return false;
2442  }
2443 
2444  bool m_Args_ok = (m_Args[2]->GetDataType() == CMQueryNodeValue::eString);
2445  m_Args_ok = m_Args_ok && (m_Args[3]->GetDataType() == CMQueryNodeValue::eBool)
2446  && (m_Args[4]->GetDataType() == CMQueryNodeValue::eString);
2447 
2448  if (as == 6) {
2449  m_Args_ok = m_Args_ok && (m_Args[5]->GetDataType() == CMQueryNodeValue::eString);
2450  }
2451 
2452  if (!m_Args_ok) {
2453  return false;
2454  }
2455 
2456  return true;
2457 }
2458 
2459 
2460 ///////////////////////////////////////////////////////////////////////////////
2461 /// class CMacroFunction_SwapQual
2462 /// SwapQual(src_fieldname, dest_fieldname)
2463 /// Swaps the content of two fields. If one field is blank/does not exist, the other field will be removed
2464 ///
2466 
2467 void CMacroFunction_SwapQual::TheFunction()
2468 {
2469  CObjectInfo oi = m_DataIter->GetEditedObject();
2470  CMQueryNodeValue::TObs src_objs;
2471  CMQueryNodeValue::TObs dest_objs;
2472  size_t src_index = 0, dest_index = 1;
2473 
2474  if ((m_Args.size() == 3 && m_Args[0]->GetDataType() != CMQueryNodeValue::eString)
2475  || m_Args.size() == 4) {
2476  dest_index = 2;
2477  }
2478 
2479  // check that at least one of the fields exist
2480  bool src_set = x_DoFieldsExist(oi, src_objs, src_index);
2481  bool dest_set = x_DoFieldsExist(oi, dest_objs, dest_index);
2482 
2483  if (!src_set && !dest_set)
2484  return;
2485 
2486  // special attention to gene qualifiers that are removed from gene Xrefs
2487  bool is_gene_suppressed_before = false;
2488  bool is_gene_suppressed_after = false;
2489  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2490  if (dynamic_cast<const CSeq_feat*>(obj.GetPointer())) {
2492  auto gene_xref = feat->GetGeneXref();
2493  is_gene_suppressed_before = gene_xref && gene_xref->IsSuppressed();
2494  }
2495 
2496  if (!src_set) {
2497  x_SetFields(oi, src_objs, src_index);
2498  }
2499  if (!dest_set) {
2500  x_SetFields(oi, dest_objs, dest_index);
2501  }
2502 
2503  if (src_set && dest_set) {
2504  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
2505  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
2506 
2507  while (src_it != src_objs.end() && dest_it != dest_objs.end()) {
2508  CMQueryNodeValue::TObs src_prim_objs;
2509  NMacroUtil::GetPrimitiveObjInfosWithContainers(src_prim_objs, *src_it);
2510  CMQueryNodeValue::TObs dest_prim_objs;
2511  NMacroUtil::GetPrimitiveObjInfosWithContainers(dest_prim_objs, *dest_it);
2512  if (dest_prim_objs.empty() && dest_it->field.GetTypeFamily() == eTypeFamilyContainer) {
2513  // add new element when the container is empty
2514  CObjectInfo new_oi(dest_it->field.AddNewElement());
2515  dest_prim_objs.push_back(CMQueryNodeValue::SResolvedField(dest_it->field, new_oi));
2516  }
2517  x_SwapFields(src_prim_objs.front().field, dest_prim_objs.front().field);
2518 
2519  ++src_it;
2520  ++dest_it;
2521  }
2522  NMacroUtil::CleanupForTaxnameChange(src_objs.front(), oi);
2523  NMacroUtil::CleanupForTaxnameChange(dest_objs.front(), oi);
2524  }
2525  else if (!src_set) {
2526  // copy destination field into the source field
2527  x_CopyFields(dest_objs, src_objs);
2528 
2529  bool is_taxname = NMacroUtil::IsTaxname(dest_objs.front());
2530  // delete destination field
2531  NON_CONST_ITERATE(CMQueryNodeValue::TObs, it, dest_objs) {
2532  RemoveFieldByName(*it);
2533  }
2534  if (is_taxname) {
2536  }
2537  }
2538  else if (!dest_set) {
2539  // copy source field into the destination field
2540  x_CopyFields(src_objs, dest_objs);
2541 
2542  bool is_taxname = NMacroUtil::IsTaxname(src_objs.front());
2543  // delete source field
2544  NON_CONST_ITERATE(CMQueryNodeValue::TObs, it, src_objs) {
2545  RemoveFieldByName(*it);
2546  }
2547  if (is_taxname) {
2549  }
2550  }
2551 
2552  CCleanup cleanup;
2553  if (dynamic_cast<const CSeq_feat*>(obj.GetPointer())) {
2555  cleanup.BasicCleanup(*seq_feat);
2556  if (seq_feat->IsSetXref()) {
2557  auto gene_xref = seq_feat->GetGeneXref();
2558  is_gene_suppressed_after = gene_xref->IsSuppressed();
2559  if (!is_gene_suppressed_before && is_gene_suppressed_after)
2560  NMacroUtil::RemoveGeneXref(*seq_feat);
2561  }
2562  }
2563  else if (dynamic_cast<const CBioSource*>(obj.GetPointer())) {
2565  cleanup.BasicCleanup(*bsrc);
2566  if (bsrc->IsSetOrg() && bsrc->GetOrg().IsSetDb() && bsrc->GetOrg().GetDb().empty()) {
2567  bsrc->SetOrg().ResetDb();
2568  }
2569  }
2570 
2571  if (m_QualsChangedCount) {
2572  m_DataIter->SetModified();
2574  log << m_DataIter->GetBestDescr() << ": swapped " << m_QualsChangedCount << " qualifiers";
2575  x_LogFunction(log);
2576  }
2577 }
2578 
2580 {
2581  if (m_Args.size() < 2) {
2582  return false;
2583  }
2584 
2585  for (auto& it : m_Args) {
2586  CMQueryNodeValue::EType type = it->GetDataType();
2588  return false;
2589  }
2590  }
2591  return true;
2592 }
2593 
2594 
2596 {
2597  if (index >= m_Args.size())
2598  return false;
2599 
2600  CMQueryNodeValue::EType type = m_Args[index]->GetDataType();
2602  GetFieldsByName(&result, oi, m_Args[index]->GetString());
2603  }
2604  else if (type == CMQueryNodeValue::eObjects) {
2605  result = m_Args[index]->GetObjects();
2606  }
2607  else if (type == CMQueryNodeValue::eRef) {
2608  x_GetObjectsFromRef(result, index);
2609  }
2610 
2611  return (!result.empty());
2612 }
2613 
2615 {
2616  if (index >= m_Args.size())
2617  return false;
2618 
2619  // the field does not have to be set
2620  CMQueryNodeValue::EType type = m_Args[index]->GetDataType();
2622  SetFieldsByName(&result, oi, m_Args[index]->GetString());
2623  }
2624  else {
2625  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2626  x_SetField(obj, m_Args[++index]->GetString(), result);
2627  }
2628  return (!result.empty());
2629 }
2630 
2631 static void s_SetBioSourceField(CRef<CBioSource> bsrc, CObjectInfo& oi, const string& field_name, CMQueryNodeValue::TObs& dest_objs)
2632 {
2633  if (field_name.empty())
2634  return;
2635 
2636  // check if the field denotes an orgmod or a subsource modifier
2637  if (NMacroUtil::IsBiosourceModifier(field_name)) {
2638  // check if it's a subsource modifier
2641 
2642  CRef<CSubSource> sub_src(new CSubSource());
2643  sub_src->SetSubtype(st);
2644  sub_src->SetName(kEmptyStr);
2645  bsrc->SetSubtype().push_back(sub_src);
2646  CObjectInfo subsrc_oi(sub_src.GetPointer(), sub_src->GetTypeInfo());
2647  dest_objs.push_back(CMQueryNodeValue::SResolvedField(oi, subsrc_oi));
2648  // check if it's an orgmod modifier
2649  }
2652 
2653  CRef<COrgMod> orgmod(new COrgMod());
2654  orgmod->SetSubtype(st);
2655  orgmod->SetSubname(kEmptyStr);
2656  CRef<COrgName> orgname;
2657  if (!bsrc->IsSetOrgname()) {
2658  orgname.Reset(new COrgName());
2659  orgname->SetMod().push_back(orgmod);
2660  bsrc->SetOrg().SetOrgname(*orgname);
2661  }
2662  else {
2663  orgname = Ref(&(bsrc->SetOrg().SetOrgname()));
2664  orgname->SetMod().push_back(orgmod);
2665  }
2666  CObjectInfo parent_mod_oi(orgname.GetPointer(), orgname->GetThisTypeInfo());
2667  CObjectInfo orgmod_oi(orgmod.GetPointer(), orgmod->GetThisTypeInfo());
2668  dest_objs.push_back(CMQueryNodeValue::SResolvedField(parent_mod_oi, orgmod_oi));
2669  }
2670  }
2671  else {
2672  SetFieldsByName(&dest_objs, oi, field_name);
2673  }
2674 }
2675 
2676 static void s_SetFeatField(CRef<CSeq_feat> feat, CObjectInfo& oi,const string& field_name, CMQueryNodeValue::TObs& result)
2677 {
2678  if (field_name.empty())
2679  return;
2680 
2681  if (SetFieldsByName(&result, oi, field_name)) {
2682  return;
2683  }
2684 
2685  CRef<CGb_qual> new_gbqual(new CGb_qual(field_name, kEmptyStr));
2686  feat->SetQual().push_back(new_gbqual);
2687  CObjectInfo gbqual_oi(new_gbqual.GetPointer(), new_gbqual->GetTypeInfo());
2688  result.push_back(CMQueryNodeValue::SResolvedField(oi, gbqual_oi));
2689 }
2690 
2692 {
2693  CObjectInfo oi = m_DataIter->GetEditedObject();
2694  const CBioSource* cbsrc = dynamic_cast<const CBioSource*>(obj.GetPointer());
2695  const CSeq_feat* cfeat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
2696  if (cbsrc) {
2698  return s_SetBioSourceField(Ref(bsrc), oi, field_name, result);
2699  }
2700  if (cfeat) {
2702  return s_SetFeatField(Ref(feat), oi, field_name, result);
2703  }
2704 }
2705 
2706 static string s_GetEnumValue(CObjectInfo& obj)
2707 {
2708  string value;
2710  try {
2712  }
2713  catch (const CException&) {
2715  }
2716  }
2717  return value;
2718 }
2719 
2720 static bool s_SetEnumValue(CObjectInfo& obj, const string& str)
2721 {
2722  bool set = false;
2724  try {
2726  set = true;
2727  }
2728  catch (const CException&) {
2729  // if this fails, try to convert the string to int and assign again
2730  try {
2732  set = true;
2733  }
2734  catch (const CException&) {}
2735  }
2736  }
2737  return set;
2738 }
2739 
2740 
2742 {
2743  if (src.GetTypeFamily() != dest.GetTypeFamily() || src.GetTypeFamily() != eTypeFamilyPrimitive) {
2744  return false;
2745  }
2746 
2747  bool swapped = false;
2749  string tmp = src.GetPrimitiveValueString();
2753  swapped = true;
2754  }
2755  else if (dest.GetPrimitiveValueType() == ePrimitiveValueEnum) {
2756  string dest_value = s_GetEnumValue(dest);
2757  src.SetPrimitiveValueString(dest_value);
2758  swapped = s_SetEnumValue(dest, tmp);
2759  }
2760  }
2761  else if (src.GetPrimitiveValueType() == ePrimitiveValueEnum) {
2762  string tmp = s_GetEnumValue(src);
2764  swapped = s_SetEnumValue(src, dest.GetPrimitiveValueString());
2766  }
2767  else if (dest.GetPrimitiveValueType() == ePrimitiveValueEnum) {
2768  string dest_value = s_GetEnumValue(dest);
2769  swapped = s_SetEnumValue(src, dest_value);
2770  swapped = swapped && s_SetEnumValue(dest, tmp);
2771  }
2772  }
2773 
2774  if (swapped)
2776  return swapped;
2777 }
2778 
2780 {
2781  // swaps primitive values
2782  // both src and dest should be of primitive type
2783  if (src.GetTypeFamily() != dest.GetTypeFamily() || src.GetTypeFamily() != eTypeFamilyPrimitive) {
2784  return false;
2785  }
2786 
2787  switch (src.GetPrimitiveValueType()) {
2788  case ePrimitiveValueBool: {
2789  bool tmp = dest.GetPrimitiveValueBool();
2792  return true;
2793  }
2795  case ePrimitiveValueChar: {
2796  int tmp = dest.GetPrimitiveValueInt();
2799  return true;
2800  }
2801  case ePrimitiveValueReal: {
2802  double tmp = dest.GetPrimitiveValueDouble();
2805  return true;
2806  }
2807  case ePrimitiveValueString: {
2808  string tmp = dest.GetPrimitiveValueString();
2811  return true;
2812  }
2813  case ePrimitiveValueEnum: // to be done
2814  return false;
2815  default:
2816  ;
2817  }
2818  return false;
2819 }
2820 
2822 {
2823  // copy contents of the destination field into the source field
2824  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
2825  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
2826 
2827  while (src_it != src_objs.end() && dest_it != dest_objs.end()) {
2828  CMQueryNodeValue::TObs src_prim_objs;
2829  NMacroUtil::GetPrimitiveObjInfosWithContainers(src_prim_objs, *src_it);
2830  CMQueryNodeValue::TObs dest_prim_objs;
2831  NMacroUtil::GetPrimitiveObjInfosWithContainers(dest_prim_objs, *dest_it);
2832 
2833  if (dest_prim_objs.empty() && dest_it->field.GetTypeFamily() == eTypeFamilyContainer) {
2834  // add new element when the container is empty
2835  CObjectInfo new_oi(dest_it->field.AddNewElement());
2836  dest_prim_objs.push_back(CMQueryNodeValue::SResolvedField(dest_it->field, new_oi));
2837  }
2838  x_CopyFields(src_prim_objs.front().field, dest_prim_objs.front().field);
2839 
2840  ++src_it;
2841  ++dest_it;
2842  }
2843 }
2844 
2846 {
2847  if (src.GetTypeFamily() != dest.GetTypeFamily() || src.GetTypeFamily() != eTypeFamilyPrimitive) {
2848  return false;
2849  }
2850 
2851  string src_val;
2853  src_val = src.GetPrimitiveValueString();
2854  }
2855  else if (src.GetPrimitiveValueType() == ePrimitiveValueEnum) { // codon-start
2856  src_val = NStr::IntToString(src.GetPrimitiveValueInt());
2857  }
2858 
2859  string dest_val = dest.GetPrimitiveValueString();
2860  if (edit::AddValueToString(dest_val, src_val, edit::eExistingText_replace_old)) {
2861  return SetQualStringValue(dest, dest_val);
2862  }
2863  return false;
2864 }
2865 
2866 
2867 /// class CMacroFunction_SwapRelFeatQual
2868 /// SwapFeatureQual([src_obj,] src_field, dest_feat_subtype, dest_field, update_mrna);
2869 ///
2871 
2873 {
2874  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
2875  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
2876  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
2877  if (!src_feat || !scope)
2878  return;
2879 
2880  size_t src_index = 0, dest_index = 1;
2881  if (m_Args.size() == 5) {
2882  dest_index = 2;
2883  }
2884  if (m_Args[dest_index]->GetString().empty() || m_Args[dest_index + 1]->GetString().empty())
2885  return;
2886 
2887  const string& dest_feattype = m_Args[dest_index]->GetString();
2888  const string& dest_field = m_Args[dest_index + 1]->GetString();
2889  const string src_field = (m_Args[src_index]->GetDataType() == CMQueryNodeValue::eString) ?
2890  m_Args[src_index]->GetString() : kEmptyStr;
2891 
2892  // reset members
2893  m_ConstDestFeat.Reset(nullptr);
2894  m_EditDestFeat.Reset(nullptr);
2895  m_CreatedFeat.Reset(nullptr);
2896 
2897  // check that at least one of the fields exist
2898  CObjectInfo oi = m_DataIter->GetEditedObject();
2899  CMQueryNodeValue::TObs src_objs;
2900  CMQueryNodeValue::TObs dest_objs;
2902 
2903  bool src_set = x_DoFieldsExist(oi, src_objs, src_index);
2904  if (IstRNAProductField(*src_feat, src_field)) {
2905  src_set = !src_feat->GetData().GetRna().GetRnaProductName().empty();
2906  }
2907  bool dest_set = x_DoDestFeatFieldsExist(*src_feat, dest_objs, dest_feattype, dest_field);
2908 
2909  if (!src_set && !dest_set)
2910  return;
2911 
2912  if (!src_set) {
2913  x_SetFields(oi, src_objs, src_index);
2914  }
2915  if (!dest_set) {
2916  x_SetOrCreateDestFeatFields(*src_feat, dest_objs, dest_index + 1);
2917  }
2918 
2919  if (src_set && dest_set) {
2920  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
2921  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
2922 
2923  while (src_it != src_objs.end() && dest_it != dest_objs.end()) {
2924  CMQueryNodeValue::TObs src_prim_objs;
2925  NMacroUtil::GetPrimitiveObjInfosWithContainers(src_prim_objs, *src_it);
2926  CMQueryNodeValue::TObs dest_prim_objs;
2927  NMacroUtil::GetPrimitiveObjInfosWithContainers(dest_prim_objs, *dest_it);
2928  if (dest_prim_objs.empty() && dest_it->field.GetTypeFamily() == eTypeFamilyContainer) {
2929  // add new element when the container is empty
2930  CObjectInfo new_oi(dest_it->field.AddNewElement());
2931  dest_prim_objs.push_back(CMQueryNodeValue::SResolvedField(dest_it->field, new_oi));
2932  }
2933  x_SwapFields(src_prim_objs.front().field, dest_prim_objs.front().field);
2934 
2935  ++src_it;
2936  ++dest_it;
2937  }
2938 
2939  if (m_EditDestFeat && IstRNAProductField(*m_EditDestFeat, dest_field)) {
2940  while (src_it != src_objs.end()) {
2941  CMQueryNodeValue::TObs src_prim_objs;
2942  NMacroUtil::GetPrimitiveObjInfosWithContainers(src_prim_objs, *src_it);
2943 
2944  string dest_str = m_EditDestFeat->GetData().GetRna().GetRnaProductName();
2945  CObjectInfo src = src_prim_objs.front().field;
2946 
2948  string tmp = src.GetPrimitiveValueString();
2949  src.SetPrimitiveValueString(dest_str);
2950  string remainder;
2951  m_EditDestFeat->SetData().SetRna().SetRnaProductName(tmp, remainder);
2952  m_QualsChangedCount++;
2953  }
2954  ++src_it;
2955  }
2956  }
2957  else if (IstRNAProductField(*src_feat, src_field)) {
2958  while (dest_it != dest_objs.end()) {
2959  CMQueryNodeValue::TObs dest_prim_objs;
2960  NMacroUtil::GetPrimitiveObjInfosWithContainers(dest_prim_objs, *dest_it);
2961  if (dest_prim_objs.empty() && dest_it->field.GetTypeFamily() == eTypeFamilyContainer) {
2962  // add new element when the container is empty
2963  CObjectInfo new_oi(dest_it->field.AddNewElement());
2964  dest_prim_objs.push_back(CMQueryNodeValue::SResolvedField(dest_it->field, new_oi));
2965  }
2966 
2967  string src_str = src_feat->GetData().GetRna().GetRnaProductName();
2968  CObjectInfo d = dest_prim_objs.front().field;
2970  string tmp = d.GetPrimitiveValueString();
2971  d.SetPrimitiveValueString(src_str);
2972 
2973  string remainder;
2974  seq_feat->SetData().SetRna().SetRnaProductName(tmp, remainder);
2975  m_QualsChangedCount++;
2976  }
2977  ++dest_it;
2978  }
2979  }
2980  }
2981  else if (!src_set) {
2982  // copy destination field into the source field
2983  x_CopyFields(dest_objs, src_objs);
2984 
2985  if (IstRNAProductField(*seq_feat, src_field)) {
2986  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
2987 
2988  while (dest_it != dest_objs.end()) {
2989  CMQueryNodeValue::TObs dest_prim_objs;
2990  NMacroUtil::GetPrimitiveObjInfosWithContainers(dest_prim_objs, *dest_it);
2991 
2992  CObjectInfo d = dest_prim_objs.front().field;
2993  string dest_val;
2995  dest_val = d.GetPrimitiveValueString();
2996  }
2997  else if (d.GetPrimitiveValueType() == ePrimitiveValueEnum) { // codon-start
2998  dest_val = NStr::IntToString(d.GetPrimitiveValueInt());
2999  }
3000 
3001  string remainder;
3002  seq_feat->SetData().SetRna().SetRnaProductName(dest_val, remainder);
3003  m_QualsChangedCount++;
3004  ++dest_it;
3005  }
3006  }
3007 
3008  if (m_EditDestFeat && IstRNAProductField(*m_EditDestFeat, dest_field)) {
3009  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
3010 
3011  while (src_it != src_objs.end()) {
3012  CMQueryNodeValue::TObs src_prim_objs;
3013  NMacroUtil::GetPrimitiveObjInfosWithContainers(src_prim_objs, *src_it);
3014 
3015  string rna_str = m_EditDestFeat->GetData().GetRna().GetRnaProductName();
3016  CObjectInfo src = src_prim_objs.front().field;
3017  src.SetPrimitiveValueString(rna_str);
3018  m_QualsChangedCount++;
3019  ++src_it;
3020  }
3021  }
3022 
3023  // delete destination field
3024  if (m_EditDestFeat && IstRNAProductField(*m_EditDestFeat, dest_field)) {
3025  string remainder;
3026  m_EditDestFeat->SetData().SetRna().SetRnaProductName(kEmptyStr, remainder);
3027  }
3028 
3029  NON_CONST_ITERATE(CMQueryNodeValue::TObs, it, dest_objs) {
3030  if (it->parent.GetName() == "Gb-qual" && m_EditDestFeat) {
3031  const string& qual_name = m_Args[m_Args.size() - 2]->GetString();
3032  m_EditDestFeat->RemoveQualifier(qual_name);
3033  }
3034  else {
3035  RemoveFieldByName(*it);
3036  }
3037  }
3038  }
3039  else if (!dest_set) {
3040  // copy source field into the destination field
3041  x_CopyFields(src_objs, dest_objs);
3042 
3043  if (m_EditDestFeat && IstRNAProductField(*m_EditDestFeat, dest_field)) {
3044  CMQueryNodeValue::TObs::iterator src_it = src_objs.begin();
3045 
3046  while (src_it != src_objs.end()) {
3047  CMQueryNodeValue::TObs src_prim_objs;
3048  NMacroUtil::GetPrimitiveObjInfosWithContainers(src_prim_objs, *src_it);
3049 
3050  CObjectInfo src = src_prim_objs.front().field;
3051  string src_val;
3053  src_val = src.GetPrimitiveValueString();
3054  }
3055  else if (src.GetPrimitiveValueType() == ePrimitiveValueEnum) { // codon-start
3056  src_val = NStr::IntToString(src.GetPrimitiveValueInt());
3057  }
3058 
3059  string remainder;
3060  m_EditDestFeat->SetData().SetRna().SetRnaProductName(src_val, remainder);
3061  m_QualsChangedCount++;
3062  ++src_it;
3063  }
3064  }
3065 
3066  if (IstRNAProductField(*seq_feat, src_field)) {
3067  CMQueryNodeValue::TObs::iterator dest_it = dest_objs.begin();
3068 
3069  while (dest_it != dest_objs.end()) {
3070  CMQueryNodeValue::TObs dest_prim_objs;
3071  NMacroUtil::GetPrimitiveObjInfosWithContainers(dest_prim_objs, *dest_it);
3072 
3073  string rna_str = seq_feat->GetData().GetRna().GetRnaProductName();
3074  CObjectInfo d = dest_prim_objs.front().field;
3075  d.SetPrimitiveValueString(rna_str);
3076  m_QualsChangedCount++;
3077  ++dest_it;
3078  }
3079  }
3080 
3081  // delete source field
3082  if (IstRNAProductField(*seq_feat, src_field)) {
3083  string remainder;
3084  seq_feat->SetData().SetRna().SetRnaProductName(kEmptyStr, remainder);
3085  }
3086  NON_CONST_ITERATE(CMQueryNodeValue::TObs, it, src_objs) {
3087  RemoveFieldByName(*it);
3088  }
3089  }
3090 
3091  bool update_mrna = (m_Args.back()->GetDataType() == CMQueryNodeValue::eBool) ? m_Args.back()->GetBool() : false;
3092 
3093  CCleanup cleanup;
3094  cleanup.BasicCleanup(*seq_feat);
3095  if (m_EditDestFeat) {
3096  cleanup.BasicCleanup(*m_EditDestFeat);
3097  }
3098  else if (m_CreatedFeat) {
3099  cleanup.BasicCleanup(*m_CreatedFeat);
3100  }
3101 
3102  if (m_QualsChangedCount) {
3103  m_DataIter->SetModified();
3105  log << m_DataIter->GetBestDescr() << ": swapped " << m_QualsChangedCount << " qualifiers";
3106 
3108  if (m_ConstDestFeat && m_EditDestFeat) {
3109  cmd.Reset(new CCmdComposite("Change Related feature"));
3110  CSeq_feat_Handle fh = scope->GetSeq_featHandle(*m_ConstDestFeat);
3111  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *m_EditDestFeat)));
3112  if (update_mrna && m_EditDestFeat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3113  string message;
3114  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*m_EditDestFeat, *scope, message);
3115  if (upd_cmd) {
3116  cmd->AddCommand(*upd_cmd);
3117  log << ", " << message;
3118  }
3119  }
3120  }
3121  else if (m_CreatedFeat) {
3122  cmd.Reset(new CCmdComposite("Create feature"));
3123  CBioseq_Handle bsh = scope->GetBioseqHandle(m_CreatedFeat->GetLocation());
3124  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *m_CreatedFeat)));
3125  if (update_mrna && m_CreatedFeat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3126  string message;
3127  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*m_CreatedFeat, *scope, message);
3128  if (upd_cmd) {
3129  cmd->AddCommand(*upd_cmd);
3130  log << ", " << message;
3131  }
3132  }
3133  }
3134  if (cmd) {
3135  m_DataIter->RunCommand(cmd, m_CmdComposite);
3136  }
3137 
3138  if (update_mrna && src_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3140  string prot_product = edit_feat->GetData().GetProt().GetName().front();
3141  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(prot_product, obj, *scope);
3142  if (upd_cmd) {
3143  m_DataIter->RunCommand(upd_cmd, m_CmdComposite);
3144  log << ", applied " + prot_product + " to mRNA product name ";
3145  }
3146  }
3147  x_LogFunction(log);
3148  }
3149 }
3150 
3152  const CSeq_feat& src_feat, CMQueryNodeValue::TObs& result, const string& dest_feattype, const string& dest_field)
3153 {
3154  CSeqFeatData::ESubtype target_feature = NMacroUtil::GetFeatSubtype(dest_feattype);
3155  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(src_feat, target_feature, Ref(&(m_DataIter->GetSEH().GetScope())));
3156 
3157  if (!feat_list.empty() && feat_list.size() == 1) {
3158  m_ConstDestFeat.Reset(feat_list.front());
3160  m_EditDestFeat->Assign(*feat_list.front());
3161  GetFeatDestinationField(m_EditDestFeat, dest_field, result);
3162 
3163  if (IstRNAProductField(*m_EditDestFeat, dest_field)) {
3164  return (!m_EditDestFeat->GetData().GetRna().GetRnaProductName().empty());
3165  // it might return true but the output argument results is empty!
3166  }
3167  }
3168  return (!result.empty());
3169 }
3170 
3172 {
3173  if (index >= m_Args.size())
3174  return;
3175  const string& dest_field = m_Args[index]->GetString();
3176 
3178  if (!SetFeatDestinationField(m_EditDestFeat, dest_field, result)) {
3179  return;
3180  }
3181  }
3182  else {
3183  // create a new feature
3184  m_CreatedFeat = CreateNewRelatedFeature(src_feat, dest_field, m_DataIter->GetSEH().GetScope());
3185  if (!SetFeatDestinationField(m_CreatedFeat, dest_field, result)) {
3186  return;
3187  }
3188 
3189  if (src_feat.GetData().IsCdregion() && src_feat.IsSetXref() && m_CreatedFeat->GetData().IsProt()) {
3190  CObjectInfo oi = m_DataIter->GetEditedObject();
3192  NON_CONST_ITERATE(CSeq_feat::TXref, it, edit_feat->SetXref()) {
3193  if ((*it)->IsSetData() && (*it)->GetData().IsProt()) {
3194  m_CreatedFeat->SetData().SetProt().Assign((*it)->GetData().GetProt());
3195  edit_feat->SetXref().erase(it);
3196  break;
3197  }
3198  }
3199  if (edit_feat->GetXref().empty()) {
3200  edit_feat->ResetXref();
3201  }
3202  }
3203  }
3204 }
3205 
3207 {
3208  if (m_Args.size() < 4 || m_Args.size() > 5) {
3209  return false;
3210  }
3211 
3212  bool first_ok = (m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef());
3213  if (!first_ok) {
3214  return false;
3215  }
3216  for (size_t index = 1; index < m_Args.size() - 1; ++index) {
3217  if (!m_Args[index]->IsString()) {
3218  return false;
3219  }
3220  }
3221  return (m_Args.back()->IsBool());
3222 }
3223 
3224 
3225 ///////////////////////////////////////////////////////////////////////////////
3226 /// class CMacroFunction_ConvertFeatQual
3227 /// ConvertFeatureQual(src_field, dest_feature_type, dest_feature_field, capitalization, strip_name, update_mrna, existing_text_opt, delimiter)
3228 /// The last parameter is optional
3229 /// The function will NOT delete the src_field.
3230 /// To delete the src_field, use RemoveQual() function
3231 /// The related mRNA is only updated if the action is successful and the destination feature is protein
3232 ///
3235 {
3236  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
3237  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
3238  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
3239  if (!src_feat || !scope)
3240  return;
3241 
3242  const string& dest_field = m_Args[2]->GetString();
3243  if (dest_field.empty())
3244  return;
3245 
3246  CObjectInfo oi = m_DataIter->GetEditedObject();
3247  CMQueryNodeValue::TObs src_objs;
3248  if (!x_GetSourceFields(oi, 0, src_objs))
3249  return;
3250 
3251  // the destination field can be set at this point
3252  CSeqFeatData::ESubtype target_feature = NMacroUtil::GetFeatSubtype(m_Args[1]->GetString());
3253  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, target_feature, scope);
3254  CMQueryNodeValue::TObs dest_objs;
3255  bool changed = false, created = false;
3256  CRef<CSeq_feat> dest_feat;
3257  CSeq_feat_Handle fh;
3258  if (!feat_list.empty() && feat_list.size() == 1) {
3259  dest_feat.Reset(new CSeq_feat);
3260  dest_feat->Assign(*feat_list.front());
3261  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3262  return;
3263  }
3264  fh = scope->GetSeq_featHandle(*feat_list.front());
3265  changed = true;
3266  }
3267  else {
3268  // new feature
3269  dest_feat = CreateNewRelatedFeature(*src_feat, dest_field, *scope);
3270  if (!dest_feat) return;
3271  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3272  return;
3273  }
3274  created = true;
3275  if (src_feat->GetData().IsCdregion() && src_feat->IsSetXref() && dest_feat->GetData().IsProt()) {
3277  NON_CONST_ITERATE(CSeq_feat::TXref, it, edit_feat->SetXref()) {
3278  if ((*it)->IsSetData() && (*it)->GetData().IsProt()) {
3279  dest_feat->SetData().SetProt().Assign((*it)->GetData().GetProt());
3280  edit_feat->SetXref().erase(it);
3281  break;
3282  }
3283  }
3284  if (edit_feat->GetXref().empty()) {
3285  edit_feat->ResetXref();
3286  }
3287  }
3288  }
3289 
3290  size_t index = 3;
3291  const string& capitalization = m_Args[index]->GetString();
3292  //bool strip_name = m_Args[++index]->GetBool();
3293  //strip_name = false; // this option is not implemented (it's false by default)
3294  ++index;
3295  bool update_mrna = m_Args[++index]->GetBool();
3296  const string& action_type = m_Args[++index]->GetString();
3297  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
3298 
3301 
3302  if (IstRNAProductField(*dest_feat, dest_field)) {
3303  for (auto&& it : src_objs) {
3304  CMQueryNodeValue::TObs src_prim_objs;
3305  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
3306 
3307  CObjectInfo src = src_prim_objs.front().field;
3308  string src_val = x_GetSourceString(src);
3309 
3310  string remainder;
3311  dest_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
3313  }
3314  }
3315  else {
3316  ChangeFields(src_objs, dest_objs);
3317  }
3318 
3319  if (m_QualsChangedCount) {
3320  m_DataIter->SetModified();
3322  log << m_DataIter->GetBestDescr() << ": converted " << m_QualsChangedCount << " qualifiers";
3323 
3325  if (changed && fh) {
3326  cmd.Reset(new CCmdComposite("Change Related feature"));
3327  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
3328  }
3329  else if (created) {
3330  cmd.Reset(new CCmdComposite("Create feature"));
3331  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
3332  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
3333  }
3334 
3335  if (update_mrna) {
3336  if (dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3337  string message;
3338  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*dest_feat, *scope, message);
3339  if (upd_cmd) {
3340  cmd->AddCommand(*upd_cmd);
3341  log << ", " << message;
3342  }
3343  }
3344  else if(src_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3346  string prot_product = edit_feat->GetData().GetProt().GetName().front();
3347  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(prot_product, obj, *scope);
3348  if (upd_cmd) {
3349  cmd->AddCommand(*upd_cmd);
3350  log << ", applied " + prot_product + " to mRNA product name ";
3351  }
3352  }
3353  }
3354  m_DataIter->RunCommand(cmd, m_CmdComposite);
3355  x_LogFunction(log);
3356  }
3357 }
3358 
3360 {
3361  if (m_Args.size() < 7 || m_Args.size() > 8)
3362  return false;
3363 
3364  if (!( m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef()))
3365  return false;
3366 
3367  for (size_t index = 1; index < m_Args.size(); ++index) {
3368  auto type = (index != 4 && index != 5) ? CMQueryNodeValue::eString : CMQueryNodeValue::eBool;
3369  if (m_Args[index]->GetDataType() != type)
3370  return false;
3371  }
3372  return true;
3373 }
3374 
3375 
3376 ///////////////////////////////////////////////////////////////////////////////
3377 // CMacroFunction_ConvertRNARelQual
3378 /// ConvertRnaRelQual('rna'/'gene', src_field, 'gene'/'rna', dest_field, capitalization, strip_name, existing_text_opt, delimiter)
3379 /// The last parameter is optional
3380 /// Use this function when you need to convert from/to an RNA qualifier and a Gene qualifier
3381 /// Don't use this function to convert between two RNA qualifiers
3384 {
3385  // The iterator should be an RNA feature
3386  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
3387  const CSeq_feat* rna_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
3388  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
3389  if (!rna_feat || !scope)
3390  return;
3391 
3392  if (rna_feat->IsSetData() && !rna_feat->GetData().IsRna())
3393  return;
3394 
3395  const string& src_feat = m_Args[0]->GetString();
3396  const string& dest_feat = m_Args[2]->GetString();
3397  if (src_feat == "rna" && src_feat == dest_feat)
3398  return;
3399 
3400  if ((src_feat != "rna" && src_feat != "gene") || (dest_feat != "rna" && dest_feat != "gene"))
3401  return;
3402 
3403  size_t index = 4;
3404  const string& capitalization = m_Args[index]->GetString();
3405  //bool strip_name = m_Args[++index]->GetBool();
3406  //strip_name = false; // this option is not implemented (it's false by default)
3407  ++index;
3408  const string& action_type = m_Args[++index]->GetString();
3409  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
3412 
3413  if (src_feat == "rna" && dest_feat == "gene") {
3414  CObjectInfo oi = m_DataIter->GetEditedObject();
3415  CMQueryNodeValue::TObs src_objs;
3416  if (!x_GetSourceFields(oi, 1, src_objs))
3417  return;
3418 
3419  const string dest_field = m_Args[3]->GetString();
3420  // the destination field can be set at this point
3422  CMQueryNodeValue::TObs dest_objs;
3423  bool changed = false, created = false;
3424  CRef<CSeq_feat> dest_feat;
3425  CSeq_feat_Handle fh;
3426  if (!feat_list.empty() && feat_list.size() == 1) {
3427  dest_feat.Reset(new CSeq_feat);
3428  dest_feat->Assign(*feat_list.front());
3429  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3430  return;
3431  }
3432  fh = scope->GetSeq_featHandle(*feat_list.front());
3433  changed = true;
3434  }
3435  else {
3436  // new feature
3437  dest_feat = CreateNewRelatedFeature(*rna_feat, dest_field, *scope);
3438  if (!dest_feat) return;
3439  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3440  return;
3441  }
3442  created = true;
3443  }
3444 
3445  ChangeFields(src_objs, dest_objs);
3446 
3447  if (m_QualsChangedCount) {
3449  if (changed && fh) {
3450  cmd.Reset(new CCmdComposite("Change Related feature"));
3451  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
3452  }
3453  else if (created) {
3454  cmd.Reset(new CCmdComposite("Create feature"));
3455  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
3456  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
3457  }
3458 
3459  m_DataIter->RunCommand(cmd, m_CmdComposite);
3461  log << m_DataIter->GetBestDescr() << ": converted " << m_QualsChangedCount << " qualifiers";
3462  x_LogFunction(log);
3463  }
3464  }
3465  else if (src_feat == "gene" && dest_feat == "gene") {
3467  CMQueryNodeValue::TObs src_objs, dest_objs;
3468 
3469  CRef<CSeq_feat> gene_feat;
3470  CSeq_feat_Handle fh;
3471  if (!feat_list.empty() && feat_list.size() == 1) {
3472  gene_feat.Reset(new CSeq_feat);
3473  gene_feat->Assign(*feat_list.front());
3474 
3475  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
3476  if (!x_GetSourceFields(gene_oi, 1, src_objs))
3477  return;
3478 
3479  if (!x_GetDestFields(gene_oi, 3, dest_objs))
3480  return;
3481 
3482  fh = scope->GetSeq_featHandle(*feat_list.front());
3483  }
3484  else {
3485  // no gene was found
3486  return;
3487  }
3488 
3489  ChangeFields(src_objs, dest_objs);
3490 
3491  if (m_QualsChangedCount) {
3493  if (fh) {
3494  cmd.Reset(new CCmdComposite("Change Related feature"));
3495  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *gene_feat)));
3496  }
3497 
3498  m_DataIter->RunCommand(cmd, m_CmdComposite);
3500  log << m_DataIter->GetBestDescr() << ": converted " << m_QualsChangedCount << " qualifiers";
3501  x_LogFunction(log);
3502  }
3503  }
3504  else if (src_feat == "gene" && dest_feat == "rna") {
3505 
3507  CMQueryNodeValue::TObs src_objs, dest_objs;
3508  CRef<CSeq_feat> gene_feat;
3509  if (!feat_list.empty() && feat_list.size() == 1) {
3510  gene_feat.Reset(new CSeq_feat);
3511  gene_feat->Assign(*feat_list.front());
3512 
3513  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
3514  if (!x_GetSourceFields(gene_oi, 1, src_objs))
3515  return;
3516  }
3517  else {
3518  // no gene was found
3519  return;
3520  }
3521 
3522  CObjectInfo oi = m_DataIter->GetEditedObject();
3523  if (!x_GetDestFields(oi, 3, dest_objs))
3524  return;
3525 
3527  if (m_Args[3]->IsString() && NStr::EndsWith(m_Args[3]->GetString(), "::product")) {
3528  for (auto&& it : src_objs) {
3529  CMQueryNodeValue::TObs src_prim_objs;
3530  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
3531 
3532  CObjectInfo src = src_prim_objs.front().field;
3533  string src_val = x_GetSourceString(src);
3534 
3535  string remainder;
3536  edit_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
3538  }
3539  }
3540  else {
3541  ChangeFields(src_objs, dest_objs);
3542  }
3543 
3544  if (m_QualsChangedCount) {
3545  m_DataIter->SetModified();
3547  log << m_DataIter->GetBestDescr() << ": converted " << m_QualsChangedCount << " qualifiers";
3548  x_LogFunction(log);
3549  }
3550  }
3551 }
3552 
3554 {
3555  auto nr_args = m_Args.size();
3556  if (nr_args < 7 || nr_args > 8)
3557  return false;
3558 
3559  if (!m_Args[0]->IsString() || !m_Args[2]->IsString())
3560  return false;
3561 
3562  for (size_t index = 1; index < 4; index += 2) {
3563  if (!(m_Args[index]->IsString() || m_Args[index]->AreObjects() || m_Args[index]->IsRef()))
3564  return false;
3565  }
3566 
3567  for (size_t index = 4; index < nr_args; ++index) {
3568  if (index != 5 && !m_Args[index]->IsString())
3569  return false;
3570  }
3571  return (m_Args[5]->IsBool());
3572 }
3573 
3574 ///////////////////////////////////////////////////////////////////////////////
3575 /// class CMacroFunction_ParseStringQual - used when parsing SeqId
3576 /// ParseStringQual(from_field, to_field, capitalization, existing_text_opt, delimiter) // when all the text should be copied
3577 /// Last parameter is optional
3578 ///
3581 {
3582  CObjectInfo oi = m_DataIter->GetEditedObject();
3583  CMQueryNodeValue::TObs src_objs;
3584  size_t index = 0;
3585  if (!x_GetSourceFields(oi, index, src_objs))
3586  return;
3587 
3588  CMQueryNodeValue::TObs dest_objs;
3589  if (!x_GetDestFields(oi, ++index, dest_objs))
3590  return;
3591 
3592  const string& capitalization = m_Args[++index]->GetString();
3593  const string& action_type = m_Args[++index]->GetString();
3594  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
3595 
3598 
3599  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
3600  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
3601  if (src_feat && m_Args[1]->IsString() && NStr::EndsWith(m_Args[1]->GetString(), "::product")) {
3603  for (auto&& it : src_objs) {
3604  CMQueryNodeValue::TObs src_prim_objs;
3605  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
3606 
3607  CObjectInfo src = src_prim_objs.front().field;
3608  string src_val = x_GetSourceString(src);
3609 
3610  string remainder;
3611  edit_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
3613  }
3614  }
3615  else {
3616  ChangeFields(src_objs, dest_objs);
3617  }
3618  if (m_QualsChangedCount) {
3619  if (!dest_objs.empty()) {
3620  NMacroUtil::CleanupForTaxnameChange(dest_objs.front(), oi);
3621  }
3622  m_DataIter->SetModified();
3624  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " qualifiers";
3625  x_LogFunction(log);
3626  }
3627 }
3628 
3630 {
3631  if (dest.GetTypeFamily() != eTypeFamilyPrimitive) {
3632  return false;
3633  }
3634 
3636 
3637  string src_val = x_GetSourceString(src);
3638  string dest_val = dest.GetPrimitiveValueString();
3639  if (edit::AddValueToString(dest_val, src_val, m_ExistingText)) {
3640  return SetQualStringValue(dest, dest_val);
3641  }
3642  return false;
3643 }
3644 
3646 {
3647  string src_val = IOperateOnTwoQuals::x_GetSourceString(src);
3648  // it copies the whole string
3649  CSeq_entry_Handle seh = m_DataIter->GetSEH();
3650  FixCapitalizationInString(seh, src_val, m_CapChange);
3651  return src_val;
3652 }
3653 
3655 {
3656  size_t as = m_Args.size();
3657  if (as < 1 || as > 5) {
3658  return false;
3659  }
3660  for (size_t i = 0; i < 2; ++i) {
3661  if (!(m_Args[i]->IsString() || m_Args[i]->AreObjects() || m_Args[i]->IsRef()))
3662  return false;
3663  }
3664  for (size_t index = 2; index < as; ++index) {
3665  if (!m_Args[index]->IsString()) {
3666  return false;
3667  }
3668  }
3669  return true;
3670 }
3671 
3672 
3673 ///////////////////////////////////////////////////////////////////////////////
3674 /// class CMacroFunction_ParseFeatQual
3675 /// ParseFeatureQual(src_field, dest_feature_type, dest_feature_field, capitalization, update_mrna, existing_text_opt, delimiter)
3676 /// The last parameter is optional
3677 /// The related mRNA is only updated if the action is successful and the destination feature is protein
3678 ///
3681 {
3682  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
3683  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
3684  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
3685  if (!src_feat || !scope)
3686  return;
3687 
3688  const string& dest_field = m_Args[2]->GetString();
3689  if (dest_field.empty())
3690  return;
3691 
3692  CObjectInfo oi = m_DataIter->GetEditedObject();
3693  CMQueryNodeValue::TObs src_objs;
3694  if (!x_GetSourceFields(oi, 0, src_objs))
3695  return;
3696 
3697  // the destination field can be set at this point
3698  CSeqFeatData::ESubtype target_feature = NMacroUtil::GetFeatSubtype(m_Args[1]->GetString());
3699  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, target_feature, scope);
3700  CMQueryNodeValue::TObs dest_objs;
3701  bool changed = false, created = false;
3702  CRef<CSeq_feat> dest_feat;
3703  CSeq_feat_Handle fh;
3704  if (!feat_list.empty() && feat_list.size() == 1) {
3705  dest_feat.Reset(new CSeq_feat);
3706  dest_feat->Assign(*feat_list.front());
3707  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3708  return;
3709  }
3710  fh = scope->GetSeq_featHandle(*feat_list.front());
3711  changed = true;
3712  }
3713  else {
3714  // new feature
3715  dest_feat = CreateNewRelatedFeature(*src_feat, dest_field, *scope);
3716  if (!dest_feat) return;
3717  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3718  return;
3719  }
3720  created = true;
3721  if (src_feat->GetData().IsCdregion() && src_feat->IsSetXref() && dest_feat->GetData().IsProt()) {
3723  NON_CONST_ITERATE(CSeq_feat::TXref, it, edit_feat->SetXref()) {
3724  if ((*it)->IsSetData() && (*it)->GetData().IsProt()) {
3725  dest_feat->SetData().SetProt().Assign((*it)->GetData().GetProt());
3726  edit_feat->SetXref().erase(it);
3727  break;
3728  }
3729  }
3730  if (edit_feat->GetXref().empty()) {
3731  edit_feat->ResetXref();
3732  }
3733  }
3734  }
3735 
3736  size_t index = 3;
3737  const string& capitalization = m_Args[index]->GetString();
3738  bool update_mrna = m_Args[++index]->GetBool();
3739  const string& action_type = m_Args[++index]->GetString();
3740  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
3741 
3744 
3745  if (IstRNAProductField(*dest_feat, dest_field)) {
3746  for (auto&& it : src_objs) {
3747  CMQueryNodeValue::TObs src_prim_objs;
3748  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
3749 
3750  CObjectInfo src = src_prim_objs.front().field;
3751  string src_val = x_GetSourceString(src);
3752 
3753  string remainder;
3754  dest_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
3756  }
3757  }
3758  else {
3759  ChangeFields(src_objs, dest_objs);
3760  }
3761 
3762  if (m_QualsChangedCount) {
3763  m_DataIter->SetModified();
3765  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " qualifiers";
3766 
3768  if (changed && fh) {
3769  cmd.Reset(new CCmdComposite("Change Related feature"));
3770  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
3771  }
3772  else if (created) {
3773  cmd.Reset(new CCmdComposite("Create feature"));
3774  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
3775  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
3776  }
3777 
3778  if (update_mrna) {
3779  if (dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3780  string message;
3781  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(*dest_feat, *scope, message);
3782  if (upd_cmd) {
3783  cmd->AddCommand(*upd_cmd);
3784  log << ", " << message;
3785  }
3786  }
3787  else if (src_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
3789  string prot_product = edit_feat->GetData().GetProt().GetName().front();
3790  CRef<CCmdComposite> upd_cmd = UpdatemRNAProduct(prot_product, obj, *scope);
3791  if (upd_cmd) {
3792  cmd->AddCommand(*upd_cmd);
3793  log << ", applied " + prot_product + " to mRNA product name ";
3794  }
3795  }
3796  }
3797  m_DataIter->RunCommand(cmd, m_CmdComposite);
3798  x_LogFunction(log);
3799  }
3800 }
3801 
3803 {
3804  if (m_Args.size() < 6 || m_Args.size() > 7)
3805  return false;
3806 
3807  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
3808  bool first_ok = (type == CMQueryNodeValue::eString)
3810  || (type == CMQueryNodeValue::eRef);
3811  if (!first_ok)
3812  return false;
3813 
3814  for (size_t index = 1; index < m_Args.size(); ++index) {
3816  if (m_Args[index]->GetDataType() != type)
3817  return false;
3818  }
3819  return true;
3820 }
3821 
3822 
3823 ///////////////////////////////////////////////////////////////////////////////
3824 // class CMacroFunction_ParseRNARelQual
3825 /// ParseRnaRelQual('rna'/'gene', src_field, 'gene'/'rna', dest_field, capitalization, existing_text_opt, delimiter)
3826 /// The last parameter is optional
3827 /// Use this function when you need to convert from/to an RNA qualifier and a Gene qualifier
3828 /// Don't use this function to convert between two RNA qualifiers
3831 {
3832  // The iterator should be an RNA feature
3833  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
3834  const CSeq_feat* rna_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
3835  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
3836  if (!rna_feat || !scope)
3837  return;
3838 
3839  if (rna_feat->IsSetData() && !rna_feat->GetData().IsRna())
3840  return;
3841 
3842  const string& src_feat = m_Args[0]->GetString();
3843  const string& dest_feat = m_Args[2]->GetString();
3844  if (src_feat == "rna" && src_feat == dest_feat)
3845  return;
3846 
3847  if ((src_feat != "rna" && src_feat != "gene") || (dest_feat != "rna" && dest_feat != "gene"))
3848  return;
3849 
3850  size_t index = 4;
3851  const string& capitalization = m_Args[index]->GetString();
3852  const string& action_type = m_Args[++index]->GetString();
3853  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
3856 
3857  if (src_feat == "rna" && dest_feat == "gene") {
3858  CObjectInfo oi = m_DataIter->GetEditedObject();
3859  CMQueryNodeValue::TObs src_objs;
3860  if (!x_GetSourceFields(oi, 1, src_objs))
3861  return;
3862 
3863  const string dest_field = m_Args[3]->GetString();
3864  // the destination field can be set at this point
3866  CMQueryNodeValue::TObs dest_objs;
3867  bool changed = false, created = false;
3868  CRef<CSeq_feat> dest_feat;
3869  CSeq_feat_Handle fh;
3870  if (!feat_list.empty() && feat_list.size() == 1) {
3871  dest_feat.Reset(new CSeq_feat);
3872  dest_feat->Assign(*feat_list.front());
3873  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3874  return;
3875  }
3876  fh = scope->GetSeq_featHandle(*feat_list.front());
3877  changed = true;
3878  }
3879  else {
3880  // new feature
3881  dest_feat = CreateNewRelatedFeature(*rna_feat, dest_field, *scope);
3882  if (!dest_feat) return;
3883  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
3884  return;
3885  }
3886  created = true;
3887  }
3888 
3889  ChangeFields(src_objs, dest_objs);
3890 
3891  if (m_QualsChangedCount) {
3893  if (changed && fh) {
3894  cmd.Reset(new CCmdComposite("Change Related feature"));
3895  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
3896  }
3897  else if (created) {
3898  cmd.Reset(new CCmdComposite("Create feature"));
3899  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
3900  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
3901  }
3902 
3903  m_DataIter->RunCommand(cmd, m_CmdComposite);
3905  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " qualifiers";
3906  x_LogFunction(log);
3907  }
3908  }
3909  else if (src_feat == "gene" && dest_feat == "gene") {
3911  CMQueryNodeValue::TObs src_objs, dest_objs;
3912 
3913  CRef<CSeq_feat> gene_feat;
3914  CSeq_feat_Handle fh;
3915  if (!feat_list.empty() && feat_list.size() == 1) {
3916  gene_feat.Reset(new CSeq_feat);
3917  gene_feat->Assign(*feat_list.front());
3918 
3919  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
3920  if (!x_GetSourceFields(gene_oi, 1, src_objs))
3921  return;
3922 
3923  if (!x_GetDestFields(gene_oi, 3, dest_objs))
3924  return;
3925 
3926  fh = scope->GetSeq_featHandle(*feat_list.front());
3927  }
3928  else {
3929  // no gene was found
3930  return;
3931  }
3932 
3933  ChangeFields(src_objs, dest_objs);
3934 
3935  if (m_QualsChangedCount) {
3937  if (fh) {
3938  cmd.Reset(new CCmdComposite("Change Related feature"));
3939  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *gene_feat)));
3940  }
3941 
3942  m_DataIter->RunCommand(cmd, m_CmdComposite);
3944  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " qualifiers";
3945  x_LogFunction(log);
3946  }
3947  }
3948  else if (src_feat == "gene" && dest_feat == "rna") {
3950  CMQueryNodeValue::TObs src_objs, dest_objs;
3951  CRef<CSeq_feat> gene_feat;
3952  if (!feat_list.empty() && feat_list.size() == 1) {
3953  gene_feat.Reset(new CSeq_feat);
3954  gene_feat->Assign(*feat_list.front());
3955 
3956  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
3957  if (!x_GetSourceFields(gene_oi, 1, src_objs))
3958  return;
3959  }
3960  else {
3961  // no gene was found
3962  return;
3963  }
3964 
3965  CObjectInfo oi = m_DataIter->GetEditedObject();
3966  if (!x_GetDestFields(oi, 3, dest_objs))
3967  return;
3968 
3970  if (m_Args[3]->IsString() && NStr::EndsWith(m_Args[3]->GetString(), "::product")) {
3971  for (auto&& it : src_objs) {
3972  CMQueryNodeValue::TObs src_prim_objs;
3973  NMacroUtil::GetPrimitiveObjectInfos(src_prim_objs, it);
3974 
3975  CObjectInfo src = src_prim_objs.front().field;
3976  string src_val = x_GetSourceString(src);
3977 
3978  string remainder;
3979  edit_feat->SetData().SetRna().SetRnaProductName(src_val, remainder);
3981  }
3982  }
3983  else {
3984  ChangeFields(src_objs, dest_objs);
3985  }
3986 
3987  if (m_QualsChangedCount) {
3988  m_DataIter->SetModified();
3990  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " qualifiers";
3991  x_LogFunction(log);
3992  }
3993  }
3994 }
3995 
3997 {
3998  if (m_Args.size() < 6 || m_Args.size() > 7)
3999  return false;
4000 
4001  if (!m_Args[0]->IsString() || !m_Args[2]->IsString())
4002  return false;
4003 
4004  for (size_t index = 1; index < 4; index += 2) {
4005  if (!(m_Args[index]->IsString() || m_Args[index]->AreObjects() || m_Args[index]->IsRef()))
4006  return false;
4007  }
4008  for (size_t index = 4; index < m_Args.size(); ++index) {
4009  if (!m_Args[index]->IsString())
4010  return false;
4011  }
4012  return true;
4013 }
4014 
4015 
4016 /// class CMacroFunction_AddParsedText
4017 /// AddParsedText(parsed_text, to_field, capitalization, existing_text_opt, delimiter)
4018 /// Deprecated name: AECRParseStringQual
4019 /// when only the "text" portion is copied
4020 /// The capitalization change applies to the parsed text portion
4021 ///
4023 
4025 {
4026  CObjectInfo oi = m_DataIter->GetEditedObject();
4027 
4028  // obtain the wanted text portion
4029  CMQueryNodeValue& parsed_obj = m_Args[0].GetNCObject();
4030  parsed_obj.Dereference();
4031  if (parsed_obj.GetDataType() != CMQueryNodeValue::eString) {
4032  return;
4033  }
4034  string text_portion = parsed_obj.GetString();
4035  if (text_portion.empty()) {
4036  return;
4037  }
4038  size_t index = 1;
4039  // the destination field might be set during this action
4040  CMQueryNodeValue::TObs dest_objs;
4041  CMQueryNodeValue::EType dest_type = m_Args[index]->GetDataType();
4042 
4043  if (dest_type == CMQueryNodeValue::eString) {
4044  const string& dest_field = m_Args[index]->GetString();
4045  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
4046  GetDestinationObjects(obj, oi, dest_field, dest_objs);
4047  }
4048  else if (dest_type == CMQueryNodeValue::eObjects) {
4049  dest_objs = m_Args[index]->GetObjects();
4050  }
4051  else if (dest_type == CMQueryNodeValue::eRef) {
4052  x_GetObjectsFromRef(dest_objs, index);
4053  }
4054 
4055  if (dest_objs.empty()) {
4056  return;
4057  }
4058 
4059  const string& capitalization = m_Args[++index]->GetString();
4060  const string& action_type = m_Args[++index]->GetString();
4061  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
4062 
4064  ECapChange cap_change = NMacroUtil::ConvertStringtoCapitalOption(capitalization);
4065 
4066  CSeq_entry_Handle seh = m_DataIter->GetSEH();
4067  FixCapitalizationInString(seh, text_portion, cap_change);
4068  x_ParseFields(dest_objs, text_portion, existing_text);
4069 
4070  if (m_QualsChangedCount) {
4071  NMacroUtil::CleanupForTaxnameChange(dest_objs.front(), oi);
4072  m_DataIter->SetModified();
4074  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " fields";
4075  x_LogFunction(log);
4076  }
4077 }
4078 
4080  const string& text_portion, edit::EExistingText existing_text)
4081 {
4083  NMacroUtil::GetPrimitiveObjectInfos(objs, dest_objs.front());
4084  if (objs.empty() && dest_objs.front().field.GetTypeFamily() == eTypeFamilyContainer) {
4085  // add new element when the container is empty
4086  CObjectInfo new_oi(dest_objs.front().field.AddNewElement());
4087  objs.push_back(CMQueryNodeValue::SResolvedField(dest_objs.front().field, new_oi));
4088  }
4089  CObjectInfo dest = objs.front().field;
4091 
4093 
4094  string dest_val = dest.GetPrimitiveValueString();
4095  if (edit::AddValueToString(dest_val, text_portion, existing_text)) {
4096  SetQualStringValue(dest, dest_val);
4097  }
4098  }
4099 }
4100 
4101 
4103 {
4104  size_t as = m_Args.size();
4105  if (as != 4 && as != 5) {
4106  return false;
4107  }
4108 
4109  if (m_Args[0]->GetDataType() != CMQueryNodeValue::eRef) {
4110  return false;
4111  }
4112 
4113  CMQueryNodeValue::EType type = m_Args[1]->GetDataType();
4114  bool type_ok = (type == CMQueryNodeValue::eString)
4116  || (type == CMQueryNodeValue::eRef);
4117  if (!type_ok)
4118  return false;
4119 
4120  for (size_t index = 2; index < as; ++index) {
4121  if (m_Args[index]->GetDataType() != CMQueryNodeValue::eString) {
4122  return false;
4123  }
4124  }
4125 
4126  return true;
4127 }
4128 
4129 // class CMacroFunction_AddParsedFeatQual
4130 /// AddParsedTextToFeatureQual(parsed_text, dest_feature_type, dest_feature_field, capitalization, update_mrna, existing_text_opt, delimiter)
4131 /// The last parameter is optional
4132 /// The related mRNA is only updated if the action is successful and the destination feature is protein
4135 {
4136  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
4137  const CSeq_feat* src_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
4138  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
4139  if (!src_feat || !scope)
4140  return;
4141 
4142  CObjectInfo oi = m_DataIter->GetEditedObject();
4143 
4144  // obtain the wanted text portion
4145  CMQueryNodeValue& parsed_obj = m_Args[0].GetNCObject();
4146  parsed_obj.Dereference();
4147  if (parsed_obj.GetDataType() != CMQueryNodeValue::eString) {
4148  return;
4149  }
4150  string text_portion = parsed_obj.GetString();
4151  if (text_portion.empty()) {
4152  return;
4153  }
4154 
4155  const string& dest_field = m_Args[2]->GetString();
4156  if (dest_field.empty())
4157  return;
4158 
4159  // the destination field can be set at this point
4160  CSeqFeatData::ESubtype target_feature = NMacroUtil::GetFeatSubtype(m_Args[1]->GetString());
4161  NMacroUtil::TVecFeatList feat_list = edit::GetRelatedFeatures(*src_feat, target_feature, scope);
4162  CMQueryNodeValue::TObs dest_objs;
4163  bool changed = false, created = false;
4164  CRef<CSeq_feat> dest_feat;
4165  CSeq_feat_Handle fh;
4166 
4167  if (!feat_list.empty() && feat_list.size() == 1) {
4168  dest_feat.Reset(new CSeq_feat);
4169  dest_feat->Assign(*feat_list.front());
4170  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
4171  return;
4172  }
4173  fh = scope->GetSeq_featHandle(*feat_list.front());
4174  changed = true;
4175  }
4176  else {
4177  // new feature
4178  dest_feat = CreateNewRelatedFeature(*src_feat, dest_field, *scope);
4179  if (!dest_feat) return;
4180  if (!SetFeatDestinationField(dest_feat, dest_field, dest_objs)) {
4181  return;
4182  }
4183  created = true;
4184  if (src_feat->GetData().IsCdregion() && src_feat->IsSetXref() && dest_feat->GetData().IsProt()) {
4186  NON_CONST_ITERATE(CSeq_feat::TXref, it, edit_feat->SetXref()) {
4187  if ((*it)->IsSetData() && (*it)->GetData().IsProt()) {
4188  dest_feat->SetData().SetProt().Assign((*it)->GetData().GetProt());
4189  edit_feat->SetXref().erase(it);
4190  break;
4191  }
4192  }
4193  if (edit_feat->GetXref().empty()) {
4194  edit_feat->ResetXref();
4195  }
4196  }
4197  }
4198 
4199  size_t index = 3;
4200  const string& capitalization = m_Args[index]->GetString();
4201  bool update_mrna = m_Args[++index]->GetBool();
4202  const string& action_type = m_Args[++index]->GetString();
4203  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
4204 
4206  ECapChange cap_change = NMacroUtil::ConvertStringtoCapitalOption(capitalization);
4207 
4208  CSeq_entry_Handle seh = m_DataIter->GetSEH();
4209  FixCapitalizationInString(seh, text_portion, cap_change);
4210  if (IstRNAProductField(*dest_feat, dest_field)) {
4211  string remainder;
4212  dest_feat->SetData().SetRna().SetRnaProductName(text_portion, remainder);
4214  }
4215  else {
4216  x_ParseFields(dest_objs, text_portion, existing_text);
4217  }
4218 
4219  if (m_QualsChangedCount) {
4220  m_DataIter->SetModified();
4222  log << m_DataIter->GetBestDescr() << ": parsed " << m_QualsChangedCount << " fields";
4223 
4224  if (changed && fh) {
4225  CRef<CCmdComposite> cmd(new CCmdComposite("Change Related feature"));
4226  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *dest_feat)));
4227  m_DataIter->RunCommand(cmd, m_CmdComposite);
4228  }
4229  else if (created) {
4230  CRef<CCmdComposite> cmd(new CCmdComposite("Create feature"));
4231  CBioseq_Handle bsh = scope->GetBioseqHandle(dest_feat->GetLocation());
4232  cmd->AddCommand(*CRef<CCmdCreateFeatBioseq>(new CCmdCreateFeatBioseq(bsh, *dest_feat)));
4233  m_DataIter->RunCommand(cmd, m_CmdComposite);
4234  }
4235 
4236  if (update_mrna && dest_feat->GetData().GetSubtype() == CSeqFeatData::eSubtype_prot) {
4237  string message;
4238  CRef<CCmdComposite> cmd = UpdatemRNAProduct(*dest_feat, *scope, message);
4239  if (cmd) {
4240  m_DataIter->RunCommand(cmd, m_CmdComposite);
4241  log << ", " << message;
4242  }
4243  }
4244  x_LogFunction(log);
4245  }
4246 }
4247 
4249 {
4250  if (m_Args.size() < 6 || m_Args.size() > 7)
4251  return false;
4252 
4253  if (m_Args[0]->GetDataType() != CMQueryNodeValue::eRef) {
4254  return false;
4255  }
4257  for (size_t index = 1; index < m_Args.size(); ++index) {
4259  if (m_Args[index]->GetDataType() != type)
4260  return false;
4261  }
4262  return true;
4263 
4264 }
4265 
4266 ///////////////////////////////////////////////////////////////////////////////
4267 // class CMacroFunction_ParseToStructComm
4268 /// ParseToStructComment(text/src_field, structcomm_field ("fieldvalue"|"dbname"|"fieldname"), [fieldname,]
4269 /// capitalization, existing_text_opt, delimiter))
4270 ///
4272 
4274 {
4275  CConstRef<CObject> object = m_DataIter->GetScopedObject().object;
4276  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
4277  if (!scope || !object)
4278  return;
4279 
4280  CObjectInfo oi = m_DataIter->GetEditedObject();
4281  size_t index = 0;
4282 
4283  CMQueryNodeValue text_holder;
4284  string text_portion;
4285 
4286  CMQueryNodeValue::TObs res_oi;
4287  CMQueryNodeValue::EType type = m_Args[index]->GetDataType();
4289  const string& field = m_Args[0]->GetString();
4290  ResolveIdentToSimple(oi, field, text_holder);
4291  GetFieldsByName(&res_oi, oi, field);
4292  }
4293  else if (type == CMQueryNodeValue::eObjects) {
4294  res_oi = m_Args[index]->GetObjects();
4295  if (!res_oi.empty()) {
4296  ResolveIdentToSimple(res_oi.front().field, kEmptyStr, text_holder);
4297  }
4298  }
4299  else if (type == CMQueryNodeValue::eRef) {
4300  CMQueryNodeValue& objs = m_Args[index].GetNCObject();
4301  objs.Dereference();
4302 
4303  if (objs.GetDataType() == CMQueryNodeValue::eObjects) {
4304  res_oi = objs.GetObjects();
4305  if (!res_oi.empty()) {
4306  ResolveIdentToSimple(res_oi.front().field, kEmptyStr, text_holder);
4307  }
4308  }
4309  else if (objs.GetDataType() == CMQueryNodeValue::eString) {
4310  text_portion = objs.GetString();
4311  }
4312  }
4313 
4314  if (text_holder.GetDataType() == CMQueryNodeValue::eString && text_portion.empty()) {
4315  text_portion = text_holder.GetString();
4316  }
4317 
4318  if (text_portion.empty())
4319  return;
4320 
4321  x_SetFieldType(m_Args[++index]->GetString());
4322  string fieldname = (m_Type == eFieldValue) ? m_Args[++index]->GetString() : kEmptyStr;
4323  if (m_Type == eFieldError) {
4324  NCBI_THROW(CMacroExecException, eWrongArguments, "Wrong structured comment fieldname is given (consider replacing it with either 'fieldvalue', 'dbname' or 'fieldname'");
4325  }
4326 
4327  string capitalization, action_type;
4328  if (++index < m_Args.size()) {
4329  capitalization = m_Args[index]->GetString();
4330  }
4331  if (++index < m_Args.size()) {
4332  action_type = m_Args[index]->GetString();
4333  }
4334  if (capitalization.empty() || action_type.empty()) {
4335  NCBI_THROW(CMacroExecException, eWrongArguments, "Wrong arguments specified");
4336  }
4337 
4338  string delimiter = (++index < m_Args.size()) ? m_Args[index]->GetString() : kEmptyStr;
4339 
4340  ECapChange cap_change = NMacroUtil::ConvertStringtoCapitalOption(capitalization);
4341  CSeq_entry_Handle seh = m_DataIter->GetSEH();
4342  FixCapitalizationInString(seh, text_portion, cap_change);
4343 
4345  CBioseq_Handle bsh = m_DataIter->GetBioseqHandle();
4346  if (!bsh)
4347  return;
4348 
4350  CSeqdesc_CI strcomm_it(bsh, CSeqdesc::e_User);
4351  // obtain the existing structured comment
4352  for ( ; strcomm_it; ++strcomm_it) {
4353  if (CComment_rule::IsStructuredComment(strcomm_it->GetUser())) {
4354  break;
4355  }
4356  }
4357  const string kPrefix = "StructuredCommentPrefix";
4358  const string kSuffix = "StructuredCommentSuffix";
4359 
4360  if (strcomm_it) {
4361  CRef<CSeqdesc> new_desc(new CSeqdesc);
4362  new_desc->Assign(*strcomm_it);
4363 
4364  CUser_object& user_object = new_desc->SetUser();
4365  if (existing_text == edit::eExistingText_add_qual) {
4366  // add a new field to the structured comment
4367  if (m_Type == eFieldValue) {
4368  user_object.AddField(fieldname, text_portion);
4369  m_QualsChangedCount++;
4370  }
4371  }
4372  else {
4373  if (m_Type == eFieldValue) {
4374  if (user_object.HasField(fieldname)) {
4375  CUser_field& user_field = user_object.SetField(fieldname);
4376  if (user_field.IsSetData() && user_field.GetData().IsStr()) {
4377  string orig_val = user_field.GetData().GetStr();
4378  if (AddValueToString(orig_val, text_portion, existing_text)) {
4379  user_field.SetData().SetStr(orig_val);
4380  m_QualsChangedCount++;
4381  }
4382  }
4383  }
4384  else {
4385  // add a new field to the structured comment
4386  user_object.AddField(fieldname, text_portion);
4387  m_QualsChangedCount++;
4388  }
4389  }
4390  else if (m_Type == eDbName) {
4391  m_QualsChangedCount += CMacroFunction_SetStructCommDb::s_UpdateStructCommentDb(*new_desc, kPrefix, text_portion, existing_text);
4392  m_QualsChangedCount += CMacroFunction_SetStructCommDb::s_UpdateStructCommentDb(*new_desc, kSuffix, text_portion, existing_text);
4393  }
4394  else if (m_Type == eFieldName) {
4395  EDIT_EACH_USERFIELD_ON_USEROBJECT(field_it, user_object) {
4396  CUser_field& user_field = **field_it;
4397  if (user_field.GetLabel().IsStr() &&
4398  !NStr::Equal(user_field.GetLabel().GetStr(), kPrefix) &&
4399  !NStr::Equal(user_field.GetLabel().GetStr(), kSuffix)) {
4400  string orig_val = user_field.GetLabel().GetStr();
4401  if (AddValueToString(orig_val, text_portion, existing_text)) {
4402  user_field.SetLabel().SetStr(orig_val);
4403  m_QualsChangedCount++;
4404  }
4405  }
4406  }
4407  }
4408 
4409  if (m_QualsChangedCount) {
4411  CCleanup::CleanupUserObject(new_desc->SetUser());
4412 
4413  CRef<CCmdChangeSeqdesc> ecmd(new CCmdChangeSeqdesc(strcomm_it.GetSeq_entry_Handle(), *strcomm_it, *new_desc));
4414  cmd.Reset(new CCmdComposite("Update structured comment"));
4415  cmd->AddCommand(*ecmd);
4416  }
4417  }
4418 
4419  }
4420  else { // make a new one
4421  if ( existing_text == edit::eExistingText_leave_old) {
4422  return;
4423  }
4424 
4426  CRef<CSeqdesc> new_desc(new CSeqdesc());
4427  CUser_object& user_object = new_desc->SetUser();
4429  switch (m_Type) {
4430  case eFieldValue:
4431  user_object.AddField(fieldname, text_portion);
4432  m_QualsChangedCount++;
4433  break;
4434  case eDbName:
4435  user_object.AddField(kPrefix, CComment_rule::MakePrefixFromRoot(text_portion));
4436  user_object.AddField(kSuffix, CComment_rule::MakeSuffixFromRoot(text_portion));
4437  m_QualsChangedCount++;
4438  break;
4439  case eFieldName:
4440  user_object.AddField(text_portion, kEmptyStr);
4441  m_QualsChangedCount++;
4442  break;
4443  default:
4444  break;
4445  }
4446 
4447  if (m_QualsChangedCount) {
4448  cmd.Reset(new CCmdComposite("Create structured comment"));
4449  cmd->AddCommand(*(new CCmdCreateDesc(bsh.GetSeq_entry_Handle(), *new_desc)));
4450  }
4451  }
4452 
4453  if (cmd) {
4454  m_DataIter->RunCommand(cmd, m_CmdComposite);
4455 
4456  TChangedQuals report;
4457  string msg = "parsing text to structured comment ";
4458  switch (m_Type) {
4459  case eFieldValue:
4460  msg += "field '" + fieldname + "'";
4461  break;
4462  case eDbName:
4463  msg += "database name";
4464  break;
4465  case eFieldName:
4466  msg += "fieldname";
4467  break;
4468  default:
4469  break;
4470  }
4471  report[msg] = m_QualsChangedCount;
4472  CRef<IFunctionLog> fnc_log(new CGeneralFuncLog(report));
4473  x_LogChangedQuals(fnc_log);
4474  }
4475 }
4476 
4478 {
4479  if (m_Args.size() > 6 || m_Args.size() < 4) {
4480  return false;
4481  }
4482  if (!(m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef()))
4483  return false;
4484 
4485  for (size_t index = 1; index < m_Args.size(); ++index) {
4486  if (m_Args[index]->GetDataType() != CMQueryNodeValue::eString)
4487  return false;
4488  }
4489  return true;
4490 }
4491 
4492 void CMacroFunction_ParseToStructComm::x_SetFieldType(const string& strcomm_field)
4493 {
4494  m_Type = eFieldError;
4495  if (NStr::EqualNocase(strcomm_field, kStrCommFieldValue)) {
4496  m_Type = eFieldValue;
4497  }
4498  else if (NStr::EqualNocase(strcomm_field, kStrCommDbname)) {
4499  m_Type = eDbName;
4500  }
4501  else if (NStr::EqualNocase(strcomm_field, kStrCommFieldName)) {
4502  m_Type = eFieldName;
4503  }
4504 }
4505 
4506 
4507 ///////////////////////////////////////////////////////////////////////////////
4508 /// class CMacroFunction_ParsedText
4509 /// Obtains a portion of the text
4510 /// ParsedText(field_name(str/objects), left_del(str), include_left(b), right_del(str), include_right(b),
4511 /// case_sensitive(b), whole_word(b), rmv_from_parsed(b), rmv_left(b), rmv_right(b));
4512 /// left_del and right_del can be free_text, "eDigits", "eLetters"
4513 /// the last three parameters are optional
4514 /// It may also modify the content of the field
4515 ///
4518 {
4519  m_ChangedDescriptors.clear();
4520  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
4521  CObjectInfo oi = m_DataIter->GetEditedObject();
4522 
4523  CMQueryNodeValue::TObs res_oi;
4524  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
4526  const string& field = m_Args[0]->GetString();
4527  if (NStr::EqualNocase(field, "localid") || NStr::EqualNocase(field, "defline")) {
4528  x_GetSpecialField(field, res_oi, *scope);
4529  }
4530  else {
4531  ResolveIdentToSimple(oi, field, *m_Result);
4532  GetFieldsByName(&res_oi, oi, field);
4533  }
4534  }
4535  else {
4537  res_oi = m_Args[0]->GetObjects();
4538  }
4539  else if (type == CMQueryNodeValue::eRef) {
4540  x_GetObjectsFromRef(res_oi, 0);
4542  }
4543 
4544  if (res_oi.empty()) return;
4545  ResolveIdentToSimple(res_oi.front().field, kEmptyStr, *m_Result);
4546  }
4547 
4550  return;
4551  }
4552 
4553  const string& full_text = m_Result->GetString();
4555  string parsed_text = options->GetSelectedText(full_text);
4556  m_Result->SetString(parsed_text);
4557 
4558  if (options->ShouldRemoveFromParsed()) {
4559  if (!m_ChangedDescriptors.empty()) {
4560  auto it = m_ChangedDescriptors.begin();
4561  if (CSeqdesc* desc = dynamic_cast<CSeqdesc*>(it->second.GetNCPointer())) {
4562  if (desc->IsTitle()) {
4563  string orig_val = desc->GetTitle();
4564  options->RemoveSelectedText(orig_val);
4565  if (!orig_val.empty()) {
4566  desc->SetTitle() = orig_val;
4567 
4568  CRef<CCmdChangeSeqdesc> chg_cmd(new CCmdChangeSeqdesc(it->first.GetSeq_entry_Handle(), *it->first, *it->second));
4569  CRef<CCmdComposite> cmd(new CCmdComposite("Update definition line"));
4570  cmd->AddCommand(*chg_cmd);
4571  m_DataIter->RunCommand(cmd, m_CmdComposite);
4572  }
4573  // the title will not be deleted
4574  }
4575  }
4576  }
4577  else if (!res_oi.empty()) {
4579  NMacroUtil::GetPrimitiveObjectInfos(objs, res_oi.front());
4580  CObjectInfo objInfo = objs.front().field;
4582 
4583  bool is_taxname = NMacroUtil::IsTaxname(objs.front());
4584  string orig_val = objInfo.GetPrimitiveValueString();
4585  options->RemoveSelectedText(orig_val);
4586  if (orig_val.empty()) {
4587  RemoveFieldByName(res_oi.front());
4589  }
4590  else {
4591  SetQualStringValue(objInfo, orig_val);
4592  }
4593  if (is_taxname) {
4595  }
4596  }
4597  }
4598 
4599  if (m_QualsChangedCount) {
4600  m_DataIter->SetModified();
4601  }
4602 }
4603 
4605 {
4606  size_t as = m_Args.size();
4607  if (as < 7 || as > 10) {
4608  return false;
4609  }
4610  if (!(m_Args[0]->IsString() || m_Args[0]->AreObjects() || m_Args[0]->IsRef()))
4611  return false;
4612 
4613  if (!m_Args[1]->IsString() || !m_Args[2]->IsBool() || !m_Args[3]->IsString()) {
4614  return false;
4615  }
4616 
4617  for (size_t i = 4; i < as; ++i) {
4618  if (!m_Args[i]->IsBool())
4619  return false;
4620  }
4621  return true;
4622 }
4623 
4625 {
4626  CBioseq_Handle bsh = m_DataIter->GetBioseqHandle();
4627  if (bsh && bsh.IsAa()) {
4628  const CSeq_feat* cds = sequence::GetCDSForProduct(*bsh.GetCompleteBioseq(), &scope);
4629  if (cds) {
4630  bsh = scope.GetBioseqHandle(cds->GetLocation());
4631  }
4632  }
4633  if (!bsh) return false;
4634 
4635  if (NStr::EqualNocase(field, "defline")) {
4636  CSeqdesc_CI title_ci(bsh, CSeqdesc::e_Title, 1);
4637  if (title_ci && !title_ci->GetTitle().empty()) {
4638  CRef<CSeqdesc> new_desc(new CSeqdesc);
4639  new_desc->Assign(*title_ci);
4640  CObjectInfo oi(new_desc, new_desc->GetThisTypeInfo());
4641  CObjectInfo parent;
4642  objs.push_back(CMQueryNodeValue::SResolvedField(parent, oi));
4643  if (ResolveIdentToSimple(oi, "title", *m_Result)) {
4644  m_ChangedDescriptors.emplace_back(title_ci, new_desc);
4645  return true;
4646  }
4647  }
4648  }
4649  else if (NStr::EqualNocase(field, "localid")) {
4650  for (CSeqdesc_CI desc(bsh, CSeqdesc::e_User); desc; ++desc) {
4651  const CUser_object& user = desc->GetUser();
4653  if (!user.GetData().empty()) {
4654  const CRef<CUser_field>& user_field = user.GetData().front();
4655  if (user_field->IsSetLabel() && user_field->GetLabel().IsStr()
4656  && user_field->IsSetData()
4657  && NStr::EqualCase(user_field->GetLabel().GetStr(), "LocalId")) {
4658 
4659  if (user_field->GetData().IsStr()) {
4660  CObjectInfo objInfo(user_field.GetNCPointer(), user_field->GetTypeInfo());
4661  if (ResolveIdentToSimple(objInfo, "data.str", *m_Result))
4662  return true;
4663  }
4664  }
4665  }
4666  }
4667  }
4668 
4669 
4671  CBioseq& seq = const_cast<CBioseq&>(bsh.GetCompleteBioseq().GetObject());
4672  CObjectInfo objInfo(&seq, seq.GetTypeInfo());
4673  if (ResolveIdentToSimple(objInfo, "id.local.str", *m_Result)) {
4674  return true;
4675  }
4676  }
4677  }
4678  return false;
4679 }
4680 
4682 {
4683  CRef<edit::CParseTextOptions> options(new edit::CParseTextOptions());
4684  const string& left_del = m_Args[start_index]->GetString();
4685  if (NStr::EqualCase(left_del, "eDigits")) {
4686  options->SetStartDigits();
4687  }
4688  else if (NStr::EqualCase(left_del, "eLetters")) {
4689  options->SetStartLetters();
4690  }
4691  else {
4692  options->SetStartText(left_del);
4693  }
4694  options->SetIncludeStart(m_Args[++start_index]->GetBool()); // include_start
4695 
4696  const string& right_del = m_Args[++start_index]->GetString();
4697  if (NStr::EqualCase(right_del, "eDigits")) {
4698  options->SetStopDigits();
4699  }
4700  else if (NStr::EqualCase(right_del, "eLetters")) {
4701  options->SetStopLetters();
4702  }
4703  else {
4704  options->SetStopText(right_del);
4705  }
4706  options->SetIncludeStop(m_Args[++start_index]->GetBool()); // include_stop
4707 
4708  options->SetCaseInsensitive(!m_Args[++start_index]->GetBool()); // case_sensitivity
4709  options->SetWholeWord(m_Args[++start_index]->GetBool());
4710 
4711  auto nr_args = m_Args.size();
4712  if (++start_index < nr_args) {
4713  options->SetShouldRemove(m_Args[start_index]->GetBool());
4714  }
4715  if (++start_index < nr_args) {
4716  options->SetShouldRmvBeforePattern(m_Args[start_index]->GetBool());
4717  }
4718  if (++start_index < nr_args) {
4719  options->SetShouldRmvAfterPattern(m_Args[start_index]->GetBool());
4720  }
4721 
4722  return options;
4723 }
4724 
4725 
4726 // class CMacroFunction_ParsedTextFrom
4727 /// Obtains a portion of the text from a different feature that the current one
4728 /// ParsedTextFromFeature(feature_type, field_name(str/objects), left_del(str), include_left(b), right_del(str), include_right(b),
4729 /// case_sensitive(b), whole_word(b), rmv_from_parsed(b), rmv_left(b), rmv_right(b));
4730 /// left_del and right_del can be free_text, "eDigits", "eLetters"
4731 /// the last three parameters are optional
4732 /// It may also modify the content of the field
4733 ///
4736 {
4737  // The iterator should be an RNA feature - TODO: extend it to accept other features as well
4738  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
4739  const CSeq_feat* rna_feat = dynamic_cast<const CSeq_feat*>(obj.GetPointer());
4740  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
4741  if (!rna_feat || !scope)
4742  return;
4743 
4744  if (rna_feat->IsSetData() && !rna_feat->GetData().IsRna())
4745  return;
4746 
4747  const string& src_feat = m_Args[0]->GetString();
4748  if (src_feat != "gene")
4749  return;
4750 
4752  CMQueryNodeValue::TObs res_oi;
4753  CRef<CSeq_feat> gene_feat;
4754  CSeq_feat_Handle fh;
4755  if (!feat_list.empty() && feat_list.size() == 1) {
4756  gene_feat.Reset(new CSeq_feat);
4757  gene_feat->Assign(*feat_list.front());
4758 
4759  CObjectInfo gene_oi(gene_feat.GetPointer(), gene_feat->GetThisTypeInfo());
4760  size_t index = 1;
4761  if (m_Args[index]->IsString()) {
4762  if (!GetFieldsByName(&res_oi, gene_oi, m_Args[index]->GetString()))
4763  return;
4764  }
4765  else if (m_Args[index]->AreObjects()) {
4766  res_oi = m_Args[index]->GetObjects();
4767  }
4768  else if (m_Args[index]->IsRef()) {
4769  x_GetObjectsFromRef(res_oi, index);
4771  }
4772 
4773  fh = scope->GetSeq_featHandle(*feat_list.front());
4774  }
4775 
4776 
4777  if (res_oi.empty())
4778  return;
4779 
4780  ResolveIdentToSimple(res_oi.front().field, kEmptyStr, *m_Result);
4781  if (!m_Result->IsString()) {
4783  return;
4784  }
4785 
4786  const string& full_text = m_Result->GetString();
4788  string parsed_text = options->GetSelectedText(full_text);
4789  m_Result->SetString(parsed_text);
4790 
4791  if (!parsed_text.empty() && options->ShouldRemoveFromParsed()) {
4793  NMacroUtil::GetPrimitiveObjectInfos(objs, res_oi.front());
4794  CObjectInfo objInfo = objs.front().field;
4796 
4797 
4798  string orig_val = objInfo.GetPrimitiveValueString();
4799  options->RemoveSelectedText(orig_val);
4800  if (orig_val.empty()) {
4801  RemoveFieldByName(res_oi.front());
4803  }
4804  else {
4805  SetQualStringValue(objInfo, orig_val);
4806  }
4807 
4809  if (fh) {
4810  cmd.Reset(new CCmdComposite("Remove parsed text from gene"));
4811  cmd->AddCommand(*CRef<CCmdChangeSeq_feat>(new CCmdChangeSeq_feat(fh, *gene_feat)));
4812  m_DataIter->RunCommand(cmd, m_CmdComposite);
4813  }
4814  }
4815 }
4816 
4818 {
4819  size_t as = m_Args.size();
4820  if (as < 8 || as > 11) {
4821  return false;
4822  }
4823  if (!m_Args[0]->IsString() || (!(m_Args[1]->IsString() || m_Args[1]->AreObjects() || m_Args[1]->IsRef())))
4824  return false;
4825 
4826  if (!m_Args[2]->IsString() || !m_Args[3]->IsBool() || !m_Args[4]->IsString()) {
4827  return false;
4828  }
4829 
4830  for (size_t i = 5; i < as; ++i) {
4831  if (!m_Args[i]->IsBool())
4832  return false;
4833  }
4834  return true;
4835 }
4836 
4837 ///////////////////////////////////////////////////////////////////////////////
4838 /// class CMacroFunction_LocalID
4839 /// Resolves the local id: the original id if there is one, otherwise the actual local id on the sequence
4840 ///
4841 DEFINE_MACRO_FUNCNAME(CMacroFunction_LocalID, "LocalID")
4842 void CMacroFunction_LocalID::TheFunction()
4843 {
4844  CBioseq_Handle bsh = m_DataIter->GetBioseqHandle();
4845  if (!bsh) return;
4846 
4847  for (CSeqdesc_CI desc(bsh, CSeqdesc::e_User); desc; ++desc) {
4848  const CUser_object& user = desc->GetUser();
4850  if (!user.GetData().empty()) {
4851  const CRef<CUser_field>& user_field = user.GetData().front();
4852  if (user_field->IsSetLabel() && user_field->GetLabel().IsStr()
4853  && user_field->IsSetData()
4854  && NStr::EqualCase(user_field->GetLabel().GetStr(), "LocalId")) {
4855 
4856  if (user_field->GetData().IsStr()) {
4857  CObjectInfo objInfo(user_field.GetNCPointer(), user_field->GetTypeInfo());
4858  if (ResolveIdentToObjects(objInfo, "data.str", *m_Result))
4859  return;
4860  }
4861  }
4862  }
4863  }
4864  }
4865 
4866 
4867  if (m_Result->IsNotSet()) {
4868  CBioseq& seq = const_cast<CBioseq&>(bsh.GetCompleteBioseq().GetObject());
4869  CObjectInfo objInfo(&seq, seq.GetTypeInfo());
4870  ResolveIdentToObjects(objInfo, "id.local.str", *m_Result);
4871  if (m_Result->IsNotSet()) {
4872  ResolveIdentToObjects(objInfo, "id.local.id", *m_Result);
4873  }
4874  }
4875 
4876 }
4877 
4878 bool CMacroFunction_LocalID::x_ValidArguments() const
4879 {
4880  return (m_Args.empty());
4881 }
4882 
4883 
4884 ///////////////////////////////////////////////////////////////////////////////
4885 /// class CMacroFunction_SetQual
4886 /// SetQual(field_name, newValue)
4887 ///
4889 
4890 void CMacroFunction_SetQual::TheFunction()
4891 {
4892  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
4893  CMQueryNodeValue& new_value = *m_Args[1];
4894 
4895  CObjectInfo oi = m_DataIter->GetEditedObject();
4896  CMQueryNodeValue::TObs res_oi;
4898  if (!SetFieldsByName(&res_oi, oi, m_Args[0]->GetString())) {
4899  return;
4900  }
4901  }
4902  else if (type == CMQueryNodeValue::eObjects) {
4903  res_oi = m_Args[0]->GetObjects();
4904  }
4905  else if (type == CMQueryNodeValue::eRef) {
4906  x_GetObjectsFromRef(res_oi, 0);
4907  }
4908 
4909  if (res_oi.empty()) {
4910  return;
4911  }
4912 
4914  if (SetSimpleTypeValue(it->field, new_value)) {
4915  m_QualsChangedCount++;
4916  }
4917  }
4918 
4919  if (m_QualsChangedCount) {
4920  NMacroUtil::CleanupForTaxnameChange(res_oi.front(), oi);
4921  m_DataIter->SetModified();
4923  log << m_DataIter->GetBestDescr() << ": set new value to " << m_QualsChangedCount << " qualifiers";
4924  x_LogFunction(log);
4925  }
4926 }
4927 
4929 {
4930  // can accept as its first parameter: objects, string or reference
4931  CMQueryNodeValue::EType type = m_Args[0]->GetDataType();
4933  if (m_Args.size() != 2 || !first_ok)
4934  return false;
4935 
4936  if (!m_Args[1]->IsSimpleType()) {
4937  return false;
4938  }
4939  return true;
4940 }
4941 
4942 
4943 ///////////////////////////////////////////////////////////////////////////////
4944 // class CMacroFunction_AddDBLink
4945 /// AddDBLink(dblink_type, newValue, existing_text, delimiter, remove_blank)
4946 /// The last parameter is optional
4947 ///
4949 
4950 static void s_AddNewDBlinkValue(CUser_field& user_field, const string& newValue);
4951 
4952 static void s_SetVectorValue(CUser_field& user_field, const string& newValue, const string& del);
4953 
4954 static bool s_AddNewUserField(CRef<CSeqdesc>& user_object_desc, const string& dblink, const string& newValue);
4955 
4956 void CMacroFunction_AddDBLink::TheFunction()
4957 {
4958  CConstRef<CObject> obj = m_DataIter->GetScopedObject().object;
4959  const CBioseq* bseq = dynamic_cast<const CBioseq*>(obj.GetPointer());
4960  CRef<CScope> scope = m_DataIter->GetScopedObject().scope;
4961  if (!scope || !bseq || bseq->IsAa())
4962  return;
4963 
4964  size_t index = 0;
4965  const string& dblink = m_Args[index]->GetString();
4966  string newValue = NMacroUtil::GetStringValue(m_Args[++index]);
4967  const string& existing_text_option = m_Args[++index]->GetString();
4968  string delimiter;
4969  bool remove_field = false;
4970  x_GetOptionalArgs(delimiter, remove_field, index);
4971  edit::EExistingText existing_text = NMacroUtil::ActionTypeToExistingTextOption(existing_text_option, delimiter);
4972 
4974 
4975  if (!newValue.empty()) {
4976  vector<pair<CSeqdesc_CI, CRef<CSeqdesc>>> changed_descs;
4977  CBioseq_Handle bsh = m_DataIter->GetBioseqHandle();
4978  if (!bsh)
4979  return;
4980 
4981  for (CSeqdesc_CI desc_it(bsh, CSeqdesc::e_User); desc_it; ++desc_it) {
4982  if (desc_it->GetUser().GetType().IsStr() && desc_it->GetUser().GetType().GetStr() == "DBLink") {
4983  CRef<CSeqdesc> new_desc(new CSeqdesc);