validerror_feat.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:92k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: validerror_feat.cpp,v $
  4.  * PRODUCTION Revision 1000.2  2004/06/01 19:48:04  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.56
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: validerror_feat.cpp,v 1000.2 2004/06/01 19:48:04 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Author:  Jonathan Kans, Clifford Clausen, Aaron Ucko......
  35.  *
  36.  * File Description:
  37.  *   validation of Seq_feat
  38.  *   .......
  39.  *
  40.  */
  41. #include <ncbi_pch.hpp>
  42. #include <corelib/ncbistd.hpp>
  43. #include <corelib/ncbistr.hpp>
  44. #include "validatorp.hpp"
  45. #include "utilities.hpp"
  46. #include <serial/serialbase.hpp>
  47. #include <objmgr/bioseq_handle.hpp>
  48. #include <objmgr/seq_entry_handle.hpp>
  49. #include <objmgr/feat_ci.hpp>
  50. #include <objmgr/seqdesc_ci.hpp>
  51. #include <objmgr/seq_vector.hpp>
  52. #include <objmgr/scope.hpp>
  53. #include <objmgr/util/sequence.hpp>
  54. #include <objmgr/util/feature.hpp>
  55. #include <objects/seqfeat/Seq_feat.hpp>
  56. #include <objects/seqfeat/BioSource.hpp>
  57. #include <objects/seqfeat/Cdregion.hpp>
  58. #include <objects/seqfeat/Code_break.hpp>
  59. #include <objects/seqfeat/Gb_qual.hpp>
  60. #include <objects/seqfeat/Genetic_code.hpp>
  61. #include <objects/seqfeat/Genetic_code_table.hpp>
  62. #include <objects/seqfeat/Imp_feat.hpp>
  63. #include <objects/seqfeat/Org_ref.hpp>
  64. #include <objects/seqfeat/Prot_ref.hpp>
  65. #include <objects/seqfeat/RNA_ref.hpp>
  66. #include <objects/seqfeat/SubSource.hpp>
  67. #include <objects/seqfeat/Trna_ext.hpp>
  68. #include <objects/seqloc/Seq_loc.hpp>
  69. #include <objects/seqloc/Seq_interval.hpp>
  70. #include <objects/seqloc/Seq_point.hpp>
  71. #include <objects/seqloc/Textseq_id.hpp>
  72. #include <objects/seqset/Seq_entry.hpp>
  73. #include <objects/seqset/Bioseq_set.hpp>
  74. #include <objects/seq/MolInfo.hpp>
  75. #include <objects/seq/Bioseq.hpp>
  76. #include <objects/pub/Pub.hpp>
  77. #include <objects/pub/Pub_set.hpp>
  78. #include <objects/general/Dbtag.hpp>
  79. #include <algorithm>
  80. #include <string>
  81. BEGIN_NCBI_SCOPE
  82. BEGIN_SCOPE(objects)
  83. BEGIN_SCOPE(validator)
  84. using namespace sequence;
  85. // =============================================================================
  86. //                                     Public
  87. // =============================================================================
  88. CValidError_feat::CValidError_feat(CValidError_imp& imp) :
  89.     CValidError_base(imp),
  90.     m_NumGenes(0),
  91.     m_NumGeneXrefs(0)
  92. {
  93. }
  94. CValidError_feat::~CValidError_feat(void)
  95. {
  96. }
  97. void CValidError_feat::ValidateSeqFeat(const CSeq_feat& feat)
  98. {
  99.     if ( !feat.CanGetLocation() ) {
  100.         PostErr(eDiag_Critical, eErr_SEQ_FEAT_MissingLocation,
  101.             "The feature is missing a location", feat);
  102.     }
  103.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetLocation());
  104.     m_Imp.ValidateSeqLoc(feat.GetLocation(), bsh, "Location", feat);
  105.     
  106.     if ( feat.CanGetProduct() ) {
  107.         ValidateSeqFeatProduct(feat.GetProduct(), feat);
  108.     }
  109.     
  110.     ValidateFeatPartialness(feat);
  111.     
  112.     ValidateExcept(feat);
  113.     
  114.     ValidateSeqFeatData(feat.GetData(), feat);
  115.     
  116.     if (feat.CanGetDbxref ()) {
  117.         m_Imp.ValidateDbxref (feat.GetDbxref (), feat);
  118.     }
  119.     
  120.     if ( feat.CanGetComment() ) {
  121.         ValidateFeatComment(feat.GetComment(), feat);
  122.     }
  123.     if ( feat.CanGetCit() ) {
  124.         ValidateFeatCit(feat.GetCit(), feat);
  125.     }
  126.     const CGene_ref* gene_xref = feat.GetGeneXref();
  127.     if ( gene_xref != 0  &&  !gene_xref->IsSuppressed() ) {
  128.         ++m_NumGeneXrefs;
  129.     }
  130. }
  131. // =============================================================================
  132. //                                     Private
  133. // =============================================================================
  134. // static member initializations
  135. const string s_PlastidTxt[] = {
  136.   "",
  137.   "",
  138.   "chloroplast",
  139.   "chromoplast",
  140.   "",
  141.   "",
  142.   "plastid",
  143.   "",
  144.   "",
  145.   "",
  146.   "",
  147.   "",
  148.   "cyanelle",
  149.   "",
  150.   "",
  151.   "",
  152.   "apicoplast",
  153.   "leucoplast",
  154.   "proplastid",
  155.   ""
  156. };
  157. static string s_LegalRepeatTypes[] = {
  158.   "tandem", "inverted", "flanking", "terminal",
  159.   "direct", "dispersed", "other"
  160. };
  161. static string s_LegalConsSpliceStrings[] = {
  162.   "(5'site:YES, 3'site:YES)",
  163.   "(5'site:YES, 3'site:NO)",
  164.   "(5'site:YES, 3'site:ABSENT)",
  165.   "(5'site:NO, 3'site:YES)",
  166.   "(5'site:NO, 3'site:NO)",
  167.   "(5'site:NO, 3'site:ABSENT)",
  168.   "(5'site:ABSENT, 3'site:YES)",
  169.   "(5'site:ABSENT, 3'site:NO)",
  170.   "(5'site:ABSENT, 3'site:ABSENT)"
  171. };
  172. static bool s_IsLocRefSeqMrna(const CSeq_loc& loc, CScope& scope)
  173. {
  174.     CBioseq_Handle bsh = scope.GetBioseqHandle(loc);
  175.     if ( bsh ) {
  176.         ITERATE (CBioseq::TId, it, bsh.GetBioseqCore()->GetId()) {
  177.             if ( (*it)->IdentifyAccession() == CSeq_id::eAcc_refseq_mrna ) {
  178.                 return true;
  179.             }
  180.         }
  181.     }
  182.     return false;
  183. }
  184. static bool s_IsLocGEDL(const CSeq_loc& loc, CScope& scope)
  185. {
  186.     CBioseq_Handle bsh = scope.GetBioseqHandle(loc);
  187.     if ( bsh ) {
  188.         ITERATE (CBioseq::TId, it, bsh.GetBioseqCore()->GetId()) {
  189.             CSeq_id::EAccessionInfo acc_info = (*it)->IdentifyAccession();
  190.             if ( acc_info == CSeq_id::eAcc_gb_embl_ddbj  ||
  191.                  acc_info == CSeq_id::eAcc_local ) {
  192.                 return true;
  193.             }
  194.         }
  195.     }
  196.     return false;
  197. }
  198. // private member functions:
  199. void CValidError_feat::ValidateSeqFeatData
  200. (const CSeqFeatData& data,
  201.  const CSeq_feat& feat)
  202. {
  203.     switch ( data.Which () ) {
  204.     case CSeqFeatData::e_Gene:
  205.         // Validate CGene_ref
  206.         ValidateGene(data.GetGene (), feat);
  207.         break;
  208.     case CSeqFeatData::e_Cdregion:
  209.         // Validate CCdregion
  210.         ValidateCdregion(data.GetCdregion (), feat);
  211.         break;
  212.     case CSeqFeatData::e_Prot:
  213.         // Validate CProt_ref
  214.         ValidateProt(data.GetProt (), feat);
  215.         break;
  216.     case CSeqFeatData::e_Rna:
  217.         // Validate CRNA_ref
  218.         ValidateRna(data.GetRna (), feat);
  219.         break;
  220.     case CSeqFeatData::e_Pub:
  221.         // Validate CPubdesc
  222.         m_Imp.ValidatePubdesc(data.GetPub (), feat);
  223.         break;
  224.     case CSeqFeatData::e_Imp:
  225.         // Validate CPubdesc
  226.         ValidateImp(data.GetImp (), feat);
  227.         break;
  228.     case CSeqFeatData::e_Biosrc:
  229.         // Validate CBioSource
  230.         ValidateFeatBioSource(data.GetBiosrc(), feat);
  231.         break;
  232.     case CSeqFeatData::e_Org:
  233.     case CSeqFeatData::e_Region:
  234.     case CSeqFeatData::e_Seq:
  235.     case CSeqFeatData::e_Comment:
  236.     case CSeqFeatData::e_Bond:
  237.     case CSeqFeatData::e_Site:
  238.     case CSeqFeatData::e_Rsite:
  239.     case CSeqFeatData::e_User:
  240.     case CSeqFeatData::e_Txinit:
  241.     case CSeqFeatData::e_Num:
  242.     case CSeqFeatData::e_Psec_str:
  243.     case CSeqFeatData::e_Non_std_residue:
  244.     case CSeqFeatData::e_Het:
  245.         break;
  246.     default:
  247.         PostErr(eDiag_Error, eErr_SEQ_FEAT_InvalidType,
  248.             "Invalid SeqFeat type [" + 
  249.             NStr::IntToString(data.Which ()) +
  250.             "]", feat);
  251.         break;
  252.     }
  253.     if ( !data.IsGene() ) {
  254.         ValidateGeneXRef(feat);
  255.     } else {
  256.         ValidateOperon(feat);
  257.     }
  258. }
  259. void CValidError_feat::ValidateSeqFeatProduct
  260. (const CSeq_loc& prod, const CSeq_feat& feat)
  261. {
  262.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetProduct());
  263.     m_Imp.ValidateSeqLoc(feat.GetProduct(), bsh, "Product", feat);
  264.     
  265.     if ( IsOneBioseq(prod, m_Scope) ) {
  266.         const CSeq_id& sid = GetId(prod, m_Scope);
  267.     
  268.         switch ( sid.Which() ) {
  269.         case CSeq_id::e_Genbank:
  270.         case CSeq_id::e_Embl:
  271.         case CSeq_id::e_Ddbj:
  272.         case CSeq_id::e_Tpg:
  273.         case CSeq_id::e_Tpe:
  274.         case CSeq_id::e_Tpd:
  275.             {
  276.                 const CTextseq_id* tsid = sid.GetTextseq_Id();
  277.                 if ( tsid != NULL ) {
  278.                     if ( !tsid->CanGetAccession()  &&  tsid->CanGetName() ) {
  279.                         if ( m_Imp.IsNucAcc(tsid->GetName()) ) {
  280.                             PostErr(eDiag_Warning, eErr_SEQ_FEAT_BadProductSeqId,
  281.                                 "Feature product should not use "
  282.                                 "Textseq-id 'name' slot", feat);
  283.                         }
  284.                     }
  285.                 }
  286.             }
  287.             break;
  288.             
  289.         default:
  290.             break;
  291.         }
  292.     }
  293. }
  294. bool CValidError_feat::IsPlastid(int genome)
  295. {
  296.     if ( genome == CBioSource::eGenome_chloroplast  ||
  297.          genome == CBioSource::eGenome_chromoplast  ||
  298.          genome == CBioSource::eGenome_plastid      ||
  299.          genome == CBioSource::eGenome_cyanelle     ||
  300.          genome == CBioSource::eGenome_apicoplast   ||
  301.          genome == CBioSource::eGenome_leucoplast   ||
  302.          genome == CBioSource::eGenome_proplastid  ) { 
  303.         return true;
  304.     }
  305.     return false;
  306. }
  307. bool CValidError_feat::IsOverlappingGenePseudo(const CSeq_feat& feat)
  308. {
  309.     const CGene_ref* grp = feat.GetGeneXref();
  310.     if ( grp  ) {
  311.         return (grp->CanGetPseudo()  &&  grp->GetPseudo());
  312.     }
  313.     // !!! DEBUG {
  314.     // For testing purposes. Remove when test is done.
  315.     if ( m_Imp.AvoidPerfBottlenecks() ) {
  316.         return false;
  317.     }
  318.     // }
  319.     // check overlapping gene
  320.     CConstRef<CSeq_feat> overlap = 
  321.         GetOverlappingGene(feat.GetLocation(), *m_Scope);
  322.     if ( overlap ) {
  323.         if ( (overlap->CanGetPseudo()  &&  overlap->GetPseudo())  ||
  324.              (overlap->GetData().GetGene().CanGetPseudo()  &&
  325.               overlap->GetData().GetGene().GetPseudo()) ) {
  326.             return true;
  327.         }
  328.     }
  329.     return false;
  330. }
  331. bool CValidError_feat::SuppressCheck(const string& except_text)
  332. {
  333.     static string exceptions[] = {
  334.         "ribosomal slippage",
  335.         "artificial frameshift",
  336.         "nonconsensus splice site"
  337.     };
  338.     for ( size_t i = 0; i < sizeof(exceptions) / sizeof(string); ++i ) {
  339.     if ( NStr::FindNoCase(except_text, exceptions[i] ) != string::npos )
  340.          return true;
  341.     }
  342.     return false;
  343. }
  344. unsigned char CValidError_feat::Residue(unsigned char res)
  345. {
  346.     return res == 255 ? '?' : res;
  347. }
  348. int CValidError_feat::CheckForRaggedEnd
  349. (const CSeq_loc& loc, 
  350.  const CCdregion& cdregion)
  351. {
  352.     size_t len = GetLength(loc, m_Scope);
  353.     if ( cdregion.GetFrame() > CCdregion::eFrame_one ) {
  354.         len -= cdregion.GetFrame() - 1;
  355.     }
  356.     int ragged = len % 3;
  357.     if ( ragged > 0 ) {
  358.         len = GetLength(loc, m_Scope);
  359.         CSeq_loc::TRange range = CSeq_loc::TRange::GetEmpty();
  360.         ITERATE( CCdregion::TCode_break, cbr, cdregion.GetCode_break() ) {
  361.             SRelLoc rl(loc, (*cbr)->GetLoc(), m_Scope);
  362.             CRef<CSeq_loc> rel_loc = rl.Resolve(m_Scope);
  363.             range += rel_loc->GetTotalRange();
  364.         }
  365.         // allowing a partial codon at the end
  366.         TSeqPos codon_length = range.GetLength();
  367.         if ( (codon_length == 0 || codon_length == 1)  && 
  368.             range.GetTo() == len - 1 ) {
  369.             ragged = 0;
  370.         }
  371.     }
  372.     return ragged;
  373. }
  374. string CValidError_feat::MapToNTCoords
  375. (const CSeq_feat& feat,
  376.  const CSeq_loc& product,
  377.  TSeqPos pos)
  378. {
  379.     string result;
  380.     CSeq_point pnt;
  381.     pnt.SetPoint(pos);
  382.     pnt.SetStrand( GetStrand(product, m_Scope) );
  383.     try {
  384.         pnt.SetId().Assign(GetId(product, m_Scope));
  385.     } catch (CNotUnique) {}
  386.     CSeq_loc tmp;
  387.     tmp.SetPnt(pnt);
  388.     CRef<CSeq_loc> loc = ProductToSource(feat, tmp, 0, m_Scope);
  389.     
  390.     loc->GetLabel(&result);
  391.     return result;
  392. }
  393. void CValidError_feat::ValidateFeatPartialness(const CSeq_feat& feat)
  394. {
  395.     unsigned int  partial_prod = eSeqlocPartial_Complete, 
  396.         partial_loc = eSeqlocPartial_Complete;
  397.     static string parterr[2] = { "PartialProduct", "PartialLocation" };
  398.     static string parterrs[4] = {
  399.         "Start does not include first/last residue of sequence",
  400.         "Stop does not include first/last residue of sequence",
  401.         "Internal partial intervals do not include first/last residue of sequence",
  402.         "Improper use of partial (greater than or less than)"
  403.     };
  404.     partial_loc  = SeqLocPartialCheck(feat.GetLocation(), m_Scope );
  405.     if (feat.CanGetProduct ()) {
  406.         partial_prod = SeqLocPartialCheck(feat.GetProduct (), m_Scope );
  407.     }
  408.     
  409.     if ( (partial_loc  != eSeqlocPartial_Complete)  ||
  410.          (partial_prod != eSeqlocPartial_Complete)  ||   
  411.          (feat.CanGetPartial()  &&  feat.GetPartial() == true) ) {
  412.         // a feature on a partial sequence should be partial -- it often isn't
  413.         if ( (!feat.CanGetPartial()  ||  !feat.GetPartial()) &&
  414.             partial_loc != eSeqlocPartial_Complete  &&
  415.             feat.GetLocation ().Which () == CSeq_loc::e_Whole ) {
  416.             PostErr(eDiag_Info, eErr_SEQ_FEAT_PartialProblem,
  417.                 "On partial Bioseq, SeqFeat.partial should be TRUE", feat);
  418.         }
  419.         // a partial feature, with complete location, but partial product
  420.         else if ( (feat.CanGetPartial()  &&  feat.GetPartial())  &&
  421.             partial_loc == eSeqlocPartial_Complete  &&
  422.             feat.CanGetProduct () &&
  423.             feat.GetProduct ().Which () == CSeq_loc::e_Whole  &&
  424.             partial_prod != eSeqlocPartial_Complete ) {
  425.             PostErr(eDiag_Warning, eErr_SEQ_FEAT_PartialProblem,
  426.                 "When SeqFeat.product is a partial Bioseq, SeqFeat.location "
  427.                 "should also be partial", feat);
  428.         }
  429.         // gene on segmented set is now 'order', should also be partial
  430.         else if ( feat.GetData ().IsGene ()  &&
  431.             !feat.CanGetProduct ()  &&
  432.             partial_loc == eSeqlocPartial_Internal ) {
  433.             PostErr(eDiag_Warning, eErr_SEQ_FEAT_PartialProblem,
  434.                 "Gene of 'order' with otherwise complete location should "
  435.                 "have partial flag set", feat);
  436.         }
  437.         // inconsistent combination of partial/complete product,location,partial flag - part 1
  438.         else if ( partial_prod == eSeqlocPartial_Complete  &&
  439.                   feat.CanGetProduct() ) {
  440.             // if not local bioseq product, lower severity
  441.             EDiagSev sev = eDiag_Warning;
  442.             if ( IsOneBioseq(feat.GetProduct(), m_Scope) ) {
  443.                 const CSeq_id& prod_id = GetId(feat.GetProduct());
  444.                 CBioseq_Handle prod =
  445.                     m_Scope->GetBioseqHandleFromTSE(prod_id, m_Imp.GetTSE());
  446.                 if ( !prod ) {
  447.                     sev = eDiag_Info;
  448.                 }
  449.             }
  450.                         
  451.             string str("Inconsistent: Product= complete, Location= ");
  452.             if ( partial_loc != eSeqlocPartial_Complete ) {
  453.                 str += "partial, ";
  454.             } else {
  455.                 str += "complete, ";
  456.             }
  457.             str += "Feature.partial= ";
  458.             if ( feat.CanGetPartial()  &&  feat.GetPartial() ) {
  459.                 str += "TRUE";
  460.             } else {
  461.                 str += "FALSE";
  462.             }
  463.             PostErr(sev, eErr_SEQ_FEAT_PartialsInconsistent, str, feat);
  464.         }
  465.         // inconsistent combination of partial/complete product,location,partial flag - part 2
  466.         else if ( partial_loc == eSeqlocPartial_Complete  ||
  467.                   (feat.CanGetPartial()  &&  !feat.GetPartial()) ) {
  468.             string str("Inconsistent: ");
  469.             if ( feat.CanGetProduct() ) {
  470.                 str += "Product= ";
  471.                 if ( partial_prod != eSeqlocPartial_Complete ) {
  472.                     str += "partial, ";
  473.                 } else {
  474.                     str += "complete, ";
  475.                 }
  476.                 str += "Location= ";
  477.                 if ( partial_loc != eSeqlocPartial_Complete ) {
  478.                     str += "partial, ";
  479.                 } else {
  480.                     str += "complete, ";
  481.                 }
  482.                 str += "Feature.partial= ";
  483.                 if ( feat.CanGetPartial()  &&  feat.GetPartial() ) {
  484.                     str += "TRUE";
  485.                 } else {
  486.                     str += "FALSE";
  487.                 }
  488.             }
  489.             PostErr(eDiag_Warning, eErr_SEQ_FEAT_PartialsInconsistent, str, feat);
  490.         }
  491.         // 5' or 3' partial location giving unclassified partial product
  492.         else if ( (partial_loc & eSeqlocPartial_Start  ||
  493.                    partial_loc & eSeqlocPartial_Stop)  &&
  494.                    partial_prod & eSeqlocPartial_Other &&
  495.                    feat.CanGetPartial()  &&  feat.GetPartial() ) {
  496.             PostErr(eDiag_Warning, eErr_SEQ_FEAT_PartialProblem,
  497.                 "5' or 3' partial location should not have unclassified "
  498.                 "partial location", feat);
  499.         }
  500.         
  501.         // may have other error bits set as well 
  502.         unsigned int partials[2] = { partial_prod, partial_loc };
  503.         for ( int i = 0; i < 2; ++i ) {
  504.             unsigned int errtype = eSeqlocPartial_Nostart;
  505.             for ( int j = 0; j < 4; ++j ) {
  506.                 if (partials[i] & errtype) {
  507.                     if ( i == 1  &&  j < 2  &&  IsCDDFeat(feat) ) {
  508.                         // supress warning
  509.                     } else if ( i == 1  &&  j < 2  &&
  510.                         IsPartialAtSpliceSite(feat.GetLocation(), errtype) ) {
  511.                         PostErr(eDiag_Info, eErr_SEQ_FEAT_PartialProblem,
  512.                             parterr[i] + ": " + parterrs[j] + 
  513.                             " (but is at consensus splice site)", feat);
  514.                     } else if ( i == 1  &&  j < 2  &&
  515.                         (feat.GetData().Which() == CSeqFeatData::e_Gene  ||
  516.                         feat.GetData().GetSubtype() == CSeqFeatData::eSubtype_mRNA) &&
  517.                          IsSameAsCDS(feat) ) {
  518.                         PostErr(eDiag_Info, eErr_SEQ_FEAT_PartialProblem,
  519.                             parterr[i] + ": " + parterrs[j], feat);
  520.                     } else if (feat.GetData().Which() == CSeqFeatData::e_Cdregion && j == 0) {
  521.                         PostErr (eDiag_Warning, eErr_SEQ_FEAT_PartialProblem,
  522.                             parterr[i] + 
  523.                             ": 5' partial is not at start AND is not at consensus splice site",
  524.                             feat); 
  525.                     } else if (feat.GetData().Which() == CSeqFeatData::e_Cdregion && j == 1) {
  526.                         PostErr (eDiag_Warning, eErr_SEQ_FEAT_PartialProblem,
  527.                             parterr[i] + 
  528.                             ": 3' partial is not at stop AND is not at consensus splice site",
  529.                             feat);
  530.                     } else {
  531.                         PostErr (eDiag_Warning, eErr_SEQ_FEAT_PartialProblem,
  532.                             parterr[i] + ": " + parterrs[j], feat);
  533.                     }
  534.                 }
  535.                 errtype <<= 1;
  536.             }
  537.         }
  538.     }
  539. }
  540. void CValidError_feat::ValidateGene(const CGene_ref& gene, const CSeq_feat& feat)
  541. {
  542.     ++m_NumGenes;
  543.     if ( (gene.CanGetLocus()      &&  gene.GetLocus().empty())   &&
  544.          (gene.CanGetAllele()     &&  gene.GetAllele().empty())  &&
  545.          (gene.CanGetDesc()       &&  gene.GetDesc().empty())    &&
  546.          (gene.CanGetMaploc()     &&  gene.GetMaploc().empty())  &&
  547.          (gene.CanGetDb()         &&  gene.GetDb().empty())      &&
  548.          (gene.CanGetSyn()        &&  gene.GetSyn().empty())     &&
  549.          (gene.CanGetLocus_tag()  &&  gene.GetLocus_tag().empty()) ) {
  550.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_GeneRefHasNoData,
  551.             "There is a gene feature where all fields are empty", feat);
  552.     }
  553.     if ( gene.CanGetLocus()  &&  !gene.GetLocus().empty() ) {
  554.         ITERATE (string, it, gene.GetLocus() ) {
  555.             if ( isspace(*it) != 0 ) {
  556.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_LocusTagProblem,
  557.                     "Gene locus_tag '" + gene.GetLocus() + 
  558.                     "' should be a single word without any spaces", feat);
  559.                 break;
  560.             }
  561.         }         
  562.     }
  563.     if ( gene.CanGetDb () ) {
  564.         m_Imp.ValidateDbxref(gene.GetDb(), feat);
  565.     }
  566. }
  567. void CValidError_feat::ValidateCdregion (
  568.     const CCdregion& cdregion, 
  569.     const CSeq_feat& feat
  570. {
  571.     ITERATE( CSeq_feat::TQual, qual, feat.GetQual () ) {
  572.         if ( (**qual).GetQual() == "codon" ) {
  573.             PostErr(eDiag_Warning, eErr_SEQ_FEAT_WrongQualOnImpFeat,
  574.                 "Use the proper genetic code, if available, "
  575.                 "or set transl_excepts on specific codons", feat);
  576.             break;
  577.         }
  578.     }
  579.     bool pseudo = (feat.CanGetPseudo()  &&  feat.GetPseudo())  ||
  580.         IsOverlappingGenePseudo(feat);
  581.     bool conflict = cdregion.CanGetConflict()  &&  cdregion.GetConflict();
  582.     if ( !pseudo  &&  !conflict ) {
  583.         ValidateCdTrans(feat);
  584.         ValidateSplice(feat, false);
  585.         ValidateCdsProductId(feat);
  586.     } else if ( conflict ) {
  587.         ValidateCdConflict(cdregion, feat);
  588.     }
  589.     ITERATE( CCdregion::TCode_break, codebreak, cdregion.GetCode_break() ) {
  590.         ECompare comp = sequence::Compare((**codebreak).GetLoc (),
  591.             feat.GetLocation (), m_Scope );
  592.         if ( (comp != eContained) && (comp != eSame))
  593.             PostErr (eDiag_Error, eErr_SEQ_FEAT_Range, 
  594.                 "Code-break location not in coding region", feat);
  595.     }
  596.     if ( cdregion.CanGetOrf()  &&  cdregion.GetOrf ()  &&
  597.          feat.CanGetProduct () ) {
  598.         PostErr (eDiag_Warning, eErr_SEQ_FEAT_OrfCdsHasProduct,
  599.             "An ORF coding region should not have a product", feat);
  600.     }
  601.     if ( pseudo && feat.CanGetProduct () ) {
  602.         PostErr (eDiag_Warning, eErr_SEQ_FEAT_PsuedoCdsHasProduct,
  603.             "A pseudo coding region should not have a product", feat);
  604.     }
  605.     
  606.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetLocation ());
  607.     if ( bsh ) {
  608.         CSeqdesc_CI diter (bsh, CSeqdesc::e_Source);
  609.         if ( diter ) {
  610.             const CBioSource& src = diter->GetSource();
  611.             
  612.             int biopgencode = src.GetGenCode();
  613.             
  614.             if (cdregion.CanGetCode ()) {
  615.                 int cdsgencode = cdregion.GetCode().GetId();
  616.                 
  617.                 if ( biopgencode != cdsgencode ) {
  618.                     int genome = 0;
  619.                     
  620.                     if ( src.CanGetGenome() ) {
  621.                         genome = src.GetGenome();
  622.                     }
  623.                     
  624.                     if ( IsPlastid(genome) ) {
  625.                         PostErr (eDiag_Warning, eErr_SEQ_FEAT_GenCodeMismatch,
  626.                             "Genetic code conflict between CDS (code " +
  627.                             NStr::IntToString (cdsgencode) +
  628.                             ") and BioSource.genome biological context (" +
  629.                             s_PlastidTxt [genome] + ") (uses code 11)", feat);
  630.                     } else {
  631.                         PostErr (eDiag_Warning, eErr_SEQ_FEAT_GenCodeMismatch,
  632.                             "Genetic code conflict between CDS (code " +
  633.                             NStr::IntToString (cdsgencode) +
  634.                             ") and BioSource (code " +
  635.                             NStr::IntToString (biopgencode) + ")", feat);
  636.                     }
  637.                 }
  638.             }
  639.         }
  640.     }
  641.     ValidateBothStrands(feat);
  642.     ValidateBadGeneOverlap(feat);
  643.     ValidateBadMRNAOverlap(feat);
  644.     ValidateCommonCDSProduct(feat);
  645.     ValidateCDSPartial(feat);
  646. }
  647. // non-pseudo CDS must have product
  648. void CValidError_feat::ValidateCdsProductId(const CSeq_feat& feat)
  649. {
  650.     // bail if product exists
  651.     if ( feat.CanGetProduct() ) {
  652.         return;
  653.     }
  654.     
  655.     // bail if location has just stop
  656.     if ( feat.CanGetLocation() ) {
  657.         const CSeq_loc& loc = feat.GetLocation();
  658.         if ( loc.IsPartialLeft()  &&  !loc.IsPartialRight() ) {
  659.             if ( GetLength(loc, m_Scope) <= 5 ) {
  660.                 return;
  661.             }
  662.         }
  663.     }
  664.     
  665.     // supress in case of the appropriate exception
  666.     if ( feat.CanGetExcept()  &&  feat.CanGetExcept_text()  &&
  667.         !IsBlankString(feat.GetExcept_text()) ) {
  668.         if ( NStr::Find(feat.GetExcept_text(),
  669.                         "rearrangement required for product") != NPOS ) {
  670.             return;
  671.         }
  672.     }
  673.     
  674.     PostErr(eDiag_Warning, eErr_SEQ_FEAT_MissingCDSproduct,
  675.         "Expected CDS product absent", feat);
  676. }
  677. void CValidError_feat::ValidateCdConflict
  678. (const CCdregion& cdregion,
  679.  const CSeq_feat& feat)
  680. {
  681.     CBioseq_Handle nuc  = m_Scope->GetBioseqHandle(feat.GetLocation());
  682.     CBioseq_Handle prot = m_Scope->GetBioseqHandle(feat.GetProduct());
  683.     
  684.     // translate the coding region
  685.     string transl_prot;
  686.     try {
  687.         CCdregion_translate::TranslateCdregion(
  688.             transl_prot, 
  689.             nuc, 
  690.             feat.GetLocation(), 
  691.             cdregion,
  692.             false,   // do not include stop codons
  693.             false);  // do not remove trailing X/B/Z
  694.     } catch ( const runtime_error& ) {
  695.     }
  696.     CSeqVector prot_vec = prot.GetSeqVector(CBioseq_Handle::eCoding_Iupac);
  697.     string prot_seq;
  698.     prot_vec.GetSeqData(0, prot_vec.size(), prot_seq);
  699.     if ( transl_prot.empty()  ||  prot_seq.empty()  ||  transl_prot == prot_seq ) {
  700.         PostErr(eDiag_Error, eErr_SEQ_FEAT_BadConflictFlag,
  701.             "Coding region conflict flag should not be set", feat);
  702.     } else {
  703.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_ConflictFlagSet,
  704.             "Coding region conflict flag is set", feat);
  705.     }
  706. }
  707. void CValidError_feat::ValidateSplice(const CSeq_feat& feat, bool check_all)
  708. {
  709.     // !!! suppress if NCBISubValidate
  710.     //if (GetAppProperty ("NcbiSubutilValidation") != NULL)
  711.     //    return;
  712.     // specific biological exceptions suppress check
  713.     if ( feat.CanGetExcept()  &&  feat.GetExcept()  &&
  714.          feat.CanGetExcept_text() ) {
  715.         if ( SuppressCheck(feat.GetExcept_text()) ) {
  716.             return;
  717.         }
  718.     }
  719.         
  720.     size_t num_of_parts = 0;
  721.     ENa_strand  strand = eNa_strand_unknown;
  722.     // !!! The C version treated seq_loc equiv as one whereas the iterator
  723.     // treats it as many. 
  724.     const CSeq_loc& location = feat.GetLocation ();
  725.     for (CSeq_loc_CI citer(location); citer; ++citer) {
  726.         ++num_of_parts;
  727.         if ( num_of_parts == 1 ) {  // first part
  728.             strand = citer.GetStrand();
  729.         } else {
  730.             if ( strand != citer.GetStrand() ) {
  731.                 return;         //bail on mixed strand
  732.             }
  733.         }
  734.     }
  735.     if ( num_of_parts == 0 ) {
  736.         return;
  737.     }
  738.     if ( !check_all  &&  num_of_parts == 1 ) {
  739.         return;
  740.     }
  741.     
  742.     bool partial_first = location.IsPartialLeft();
  743.     bool partial_last = location.IsPartialRight();
  744.     size_t counter = 0;
  745.     const CSeq_id* last_id = 0;
  746.     CBioseq_Handle bsh;
  747.     size_t seq_len = 0;
  748.     CSeqVector seq_vec;
  749.     string label;
  750.     for (CSeq_loc_CI citer(location); citer; ++citer) {
  751.         ++counter;
  752.         const CSeq_id& seq_id = citer.GetSeq_id();
  753.         if ( last_id == 0  ||  !last_id->Match(seq_id) ) {
  754.             bsh = m_Scope->GetBioseqHandle(seq_id);
  755.             if ( !bsh ) {
  756.                 break;
  757.             }
  758.             seq_vec = 
  759.                 bsh.GetSeqVector(CBioseq_Handle::eCoding_Iupac,
  760.                                  citer.GetStrand());
  761.             seq_len = seq_vec.size();
  762.             // get the label. if m_SuppressContext flag in true, 
  763.             // get the worst label.
  764.             const CBioseq& bsq = *bsh.GetCompleteBioseq();
  765.             bsq.GetLabel(&label, CBioseq::eContent, m_Imp.IsSuppressContext());
  766.             last_id = &seq_id;
  767.         }
  768.         TSeqPos acceptor = citer.GetRange().GetFrom();
  769.         TSeqPos donor = citer.GetRange().GetTo();
  770.         TSeqPos start = acceptor;
  771.         TSeqPos stop = donor;
  772.         if ( citer.GetStrand() == eNa_strand_minus ) {
  773.             swap(acceptor, donor);
  774.             stop = seq_len - donor - 1;
  775.             start = seq_len - acceptor - 1;
  776.         }
  777.         // set severity level
  778.         // genomic product set or NT_ contig always relaxes to SEV_WARNING
  779.         EDiagSev sev = eDiag_Warning;
  780.         if ( m_Imp.IsSpliceErr()                   &&
  781.              !(m_Imp.IsGPS() || m_Imp.IsRefSeq())  &&
  782.              !check_all ) {
  783.             sev = eDiag_Error;
  784.         }
  785.         // check donor on all but last exon and on sequence
  786.         if ( ((check_all && !partial_last)  ||  counter < num_of_parts)  &&
  787.              (stop < seq_len - 2) ) {
  788.             try {
  789.                 CSeqVector::TResidue res1 = seq_vec[stop + 1];    
  790.                 CSeqVector::TResidue res2 = seq_vec[stop + 2];
  791.                 if ( IsResidue(res1)  &&  IsResidue(res2) ) {
  792.                     if ( (res1 != 'G')  || (res2 != 'T' ) ) {
  793.                         string msg;
  794.                         if ( (res1 == 'G')  && (res2 == 'C' ) ) { // GC minor splice site
  795.                             sev = eDiag_Info;
  796.                             msg = "Rare splice donor consensus (GC) found instead of "
  797.                                 "(GT) after exon ending at position " +
  798.                                 NStr::IntToString(donor + 1) + " of " + label;
  799.                         } else {
  800.                             msg = "Splice donor consensus (GT) not found after exon"
  801.                                 " ending at position " + 
  802.                                 NStr::IntToString(donor + 1) + " of " + label;
  803.                         }
  804.                         PostErr(sev, eErr_SEQ_FEAT_NotSpliceConsensus, msg, feat);
  805.                     }
  806.                 }
  807.             } catch ( exception& ) {
  808.                 break;
  809.             }
  810.         }
  811.         if ( ((check_all && !partial_first)  ||  counter != 1)  &&
  812.              (start > 1) ) {
  813.             try {
  814.                 CSeqVector::TResidue res1 = seq_vec[start - 2];
  815.                 CSeqVector::TResidue res2 = seq_vec[start - 1];
  816.                 
  817.                 if ( IsResidue(res1)  &&  IsResidue(res2) ) {
  818.                     if ( (res1 != 'A')  ||  (res2 != 'G') ) {
  819.                         PostErr(sev, eErr_SEQ_FEAT_NotSpliceConsensus,
  820.                             "Splice acceptor consensus (AG) not found before "
  821.                             "exon starting at position " + 
  822.                             NStr::IntToString(acceptor + 1) + " of " + label, feat);
  823.                     }
  824.                 }
  825.             } catch ( exception& ) {
  826.             }
  827.         }
  828.     } // end of for loop
  829. }
  830. void CValidError_feat::ValidateProt(const CProt_ref& prot, const CSerialObject& obj) 
  831. {
  832.     CProt_ref::EProcessed processed = CProt_ref::eProcessed_not_set;
  833.     if ( prot.CanGetProcessed() ) {
  834.         processed = prot.GetProcessed();
  835.     }
  836.     bool empty = true;
  837.     if ( processed != CProt_ref::eProcessed_signal_peptide  &&
  838.          processed != CProt_ref::eProcessed_transit_peptide ) {
  839.         if ( prot.CanGetName()  &&
  840.             (!prot.GetName().empty()  ||  !prot.GetName().front().empty()) ) {
  841.             empty = false;
  842.         }
  843.         if ( prot.CanGetDesc()  &&  !prot.GetDesc().empty() ) {
  844.             empty = false;
  845.         }
  846.         if ( prot.CanGetEc()  &&  !prot.GetEc().empty() ) {
  847.             empty = false;
  848.         }
  849.         if ( prot.CanGetActivity()  &&  !prot.GetActivity().empty() ) {
  850.             empty = false;
  851.         }
  852.         if ( prot.CanGetDb()  &&  !prot.GetDb().empty() ) {
  853.             empty = false;
  854.         }
  855.         if ( empty ) {
  856.             PostErr(eDiag_Warning, eErr_SEQ_FEAT_ProtRefHasNoData,
  857.                 "There is a protein feature where all fields are empty", obj);
  858.         }
  859.     }
  860.     if ( prot.CanGetDb () ) {
  861.         m_Imp.ValidateDbxref(prot.GetDb(), obj);
  862.     }
  863. }
  864. void CValidError_feat::ValidateRna(const CRNA_ref& rna, const CSeq_feat& feat) 
  865. {
  866.     const CRNA_ref::EType& rna_type = rna.GetType ();
  867.     if ( rna_type == CRNA_ref::eType_mRNA ) {
  868.         bool pseudo = (feat.CanGetPseudo()  &&  feat.GetPseudo())  ||  IsOverlappingGenePseudo(feat);
  869.         
  870.         if ( !pseudo ) {
  871.             ValidateMrnaTrans(feat);      /* transcription check */
  872.             ValidateSplice(feat, false);
  873.         }
  874.         ValidateBothStrands(feat);
  875.         ValidateBadGeneOverlap(feat);
  876.         ValidateCommonMRNAProduct(feat);
  877.     }
  878.     if ( rna.CanGetExt()  &&
  879.          rna.GetExt().Which() == CRNA_ref::C_Ext::e_TRNA ) {
  880.         const CTrna_ext& trna = rna.GetExt ().GetTRNA ();
  881.         if ( trna.CanGetAnticodon () ) {
  882.             ECompare comp = sequence::Compare(trna.GetAnticodon(), 
  883.                                               feat.GetLocation());
  884.             if ( comp != eContained  &&  comp != eSame ) {
  885.                 PostErr (eDiag_Error, eErr_SEQ_FEAT_Range,
  886.                     "Anticodon location not in tRNA", feat);
  887.             }
  888.             if ( GetLength( trna.GetAnticodon (), m_Scope ) != 3 ) {
  889.                 PostErr (eDiag_Warning, eErr_SEQ_FEAT_Range,
  890.                     "Anticodon is not 3 bases in length", feat);
  891.             }
  892.         }
  893.         ValidateTrnaCodons(trna, feat);
  894.     }
  895.     if ( rna_type == CRNA_ref::eType_tRNA ) {
  896.         ITERATE ( CSeq_feat::TQual, gbqual, feat.GetQual () ) {
  897.             if ( NStr::CompareNocase((**gbqual).GetVal (), "anticodon") == 0 ) {
  898.                 PostErr (eDiag_Error, eErr_SEQ_FEAT_InvalidQualifierValue,
  899.                     "Unparsed anticodon qualifier in tRNA", feat);
  900.                 break;
  901.             }
  902.         }
  903.         /* tRNA with string extension */
  904.         if ( rna.CanGetExt()  &&  
  905.              rna.GetExt().Which () == CRNA_ref::C_Ext::e_Name ) {
  906.             PostErr (eDiag_Error, eErr_SEQ_FEAT_InvalidQualifierValue,
  907.                 "Unparsed product qualifier in tRNA", feat);
  908.         }
  909.     }
  910.     if ( rna_type == CRNA_ref::eType_unknown ) {
  911.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_RNAtype0,
  912.             "RNA type 0 (unknown) not supported", feat);
  913.     }
  914.     if ( feat.CanGetProduct() ) {
  915.         ValidateRnaProductType(rna, feat);
  916.     }
  917. }
  918. void CValidError_feat::ValidateRnaProductType
  919. (const CRNA_ref& rna,
  920.  const CSeq_feat& feat)
  921. {
  922.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetProduct());
  923.     if ( !bsh ) {
  924.         return;
  925.     }
  926.     CSeqdesc_CI di(bsh, CSeqdesc::e_Molinfo);
  927.     if ( !di ) {
  928.         return;
  929.     }
  930.     const CMolInfo& mol_info = di->GetMolinfo();
  931.     if ( !mol_info.CanGetBiomol() ) {
  932.         return;
  933.     }
  934.     int biomol = mol_info.GetBiomol();
  935.     
  936.     switch ( rna.GetType() ) {
  937.     case CRNA_ref::eType_mRNA:
  938.         if ( biomol == CMolInfo::eBiomol_mRNA ) {
  939.             return;
  940.         }        
  941.         break;
  942.     case CRNA_ref::eType_tRNA:
  943.         if ( biomol == CMolInfo::eBiomol_tRNA ) {
  944.             return;
  945.         }
  946.         break;
  947.     case CRNA_ref::eType_rRNA:
  948.         if ( biomol == CMolInfo::eBiomol_rRNA ) {
  949.             return;
  950.         }
  951.         break;
  952.     default:
  953.         return;
  954.     }
  955.     PostErr(eDiag_Error, eErr_SEQ_FEAT_RnaProductMismatch,
  956.         "Type of RNA does not match MolInfo of product Bioseq", feat);
  957. }
  958. int s_LegalNcbieaaValues[] = { 42, 65, 66, 67, 68, 69, 70, 71, 72, 73,
  959.                                75, 76, 77, 78, 80, 81, 82, 83, 84, 85,
  960.                                86, 87, 88, 89, 90 };
  961. void CValidError_feat::ValidateTrnaCodons(const CTrna_ext& trna, const CSeq_feat& feat)
  962. {
  963.     // Make sure AA coding is ncbieaa.
  964.     if ( trna.GetAa().Which() != CTrna_ext::C_Aa::e_Ncbieaa ) {
  965.         PostErr (eDiag_Error, eErr_SEQ_FEAT_TrnaCodonWrong,
  966.             "tRNA AA coding does not match ncbieaa", feat);
  967.         return;
  968.     }
  969.     unsigned char aa = trna.GetAa().GetNcbieaa();
  970.     // make sure the amino acid is valid
  971.     bool found = false;
  972.     for ( int i = 0; i < 25; ++i ) {
  973.         if ( aa == s_LegalNcbieaaValues[i] ) {
  974.             found = true;
  975.             break;
  976.         }
  977.     }
  978.     if ( !found ) {
  979.         PostErr(eDiag_Error, eErr_SEQ_FEAT_BadTrnaAA, 
  980.             "Invalid tRNA amino acid (" + NStr::IntToString(aa) + ")", feat);
  981.         return;
  982.     }
  983.     // Retrive the Genetic code id for the tRNA
  984.     int gcode = 0;
  985.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetLocation());
  986.     if ( !bsh ) {
  987.         gcode = 1;
  988.     } else {
  989.         // need only the closest biosoure.
  990.         CSeqdesc_CI diter(bsh, CSeqdesc::e_Source);
  991.         if ( diter ) {
  992.             gcode = diter->GetSource().GetGenCode();
  993.         }
  994.     }
  995.     
  996.     const string& ncbieaa = CGen_code_table::GetNcbieaa(gcode);
  997.     if ( ncbieaa.length() != 64 ) {
  998.         return;
  999.     }
  1000.     
  1001.     EDiagSev sev = (aa == 'U') ? eDiag_Warning : eDiag_Error;
  1002.     ITERATE( CTrna_ext::TCodon, iter, trna.GetCodon() ) {
  1003.         // test that codon value is in range 0 - 63
  1004.         if ( *iter < 0  ||  *iter > 63 ) {
  1005.             PostErr(sev, eErr_SEQ_FEAT_TrnaCodonWrong,
  1006.                 "tRNA codon value (" + NStr::IntToString(*iter) + 
  1007.                 ") out of range", feat);
  1008.             continue;
  1009.         }
  1010.         unsigned char taa = ncbieaa[*iter];
  1011.         if ( taa != aa ) {
  1012.             PostErr(sev, eErr_SEQ_FEAT_TrnaCodonWrong,
  1013.                 "tRNA codon does not match genetic code", feat);
  1014.         }
  1015.     }
  1016. }
  1017. void CValidError_feat::ValidateImp(const CImp_feat& imp, const CSeq_feat& feat)
  1018. {
  1019.     CSeqFeatData::ESubtype subtype = feat.GetData().GetSubtype();
  1020.     string key = imp.GetKey();
  1021.     switch ( subtype ) {
  1022.     case CSeqFeatData::eSubtype_exon:
  1023.         if ( m_Imp.IsValidateExons() ) {
  1024.             ValidateSplice(feat, true);
  1025.         }
  1026.         break;
  1027.     case CSeqFeatData::eSubtype_bad:
  1028.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_UnknownImpFeatKey, 
  1029.             "Unknown feature key " + key, feat);
  1030.         break;
  1031.     case CSeqFeatData::eSubtype_virion:
  1032.     case CSeqFeatData::eSubtype_mutation:
  1033.     case CSeqFeatData::eSubtype_allele:
  1034.         PostErr(eDiag_Error, eErr_SEQ_FEAT_UnknownImpFeatKey,
  1035.             "Feature key " + key + " is no longer legal", feat);
  1036.         break;
  1037.     case CSeqFeatData::eSubtype_polyA_site:
  1038.         {
  1039.             CSeq_loc::TRange range = feat.GetLocation().GetTotalRange();
  1040.             if ( range.GetFrom() != range.GetTo() ) {
  1041.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_PolyAsiteNotPoint,
  1042.                     "PolyA_site should be a single point", feat);
  1043.             }
  1044.         }
  1045.         break;
  1046.     case CSeqFeatData::eSubtype_mat_peptide:
  1047.     case CSeqFeatData::eSubtype_sig_peptide:
  1048.     case CSeqFeatData::eSubtype_transit_peptide:
  1049.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_InvalidForType,
  1050.             "Peptide processing feature should be converted to the "
  1051.             "appropriate protein feature subtype", feat);
  1052.         ValidatePeptideOnCodonBoundry(feat, key);
  1053.         break;
  1054.         
  1055.     case CSeqFeatData::eSubtype_mRNA:
  1056.     case CSeqFeatData::eSubtype_tRNA:
  1057.     case CSeqFeatData::eSubtype_rRNA:
  1058.     case CSeqFeatData::eSubtype_snRNA:
  1059.     case CSeqFeatData::eSubtype_scRNA:
  1060.     case CSeqFeatData::eSubtype_snoRNA:
  1061.     case CSeqFeatData::eSubtype_misc_RNA:
  1062.     case CSeqFeatData::eSubtype_precursor_RNA:
  1063.     // !!! what about other RNA types (preRNA, otherRNA)?
  1064.         PostErr(eDiag_Error, eErr_SEQ_FEAT_InvalidForType,
  1065.               "RNA feature should be converted to the appropriate RNA feature "
  1066.               "subtype, location should be converted manually", feat);
  1067.         break;
  1068.     case CSeqFeatData::eSubtype_Imp_CDS:
  1069.         {
  1070.             // impfeat CDS must be pseudo; fail if not
  1071.             bool pseudo = (feat.CanGetPseudo()  &&  feat.GetPseudo())  ||
  1072.                           IsOverlappingGenePseudo(feat);
  1073.             if ( !pseudo ) {
  1074.                 PostErr(eDiag_Info, eErr_SEQ_FEAT_ImpCDSnotPseudo,
  1075.                     "ImpFeat CDS should be pseudo", feat);
  1076.             }
  1077.             ITERATE( CSeq_feat::TQual, gbqual, feat.GetQual() ) {
  1078.                 if ( NStr::CompareNocase( (*gbqual)->GetQual(), "translation") == 0 ) {
  1079.                     PostErr(eDiag_Error, eErr_SEQ_FEAT_ImpCDShasTranslation,
  1080.                         "ImpFeat CDS with /translation found", feat);
  1081.                 }
  1082.             }
  1083.         }
  1084.         break;
  1085.     default:
  1086.         break;
  1087.     }// end of switch statement  
  1088.     
  1089.     // validate the feature's location
  1090.     if ( imp.CanGetLoc() ) {
  1091.         const string& imp_loc = imp.GetLoc();
  1092.         if ( imp_loc.find("one-of") != string::npos ) {
  1093.             PostErr(eDiag_Error, eErr_SEQ_FEAT_ImpFeatBadLoc,
  1094.                 "ImpFeat loc " + imp_loc + 
  1095.                 " has obsolete 'one-of' text for feature " + key, feat);
  1096.         } else if ( feat.GetLocation().IsInt() ) {
  1097.             const CSeq_interval& seq_int = feat.GetLocation().GetInt();
  1098.             string temp_loc = NStr::IntToString(seq_int.GetFrom() + 1) +
  1099.                 ".." + NStr::IntToString(seq_int.GetTo() + 1);
  1100.             if ( imp_loc != temp_loc ) {
  1101.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_ImpFeatBadLoc,
  1102.                     "ImpFeat loc " + imp_loc + " does not equal feature location " +
  1103.                     temp_loc + "for feature " + key, feat);
  1104.             }
  1105.         }
  1106.     }
  1107.     if ( feat.CanGetQual() ) {
  1108.         ValidateImpGbquals(imp, feat);
  1109.         // Make sure a feature has its mandatory qualifiers
  1110.         ITERATE( CFeatQualAssoc::TGBQualTypeVec,
  1111.                  required,
  1112.                  CFeatQualAssoc::GetMandatoryGbquals(subtype) ) {
  1113.             bool found = false;
  1114.             ITERATE( CSeq_feat::TQual, qual, feat.GetQual() ) {
  1115.                 if ( CGbqualType::GetType(**qual) == *required ) {
  1116.                     found = true;
  1117.                     break;
  1118.                 }
  1119.             }
  1120.             if ( !found ) {
  1121.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_MissingQualOnImpFeat,
  1122.                     "Missing qualifier " + CGbqualType::GetString(*required) +
  1123.                     " for feature " + key, feat);
  1124.             }
  1125.         }
  1126.     }
  1127. }
  1128. void CValidError_feat::ValidateImpGbquals
  1129. (const CImp_feat& imp,
  1130.  const CSeq_feat& feat)
  1131. {
  1132.     CSeqFeatData::ESubtype ftype = feat.GetData().GetSubtype();
  1133.     const string& key = imp.GetKey();
  1134.     ITERATE( CSeq_feat::TQual, qual, feat.GetQual() ) {
  1135.         const string& qual_str = (*qual)->GetQual();
  1136.         if ( qual_str == "gsdb_id" ) {
  1137.             continue;
  1138.         }
  1139.         CGbqualType::EType gbqual = CGbqualType::GetType(qual_str);
  1140.         
  1141.         if ( gbqual == CGbqualType::e_Bad ) {
  1142.             if ( !qual_str.empty() ) {
  1143.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_UnknownImpFeatQual,
  1144.                     "Unknown qualifier " + qual_str, feat);
  1145.             } else {
  1146.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_UnknownImpFeatQual,
  1147.                     "Empty qualifier", feat);
  1148.             }
  1149.         } else {
  1150.             if ( !CFeatQualAssoc::IsLegalGbqual(ftype, gbqual) ) {
  1151.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_WrongQualOnImpFeat,
  1152.                     "Wrong qualifier " + qual_str + " for feature " + 
  1153.                     key, feat);
  1154.             }
  1155.             
  1156.             string val = (*qual)->GetVal();
  1157.             
  1158.             bool error = false;
  1159.             switch ( gbqual ) {
  1160.             case CGbqualType::e_Rpt_type:
  1161.                 {{
  1162.                     for ( size_t i = 0; 
  1163.                     i < sizeof(s_LegalRepeatTypes) / sizeof(string); 
  1164.                     ++i ) {
  1165.                         if ( val.find(s_LegalRepeatTypes[i]) != string::npos ) {
  1166.                             bool left = false, right = false;
  1167.                             if ( i > 0 ) {
  1168.                                 left = val[i-1] == ','  ||  val[i-1] == '(';
  1169.                             }
  1170.                             if ( i < val.length() - 1 ) {
  1171.                                 right = val[i+1] == ','  ||  val[i+1] == ')';
  1172.                             }
  1173.                             if ( left  &&  right ) {
  1174.                                 error = true;
  1175.                             }
  1176.                             break;
  1177.                         }
  1178.                     }
  1179.                 }}
  1180.                 break;
  1181.                 
  1182.             case CGbqualType::e_Rpt_unit:
  1183.                 {{
  1184.                     bool found = false, multiple_rpt_unit = true;
  1185.                     ITERATE(string, it, val) {
  1186.                         if ( *it <= ' ' ) {
  1187.                             found = true;
  1188.                         } else if ( *it == '('  ||  *it == ')'  ||
  1189.                             *it == ','  ||  *it == '.'  ||
  1190.                             isdigit(*it) ) {
  1191.                         } else {
  1192.                             multiple_rpt_unit = false;
  1193.                         }
  1194.                     }
  1195.                     /*
  1196.                     if ( found || 
  1197.                     (!multiple_rpt_unit && val.length() > 48) ) {
  1198.                     error = true;
  1199.                     }
  1200.                     */
  1201.                     if ( NStr::CompareNocase(key, "repeat_region") == 0  &&
  1202.                         !multiple_rpt_unit  &&
  1203.                         val.length() == GetLength(feat.GetLocation(), m_Scope) ) {
  1204.                         bool just_nuc_letters = true;
  1205.                         static const string nuc_letters = "ACGTNacgtn";
  1206.                         
  1207.                         ITERATE(string, it, val) {
  1208.                             if ( nuc_letters.find(*it) == NPOS ) {
  1209.                                 just_nuc_letters = false;
  1210.                                 break;
  1211.                             }
  1212.                         }
  1213.                         
  1214.                         if ( just_nuc_letters ) {
  1215.                             CSeqVector vec = GetSequenceFromFeature(feat, *m_Scope);
  1216.                             if ( !vec.empty() ) {
  1217.                                 string vec_data;
  1218.                                 vec.GetSeqData(0, vec.size(), vec_data);
  1219.                                 if ( NStr::CompareNocase(val, vec_data) != 0 ) {
  1220.                                     PostErr(eDiag_Warning, eErr_SEQ_FEAT_InvalidQualifierValue,
  1221.                                         "repeat_region /rpt_unit and underlying"
  1222.                                         "sequence do not match", feat);
  1223.                                 }
  1224.                             }
  1225.                             
  1226.                             
  1227.                         }
  1228.                     }
  1229.                 }}
  1230.                 break;
  1231.                 
  1232.             case CGbqualType::e_Label:
  1233.                 {{
  1234.                     bool only_digits = true,
  1235.                         has_spaces = false;
  1236.                     
  1237.                     ITERATE(string, it, val) {
  1238.                         if ( isspace(*it) ) {
  1239.                             has_spaces = true;
  1240.                         }
  1241.                         if ( !isdigit(*it) ) {
  1242.                             only_digits = false;
  1243.                         }
  1244.                     }
  1245.                     error = only_digits  ||  has_spaces;
  1246.                 }}
  1247.                 break;
  1248.                 
  1249.             case CGbqualType::e_Cons_splice:
  1250.                 {{
  1251.                     error = true;
  1252.                     for (size_t i = 0; 
  1253.                          i < sizeof(s_LegalConsSpliceStrings) / sizeof(string);
  1254.                          ++i) {
  1255.                         if ( NStr::CompareNocase(val, s_LegalConsSpliceStrings[i]) == 0 ) {
  1256.                             error = false;
  1257.                             break;
  1258.                         }
  1259.                     }
  1260.                 }}
  1261.                 break;
  1262.         default:
  1263.             break;
  1264.             } // end of switch statement
  1265.             if ( error ) {
  1266.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_InvalidQualifierValue,
  1267.                     val + " is not a legal value for qualifier " + qual_str, feat);
  1268.             }
  1269.         }
  1270.     }  // end of ITERATE 
  1271. }
  1272. void CValidError_feat::ValidatePeptideOnCodonBoundry
  1273. (const CSeq_feat& feat, 
  1274.  const string& key)
  1275. {
  1276.     const CSeq_loc& loc = feat.GetLocation();
  1277.     // !!! DEBUG {
  1278.     if( m_Imp.AvoidPerfBottlenecks() ) {
  1279.         return;
  1280.     } 
  1281.     // } DEBUG
  1282.     CConstRef<CSeq_feat> cds = GetOverlappingCDS(loc, *m_Scope);
  1283.     if ( !cds ) {
  1284.         return;
  1285.     }
  1286.     const CCdregion& cdr = cds->GetData().GetCdregion();
  1287.     CSeq_loc_CI first, last;
  1288.     for ( CSeq_loc_CI sl_iter(loc); sl_iter; ++sl_iter ) {
  1289.         if ( !first ) {
  1290.             first = sl_iter;
  1291.         }
  1292.         last = sl_iter;
  1293.     }
  1294.         
  1295.     if ( !first  ||  !last ) {
  1296.         return;
  1297.     }
  1298.     TSeqPos pos1 = LocationOffset(loc, first.GetSeq_loc(), eOffset_FromStart);
  1299.     TSeqPos pos2 = LocationOffset(loc, last.GetSeq_loc(), eOffset_FromEnd);
  1300.     TSeqPos mod1 = (pos1 - cdr.GetFrame()) %3;
  1301.     TSeqPos mod2 = (pos2 - cdr.GetFrame()) %3;
  1302.     if ( loc.IsPartialLeft() ) {
  1303.         mod1 = 0;
  1304.     }
  1305.     if ( loc.IsPartialRight() ) {
  1306.         mod2 = 2;
  1307.     }
  1308.     if ( (mod1 != 0)  &&  (mod2 != 2) ) {
  1309.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_PeptideFeatOutOfFrame,
  1310.             "Start and stop of " + key + " are out of frame with CDS codons",
  1311.             feat);
  1312.     } else if (mod1 != 0) {
  1313.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_PeptideFeatOutOfFrame, 
  1314.             "Start of " + key + " is out of frame with CDS codons", feat);
  1315.     } else if (mod2 != 2) {
  1316.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_PeptideFeatOutOfFrame,
  1317.             "Stop of " + key + " is out of frame with CDS codons", feat);
  1318.     }
  1319. }
  1320. void CValidError_feat::ValidateMrnaTrans(const CSeq_feat& feat)
  1321. {
  1322.     static string s_BypassMrnaTransCheck[] = {
  1323.         "RNA editing",
  1324.         "reasons given in citation",
  1325.         "artificial frameshift",
  1326.         "unclassified transcription discrepancy",
  1327.         "rearrangement required for product"
  1328.     };
  1329.     if ( feat.CanGetPseudo()  &&  feat.GetPseudo() ) {
  1330.         return;
  1331.     }
  1332.     if ( !feat.CanGetProduct() ) {
  1333.         return;
  1334.     }
  1335.     // biological exception
  1336.     
  1337.     if ( feat.CanGetExcept()  &&  feat.GetExcept()  &&
  1338.          feat.CanGetExcept_text() ) {
  1339.         size_t except_num = sizeof(s_BypassMrnaTransCheck) / sizeof(string);
  1340.         for ( size_t i = 0; i < except_num; ++i ) {
  1341.             if ( NStr::FindNoCase(feat.GetExcept_text(), 
  1342.                 s_BypassMrnaTransCheck[i]) != string::npos ) {
  1343.                 return; 
  1344.             }
  1345.         }
  1346.     }
  1347.     CBioseq_Handle nuc = m_Scope->GetBioseqHandle(feat.GetLocation());
  1348.     CBioseq_Handle rna = m_Scope->GetBioseqHandle(feat.GetProduct());
  1349.     if ( !nuc  ||  !rna ) {
  1350.         return;
  1351.     }
  1352.     CSeqVector nuc_vec = nuc.GetSequenceView(feat.GetLocation(),
  1353.         CBioseq_Handle::eViewConstructed,
  1354.         CBioseq_Handle::eCoding_Iupac);
  1355.     CSeqVector rna_vec = rna.GetSequenceView(feat.GetProduct(),
  1356.         CBioseq_Handle::eViewConstructed,
  1357.         CBioseq_Handle::eCoding_Iupac);
  1358.     size_t nuc_len = nuc_vec.size();
  1359.     size_t rna_len = rna_vec.size();
  1360.     if ( nuc_len != rna_len ) {
  1361.         if ( nuc_len < rna_len ) {
  1362.             size_t count_a = 0, count_no_a = 0;
  1363.             // count 'A's in the tail
  1364.             for ( CSeqVector_CI iter(rna_vec, nuc_len); iter; ++iter ) {
  1365.                 if ( (*iter == 'A')  ||  (*iter == 'a') ) {
  1366.                     ++count_a;
  1367.                 } else {
  1368.                     ++count_no_a;
  1369.                 }
  1370.             }
  1371.             if ( count_a < (19 * count_no_a) ) { // less then 5%
  1372.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_TranscriptLen,
  1373.                     "Transcript length [" + NStr::IntToString(nuc_len) + 
  1374.                     "] less than product length [" + NStr::IntToString(rna_len) + 
  1375.                     "], and tail < 95% polyA", feat);
  1376.             } else {
  1377.                 PostErr(eDiag_Info, eErr_SEQ_FEAT_TranscriptLen,
  1378.                     "Transcript length [" + NStr::IntToString(nuc_len) + 
  1379.                     "] less than product length [" + NStr::IntToString(rna_len) +
  1380.                     "], but tail >= 95% polyA", feat);
  1381.             }
  1382.             // allow base-by-base comparison on common length
  1383.             rna_len = nuc_len; 
  1384.         } else {
  1385.             PostErr(eDiag_Error, eErr_SEQ_FEAT_TranscriptLen,
  1386.                 "Transcript length [" + NStr::IntToString(nuc_vec.size()) + "] " +
  1387.                 "does not match product length [" + 
  1388.                 NStr::IntToString(rna_vec.size()) + "]", feat);
  1389.         }
  1390.     } 
  1391.     if ( (nuc_len == rna_len)  &&  (nuc_len > 0) ) {
  1392.         // compare content of common length
  1393.         CSeqVector_CI nuc_ci(nuc_vec);
  1394.         CSeqVector_CI rna_ci(rna_vec);
  1395.         size_t mismatches = 0;
  1396.         while ( nuc_ci  &&  rna_ci ) {
  1397.             if ( *nuc_ci != *rna_ci ) {
  1398.                 ++mismatches;
  1399.             }
  1400.             ++nuc_ci;
  1401.             ++rna_ci;
  1402.         }
  1403.         if ( mismatches > 0 ) {
  1404.             PostErr(eDiag_Error, eErr_SEQ_FEAT_TranscriptMismatches,
  1405.                 "There are " + NStr::IntToString(mismatches) + 
  1406.                 " mismatches out of " + NStr::IntToString(nuc_len) +
  1407.                 " bases between the transcript and product sequence", feat);
  1408.         }
  1409.     }
  1410. }
  1411. void CValidError_feat::ValidateCommonMRNAProduct(const CSeq_feat& feat)
  1412. {
  1413.     if ( (feat.CanGetPseudo()  &&  feat.GetPseudo())  ||
  1414.          IsOverlappingGenePseudo(feat) ) {
  1415.         return;
  1416.     }
  1417.     if ( !feat.CanGetProduct() ) {
  1418.         return;
  1419.     }
  1420.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetProduct());
  1421.     if ( !bsh ) {
  1422.         const CSeq_id& sid = GetId(feat.GetProduct(), m_Scope);
  1423.         if ( sid.IsLocal() ) {
  1424.             if ( m_Imp.IsGPS()  ||  m_Imp.IsRefSeq() ) {
  1425.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_MissingMRNAproduct,
  1426.                     "Product Bioseq of mRNA feature is not "
  1427.                     "packaged in the record", feat);
  1428.             }
  1429.         }
  1430.     } else {
  1431.         // !!! DEBUG {
  1432.         if( m_Imp.AvoidPerfBottlenecks() ) {
  1433.             return;
  1434.         } 
  1435.         // } DEBUG
  1436.         CFeat_CI mrna(
  1437.             bsh, 
  1438.             0, 0,
  1439.             CSeqFeatData::e_Rna,
  1440.             SAnnotSelector::eOverlap_TotalRange,
  1441.             SAnnotSelector::eResolve_None,
  1442.             CFeat_CI::e_Product);
  1443.         while ( mrna ) {
  1444.             if ( &(mrna->GetOriginalFeature()) != &feat ) {
  1445.                     PostErr(eDiag_Critical, eErr_SEQ_FEAT_MultipleMRNAproducts,
  1446.                         "Same product Bioseq from multiple mRNA features", feat);
  1447.                     break;
  1448.             }
  1449.             ++mrna;
  1450.         }
  1451.     }
  1452. }
  1453. void CValidError_feat::ValidateBothStrands(const CSeq_feat& feat)
  1454. {
  1455.     const CSeq_loc& location = feat.GetLocation ();
  1456.     for ( CSeq_loc_CI citer (location); citer; ++citer ) {
  1457.         if ( citer.GetStrand () == eNa_strand_both ) {
  1458.             PostErr (eDiag_Error, eErr_SEQ_FEAT_BothStrands, 
  1459.                 "mRNA or CDS may not be on both strands", feat);  
  1460.             break;
  1461.         }
  1462.     }
  1463. }
  1464. // Precondition: feat is a coding region
  1465. void CValidError_feat::ValidateCommonCDSProduct
  1466. (const CSeq_feat& feat)
  1467. {
  1468.     if ( (feat.CanGetPseudo()  &&  feat.GetPseudo())  ||
  1469.           IsOverlappingGenePseudo(feat) ) {
  1470.         return;
  1471.     }
  1472.     
  1473.     if ( !feat.CanGetProduct() ) {
  1474.         return;
  1475.     }
  1476.     
  1477.     const CCdregion& cdr = feat.GetData().GetCdregion();
  1478.     if ( cdr.CanGetOrf() ) {
  1479.         return;
  1480.     }
  1481.     CBioseq_Handle prod = m_Scope->GetBioseqHandle(feat.GetProduct());
  1482.     if ( !prod ) {
  1483.         const CSeq_id* sid = 0;
  1484.         try {
  1485.             sid = &(GetId(feat.GetProduct(), m_Scope));
  1486.         } catch (const CNotUnique&) {}
  1487.         // okay to have far RefSeq product, but only if genomic product set
  1488.         if ( sid == 0  ||  !sid->IsOther() ) {
  1489.             if ( m_Imp.IsGPS() ) {
  1490.                 return;
  1491.             }
  1492.         }
  1493.         // or just a bioseq
  1494.         if ( m_Imp.GetTSE().IsSeq() ) {
  1495.             return;
  1496.         }
  1497.         // or in a standalone Seq-annot
  1498.         if ( m_Imp.IsStandaloneAnnot() ) {
  1499.             return;
  1500.         }
  1501.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_MultipleCDSproducts,
  1502.             "Unable to find product Bioseq from CDS feature", feat);
  1503.         return;
  1504.     }
  1505.     CBioseq_Handle nuc  = m_Scope->GetBioseqHandle(feat.GetLocation());
  1506.     if ( nuc ) {
  1507.         CSeq_entry_Handle prod_nps = 
  1508.             prod.GetExactComplexityLevel(CBioseq_set::eClass_nuc_prot);
  1509.         CSeq_entry_Handle nuc_nps = 
  1510.             nuc.GetExactComplexityLevel(CBioseq_set::eClass_nuc_prot);
  1511.         if ( (prod_nps != nuc_nps)  &&  (!m_Imp.IsNT())  &&  (!m_Imp.IsGPS()) ) {
  1512.             PostErr(eDiag_Error, eErr_SEQ_FEAT_CDSproductPackagingProblem,
  1513.                 "Protein product not packaged in nuc-prot set with nucleotide", 
  1514.                 feat);
  1515.         }
  1516.     }
  1517.     const CSeq_feat* sfp = GetCDSForProduct(prod);
  1518.     if ( sfp == 0 ) {
  1519.         return;
  1520.     }
  1521.     
  1522.     if ( &feat != sfp ) {
  1523.         // if genomic product set, with one cds on contig and one on cdna,
  1524.         // do not report.
  1525.         if ( m_Imp.IsGPS() ) {
  1526.             // feature packaging test will do final contig vs. cdna check
  1527.             CBioseq_Handle sfh = m_Scope->GetBioseqHandle(sfp->GetLocation());
  1528.             if ( nuc != sfh ) {
  1529.                 return;
  1530.             }
  1531.         }
  1532.         PostErr(eDiag_Critical, eErr_SEQ_FEAT_MultipleCDSproducts, 
  1533.             "Same product Bioseq from multiple CDS features", feat);
  1534.     }
  1535. }
  1536. void CValidError_feat::ValidateCDSPartial(const CSeq_feat& feat)
  1537. {
  1538.     if ( !feat.CanGetProduct()  ||  !feat.CanGetLocation() ) {
  1539.         return;
  1540.     }
  1541.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetProduct());
  1542.     if ( !bsh ) {
  1543.         return;
  1544.     }
  1545.     CSeqdesc_CI sd(bsh, CSeqdesc::e_Molinfo);
  1546.     if ( !sd ) {
  1547.         return;
  1548.     }
  1549.     const CMolInfo& molinfo = sd->GetMolinfo();
  1550.     const CSeq_loc& loc = feat.GetLocation();
  1551.     bool partial5 = loc.IsPartialLeft();
  1552.     bool partial3 = loc.IsPartialRight();
  1553.     if ( molinfo.CanGetCompleteness() ) {
  1554.         switch ( molinfo.GetCompleteness() ) {
  1555.         case CMolInfo::eCompleteness_unknown:
  1556.             break;
  1557.         case CMolInfo::eCompleteness_complete:
  1558.             if ( partial5 || partial3 ) {
  1559.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1560.                     "CDS is partial but protein is complete", feat);
  1561.             }
  1562.             break;
  1563.         case CMolInfo::eCompleteness_partial:
  1564.             break;
  1565.         case CMolInfo::eCompleteness_no_left:
  1566.             if ( !partial5 ) {
  1567.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1568.                     "CDS is 5' complete but protein is NH2 partial", feat);
  1569.             }
  1570.             if ( partial3 ) {
  1571.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1572.                     "CDS is 3' partial but protein is NH2 partial", feat);
  1573.             }
  1574.             break;
  1575.         case CMolInfo::eCompleteness_no_right:
  1576.             if (! partial3) {
  1577.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1578.                     "CDS is 3' complete but protein is CO2 partial", feat);
  1579.             }
  1580.             if (partial5) {
  1581.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1582.                     "CDS is 5' partial but protein is CO2 partial", feat);
  1583.             }
  1584.             break;
  1585.         case CMolInfo::eCompleteness_no_ends:
  1586.             if ( partial5 && partial3 ) {
  1587.             } else if ( partial5 ) {
  1588.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1589.                     "CDS is 5' partial but protein has neither end", feat);
  1590.             } else if ( partial3 ) {
  1591.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1592.                     "CDS is 3' partial but protein has neither end", feat);
  1593.             } else {
  1594.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1595.                     "CDS is complete but protein has neither end", feat);
  1596.             }
  1597.             break;
  1598.         case CMolInfo::eCompleteness_has_left:
  1599.             break;
  1600.         case CMolInfo::eCompleteness_has_right:
  1601.             break;
  1602.         case CMolInfo::eCompleteness_other:
  1603.             break;
  1604.         default:
  1605.             break;
  1606.         }
  1607.     }
  1608. }
  1609. void CValidError_feat::ValidateBadMRNAOverlap(const CSeq_feat& feat)
  1610. {
  1611.     const CSeq_loc& loc = feat.GetLocation();
  1612.     // !!! DEBUG {
  1613.     if( m_Imp.AvoidPerfBottlenecks() ) {
  1614.         return;
  1615.     } 
  1616.     // } DEBUG
  1617.     CConstRef<CSeq_feat> mrna = GetBestOverlappingFeat(
  1618.         loc,
  1619.         CSeqFeatData::eSubtype_mRNA,
  1620.         eOverlap_Simple,
  1621.         *m_Scope);
  1622.     if ( !mrna ) {
  1623.         return;
  1624.     }
  1625.     mrna = GetBestOverlappingFeat(
  1626.         loc,
  1627.         CSeqFeatData::eSubtype_mRNA,
  1628.         eOverlap_CheckIntervals,
  1629.         *m_Scope);
  1630.     if ( mrna ) {
  1631.         return;
  1632.     }
  1633.     mrna = GetBestOverlappingFeat(
  1634.         loc,
  1635.         CSeqFeatData::eSubtype_mRNA,
  1636.         eOverlap_Interval,
  1637.         *m_Scope);
  1638.     if ( !mrna ) {
  1639.         return;
  1640.     }
  1641.     mrna = GetBestOverlappingFeat(
  1642.         loc,
  1643.         CSeqFeatData::eSubtype_mRNA,
  1644.         eOverlap_Subset,
  1645.         *m_Scope);
  1646.     EDiagSev sev = eDiag_Error;
  1647.     if ( m_Imp.IsNC()  ||  m_Imp.IsNT()  ||  
  1648.          (feat.CanGetExcept()  &&  feat.GetExcept()) ) {
  1649.         sev = eDiag_Warning;
  1650.     }
  1651.     if ( mrna ) {
  1652.         // ribosomal slippage exception suppresses CDSmRNArange warning
  1653.         bool supress = false;
  1654.         if ( feat.CanGetExcept_text() ) {
  1655.             const CSeq_feat::TExcept_text& text = feat.GetExcept_text();
  1656.             if ( NStr::FindNoCase(text, "ribosomal slippage") != NPOS ) {
  1657.                 supress = true;
  1658.             }
  1659.         }
  1660.         if ( !supress ) {
  1661.             PostErr(sev, eErr_SEQ_FEAT_CDSmRNArange,
  1662.                 "mRNA contains CDS but internal intron-exon boundaries "
  1663.                 "do not match", feat);
  1664.         }
  1665.     } else {
  1666.         PostErr(sev, eErr_SEQ_FEAT_CDSmRNArange,
  1667.             "mRNA overlaps or contains CDS but does not completely "
  1668.             "contain intervals", feat);
  1669.     }
  1670. }
  1671. void CValidError_feat::ValidateBadGeneOverlap(const CSeq_feat& feat)
  1672. {
  1673.     const CGene_ref* grp = feat.GetGeneXref();
  1674.     if ( grp != 0 ) {
  1675.         return;
  1676.     }
  1677.     // !!! DEBUG {
  1678.     if( m_Imp.AvoidPerfBottlenecks() ) {
  1679.         return;
  1680.     } 
  1681.     // } DEBUG
  1682.     
  1683.     // !!! can't look for contating feature using GetBestOverlap.
  1684.     // This implementation should be changed once the above is supported
  1685.     // look for overlapping genes
  1686.     CConstRef<CSeq_feat> gene = 
  1687.         GetOverlappingGene(feat.GetLocation(), *m_Scope);
  1688.     if ( gene ) {
  1689.         return;
  1690.     }
  1691.     // look for intersecting genes
  1692.     gene = GetBestOverlappingFeat(
  1693.         feat.GetLocation(),
  1694.         CSeqFeatData::e_Gene,
  1695.         eOverlap_Simple,
  1696.         *m_Scope);
  1697.     if ( !gene ) {
  1698.         return;
  1699.     }
  1700.     // found an intersecting (but not overlapping) gene
  1701.     EDiagSev sev = eDiag_Error;
  1702.     if ( m_Imp.IsNC()  ||  m_Imp.IsNT() ) {
  1703.         sev = eDiag_Warning;
  1704.     }
  1705.     if ( feat.GetData().IsCdregion() ) {
  1706.         PostErr(sev, eErr_SEQ_FEAT_CDSgeneRange, 
  1707.             "gene overlaps CDS but does not completely contain it", feat);
  1708.     } else if ( feat.GetData().IsRna() ) {
  1709.         PostErr(sev, eErr_SEQ_FEAT_mRNAgeneRange,
  1710.             "gene overlaps mRNA but does not completely contain it", feat);
  1711.     }
  1712. }
  1713. static const string s_LegalExceptionStrings[] = {
  1714.   "RNA editing",
  1715.   "reasons given in citation",
  1716.   "ribosomal slippage",
  1717.   "trans-splicing",
  1718.   "alternative processing",
  1719.   "artificial frameshift",
  1720.   "nonconsensus splice site",
  1721.   "rearrangement required for product",
  1722.   "modified codon recognition",
  1723.   "alternative start codon"
  1724. };
  1725. static const string s_RefseqExceptionStrings [] = {
  1726.   "unclassified transcription discrepancy",
  1727.   "unclassified translation discrepancy",
  1728. };
  1729. void CValidError_feat::ValidateExcept(const CSeq_feat& feat)
  1730. {
  1731.     if ( feat.CanGetExcept() &&  !feat.GetExcept()  &&
  1732.          feat.CanGetExcept_text()  &&  !feat.GetExcept_text().empty() ) {
  1733.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_ExceptInconsistent,
  1734.             "Exception text is set, but exception flag is not set", feat);
  1735.     }
  1736.     if ( feat.CanGetExcept_text()  &&  !feat.GetExcept_text ().empty() ) {
  1737.         ValidateExceptText(feat.GetExcept_text(), feat);
  1738.     }
  1739. }
  1740. void CValidError_feat::ValidateExceptText(const string& text, const CSeq_feat& feat)
  1741. {
  1742.     if ( text.empty() ) return;
  1743.     EDiagSev sev = eDiag_Error;
  1744.     bool found = false;
  1745.     string str;
  1746.     string::size_type   begin = 0, end, textlen = text.length();
  1747.     const string* except_begin = s_LegalExceptionStrings;
  1748.     const string* except_end = 
  1749.         &(s_LegalExceptionStrings[sizeof(s_LegalExceptionStrings) / sizeof(string)]);
  1750.     const string* refseq_begin = s_RefseqExceptionStrings;
  1751.     const string* refseq_end = 
  1752.         &(s_RefseqExceptionStrings[sizeof(s_RefseqExceptionStrings) / sizeof(string)]);
  1753.     
  1754.     while ( begin < textlen ) {
  1755.         found = false;
  1756.         end = min( text.find_first_of(',', begin), textlen );
  1757.         str = NStr::TruncateSpaces( text.substr(begin, end) );
  1758.         if ( find(except_begin, except_end, str) != except_end ) {
  1759.             found = true;
  1760.         }
  1761.         if ( !found  &&  (m_Imp.IsGPS() || m_Imp.IsRefSeq()) ) {
  1762.             if ( find(refseq_begin, refseq_end, str) != refseq_end ) {
  1763.                found = true;
  1764.             }
  1765.         }
  1766.         if ( !found ) {
  1767.             if ( m_Imp.IsNC()  ||  m_Imp.IsNT() ) {
  1768.                 sev = eDiag_Warning;
  1769.             }
  1770.             PostErr(sev, eErr_SEQ_FEAT_InvalidQualifierValue,
  1771.                 str + " is not legal exception explanation", feat);
  1772.         }
  1773.         begin = end + 1;
  1774.     }
  1775. }
  1776. static const string s_BypassCdsTransCheck[] = {
  1777.   "RNA editing",
  1778.   "reasons given in citation",
  1779.   "artificial frameshift",
  1780.   "unclassified translation discrepancy",
  1781.   "rearrangement required for product"
  1782. };
  1783. void CValidError_feat::ReportCdTransErrors
  1784. (const CSeq_feat& feat,
  1785.  bool show_stop,
  1786.  bool got_stop, 
  1787.  bool no_end,
  1788.  int ragged)
  1789. {
  1790.     if ( show_stop ) {
  1791.         if ( !got_stop  && !no_end ) {
  1792.             PostErr(eDiag_Error, eErr_SEQ_FEAT_NoStop, 
  1793.                 "Missing stop codon", feat);
  1794.         } else if ( got_stop  &&  no_end ) {
  1795.             PostErr (eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  1796.                 "Got stop codon, but 3'end is labeled partial", feat);
  1797.         } else if ( got_stop  &&  !no_end  &&  ragged ) {
  1798.             PostErr (eDiag_Error, eErr_SEQ_FEAT_TransLen, 
  1799.                 "Coding region extends " + NStr::IntToString(ragged) +
  1800.                 " base(s) past stop codon", feat);
  1801.         }
  1802.     }
  1803. }
  1804. void CValidError_feat::ValidateCdTrans(const CSeq_feat& feat)
  1805. {
  1806.     bool prot_ok = true;
  1807.     int  ragged = 0;
  1808.     // biological exception
  1809.     size_t except_num = sizeof(s_BypassCdsTransCheck) / sizeof(string);
  1810.     if ( feat.CanGetExcept()  &&  feat.GetExcept()  &&
  1811.          feat.CanGetExcept_text() ) {
  1812.         for ( size_t i = 0; i < except_num; ++i ) {
  1813.             if ( NStr::FindNoCase(feat.GetExcept_text(), 
  1814.                 s_BypassCdsTransCheck[i]) != NPOS ) {
  1815.                 return; 
  1816.             }
  1817.         }
  1818.     }
  1819.     
  1820.     // pseuogene
  1821.     if ( (feat.CanGetPseudo()  &&  feat.GetPseudo())  ||
  1822.          IsOverlappingGenePseudo(feat) ) {
  1823.         return;
  1824.     }
  1825.     if ( !feat.CanGetProduct() ) {
  1826.         // bail if no product exists. shuold be checked elsewhere.
  1827.         return;
  1828.     }
  1829.     const CCdregion& cdregion = feat.GetData().GetCdregion();
  1830.     const CSeq_loc& location = feat.GetLocation();
  1831.     const CSeq_loc& product = feat.GetProduct();
  1832.     
  1833.     int gc = 0;
  1834.     if ( cdregion.CanGetCode() ) {
  1835.         // We assume that the id is set for all Genetic_code
  1836.         gc = cdregion.GetCode().GetId();
  1837.     }
  1838.     string gccode = NStr::IntToString(gc);
  1839.     string transl_prot;   // translated protein
  1840.     CBioseq_Handle nuc_handle = m_Scope->GetBioseqHandle(location);
  1841.     if ( !nuc_handle ) {
  1842.         return;
  1843.     }
  1844.     bool alt_start = false;
  1845.     try {
  1846.         CCdregion_translate::TranslateCdregion(
  1847.             transl_prot, 
  1848.             nuc_handle, 
  1849.             location, 
  1850.             cdregion,
  1851.             true,   // include stop codons
  1852.             false,  // do not remove trailing X/B/Z
  1853.             &alt_start);
  1854.     } catch (CException&) {
  1855.     }
  1856.     if ( transl_prot.empty() ) {
  1857.         PostErr (eDiag_Error, eErr_SEQ_FEAT_CdTransFail, 
  1858.             "Unable to translate", feat);
  1859.         return;
  1860.     }
  1861.     // check alternative start codon
  1862.     if ( alt_start  &&  gc == 1 ) {
  1863.         EDiagSev sev = eDiag_Warning;
  1864.         if ( s_IsLocRefSeqMrna(feat.GetLocation(), *m_Scope) ) {
  1865.             sev = eDiag_Error;
  1866.         } else if ( s_IsLocGEDL(feat.GetLocation(), *m_Scope) ) {
  1867.             sev = eDiag_Info;
  1868.         }
  1869.         if ( sev != eDiag_Info  &&  
  1870.              feat.CanGetExcept()  &&  feat.GetExcept()  &&
  1871.              feat.CanGetExcept_text()  &&  !feat.GetExcept_text().empty() ) {
  1872.             if ( feat.GetExcept_text().find("alternative start codon") != NPOS ) {
  1873.                 sev = eDiag_Info;
  1874.             }
  1875.         }
  1876.         if ( sev != eDiag_Info ) {
  1877.             PostErr(sev, eErr_SEQ_FEAT_AltStartCodon,
  1878.                 "Alternative start codon used", feat);
  1879.         }
  1880.     }
  1881.     bool no_end = false;
  1882.     unsigned int part_loc = SeqLocPartialCheck(location, m_Scope);
  1883.     unsigned int part_prod = SeqLocPartialCheck(product, m_Scope);
  1884.     if ( (part_loc & eSeqlocPartial_Stop)  ||
  1885.         (part_prod & eSeqlocPartial_Stop) ) {
  1886.         no_end = true;
  1887.     } else {    
  1888.         // complete stop, so check for ragged end
  1889.         ragged = CheckForRaggedEnd(location, cdregion);
  1890.     }
  1891.     
  1892.     // check for code break not on a codon
  1893.     ValidateCodeBreakNotOnCodon(feat, location, cdregion);
  1894.     
  1895.     if ( cdregion.GetFrame() > CCdregion::eFrame_one ) {
  1896.         EDiagSev sev = s_IsLocRefSeqMrna(feat.GetLocation(), *m_Scope) ?
  1897.             eDiag_Error : eDiag_Warning;
  1898.         if ( !(part_loc & eSeqlocPartial_Start) ) {
  1899.             PostErr(sev, eErr_SEQ_FEAT_PartialProblem, 
  1900.                 "Suspicious CDS location - frame > 1 but not 5' partial", feat);
  1901.         } else if ( (part_loc & eSeqlocPartial_Nostart)  && 
  1902.             !IsPartialAtSpliceSite(location, eSeqlocPartial_Nostart) ) {
  1903.             PostErr(sev, eErr_SEQ_FEAT_PartialProblem, 
  1904.                 "Suspicious CDS location - frame > 1 and not at consensus splice site",
  1905.                 feat);
  1906.         }
  1907.     }
  1908.     
  1909.     bool no_beg = false;
  1910.     
  1911.     size_t stop_count = 0;
  1912.     if ( (part_loc & eSeqlocPartial_Start)  ||
  1913.          (part_prod & eSeqlocPartial_Start) ) {
  1914.         no_beg = true;
  1915.     }
  1916.     
  1917.     bool got_dash = (transl_prot[0] == '-');
  1918.     bool got_stop = (transl_prot[transl_prot.length() - 1] == '*');
  1919.     
  1920.     // count internal stops
  1921.     ITERATE( string, it, transl_prot ) {
  1922.         if ( *it == '*' ) {
  1923.             ++stop_count;
  1924.         }
  1925.     }
  1926.     if ( got_stop ) {
  1927.         --stop_count;
  1928.     }
  1929.     
  1930.     if ( stop_count > 0 ) {
  1931.         if ( got_dash ) {
  1932.             PostErr(eDiag_Error, eErr_SEQ_FEAT_StartCodon,
  1933.                 "Illegal start codon and " + 
  1934.                 NStr::IntToString(stop_count) +
  1935.                 " internal stops. Probably wrong genetic code [" + gccode + "]",
  1936.                 feat);
  1937.         } else {
  1938.             PostErr(eDiag_Error, eErr_SEQ_FEAT_InternalStop, 
  1939.                 NStr::IntToString(stop_count) + 
  1940.                 " internal stops. Genetic code [" + gccode + "]", feat);
  1941.             prot_ok = false;
  1942.             if ( stop_count > 5 ) {
  1943.                 return;
  1944.             }
  1945.         }
  1946.     } else if ( got_dash ) {
  1947.         PostErr(eDiag_Error, eErr_SEQ_FEAT_StartCodon, 
  1948.             "Illegal start codon used. Wrong genetic code [" +
  1949.             gccode + "] or protein should be partial", feat);
  1950.     }
  1951.     
  1952.     bool show_stop = true;
  1953.     CBioseq_Handle prot_handle = m_Scope->GetBioseqHandle(product);
  1954.     if ( !prot_handle ) {
  1955.         if ( transl_prot.length() > 6 ) {
  1956.             if ( !(m_Imp.IsNG() || m_Imp.IsNT()) ) {
  1957.                 EDiagSev sev = eDiag_Error;
  1958.                 if ( IsDeltaOrFarSeg(location, m_Scope) ) {
  1959.                     sev = eDiag_Warning;
  1960.                 }
  1961.                 if ( m_Imp.IsNC() ) {
  1962.                     sev = eDiag_Warning;
  1963.                     if ( nuc_handle.GetTopLevelSeqEntry().IsSeq() ) {
  1964.                         sev = eDiag_Info;
  1965.                     }
  1966.                 }
  1967.                 if ( sev != eDiag_Info ) {
  1968.                     PostErr(sev, eErr_SEQ_FEAT_NoProtein, 
  1969.                         "No protein Bioseq given", feat);
  1970.                 }
  1971.             }
  1972.         }
  1973.         ReportCdTransErrors(feat, show_stop, got_stop, no_end, ragged);
  1974.         return;
  1975.     }
  1976.     CSeqVector prot_vec = prot_handle.GetSeqVector();
  1977.     prot_vec.SetCoding(CSeq_data::e_Ncbieaa);
  1978.     size_t prot_len = prot_vec.size(); 
  1979.     size_t len = transl_prot.length();
  1980.     if ( got_stop  &&  (len == prot_len + 1) ) { // ok, got stop
  1981.         --len;
  1982.     }
  1983.     // ignore terminal 'X' from partial last codon if present
  1984.     
  1985.     while ( prot_len > 0 ) {
  1986.         if ( prot_vec[prot_len - 1] == 'X' ) {  //remove terminal X
  1987.             --prot_len;
  1988.         } else {
  1989.             break;
  1990.         }
  1991.     }
  1992.     
  1993.     while ( len > 0 ) {
  1994.         if ( transl_prot[len - 1] == 'X' ) {  //remove terminal X
  1995.             --len;
  1996.         } else {
  1997.             break;
  1998.         }
  1999.     }
  2000.     vector<TSeqPos> mismatches;
  2001.     if ( len == prot_len )  {                // could be identical
  2002.         for ( TSeqPos i = 0; i < len; ++i ) {
  2003.             if ( transl_prot[i] != prot_vec[i] ) {
  2004.                 mismatches.push_back(i);
  2005.                 prot_ok = false;
  2006.             }
  2007.         }
  2008.     } else {
  2009.         PostErr(eDiag_Error, eErr_SEQ_FEAT_TransLen,
  2010.             "Given protein length [" + NStr::IntToString(prot_len) + 
  2011.             "] does not match translation length [" + 
  2012.             NStr::IntToString(len) + "]", feat);
  2013.     }
  2014.     
  2015.     // Mismatch on first residue
  2016.     string msg;
  2017.     if ( !mismatches.empty() && mismatches.front() == 0 ) {
  2018.         if ( feat.GetPartial() && (!no_beg) && (!no_end)) {
  2019.             PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem, 
  2020.                 "Start of location should probably be partial",
  2021.                 feat);
  2022.         } else if ( transl_prot[mismatches.front()] == '-' ) {
  2023.             PostErr(eDiag_Error, eErr_SEQ_FEAT_StartCodon,
  2024.                 "Illegal start codon used. Wrong genetic code [" +
  2025.                 gccode + "] or protein should be partial", feat);
  2026.         }
  2027.     }
  2028.     char prot_res, transl_res;
  2029.     string nuclocstr;
  2030.     if ( mismatches.size() > 10 ) {
  2031.         // report total number of mismatches and the details of the 
  2032.         // first and last.
  2033.         nuclocstr = MapToNTCoords(feat, product, mismatches.front());
  2034.         prot_res = prot_vec[mismatches.front()];
  2035.         transl_res = Residue(transl_prot[mismatches.front()]);
  2036.         msg = 
  2037.             NStr::IntToString(mismatches.size()) + " mismatches found. " +
  2038.             "First mismatch at " + NStr::IntToString(mismatches.front() + 1) +
  2039.             ", residue in protein [" + prot_res + "]" +
  2040.             " != translation [" + transl_res + "]";
  2041.         if ( !nuclocstr.empty() ) {
  2042.             msg += " at " + nuclocstr;
  2043.         }
  2044.         nuclocstr = MapToNTCoords(feat, product, mismatches.back());
  2045.         prot_res = prot_vec[mismatches.back()];
  2046.         transl_res = Residue(transl_prot[mismatches.back()]);
  2047.         msg += 
  2048.             ". Last mismatch at " + NStr::IntToString(mismatches.back() + 1) +
  2049.             ", residue in protein [" + prot_res + "]" +
  2050.             " != translation [" + transl_res + "]";
  2051.         if ( !nuclocstr.empty() ) {
  2052.             msg += " at " + nuclocstr;
  2053.         }
  2054.         msg += ". Genetic code [" + gccode + "]";
  2055.         PostErr(eDiag_Error, eErr_SEQ_FEAT_MisMatchAA, msg, feat);
  2056.     } else {
  2057.         // report individual mismatches
  2058.         for ( size_t i = 0; i < mismatches.size(); ++i ) {
  2059.             nuclocstr = MapToNTCoords(feat, product, mismatches[i]);
  2060.             prot_res = prot_vec[mismatches[i]];
  2061.             transl_res = Residue(transl_prot[mismatches[i]]);
  2062.             msg += 
  2063.                 "Residue " + NStr::IntToString(mismatches[i] + 1) + 
  2064.                 " in protein [" + prot_res + "]" +
  2065.                 " != translation [" + transl_res + "]";
  2066.             if ( !nuclocstr.empty() ) {
  2067.                 msg += " at " + nuclocstr;
  2068.             }
  2069.             PostErr(eDiag_Error, eErr_SEQ_FEAT_MisMatchAA, msg, feat);
  2070.         }
  2071.     }
  2072.     if ( feat.CanGetPartial()  &&  feat.GetPartial()  &&
  2073.          mismatches.empty() ) {
  2074.         if ( !no_beg  && !no_end ) {
  2075.             if ( !got_stop ) {
  2076.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem, 
  2077.                     "End of location should probably be partial", feat);
  2078.             } else {
  2079.                 PostErr(eDiag_Error, eErr_SEQ_FEAT_PartialProblem,
  2080.                     "This SeqFeat should not be partial", feat);
  2081.             }
  2082.             show_stop = false;
  2083.         }
  2084.     }
  2085.     ReportCdTransErrors(feat, show_stop, got_stop, no_end, ragged);
  2086. }
  2087. void CValidError_feat::ValidateCodeBreakNotOnCodon
  2088. (const CSeq_feat& feat,
  2089.  const CSeq_loc& loc, 
  2090.  const CCdregion& cdregion)
  2091. {
  2092.     TSeqPos len = GetLength(loc, m_Scope);
  2093.     ITERATE( CCdregion::TCode_break, cbr, cdregion.GetCode_break() ) {
  2094.         size_t codon_length = GetLength((*cbr)->GetLoc(), m_Scope);
  2095.         TSeqPos from = LocationOffset(loc, (*cbr)->GetLoc(), 
  2096.             eOffset_FromStart, m_Scope);
  2097.         TSeqPos to = from + codon_length - 1;
  2098.         
  2099.         // check for code break not on a codon
  2100.         if ( codon_length == 3  ||
  2101.             ((codon_length == 1 || codon_length == 2)  && 
  2102.             to == len - 1) ) {
  2103.             size_t start_pos;
  2104.             switch ( cdregion.GetFrame() ) {
  2105.             case CCdregion::eFrame_two:
  2106.                 start_pos = 1;
  2107.                 break;
  2108.             case CCdregion::eFrame_three:
  2109.                 start_pos = 2;
  2110.                 break;
  2111.             default:
  2112.                 start_pos = 0;
  2113.                 break;
  2114.             }
  2115.             if ( (from % 3) != start_pos ) {
  2116.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_TranslExceptPhase,
  2117.                     "transl_except qual out of frame.", feat);
  2118.             }
  2119.         }
  2120.     }
  2121. }
  2122. // Check for redundant gene Xref
  2123. void CValidError_feat::ValidateGeneXRef(const CSeq_feat& feat)
  2124. {
  2125.     const CGene_ref* grp = feat.GetGeneXref();
  2126.     if ( !grp  ||  grp->IsSuppressed() ) {
  2127.         return;
  2128.     }
  2129.     // !!! DEBUG {
  2130.     if( m_Imp.AvoidPerfBottlenecks() ) {
  2131.         return;
  2132.     } 
  2133.     // } DEBUG
  2134.     
  2135.     CConstRef<CSeq_feat> overlap = 
  2136.         GetOverlappingGene(feat.GetLocation(), *m_Scope);
  2137.     if ( !overlap ) {
  2138.         return;
  2139.     }
  2140.     
  2141.     const CGene_ref& gene = overlap->GetData().GetGene();
  2142.     if ( !gene.IsSuppressed() ) {
  2143.         if ( gene.CanGetAllele()  && !gene.GetAllele().empty() ) {
  2144.             const string& allele = gene.GetAllele();
  2145.             ITERATE(CSeq_feat::TQual, qual_iter, feat.GetQual()) {
  2146.                 const CGb_qual& qual = **qual_iter;
  2147.                 if ( qual.CanGetQual()  &&
  2148.                      NStr::Compare(qual.GetQual(), "allele") == 0 ) {
  2149.                     if ( qual.CanGetVal()  &&
  2150.                          NStr::CompareNocase(qual.GetVal(), allele) == 0 ) {
  2151.                         PostErr(eDiag_Warning, eErr_SEQ_FEAT_InvalidQualifierValue,
  2152.                             "Redundant allele qualifier (" + allele + 
  2153.                             ") on gene and feature", feat);
  2154.                     } else {
  2155.                         PostErr(eDiag_Warning, eErr_SEQ_FEAT_InvalidQualifierValue,
  2156.                             "Mismatched allele qualifier on gene (" + allele + 
  2157.                             ") and feature (" + qual.GetVal() +")", feat);
  2158.                     }
  2159.                 }
  2160.             }
  2161.         }
  2162.     }
  2163.     string label, gene_label;
  2164.     grp->GetLabel(&label);
  2165.     gene.GetLabel(&gene_label);
  2166.     
  2167.     if ( NStr::CompareNocase(label, gene_label) == 0 ) {
  2168.         PostErr(eDiag_Warning, eErr_SEQ_FEAT_UnnecessaryGeneXref,
  2169.             "Unnecessary gene cross-reference " + label, feat);
  2170.     }
  2171. }
  2172. void CValidError_feat::ValidateOperon(const CSeq_feat& gene)
  2173. {
  2174.     CConstRef<CSeq_feat> operon = 
  2175.         GetOverlappingOperon(gene.GetLocation(), *m_Scope);
  2176.     if ( !operon  ||  !operon->CanGetQual() ) {
  2177.         return;
  2178.     }
  2179.     string label;
  2180.     feature::GetLabel(gene, &label, feature::eContent, m_Scope);
  2181.     if ( label.empty() ) {
  2182.         return;
  2183.     }
  2184.     ITERATE(CSeq_feat::TQual, qual_iter, gene.GetQual()) {
  2185.         const CGb_qual& qual = **qual_iter;
  2186.         if( qual.CanGetQual()  &&  qual.CanGetVal() ) {
  2187.             if ( NStr::Compare(qual.GetQual(), "operon") == 0  &&
  2188.                  NStr::CompareNocase(qual.GetVal(), label) ) {
  2189.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_InvalidQualifierValue,
  2190.                     "Operon is same as gene - " + label, gene);
  2191.             }
  2192.         }
  2193.     }
  2194. }
  2195. bool CValidError_feat::IsPartialAtSpliceSite
  2196. (const CSeq_loc& loc,
  2197.  unsigned int tag)
  2198. {
  2199.     if ( tag != eSeqlocPartial_Nostart && tag != eSeqlocPartial_Nostop ) {
  2200.         return false;
  2201.     }
  2202.     CSeq_loc_CI first, last;
  2203.     for ( CSeq_loc_CI sl_iter(loc); sl_iter; ++sl_iter ) { // EQUIV_IS_ONE not supported
  2204.         if ( !first ) {
  2205.             first = sl_iter;
  2206.         }
  2207.         last = sl_iter;
  2208.     }
  2209.     if ( first.GetStrand() != last.GetStrand() ) {
  2210.         return false;
  2211.     }
  2212.     CSeq_loc_CI temp = (tag == eSeqlocPartial_Nostart) ? first : last;
  2213.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(temp.GetSeq_id());
  2214.     if ( !bsh ) {
  2215.         return false;
  2216.     }
  2217.     
  2218.     TSeqPos acceptor = temp.GetRange().GetFrom();
  2219.     TSeqPos donor = temp.GetRange().GetTo();
  2220.     TSeqPos start = acceptor;
  2221.     TSeqPos stop = donor;
  2222.     CSeqVector vec = bsh.GetSeqVector(CBioseq_Handle::eCoding_Iupac,
  2223.         temp.GetStrand());
  2224.     TSeqPos len = vec.size();
  2225.     if ( temp.GetStrand() == eNa_strand_minus ) {
  2226.         swap(acceptor, donor);
  2227.         stop = len - donor - 1;
  2228.         start = len - acceptor - 1;
  2229.     }
  2230.     bool result = false;
  2231.     
  2232.     if ( (tag == eSeqlocPartial_Nostop)  &&  (stop < len - 2) ) {
  2233.         try {
  2234.             CSeqVector::TResidue res1 = vec[stop + 1];
  2235.             CSeqVector::TResidue res2 = vec[stop + 2];
  2236.             if ( IsResidue(res1)  &&  IsResidue(res2) ) {
  2237.                 if ( (res1 == 'G'  &&  res2 == 'T')  || 
  2238.                     (res1 == 'G'  &&  res2 == 'C') ) {
  2239.                     result = true;
  2240.                 }
  2241.             }
  2242.         } catch ( exception& ) {
  2243.             return false;
  2244.         }
  2245.     } else if ( (tag == eSeqlocPartial_Nostart)  &&  (start > 1) ) {
  2246.         try {
  2247.             CSeqVector::TResidue res1 = vec[start - 2];    
  2248.             CSeqVector::TResidue res2 = vec[start - 1];
  2249.         
  2250.             if ( IsResidue(res1)  &&  IsResidue(res2) ) {
  2251.                 if ( (res1 == 'A')  &&  (res2 == 'G') ) { 
  2252.                     result = true;
  2253.                 }
  2254.             }
  2255.         } catch ( exception& ) {
  2256.             return false;
  2257.         }
  2258.     }
  2259.     return result;    
  2260. }
  2261. void CValidError_feat::ValidateFeatCit
  2262. (const CPub_set& cit,
  2263.  const CSeq_feat& feat)
  2264. {
  2265.     if ( !feat.CanGetCit() ) {
  2266.         return;
  2267.     }
  2268.     if ( cit.IsPub() ) {
  2269.         ITERATE( CPub_set::TPub, pi, cit.GetPub() ) {
  2270.             if ( (*pi)->IsEquiv() ) {
  2271.                 PostErr(eDiag_Warning, eErr_SEQ_FEAT_UnnecessaryCitPubEquiv,
  2272.                     "Citation on feature has unexpected internal Pub-equiv",
  2273.                     feat);
  2274.                 return;
  2275.             }
  2276.         }
  2277.     }
  2278. }
  2279. void CValidError_feat::ValidateFeatComment
  2280. (const string& comment,
  2281.  const CSeq_feat& feat)
  2282. {
  2283.     if ( m_Imp.IsSerialNumberInComment(comment) ) {
  2284.         PostErr(eDiag_Info, eErr_SEQ_FEAT_SerialInComment,
  2285.             "Feature comment may refer to reference by serial number - "
  2286.             "attach reference specific comments to the reference "
  2287.             "REMARK instead.", feat);
  2288.     }
  2289. }
  2290. void CValidError_feat::ValidateFeatBioSource
  2291. (const CBioSource& bsrc,
  2292.  const CSeq_feat& feat)
  2293. {
  2294.     if ( bsrc.CanGetIs_focus() ) {
  2295.         PostErr(eDiag_Error, eErr_SEQ_FEAT_FocusOnBioSourceFeature,
  2296.             "Focus must be on BioSource descriptor, not BioSource feature.",
  2297.             feat);
  2298.     }
  2299.     CBioseq_Handle bsh = m_Scope->GetBioseqHandle(feat.GetLocation());
  2300.     if ( !bsh ) {
  2301.         return;
  2302.     }
  2303.     CSeqdesc_CI dbsrc_i(bsh, CSeqdesc::e_Source);
  2304.     if ( !dbsrc_i ) {
  2305.         return;
  2306.     }
  2307.     
  2308.     const COrg_ref& org = bsrc.GetOrg();           
  2309.     const CBioSource& dbsrc = dbsrc_i->GetSource();
  2310.     const COrg_ref& dorg = dbsrc.GetOrg(); 
  2311.     if ( org.CanGetTaxname()  &&  !org.GetTaxname().empty()  &&
  2312.             dorg.CanGetTaxname() ) {
  2313.         string taxname = org.GetTaxname();
  2314.         string dtaxname = dorg.GetTaxname();
  2315.         if ( NStr::CompareNocase(taxname, dtaxname) != 0 ) {
  2316.             if ( !dbsrc.CanGetIs_focus()  &&  !IsTransgenic(dbsrc) ) {
  2317.                 PostErr(eDiag_Error, eErr_SEQ_DESCR_BioSourceNeedsFocus,
  2318.                     "BioSource descriptor must have focus or transgenic "
  2319.                     "when BioSource feature with different taxname is "
  2320.                     "present.", feat);
  2321.             }
  2322.         }
  2323.     }
  2324.     m_Imp.ValidateBioSource(bsrc, feat);
  2325. }
  2326. bool CValidError_feat::IsTransgenic(const CBioSource& bsrc)
  2327. {
  2328.     if ( bsrc.CanGetSubtype() ) {
  2329.         ITERATE( CBioSource::TSubtype, sub, bsrc.GetSubtype() ) {
  2330.             if ( (*sub)->GetSubtype() == CSubSource::eSubtype_transgenic ) {
  2331.                 return true;
  2332.             }
  2333.         }
  2334.     }
  2335.     return false;
  2336. }
  2337. // REQUIRES: feature is either Gene or mRNA
  2338. bool CValidError_feat::IsSameAsCDS(const CSeq_feat& feat)
  2339. {
  2340.     EOverlapType overlap_type;
  2341.     if ( feat.GetData().IsGene() ) {
  2342.         overlap_type = eOverlap_Simple;
  2343.     } else if ( feat.GetData().GetSubtype() == CSeqFeatData::eSubtype_mRNA ) {
  2344.         overlap_type = eOverlap_CheckIntervals;
  2345.     } else {
  2346.         return false;
  2347.     }
  2348.     CConstRef<CSeq_feat> cds = GetBestOverlappingFeat(
  2349.             feat.GetLocation(),
  2350.             CSeqFeatData::e_Cdregion,
  2351.             overlap_type,
  2352.             *m_Scope);
  2353.         if ( cds ) {
  2354.             if ( TestForOverlap(
  2355.                     cds->GetLocation(),
  2356.                     feat.GetLocation(),
  2357.                     eOverlap_Simple) == 0 ) {
  2358.                 return true;
  2359.             }
  2360.         }
  2361.     return false;
  2362. }
  2363. bool CValidError_feat::IsCDDFeat(const CSeq_feat& feat) const
  2364. {
  2365.     if ( feat.GetData().IsRegion() ) {
  2366.         if ( feat.CanGetDbxref() ) {
  2367.             ITERATE(CSeq_feat::TDbxref, db, feat.GetDbxref()) {
  2368.                 if ( (*db)->CanGetDb()  &&
  2369.                     NStr::Compare((*db)->GetDb(), "CDD") == 0 ) {
  2370.                     return true;
  2371.                 }
  2372.             }
  2373.         }
  2374.     }
  2375.     return false;
  2376. }
  2377. END_SCOPE(validator)
  2378. END_SCOPE(objects)
  2379. END_NCBI_SCOPE
  2380. /*
  2381. * ===========================================================================
  2382. *
  2383. * $Log: validerror_feat.cpp,v $
  2384. * Revision 1000.2  2004/06/01 19:48:04  gouriano
  2385. * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.56
  2386. *
  2387. * Revision 1.56  2004/05/26 14:53:30  shomrat
  2388. * removed non-preferred variants ribosome slippage, trans splicing, alternate processing, and non-consensus splice site
  2389. *
  2390. * Revision 1.55  2004/05/21 21:42:56  gorelenk
  2391. * Added PCH ncbi_pch.hpp
  2392. *
  2393. * Revision 1.54  2004/04/23 16:27:09  shomrat
  2394. * Stop using CBioseq_Handle deprecated interface
  2395. *
  2396. * Revision 1.53  2004/04/05 15:56:15  grichenk
  2397. * Redesigned CAnnotTypes_CI: moved all data and data collecting
  2398. * functions to CAnnotDataCollector. CAnnotTypes_CI is no more
  2399. * inherited from SAnnotSelector.
  2400. *
  2401. * Revision 1.52  2004/03/25 20:10:13  shomrat
  2402. * Fix statement with no effect
  2403. *
  2404. * Revision 1.51  2004/03/25 18:33:47  shomrat
  2405. * Bug fix
  2406. *
  2407. * Revision 1.50  2004/03/19 14:48:53  shomrat
  2408. * use ERR_SEQ_FEAT_PartialsInconsistent in two places
  2409. *
  2410. * Revision 1.49  2004/03/10 21:24:44  shomrat
  2411. * suppress eErr_SEQ_FEAT_AltStartCodon for GenBank/EMBL/DDBJ and Local
  2412. *
  2413. * Revision 1.48  2004/03/01 18:44:28  shomrat
  2414. * Check alternative start codon
  2415. *
  2416. * Revision 1.47  2004/02/05 20:08:32  shomrat
  2417. * Using convenience functions for overlapping features
  2418. *
  2419. * Revision 1.46  2004/01/16 20:09:15  shomrat
  2420. * Implemented check for locus tag problem
  2421. *
  2422. * Revision 1.45  2003/12/16 17:35:39  shomrat
  2423. * catch all exceptions, not just runtime
  2424. *
  2425. * Revision 1.44  2003/12/16 16:21:34  shomrat
  2426. * Implemented ValidateCdConflict
  2427. *
  2428. * Revision 1.43  2003/10/27 14:18:03  shomrat
  2429. * ValidateImp warns if repeat_region /rpt_unit has same length as feature location but does not have matching sequence
  2430. *
  2431. * Revision 1.42  2003/10/24 17:57:25  shomrat
  2432. * warn about allele gbqual when inheriting allele from gene; warn about operon when same as gene
  2433. *
  2434. * Revision 1.41  2003/10/21 13:21:52  shomrat
  2435. * added * Terminator codon
  2436. *
  2437. * Revision 1.40  2003/10/20 16:13:28  shomrat
  2438. * count gene and gene xrefs
  2439. *
  2440. * Revision 1.39  2003/10/13 18:48:52  shomrat
  2441. * added tests for TrnaCodonWrong and BadTrnaAA
  2442. *
  2443. * Revision 1.38  2003/10/13 12:49:37  shomrat
  2444. *                     // now any text will be allowed
  2445. *
  2446. * Revision 1.37  2003/10/01 21:00:24  shomrat
  2447. * suppress partial not at end warning for CDD region
  2448. *
  2449. * Revision 1.36  2003/09/22 20:25:10  shomrat
  2450. * lower severity for far product partial inconsistency and mrnatranscheck, also check for 95% polyA
  2451. *
  2452. * Revision 1.35  2003/07/24 20:16:18  vasilche
  2453. * Fixed typedefs for dbxref: list<CRef<CDbtag>> -> vector<CRef<CDbtag>>
  2454. *
  2455. * Revision 1.34  2003/07/21 21:19:43  shomrat
  2456. * Make sure we can get before trying to call GetXXX methods
  2457. *
  2458. * Revision 1.33  2003/07/02 21:04:48  shomrat
  2459. * added CheckCDSPartial to check cds->location partials against product molinfo
  2460. *
  2461. * Revision 1.32  2003/06/17 13:40:48  shomrat
  2462. * Use SeqVector_CI to improve performance
  2463. *
  2464. * Revision 1.31  2003/06/02 16:06:43  dicuccio
  2465. * Rearranged src/objects/ subtree.  This includes the following shifts:
  2466. *     - src/objects/asn2asn --> arc/app/asn2asn
  2467. *     - src/objects/testmedline --> src/objects/ncbimime/test
  2468. *     - src/objects/objmgr --> src/objmgr
  2469. *     - src/objects/util --> src/objmgr/util
  2470. *     - src/objects/alnmgr --> src/objtools/alnmgr
  2471. *     - src/objects/flat --> src/objtools/flat
  2472. *     - src/objects/validator --> src/objtools/validator
  2473. *     - src/objects/cddalignview --> src/objtools/cddalignview
  2474. * In addition, libseq now includes six of the objects/seq... libs, and libmmdb
  2475. * replaces the three libmmdb? libs.
  2476. *
  2477. * Revision 1.30  2003/05/28 16:25:42  shomrat
  2478. * Minor corrections.
  2479. *
  2480. * Revision 1.29  2003/05/15 20:00:44  shomrat
  2481. * Bug fix in ValidateImpGbquals
  2482. *
  2483. * Revision 1.28  2003/05/14 21:20:50  shomrat
  2484. * Changes to ValidateGeneXRef
  2485. *
  2486. * Revision 1.27  2003/05/05 15:36:15  shomrat
  2487. * Implemented ValidateCdsProductId
  2488. *
  2489. * Revision 1.26  2003/04/24 16:16:00  vasilche
  2490. * Added missing includes and forward class declarations.
  2491. *
  2492. * Revision 1.25  2003/04/07 14:58:36  shomrat
  2493. * Added information to error postings
  2494. *
  2495. * Revision 1.24  2003/04/04 18:42:22  shomrat
  2496. * Increased robustness in face of exceptions and minor bug fixes
  2497. *
  2498. * Revision 1.23  2003/03/31 14:40:13  shomrat
  2499. * $id: -> $id$
  2500. *
  2501. * Revision 1.22  2003/03/28 16:26:47  shomrat
  2502. * Removed IsResidue (moved to utilities)
  2503. *
  2504. * Revision 1.21  2003/03/21 21:15:27  shomrat
  2505. * Implemented ValidateSeqFeatProduct
  2506. *
  2507. * Revision 1.20  2003/03/20 18:56:44  shomrat
  2508. * Supress error in case of Seq-annot validation
  2509. *
  2510. * Revision 1.19  2003/03/18 21:48:37  grichenk
  2511. * Removed obsolete class CAnnot_CI
  2512. *
  2513. * Revision 1.18  2003/03/11 16:04:09  kuznets
  2514. * iterate -> ITERATE
  2515. *
  2516. * Revision 1.17  2003/02/14 21:54:21  shomrat
  2517. * Change use of GetBestOverlappingFeat due to addition of contains option
  2518. *
  2519. * Revision 1.16  2003/02/12 18:05:52  shomrat
  2520. * SerialNumberInComment was moved to validerror_imp; and a few bug fixes
  2521. *
  2522. * Revision 1.15  2003/02/10 15:54:02  grichenk
  2523. * Use CFeat_CI->GetMappedFeature() and GetOriginalFeature()
  2524. *
  2525. * Revision 1.14  2003/02/07 21:25:04  shomrat
  2526. * SameAsCDS checks partial gene or mRNA for CDS with proper intervals, drops severity to INFO; Bug fixes
  2527. *
  2528. * Revision 1.13  2003/02/03 20:21:41  shomrat
  2529. * Skip use of feature indexing if performance flag is set (testing)
  2530. *
  2531. * Revision 1.12  2003/02/03 18:03:22  shomrat
  2532. * Change in use of GetSequenceView and style improvments
  2533. *
  2534. * Revision 1.11  2003/01/31 18:28:03  shomrat
  2535. * Re-implementation of ValidateBadGeneOverlap
  2536. *
  2537. * Revision 1.10  2003/01/30 20:26:17  shomrat
  2538. * Explicitly call sequence::Compare
  2539. *
  2540. * Revision 1.9  2003/01/29 21:58:26  shomrat
  2541. * Commented check for multiple CDS due to feature indexing implementation issues
  2542. *
  2543. * Revision 1.8  2003/01/29 17:50:06  shomrat
  2544. * Bug fix in ValidateExceptText
  2545. *
  2546. * Revision 1.7  2003/01/24 21:21:00  shomrat
  2547. * Bug fixes in ValidateMrnaTrans & ValidateBadGeneOverlap
  2548. *
  2549. * Revision 1.6  2003/01/21 20:11:11  shomrat
  2550. * Added check for RNA product mismatch
  2551. *
  2552. * Revision 1.5  2003/01/10 16:29:51  shomrat
  2553. * Added Checks for eErr_SEQ_FEAT_UnnecessaryCitPubEquiv and eErr_SEQ_DESCR_BioSourceNeedsFocus
  2554. *
  2555. * Revision 1.4  2003/01/06 16:43:01  shomrat
  2556. * naming convention
  2557. *
  2558. * Revision 1.3  2003/01/02 22:13:04  shomrat
  2559. * Implemented checks relying on overlapping features (IsOverlappingGenePseudo, PeptideOnCodonBoundry, CommonCDSProduct, BadMRNAOverlap, BadGeneOverlap, GeneXRef); Check for bad product seq id in ValidateSeqFeat
  2560. *
  2561. * Revision 1.2  2002/12/24 16:54:02  shomrat
  2562. * Changes to include directives
  2563. *
  2564. * Revision 1.1  2002/12/23 20:16:34  shomrat
  2565. * Initial submission after splitting former implementation
  2566. *
  2567. *
  2568. * ===========================================================================
  2569. */