1 /* $Id: cache.cpp 609816 2020-06-08 15:50:52Z grichenk $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Vladimir Soussov, Michael Domrachev
27  *
28  * File Description:
29  *     NCBI Taxonomy information retreival library caching implementation
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 
35 #include <serial/serial.hpp>
36 #include <serial/objistrasn.hpp>
37 
38 #include <objects/taxon1/taxon1.hpp>
39 #include "cache.hpp"
40 
41 #include <vector>
42 #include <algorithm>
43 
44 BEGIN_NCBI_SCOPE
45 BEGIN_objects_SCOPE
46 
47 
COrgRefCache(CTaxon1 & host)48 COrgRefCache::COrgRefCache( CTaxon1& host )
49     : m_host( host ), m_ppEntries( 0 ), m_nCacheCapacity( 10 )
50 {
51     return;
52 }
53 
~COrgRefCache()54 COrgRefCache::~COrgRefCache()
55 {
56     delete[] m_ppEntries;
57     for( list<SCacheEntry*>::iterator i = m_lCache.begin();
58          i != m_lCache.end();
59          ++i ) {
60         delete *i;
61     }
62 }
63 
64 bool
Init(unsigned nCapacity)65 COrgRefCache::Init( unsigned nCapacity )
66 {
67     CTaxon1_req  req;
68     CTaxon1_resp resp;
69 
70     req.SetMaxtaxid();
71 
72     if( m_host.SendRequest( req, resp ) ) {
73         if( resp.IsMaxtaxid() ) {
74             // Correct response, return object
75             m_nMaxTaxId = TAX_ID_TO(unsigned, resp.GetMaxtaxid());
76             m_nMaxTaxId += m_nMaxTaxId/10;
77             m_ppEntries = new CTaxon1Node*[m_nMaxTaxId];
78             memset( m_ppEntries, '\0', m_nMaxTaxId*sizeof(*m_ppEntries) );
79         } else { // Internal: wrong respond type
80             m_host.SetLastError( "Response type is not Maxtaxid" );
81             return false;
82         }
83     } else {
84         return false;
85     }
86     CTaxon1_name* pNode = ( new CTaxon1_name );
87     pNode->SetTaxid( TAX_ID_CONST(1) );
88     pNode->SetOname().assign("root");
89     pNode->SetCde( 0x40000000 ); // Gene bank hidden
90     CTaxon1Node* pRoot = new CTaxon1Node( CRef<CTaxon1_name>(pNode) );
91     m_tPartTree.SetRoot( pRoot );
92     SetIndexEntry( 1, pRoot );
93 
94     if( nCapacity != 0 ) {
95         m_nCacheCapacity = nCapacity;
96     }
97 //    InitRanks();
98 //    InitDivisions();
99     return true;
100 }
101 
102 bool
Lookup(TTaxId tax_id,CTaxon1Node ** ppNode)103 COrgRefCache::Lookup( TTaxId tax_id, CTaxon1Node** ppNode )
104 {
105     if( TAX_ID_TO(unsigned, tax_id) < m_nMaxTaxId ) {
106         *ppNode = m_ppEntries[TAX_ID_TO(unsigned, tax_id)];
107     } else {
108         *ppNode = NULL;
109     }
110     return *ppNode != NULL;
111 }
112 
113 bool
LookupAndAdd(TTaxId tax_id,CTaxon1Node ** ppData)114 COrgRefCache::LookupAndAdd( TTaxId tax_id, CTaxon1Node** ppData )
115 {
116     *ppData = NULL;
117     if(TAX_ID_TO(unsigned, tax_id) < m_nMaxTaxId ) {
118         CTaxon1Node* pNode = ( m_ppEntries[TAX_ID_TO(unsigned, tax_id)] );
119         if( pNode ) {
120             *ppData = pNode;
121             return true;
122         } else { // Add the entry from server
123             CTaxon1_req  req;
124             CTaxon1_resp resp;
125 
126             req.SetTaxalineage(TAX_ID_TO(int, tax_id) );
127 
128             if( m_host.SendRequest( req, resp ) ) {
129                 if( resp.IsTaxalineage() ) {
130                     // Correct response, return object
131                     list< CRef<CTaxon1_name> >& lLin = resp.SetTaxalineage();
132                     CTaxon1Node* pParent = 0;
133                     pNode   = 0;
134                     // Check if this is a secondary node
135                     if( lLin.front()->GetTaxid() != tax_id ) {
136                         // Secondary node, try to get primary from index
137                         pNode = m_ppEntries[TAX_ID_TO(unsigned, lLin.front()->GetTaxid()) ];
138                     }
139                     if( !pNode ) {
140                         list< CRef< CTaxon1_name > >::reverse_iterator i;
141                         // Fill in storage
142                         for( i = lLin.rbegin(); i != lLin.rend(); ++i ) {
143                             if( !m_ppEntries[TAX_ID_TO(unsigned, (*i)->GetTaxid()) ] ) {
144                                 // Create node
145                                 break;
146                             } else {
147                                 pParent = m_ppEntries[TAX_ID_TO(unsigned, (*i)->GetTaxid()) ];
148                             }
149                         }
150                         // Create tree iterator
151                         CTreeIterator* pIt = ( m_tPartTree.GetIterator() );
152                         if( !pParent ) {
153                             pParent = static_cast<CTaxon1Node*>(pIt->GetNode());
154                         }
155                         pIt->GoNode( pParent );
156                         for( ; i != lLin.rend(); ++i ) {
157                             pNode = new CTaxon1Node(*i);
158                             m_ppEntries[TAX_ID_TO(unsigned, pNode->GetTaxId()) ] = pNode;
159                             pIt->AddChild( pNode );
160                             pIt->GoNode( pNode );
161                         }
162                         delete pIt;
163                     } else { // Store secondary in index
164                         m_ppEntries[TAX_ID_TO(unsigned, tax_id) ] = pNode;
165                     }
166                     _ASSERT( pNode );
167                     *ppData = pNode;
168                     return true;
169                 } else { // Internal: wrong respond type
170                     m_host.SetLastError( "Unable to get node lineage: "
171 					 "Response type is not Taxalineage" );
172                     return false;
173                 }
174             }
175         }
176     }
177     return false;
178 }
179 
180 bool
LookupAndInsert(TTaxId tax_id,CTaxon2_data ** ppData)181 COrgRefCache::LookupAndInsert( TTaxId tax_id, CTaxon2_data** ppData )
182 {
183     CTaxon1Node* pNode = ( NULL );
184     *ppData = NULL;
185 
186     if( LookupAndAdd( tax_id, &pNode ) && pNode ) {
187         SCacheEntry* pEntry = ( pNode->GetEntry() );
188         if( !pEntry ) {
189             if( !Insert2( *pNode ) )
190                 return false;
191             pEntry = pNode->GetEntry();
192         } else {
193             m_lCache.remove( pEntry );
194             m_lCache.push_front( pEntry );
195         }
196         *ppData = pEntry->GetData();
197         return true;
198     }
199     return false;
200 }
201 
202 bool
Lookup(TTaxId tax_id,CTaxon2_data ** ppData)203 COrgRefCache::Lookup( TTaxId tax_id, CTaxon2_data** ppData )
204 {
205     if(TAX_ID_TO(unsigned, tax_id) < m_nMaxTaxId ) {
206         CTaxon1Node* pNode = ( m_ppEntries[TAX_ID_TO(unsigned, tax_id)] );
207         SCacheEntry* pEntry;
208         if( pNode && (pEntry=pNode->GetEntry()) ) {
209             // Move in the list
210             m_lCache.remove( pEntry );
211             m_lCache.push_front( pEntry );
212             *ppData = pEntry->GetData();
213             return true;
214         }
215     }
216     *ppData = NULL;
217     return false;
218 }
219 
220 bool
Insert2(CTaxon1Node & node)221 COrgRefCache::Insert2( CTaxon1Node& node )
222 {
223     CTaxon1_req  req;
224     CTaxon1_resp resp;
225 
226     req.SetLookup().SetTaxId( node.GetTaxId() );
227     // Set version db tag
228     COrgrefProp::SetOrgrefProp( req.SetLookup(), "version", 2 );
229     if( m_host.m_bWithSynonyms ) {
230 	COrgrefProp::SetOrgrefProp( req.SetLookup(), "syn", m_host.m_bWithSynonyms );
231     }
232 
233     if( m_host.SendRequest( req, resp ) ) {
234         if( resp.IsLookup() ) {
235             // Correct response, return object
236 	    struct SCacheEntry* pEntry = ( new SCacheEntry );
237 	    pEntry->m_pTax2 = new CTaxon2_data();
238 	    pEntry->m_pTreeNode = &node;
239 
240             SerialAssign< COrg_ref >( pEntry->m_pTax2->SetOrg(), resp.GetLookup().GetOrg() );
241 	    m_host.x_ConvertOrgrefProps( *pEntry->m_pTax2 );
242 
243 	    // Remove last element from list
244 	    if( m_lCache.size() >= m_nCacheCapacity ) {
245 		CTaxon1Node* pNode = m_lCache.back()->m_pTreeNode;
246 		pNode->m_cacheEntry = NULL;
247 		delete m_lCache.back();
248 		m_lCache.pop_back();
249 	    }
250 
251 	    node.m_cacheEntry = pEntry;
252 	    m_lCache.push_front( pEntry );
253 
254 	    return true;
255 
256         } else { // Internal: wrong respond type
257             m_host.SetLastError( "Response type is not Lookup" );
258         }
259     }
260 
261     return false;
262 }
263 
264 TTaxRank
FindRankByName(const char * pchName)265 COrgRefCache::FindRankByName( const char* pchName )
266 {
267     if( InitRanks() ) {
268 	int vid = m_rankStorage.FindValueIdByField( "rank_txt", pchName );
269         if( vid != CDomainStorage::kIllegalValue ) {
270 	    return m_rankStorage.HasField("oldid") ? m_rankStorage.FindFieldValueById( vid, "oldid" ) : vid;
271 	}
272     }
273     return -1000;
274 }
275 
276 
277 const char*
GetRankName(int rank)278 COrgRefCache::GetRankName( int rank )
279 {
280     if( InitRanks() ) {
281         if( m_rankStorage.HasField("oldid") ) {
282 	    int vid = m_rankStorage.FindValueIdByField( "oldid", rank );
283             if( vid != CDomainStorage::kIllegalValue ) {
284 	        return m_rankStorage.FindFieldStringById( vid, "rank_txt" ).c_str();
285 	    }
286         } else { // old style ranks, use rank_txt
287             const string& s = m_rankStorage.FindFieldStringById( rank, "rank_txt" );
288             if( !s.empty() ) {
289                 return s.c_str();
290             }
291         }
292     }
293     return NULL;
294 }
295 
296 bool
InitDomain(const string & name,CDomainStorage & storage)297 COrgRefCache::InitDomain( const string& name, CDomainStorage& storage )
298 {
299     CTaxon1_req  req;
300     CTaxon1_resp resp;
301 
302     req.SetGetdomain(name);
303 
304     if( m_host.SendRequest( req, resp ) ) {
305 	if( resp.IsGetdomain() ) {
306 	    // Correct response, return object
307 	    list< CRef< CTaxon1_info > >& lRecords = resp.SetGetdomain();
308 	    // Get header [0]:id,nof_fields,name
309 	    storage.SetId( lRecords.front()->GetIval1() );
310 	    int nof_fields = lRecords.front()->GetIval2();
311 	    storage.SetName( lRecords.front()->GetSval() );
312 	    lRecords.pop_front();
313 	    // read in field descriptions [1..nof_fields]:field_no,val_type,field_name
314 	    while( nof_fields-- && !lRecords.empty() ) {
315 		storage.AddField( lRecords.front()->GetIval1(), lRecords.front()->GetIval2(), lRecords.front()->GetSval() );
316 		lRecords.pop_front();
317 	    }
318 	    // Fill in storage
319 	    for( list< CRef< CTaxon1_info > >::const_iterator i = lRecords.begin();
320 		 i != lRecords.end(); ++i ) {
321 		if( (*i)->IsSetSval() ) {
322 		    storage.InsertFieldValue( (*i)->GetIval1(), (*i)->GetIval2(), (*i)->GetSval() );
323 		} else {
324 		    storage.InsertFieldValue( (*i)->GetIval1(), (*i)->GetIval2() );
325 		}
326 	    }
327 	    return true;
328 	} else { // Internal: wrong respond type
329 	    m_host.SetLastError( "Invalid response type" );
330 	    return false;
331 	}
332     }
333     return false;
334 }
335 
336 bool
InitRanks()337 COrgRefCache::InitRanks()
338 {
339     if( m_rankStorage.empty() ) {
340 	if( InitDomain("rank",m_rankStorage) ) {
341 	    // Find some old ranks
342 	    m_nSuperkingdomRank = FindRankByName( "superkingdom" );
343 	    if( m_nSuperkingdomRank < -10 ) {
344 		m_host.SetLastError( "Superkingdom rank was not found" );
345 		return false;
346 	    }
347 	    m_nGenusRank = FindRankByName( "genus" );
348 	    if( m_nGenusRank < -10 ) {
349 		m_host.SetLastError( "Genus rank was not found" );
350 		return false;
351 	    }
352 	    m_nSpeciesRank = FindRankByName( "species" );
353 	    if( m_nSpeciesRank < -10 ) {
354 		m_host.SetLastError( "Species rank was not found" );
355 		return false;
356 	    }
357 	    m_nSubspeciesRank = FindRankByName( "subspecies" );
358 	    if( m_nSubspeciesRank < -10 ) {
359 		m_host.SetLastError( "Subspecies rank was not found" );
360 		return false;
361 	    }
362 	} else {
363 	    return false;
364 	}
365     }
366     return true;
367 }
368 
369 const char*
GetNameClassName(short nc)370 COrgRefCache::GetNameClassName( short nc )
371 {
372     if( !InitNameClasses() ) return NULL;
373     TNameClassMapCI ci( m_ncStorage.find( nc ) );
374     if( ci != m_ncStorage.end() ) {
375         return ci->second.c_str();
376     }
377     return NULL;
378 }
379 
380 TTaxNameClass
FindNameClassByName(const char * pchName)381 COrgRefCache::FindNameClassByName( const char* pchName )
382 {
383     if( !InitNameClasses() ) return -1;
384     for( TNameClassMapCI ci = m_ncStorage.begin();
385          ci != m_ncStorage.end();
386          ++ci )
387         if( ci->second.compare( pchName ) == 0 )
388             return ci->first;
389     return -1;
390 }
391 
392 bool
InitNameClasses()393 COrgRefCache::InitNameClasses()
394 {
395     if( m_ncStorage.size() == 0 ) {
396 
397         CTaxon1_req  req;
398         CTaxon1_resp resp;
399 
400         req.SetGetcde();
401 
402         if( m_host.SendRequest( req, resp ) ) {
403             if( resp.IsGetcde() ) {
404                 // Correct response, return object
405                 const list< CRef< CTaxon1_info > >&
406                     l = ( resp.GetGetcde() );
407                 // Fill in storage
408                 for( list< CRef< CTaxon1_info > >::const_iterator
409                          i = l.begin();
410                      i != l.end(); ++i )
411                     m_ncStorage
412                         .insert( TNameClassMap::value_type((*i)->GetIval1(),
413                                                            (*i)->GetSval()) );
414             } else { // Internal: wrong respond type
415                 m_host.SetLastError( "Response type is not Getcde" );
416                 return false;
417             }
418         }
419 
420         m_ncPrefCommon = FindNameClassByName( "genbank common name" );
421         if( m_ncPrefCommon < 0 ) {
422             m_host.SetLastError( "Genbank common name class was not found" );
423             return false;
424         }
425         m_ncCommon = FindNameClassByName( "common name" );
426         if( m_ncCommon < 0 ) {
427             m_host.SetLastError( "Common name class was not found" );
428             return false;
429         }
430     }
431     return true;
432 }
433 
434 TTaxDivision
FindDivisionByCode(const char * pchCode)435 COrgRefCache::FindDivisionByCode( const char* pchCode )
436 {
437     if( !InitDivisions() || pchCode == 0 ) return -1;
438     int result = -1;
439     result = m_divStorage.FindValueIdByField( "div_cde", pchCode );
440     if( result == CDomainStorage::kIllegalValue ) {
441 	result = -1;
442     }
443     return result;
444 }
445 
446 TTaxDivision
FindDivisionByName(const char * pchName)447 COrgRefCache::FindDivisionByName( const char* pchName )
448 {
449     if( !InitDivisions() || pchName == 0 ) return -1;
450     int result = -1;
451     result = m_divStorage.FindValueIdByField( "div_txt", pchName );
452     if( result == CDomainStorage::kIllegalValue ) {
453 	result = -1;
454     }
455     return result;
456 }
457 
458 const char*
GetDivisionCode(short div_id)459 COrgRefCache::GetDivisionCode( short div_id )
460 {
461     if( !InitDivisions() ) return NULL;
462     const string& sCode = m_divStorage.FindFieldStringById( div_id, "div_cde" );
463     if( !sCode.empty() ) {
464         return sCode.c_str();
465     }
466     return NULL;
467 }
468 
469 const char*
GetDivisionName(short div_id)470 COrgRefCache::GetDivisionName( short div_id )
471 {
472     if( !InitDivisions() ) return NULL;
473     const string& s = m_divStorage.FindFieldStringById( div_id, "div_txt" );
474     if( !s.empty() ) {
475         return s.c_str();
476     }
477     return NULL;
478 }
479 
480 
481 bool
InitDivisions()482 COrgRefCache::InitDivisions()
483 {
484     if( m_divStorage.empty() ) {
485 
486 	if( !InitDomain("division", m_divStorage) ) {
487 	    return false;
488 	}
489 //         CTaxon1_req  req;
490 //         CTaxon1_resp resp;
491 
492 //         req.SetGetdivs();
493 
494 //         if( m_host.SendRequest( req, resp ) ) {
495 //             if( resp.IsGetdivs() ) {
496 //                 // Correct response, return object
497 //                 const list< CRef< CTaxon1_info > >&
498 //                     l = ( resp.GetGetdivs() );
499 //                 // Fill in storage
500 //                 for( list< CRef< CTaxon1_info > >::const_iterator
501 //                          i = l.begin();
502 //                      i != l.end(); ++i ) {
503 //                     SDivision& div = ( m_divStorage[(*i)->GetIval1()] );
504 //                     div.m_sName.assign( (*i)->GetSval() );
505 //                     int code = (*i)->GetIval2();
506 //                     for(int k= 0; k < 3; k++) {
507 //                         div.m_sCode.append( 1U, (code >> (8*(3-k))) & 0xFF );
508 //                     }
509 //                     div.m_sCode.append( 1U, code & 0xFF );
510 //                 }
511 //             } else { // Internal: wrong response type
512 //                 m_host.SetLastError( "Response type is not Getdivs" );
513 //                 return false;
514 //             }
515 //         }
516     }
517     return true;
518 }
519 
520 void
SetIndexEntry(int id,CTaxon1Node * pNode)521 COrgRefCache::SetIndexEntry( int id, CTaxon1Node* pNode )
522 {
523     m_ppEntries[id] = pNode;
524 }
525 
526 //=======================================================
527 //
528 //   Iterators implementation
529 //
530 bool
IsLastChild() const531 CTaxTreeConstIterator::IsLastChild() const
532 {
533     const CTreeContNodeBase* pOldNode = m_it->GetNode();
534     bool bResult = true;
535 
536     while( m_it->GoParent() ) {
537         if( IsVisible( m_it->GetNode() ) ) {
538             const CTreeContNodeBase* pParent = m_it->GetNode();
539             m_it->GoNode( pOldNode );
540             while( m_it->GetNode() != pParent ) {
541                 if( m_it->GoSibling() ) {
542                     bResult = !NextVisible( pParent );
543                     break;
544                 }
545                 if( !m_it->GoParent() ) {
546                     break;
547                 }
548             }
549             break;
550         }
551     }
552     m_it->GoNode( pOldNode );
553     return bResult;
554 }
555 
556 bool
IsFirstChild() const557 CTaxTreeConstIterator::IsFirstChild() const
558 {
559     const CTreeContNodeBase* pOldNode = m_it->GetNode();
560     bool bResult = false;
561 
562     while( m_it->GoParent() ) {
563         if( IsVisible( m_it->GetNode() ) ) {
564             const CTreeContNodeBase* pParent = m_it->GetNode();
565             if( m_it->GoChild() ) {
566                 bResult = NextVisible(pParent) && m_it->GetNode() == pOldNode;
567             }
568             break;
569         }
570     }
571     m_it->GoNode( pOldNode );
572     return bResult;
573 }
574 
575 bool
IsTerminal() const576 CTaxTreeConstIterator::IsTerminal() const
577 {
578     const CTreeContNodeBase* pOldNode = m_it->GetNode();
579 
580     if( m_it->GoChild() ) {
581         bool bResult = NextVisible( pOldNode );
582         m_it->GoNode( pOldNode );
583         return !bResult;
584     }
585     return true;
586 }
587 
588 bool
NextVisible(const CTreeContNodeBase * pParent) const589 CTaxTreeConstIterator::NextVisible( const CTreeContNodeBase* pParent ) const
590 {
591     if( m_it->GetNode() == pParent ) {
592         return false;
593     }
594  next:
595     if( IsVisible( m_it->GetNode() ) ) {
596         return true;
597     }
598     if( m_it->GoChild() ) {
599         goto next;
600     } else if( m_it->GoSibling() ) {
601         goto next;
602     } else {
603         while( m_it->GoParent() && m_it->GetNode() != pParent ) {
604             if( m_it->GoSibling() ) {
605                 goto next;
606             }
607         }
608     }
609     return false;
610 }
611 
612 bool
GoParent()613 CTaxTreeConstIterator::GoParent()
614 {
615     const CTreeContNodeBase* pOldNode = m_it->GetNode();
616     bool bResult = false;
617     while( m_it->GoParent() ) {
618         if( IsVisible( m_it->GetNode() ) ) {
619             bResult = true;
620             break;
621         }
622     }
623     if( !bResult ) {
624         m_it->GoNode( pOldNode );
625     }
626     return bResult;
627 }
628 
629 bool
GoChild()630 CTaxTreeConstIterator::GoChild()
631 {
632     const CTreeContNodeBase* pOldNode = m_it->GetNode();
633     bool bResult = false;
634 
635     if( m_it->GoChild() ) {
636         bResult = NextVisible( pOldNode );
637     }
638     if( !bResult ) {
639         m_it->GoNode( pOldNode );
640     }
641     return bResult;
642 }
643 
644 bool
GoSibling()645 CTaxTreeConstIterator::GoSibling()
646 {
647     const CTreeContNodeBase* pOldNode = m_it->GetNode();
648     bool bResult = false;
649 
650     if( GoParent() ) {
651         const CTreeContNodeBase* pParent = m_it->GetNode();
652         m_it->GoNode( pOldNode );
653         while( m_it->GetNode() != pParent ) {
654             if( m_it->GoSibling() ) {
655                 bResult = NextVisible( pParent );
656                 break;
657             }
658             if( !m_it->GoParent() ) {
659                 break;
660             }
661         }
662         if( !bResult ) {
663             m_it->GoNode( pOldNode );
664         }
665     }
666     return bResult;
667 }
668 
669 bool
GoNode(const ITaxon1Node * pNode)670 CTaxTreeConstIterator::GoNode( const ITaxon1Node* pNode )
671 {
672     const CTreeContNodeBase* pTaxNode = CastIC( pNode );
673 
674     if( pNode && IsVisible( pTaxNode ) ) {
675         return m_it->GoNode( pTaxNode );
676     }
677     return false;
678 }
679 
680 bool
GoAncestor(const ITaxon1Node * pINode)681 CTaxTreeConstIterator::GoAncestor(const ITaxon1Node* pINode)
682 {
683     const CTreeContNodeBase* pNode = CastIC( pINode );
684     if( pNode && IsVisible( pNode ) ) {
685         const CTreeContNodeBase* pOldNode = m_it->GetNode();
686 
687         vector< const CTreeContNodeBase* > v;
688         do {
689             v.push_back( m_it->GetNode() );
690         } while( GoParent() );
691 
692         m_it->GoNode( pNode );
693         vector< const CTreeContNodeBase* >::const_iterator vi;
694         do {
695             vi = find( v.begin(), v.end(), m_it->GetNode() );
696             if( vi != v.end() ) {
697                 return true;
698             }
699         } while( GoParent() );
700         // Restore old position
701         m_it->GoNode( pOldNode );
702     }
703     return false;
704 }
705 
706 bool
BelongSubtree(const ITaxon1Node * pIRoot) const707 CTaxTreeConstIterator::BelongSubtree(const ITaxon1Node* pIRoot) const
708 {
709     const CTreeContNodeBase* pRoot = CastIC( pIRoot );
710     if( pRoot && IsVisible( pRoot ) ) {
711         const CTreeContNodeBase* pOldNode = m_it->GetNode();
712         do {
713             if( IsVisible( m_it->GetNode() ) ) {
714                 if( m_it->GetNode() == pRoot ) {
715                     m_it->GoNode( pOldNode );
716                     return true;
717                 }
718             }
719         } while( m_it->GoParent() );
720         m_it->GoNode( pOldNode );
721     }
722     return false;
723 }
724 
725 // check if given node belongs to subtree pointed by cursor
726 bool
AboveNode(const ITaxon1Node * pINode) const727 CTaxTreeConstIterator::AboveNode(const ITaxon1Node* pINode) const
728 {
729     const CTreeContNodeBase* pNode = CastIC( pINode );
730     if( pNode == m_it->GetNode() ) { // Node is not above itself
731         return false;
732     }
733 
734     if( pNode && IsVisible( pNode ) ) {
735         const CTreeContNodeBase* pOldNode = m_it->GetNode();
736         m_it->GoNode( pNode );
737         do {
738             if( IsVisible( m_it->GetNode() ) ) {
739                 if( m_it->GetNode() == pOldNode ) {
740                     m_it->GoNode( pOldNode );
741                     return true;
742                 }
743             }
744         } while( m_it->GoParent() );
745         m_it->GoNode( pOldNode );
746     }
747     return false;
748 }
749 
750 bool
IsVisible(const CTreeContNodeBase * pNode) const751 CTreeLeavesBranchesIterator::IsVisible( const CTreeContNodeBase* pNode ) const
752 {
753     return pNode &&
754         ( pNode->IsRoot() || pNode->IsTerminal() ||
755           !pNode->Child()->IsLastChild() );
756 }
757 
758 bool
IsVisible(const CTreeContNodeBase * pNode) const759 CTreeBestIterator::IsVisible( const CTreeContNodeBase* pNode ) const
760 {
761     return pNode &&
762         ( pNode->IsRoot() || pNode->IsTerminal() ||
763           !pNode->Child()->IsLastChild() ||
764           !(pNode->IsLastChild() && pNode->IsFirstChild()) );
765 
766 }
767 
768 bool
IsVisible(const CTreeContNodeBase * pNode) const769 CTreeBlastIterator::IsVisible( const CTreeContNodeBase* pNode ) const
770 {
771     return pNode && ( pNode->IsRoot() ||
772                       !CastCI(pNode)->GetBlastName().empty() );
773 }
774 
775 /////////////////////////////////////////////////////////////////
776 //  CDomainStorage impl
777 //
CDomainStorage()778 CDomainStorage::CDomainStorage()
779     : m_id(0)
780 {
781 }
782 
783 void
AddField(int field_no,int val_type,const string & name)784 CDomainStorage::AddField( int field_no, int val_type, const string& name )
785 {
786     m_fields.insert( TFieldMap::value_type( name, field_no ) );
787     if( m_types.size() <= field_no ) {
788         m_types.resize(field_no+1);
789     }
790     m_types[field_no] = val_type;
791 }
792 
793 bool
HasField(const string & field_name) const794 CDomainStorage::HasField( const string& field_name ) const
795 {
796     return m_fields.find( field_name ) != m_fields.end();
797 }
798 
799 void
InsertFieldValue(int val_id,int str_len,const string & str)800 CDomainStorage::InsertFieldValue( int val_id, int str_len, const string& str )
801 {
802     vector< CDomainStorage::TValue >& val = m_values[val_id];
803     val.resize( val.size()+1 );
804     TValue& v = val.back();
805     v.m_int = str_len;
806     v.m_str = str;
807 }
808 
809 void
InsertFieldValue(int val_id,int value)810 CDomainStorage::InsertFieldValue( int val_id, int value )
811 {
812     InsertFieldValue( val_id, value, "" );
813 }
814 
815 int
FindValueIdByField(const string & fieldName,const string & searchstring) const816 CDomainStorage::FindValueIdByField( const string& fieldName, const string& searchstring ) const
817 {
818     TFieldMap::const_iterator ci = m_fields.find(fieldName);
819     if( ci != m_fields.end() ) {
820 	ITERATE( TValues, cj, m_values ) {
821 	    if( cj->second[ci->second].m_str == searchstring ) {
822                 return cj->first;
823             }
824         }
825     }
826     return kIllegalValue;
827 }
828 
829 int
FindValueIdByField(const string & fieldName,int fieldValue) const830 CDomainStorage::FindValueIdByField( const string& fieldName, int fieldValue ) const
831 {
832     TFieldMap::const_iterator ci = m_fields.find(fieldName);
833     if( ci != m_fields.end() ) {
834 	ITERATE( TValues, cj, m_values ) {
835 	    if( cj->second[ci->second].m_int == fieldValue ) {
836 		return cj->first;
837 	    }
838 	}
839     }
840     return kIllegalValue;
841 }
842 
843 int
FindFieldValueById(int value_id,const string & fieldName) const844 CDomainStorage::FindFieldValueById( int value_id, const string& fieldName ) const
845 {
846     TFieldMap::const_iterator ci = m_fields.find(fieldName);
847     TValues::const_iterator cj = m_values.find(value_id);
848     if( ci != m_fields.end() && cj != m_values.end() ) {
849 	return cj->second[ci->second].m_int;
850     }
851     return kIllegalValue;
852 }
853 
854 const string&
FindFieldStringById(int value_id,const string & fieldName) const855 CDomainStorage::FindFieldStringById( int value_id, const string& fieldName ) const
856 {
857     TFieldMap::const_iterator ci = m_fields.find(fieldName);
858     TValues::const_iterator cj = m_values.find(value_id);
859     if( ci != m_fields.end() && cj != m_values.end() ) {
860 	return cj->second[ci->second].m_str;
861     }
862     return kEmptyStr;
863 }
864 
865 END_objects_SCOPE
866 END_NCBI_SCOPE
867